函數(shù)式編程
1.什么是函數(shù)式編程?
functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data
來自維基百科的定義,解釋道:
函數(shù)式編程是一種編程范式用以構建計算機程序結構和元素,用數(shù)學函數(shù)的標準來評估程序的運算,避免改變狀態(tài)和可變數(shù)據(jù)。
從定義我們可以get到兩個點:
- 數(shù)學函數(shù)的標準是什么標準?
- 為什么要避免狀態(tài)和可變數(shù)據(jù)?
1.1 數(shù)學函數(shù)的標準
在數(shù)學中,函數(shù)為兩集合間的一種對應關系:輸入值集合中的每項元素皆能對應唯一一項輸出值集合中的元素。例如實數(shù)x對應到其平方x*x的關系就是一個函數(shù),若以3作為此函數(shù)的輸入值,所得的輸出值便是9。
這一定義的關鍵詞為【唯一】,表示著如果我有一個函數(shù)y=f(x),對于唯一確定的x,我們必定能得到唯一的y。
這一定義又與我們react中常聽到的"Pure functions"(純函數(shù))類似,我們用來判定一個函數(shù)是否為純函數(shù):
- 如果給出相同的參數(shù),它返回相同的結果
- 它不會引起任何可觀察到的副作用
Tips: 函數(shù)副作用指當調(diào)用函數(shù)時,除了返回函數(shù)值之外,還對主調(diào)用函數(shù)產(chǎn)生附加的影響。例如修改全局變量(函數(shù)外的變量)或修改參數(shù)
1.2 為什么要避免狀態(tài)和可變數(shù)據(jù)?
首先,我們來看看一個函數(shù)引起狀態(tài)和數(shù)據(jù)改變的情況。
function fn() {
let num = 100;
return function () {
return num - 1;
}
}
f = fn();
f();
f();
上面這段程序大家都很熟悉,典型的閉包,每次調(diào)用f(),函數(shù)都會在上一次調(diào)用的基礎上執(zhí)行減1操作,很多時候編程中我們需要使用到這樣的技巧。
這就是一個典型的函數(shù)引起數(shù)據(jù)改變的例子。乍一看沒啥問題,那么如果我們是處于一個并發(fā)操作高的環(huán)境呢。
如果有100個人,同時請求執(zhí)行這段代碼,我們能預想到實際執(zhí)行返回結果嗎?
很顯然,不能,在服務端經(jīng)常會遇到這種情況,我們常見的解決方案就是【加鎖】,一次僅執(zhí)行一次,那么執(zhí)行結果就變得可控了。
但是,函數(shù)式編程確能很好的解決這個問題,函數(shù)式編程的主要思想就是,對于一個確定的操作,我返回唯一的結果,從而保證有跡可循。
通常,我們改變數(shù)據(jù)和狀態(tài)的方式是通過賦值。那么可想而知,我們可以大膽地推測函數(shù)式編程的核心規(guī)則是【避免賦值】。在另一方面,Immutable.js保證數(shù)據(jù)一旦創(chuàng)建,就不能再被更改,也避免了狀態(tài)和數(shù)據(jù)的改變。
2.正宗的函數(shù)式編程語言——Lisp
Lisp是正宗的函數(shù)式編程語言,了解函數(shù)式我們不得不究其根源了解一下Lisp。
2.1 Lisp的基本語法
2.1.0 Lisp環(huán)境
首先,您需要獲得一個LISP解釋器(GNU Common Lisp)
Windows 用戶安裝完了之后,您只需從開始菜單中找到它并單擊以運行就可以了。
Linux 用戶安裝完之后,在終端運行 gcl 即可啟動LISP編譯環(huán)境。
下文中的 > 號表示開始輸入指令
2.1.1 表達式(算數(shù)表達式)
> (+ 1 2) // 3
> (+ 1 2 3 4) // 10
> (+ (*3 3) (* 4 4) ) // 25
復雜一點的表達式
(+ (*3 (+ (* 2 4) (+3 5) ) ) (+ (-10 7) 6 ) ) // ???
Lisp的求值規(guī)則:
- 求各子表達式的值(右值)
- 將所有右值應用到運算符上就得最終結果
2.1.2 命名(定義)
> (define size 5)
> (* size 2) // 10
> (define (add a b) (+ a b))
> (add 1 2) // 3
2.1.3 if
> (define (abs x)
(if (< x 0)
(- x)
x
)
)
2.1.4 遞歸&迭代(以階乘為例)
遞歸
(define (factorial n)
(if (=n 1)
1
(* n (factorial (- n 1))
)
)
迭代
> (define (factorial n)
(fact-iter 1 1 n)
)
> (define (fact-iter result n n-max)
(if (> n max-n)
result
(fact-iter
(* n result)
(+ n 1)
(max -n)
)
)
- 遞歸: 先遞進(展開),再回歸(求值)
- 迭代: 從一個狀態(tài)到下一個狀態(tài)(有多個變量表示狀態(tài),每次更新這幾個變量)
2.1.5 more
更多精彩,盡在無盡的探索中ANSI Common Lisp 中文翻譯版