Functional Programming 系列(二)- 純函式(Pure Function)

00如是說
Coding Fighter
Published in
5 min readDec 1, 2020
Photo by James Ahlberg on Unsplash

安安大家!這次想跟大家分享的是「純函式」,相信第一次聽到這個名詞的人,心理想的一定是

???

函式就函式,還有什麼純不純的嗎?不純的是中毒了是不是。

不要急,我們繼續往下看就知道了。

纯函式(Pure Function)

  • 無狀態(Statelessness):對於一個函式,應該要保持不管何時都是同輸入、同輸出
  • 無副作用(Not Side Effect):指的就是不要去修改入参或是外部的變數

無狀態

同輸入、同輸出,這兩個詞聽起來很模糊,我舉兩個 Array 的方法給大家看看:

可以看到在相同的參數下,slice 方法不管呼叫幾次都是相同的結果,但 splice 方法就不同了,他會去破壞你輸入的陣列,導致你每一次得到的結果都不同,這裡很明顯地看出

slice 符合無狀態,但 splice 並沒有。

無副作用

修改外部變數比較直觀就不多說,那不能修改入參的原因是什麼?如果有用過 Eslint 的人,對下方這張圖應該不陌生:

no-param-reassign

如果是第一次遇到這個錯誤提示的人,可能會覺得疑惑,為什麼不能這樣做呢?

這裡我先將 Eslint 的該 Rule 屏蔽掉,並寫兩個完整的對應例子來看看:

遵循 Eslint 規則

違背 Eslint 規則

嗯...都一樣不是嗎?

的確,以上述例子來說是不會有什麼影響的,但如果今天我們改變的是一個物件的 Property 呢?

這裡我一樣寫兩個完整的對應例子來看看:

遵循 Eslint 規則

違背 Eslint 規則

看出端倪了嗎?我的外部變數竟然跟著被改變了 😱,為什麼會有這種情況呢?

問題就出在 Call By Reference 跟 Call By Value 。

如果沒有聽過的可以先看看以下這篇:

我們假設一下,今天有個幾百行的 Code 需要你去 Debug,結果在之中藏了類似的 Code,導致你在外部的變數莫名被改變了,這的確很讓人困擾對吧!尤其原本的 Code 不是你寫的,或是你很久以前寫的,根本沒辦法馬上料想到問題在哪裡,因此我們才必須避免這種情況發生。

上面所說的,主要是介紹要符合 Pure Function 必須滿足的條件。接下來,想說說除了利於維護之外,還能夠為開發帶來什麼變化。

對我個人感受比較明顯的有以下幾點:

  • 可預測性:因為無狀態的關係,所以可以預測該 Function 的結果。
  • 可緩存:由於可預測,因此可以把一些事先知道的結果緩存起來,這點我自己其實也是接觸到 FP 才發現:「哇!原來還可以這樣做」,我在這也附上一個簡單的 memorize 函式給大家看看:

下面這個是一個斐波那契數列(費氏數列),怕有人看不懂這段 code 主要在做什麼,我大概解釋一下什麼是斐波那契數列。

這個數列指的就是從 0 跟 1 開始,之後的每個數字都是前面兩個數的加總,所以大致上會長這樣

0、1、1、2、3、5、8、13、21、34、55...以此類推。

從上面可以看到我如果只有調用 fibonacci(10) 的話,在取前面每個數字時,都會進 function 做運算,但我如果在之前有調用過 fibonacci(6) 的話,接下來調用 fubonacci(10) 的時候可以發現,數列索引 6 以下的不會再進行運算,而是直接從緩存資料拿,因為已經計算過並且緩存起來了。

可以試想一下,如果是要做大筆資料的運算,對效能是多大的提升!!

但是!!但是!!對,凡事都有但是

原諒我對這緩存方法解釋如此之長,但我一定要強調,在做 memorize 的時候,一定要注意

內存洩漏

內存洩漏

內存洩漏

很重要所以講三遍,要不是不能選顏色,我一定用紅字標起來 😩。

因為透過 Closure 變數的關係,記憶體一直沒有釋放,越積越多 (尤其是 SPA 網站)就會造成網頁卡頓,我不希望有人因為看了我的文章之後,興高采烈地想幫公司網頁優化一下,結果某天老闆問你說:

「客戶說瀏覽我們的網頁手機會越來越燙是正常的嗎?」

結果後來發現是這個問題之後,老闆 7 pu pu 地問你說是誰教你的?

然後你就說:「哦!是看了那個00的文章學的啦!」

之後我就成為了教人怎麼用網頁搞爛客戶設備的技術作家

拜託不要,我才剛開始寫文章沒多久 😭😭😭。

那你可能會問說:「那這個還有什麼用處嗎?又不能使用。」

當然不是不能使用,是你要用對方法。

這裡建議你要使用的時候,要做 limited,也就是必須實作緩存上限,超過了就要把舊的清掉,才不會造成上述問題。

  • 可並行運算:由於不依賴外部變數的關係,因此可使用像是 Web Worker (有機會會寫一篇文章介紹)進行多線程運算,如果沒有不依賴外部變數這個特性的話,可能會因為執行順序不同,導致你每次拿到的變數不一樣,因為這個變數在其他地方被修改掉了。
  • 可組合性:因為同輸入一定同輸出,組合成的函式也一定是如此。如果你今天是用一堆 Impure Function 去組合的話,很有可能會造成輸出的結果不固定,而你也不好找出是當中的哪一個 Function 造成的。
  • 提升等級:讓人覺得你寫的東西乾乾淨淨的,一定是高手。(沒啦!我亂說的XD

以上是我這次分享的內容,如果有認為我說錯或是想跟我討論的歡迎留言給我哦!

--

--