天天看點

準備充分了嘛就想學函數式程式設計?(Part 3)

<b>本文講的是準備充分了嘛就想學函數式程式設計?(Part 3),</b>

<b></b>

準備充分了嘛就想學函數式程式設計?(Part 3)

作為程式員,懶惰是我們的美德。我們不想不斷重複地建構,測試,部署寫過的代碼。

我們希望有辦法可以一處寫完,各處複用。

代碼複用聽起來很棒,實作起來很困難。如果代碼寫的非常明确,就不能複用。太泛化的話,最開始用都困難。

是以我們需要權衡,有種方案是寫簡短可複用的代碼,我們可以将它們當作零件用來組合成更複雜的代碼。

在函數式程式設計中,函數就是我們的零件。我們可以用它們來完成指定的任務,再像樂高積木一樣拼湊在一起。

這被稱作函數組合。

該怎麼實作呢,讓我們從兩個 JavaScript 函數開始:

好多了,現在設想我們再需要一個函數,它可以接受一個值作為參數,将值加 10,再乘以 5,把結果傳回。我們可以寫成:

盡管這隻是一個很簡單的需求,我們仍不想重新寫一個新的函數。首先,我們可能會因為忘記括号而出錯。

其次,我們已經有一個函數可以将值加上 10,另一個函數将值可以乘以 5。我們這是在重複之前的工作。

是以,我們用 add10 和 mult5 來建構我們的新函數:

我們可以用現有的函數來建立 mult5AfterAdd10 函數,但其實有更好的辦法。

在數學中, f ∘ g 是函數的組合,是以讀作 “函數 f 與函數 g 的複合函數”,或者更通用的說法是 “在 g 之後調用 f”。是以 (f ∘ g)(x) 相當于先以 x 為自變量調用函數 g,再以結果為自變量調用函數 f,簡寫成 f(g(x))。

對于我們的例子,我們可以用 mult5 ∘ add10 或者 “mult5 after add10” 來表示,是以我們的函數名叫mult5(add10(value)) 。

在 Elm 中,你可以用插入運算符 &lt;&lt; 來組合函數。這在帶給我們一種資料是怎樣流動的視覺效果。首先,value 傳入 add10中,再将結果作為參數傳入 mult5 中。

注意 mult5AfterAdd10 中的括号,比如 (mult5 &lt;&lt; add10)。這裡是說明函數是先組合,再傳入參數 value 的。

你可以用這種方式随意組合函數:

這裡 x 傳入函數 t ,将運算結果傳給函數 r,然後再将結果傳給函數 s,這樣一直進行下去。如果你要在 JavaScript 裡實作類似的功能,看起來會是這樣 g(h(s(r(t(x))))),括号的惡夢。

準備充分了嘛就想學函數式程式設計?(Part 3)

有一種可以不需要指定參數的函數寫法,叫做 Point-Free 表示法。開始時,這種風格看起來有些奇怪,随着使用繼續,你會開始欣賞它帶來的簡潔性。

你可以注意到,在 mult5AfterAdd10 裡我們有兩處用到 value 變量。一處是在參數清單中,一處是内部使用時。

其實這個參數并不是必須的,因為 add10,組合中最外側的函數,和函數組合接受的參數相同。與下面的 point-free 版本是等價的:

用這種 point-free 風格表示法有很多好處。

首先,我們不需要指定多餘的參數。因為我們不要明确指定它們,我們可以不用去費心給它們起名字。

其次,因為更簡潔,閱讀和了解起來也更容易。這個例子非常簡單,但是想象一下如果函數有很多參數的情況。

準備充分了嘛就想學函數式程式設計?(Part 3)

到目前為止,我們已經見過函數組合是怎樣工作的,我們如何用 Point-Free 風格的寫法來提高代碼的簡潔性,清晰性和靈活性。

現在讓我們嘗試在稍微不同的場景中運用這些思想。設想我們用 add 替換 add10:

我們如何隻用這兩個函數來組合 mult5After10 呢?

繼續讀之前請先思考這個問題,想一想,試着做一做。

好,如果你真的花時間想了,也許你會想到這樣的方案:

但是這不行,為什麼?因為 add 函數需要兩個參數。

用 Elm 也許不明顯,可以用 JavaScript 寫:

這段代碼是錯的,為什麼?

因為在這裡 add 函數隻接受了兩個參數中的一個,然後錯誤結果再被傳入 mult5 函數,結果也是錯的。

實際上,在 Elm 中,編譯器不會讓你寫出這種殘缺的代碼( Elm 的優點之一)

這樣不是 point-free 的,但可用。但是現在不再是函數組合了,我寫了一個新的函數。另外,如果函數更複雜,例如,如果我想用 mult5AfterAdd10 與其他函數組合,那就會很麻煩。

可見函數組合的可用性有限,因為我們不能将這兩個函數結合在一起。太糟了。

我們怎樣解決這個問題呢?我們需要做什麼?

如果我們可以找到一種方法可以讓 add 函數先接受一個參數,再在後面調用 mult5AfterAdd10 時接受第二個參數就太棒了。

真的有這樣一種方法,叫做 柯裡化 。

準備充分了嘛就想學函數式程式設計?(Part 3)

到目前暫時足夠消化一段了。

在文章接下來的部分裡,我會涉及到柯裡化,函數式程式設計中常見的函數(如 map,filter,fold 等),參照透明性等。

<b>原文釋出時間為:2016年11月17日</b>

<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>

繼續閱讀