什么是函數式編程
函數式編程(Functional Programming, FP),FP 是編程范式之一,我們常聽說的編程范式還有面向過程
編程、面向對象編程。
- 面向對象編程的思維方式:把現實世界中的事物抽象成程序世界中的類和對象,通過封裝、繼承和
多態來演示事物事件的聯系 - 函數式編程的思維方式:把現實世界的事物和事物之間的聯系抽象到程序世界(對運算過程進行抽
象)- 程序的本質:根據輸入通過某種運算獲得相應的輸出,程序開發過程中會涉及很多有輸入和
輸出的函數 - x -> f(聯系、映射) -> y,y=f(x)
- 函數式編程中的函數指的不是程序中的函數(方法),而是數學中的函數即映射關系,例如:y = sin(x),x和y的關系
- 相同的輸入始終要得到相同的輸出(純函數)
- 函數式編程用來描述數據(函數)之間的映射
- 程序的本質:根據輸入通過某種運算獲得相應的輸出,程序開發過程中會涉及很多有輸入和
特點
函數式編程具有五個鮮明的特點。
1. 函數是"第一等公民"
所謂"第一等公民"(first class),指的是函數與其他數據類型一樣,處于平等地位,可以賦值給其他變量,也可以作為參數,傳入另一個函數,或者作為別的函數的返回值。
舉例來說,下面代碼中的print變量就是一個函數,可以作為另一個函數的參數。
var print = function(i){ console.log(i);};
[1,2,3].forEach(print);
2. 只用"表達式",不用"語句"
"表達式"(expression)是一個單純的運算過程,總是有返回值;"語句"(statement)是執行某種操作,沒有返回值。函數式編程要求,只使用表達式,不使用語句。也就是說,每一步都是單純的運算,而且都有返回值。
原因是函數式編程的開發動機,一開始就是為了處理運算(computation),不考慮系統的讀寫(I/O)。"語句"屬于對系統的讀寫操作,所以就被排斥在外。
當然,實際應用中,不做I/O是不可能的。因此,編程過程中,函數式編程只要求把I/O限制到最小,不要有不必要的讀寫行為,保持計算過程的單純性。
3. 沒有"副作用"
所謂"副作用"(side effect),指的是函數內部與外部互動(最典型的情況,就是修改全局變量的值),產生運算以外的其他結果。
函數式編程強調沒有"副作用",意味著函數要保持獨立,所有功能就是返回一個新的值,沒有其他行為,尤其是不得修改外部變量的值。
4. 不修改狀態
上一點已經提到,函數式編程只是返回新的值,不修改系統變量。因此,不修改變量,也是它的一個重要特點。
在其他類型的語言中,變量往往用來保存"狀態"(state)。不修改變量,意味著狀態不能保存在變量中。函數式編程使用參數保存狀態,最好的例子就是遞歸。下面的代碼是一個將字符串逆序排列的函數,它演示了不同的參數如何決定了運算所處的"狀態"。
function reverse(string) {
if(string.length == 0) {
return string;
} else {
return reverse(string.substring(1, string.length)) + string.substring(0, 1);
}
}
由于使用了遞歸,函數式語言的運行速度比較慢,這是它長期不能在業界推廣的主要原因。
5. 引用透明
引用透明(Referential transparency),指的是函數的運行不依賴于外部變量或"狀態",只依賴于輸入的參數,任何時候只要參數相同,引用函數所得到的返回值總是相同的。
有了前面的第三點和第四點,這點是很顯然的。其他類型的語言,函數的返回值往往與系統狀態有關,不同的狀態之下,返回值是不一樣的。這就叫"引用不透明",很不利于觀察和理解程序的行為。
高階函數
高階函數 (Higher-order function)
- 可以把函數作為參數傳遞給另一個函數
- 可以把函數作為另一個函數的返回結果
- 函數作為參數
常用高階函數
- forEach
- map
- filter
- every
- some
- find/findIndex
- reduce
- sort
純函數
- 純函數:相同的輸入永遠會得到相同的輸出,而且沒有任何可觀察的副作用
- 函數式編程不會保留計算中間的結果,所以變量是不可變的(無狀態的)
- 我們可以把一個函數的執行結果交給另一個函數去處理
純函數的好處
- 可測試
- 并行處理:純函數不需要訪問共享的內存數據,所以在并行環境下可以任意運行純函數 (Web Worker)
柯里化
柯里化 (Currying):
當一個函數有多個參數的時候先傳遞一部分參數調用它(這部分參數以后永遠不變)
然后返回一個新的函數接收剩余的參數,返回結果
函子
- 容器:包含值和值的變形關系(這個變形關系就是函數)
- 函子:是一個特殊的容器,通過一個普通的對象來實現,該對象具有 map 方法,map 方法可以運
行一個函數對值進行處理(變形關系)
函子的意義
- 函數式編程的運算不直接操作值,而是由函子完成
- 函子就是一個實現了 map 契約的對象
- 我們可以把函子想象成一個盒子,這個盒子里封裝了一個值
- 想要處理盒子中的值,我們需要給盒子的 map 方法傳遞一個處理值的函數(純函數),由這
個函數來對值進行處理 - 最終 map 方法返回一個包含新值的盒子(函子)
常見函子
- Maybe
- Either
- IO
- Point
- Monad