前一篇算是站在一個 High Level 角度大略說明 FP 好處,這一篇開始會有一系列 Buzz Words for FP 的介紹,這些專有名詞也算是讓新手卻步的門檻,但其實沒有這麼難,我覺得比較像是用不同角度去解釋類似的事情。
Functional programming 是 Declarative Paradigm 的代表,邏輯為用較為抽象的程式碼,理解程式碼想要達到怎樣的目標,Function 之間不會互相共用 state 狀態,而 Imperative 代表 OOP 比較容易寫出狀態互相依賴的程式碼。
Imperative programming is a programming paradigm that uses statements that change a program's state.
著重在 HOW,具體表達程式碼該做什麼才能達到目標,比較像我以前撰寫程式的思維,程式一步一步按著順序照著你給他指示執行。Imperative 比較常運用 Statement ,也就是 if
, while
, for
, switch
等。
Declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.
著重在該做什麼 WHAT ,並採取抽象化流程。Declarative 比較常運用表達式 expression,表達式特色是單純運算並一定會有返回值
舉一個例子,將 Array 加總
// Imperative is How to do stuff
var array = [3,2,1]
var total = 0
for( var i = 0 ; i <= array.length ; i ++ ){
total += array[i]
}
可以看到上面是一個運算的過程,著重 "如何" 達到預期結果!
// Declarative is What's stuff
var array = [3,2,1]
array.reduce( function( previous, current ){
return previous + current
})
Declarative 則是可以直接知道做了 "什麼",恩,看不太出來 Declarative 的好處啊?
我再舉一個更複雜的例子,今天有一個函式 doStuff
,input 是一個字串 'Welcome to FP world '
,需要
welcome to fp world
welcome world
world welcome
// Imperative is How to do
// 'Welcome to FP world'
const doStuff = str => {
const lower = str.toLowerCase() // 'welcome to fp world'
const words = lower.split('') // ['welcome', 'to', 'fp', 'world']
words.reverse() // ['world', 'fp', 'to', 'welcome']
for(let i in words) {
words[i] = words[i].trim()
} // ['world', 'fp', 'to', 'welcome']
let keepers = []
for(let i in words) {
if(words[i].length > 3) {
keepers.push(words[i])
} // ['world', 'welcome']
}
return keepers.join(' ') // 'world welcome'
}
doStuff('Welcome to FP world')
以上可以看到他照著需求一步一步達到目標,中間也是用到很多 statement 跟共用 state (eg. keepers
)。
// Declarative is What's xxx
// 'Welcome to FP world'
const doStuff = _.compose(
join(' '),
_.filter(x => x.length > 3),
reverse,
_.map(trim),
split(' '),
toLowerCase
)
換成 FP 就變得相當易讀。
Note. 感謝 Mike 與 良葛格 在為什麼要學 Functional Programming? 留言中幫我釐清,並不是要屏棄 OOP 直接投入 FP,而是清楚他們的優缺點後,再套用到合適的專案,甚至也可以擷取各自優點兩個一起用。
這個表格是參考這邊,用不同面向來比較兩種 paradigm,自己覺得蠻有幫助所以就附註在這裡。
BASIS | FP | OOP
------------- | -------------
Data | Uses immutable data. | Uses mutable data.
Model | Follow a declarative programming model. | Follow an imperative programming model.
Execution | The statements can be executed in any order. | The statements should be executed in particular order.
Iteration | Recursion is used for iterative data. | Loops are used for iterative data.
Element | The basic elements are Variables and Functions. | The basic elements are objects and methods.
Use | Often be used when there are few things with more operations. | Often be used when there are many things with few operations.
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。
例子的寫法不太對
const words = lower.split(' ') // ['welcome', 'to', 'fp', 'world ']
這裡會是產出 ['welcome', 'to', 'fp', 'world', '']
另外筆誤的地方
return keepers.join('-') // 'world welcome'
寫文章辛苦了
真的是筆誤(汗),已更新,謝謝
Declarative is What to do 建議改為 Declarative is What's xxx ... 也就是宣告 xxx 是什麼。
類似地,「Declarative 則是可以直接知道做了 "什麼"」建議改為「Declarative 則是定義 xxx 是 "什麼"」
將 Array 加總的例子,建議可以是:
function sum(array) {
// 做加總必須累加
let total = 0
for(let i = 0 ; i < array.length ; i ++ ){
total += array[i]
}
return total;
}
console.log(sum([3, 2, 1]));
function sum(array) {
// 空陣列,總和就是 0
if(array.length === 0) {
return 0;
}
let head = array[0];
let tail = array.slice(1);
// 總和就是陣列第一項的值與餘下陣列的總和
return head + sum(tail);
}
console.log(sum([3, 2, 1]));
reduce
本身在 FP 中,算是比較高階的抽象了,以上例來說,若你將 sun
的 head + sum(tail)
部份,想辦法再抽離出來,就會朝著 reduce
的方向重構,因此 reduce
的運用通常會封裝為函式,突顯其意圖,例如:
function sum(array) {
return array.reduce((accumulator, elem) => accumulator + elem);
}
console.log(sum([3, 2, 1]));
這段範例:
// Imperative is How to do
// 'Welcome to FP world'
const doStuff = str => {
...
}
doStuff('Welcome to FP world')
改一下註解部份:
// Imperative is How to do stuff
// 'Welcome to FP world'
const doStuff = str => {
...
}
doStuff('Welcome to FP world')
這段範例:
// Declarative is What to do
// 'Welcome to FP world'
const doStuff = _.compose(
join(' '),
_.filter(x => x.length > 3),
reverse,
_.map(trim),
split(' '),
toLowerCase
)
建議改一下註解與命名:
// Declarative is What's stuff
// 'Welcome to FP world'
const stuff = _.compose(
join(' '),
_.filter(x => x.length > 3),
reverse,
_.map(trim),
split(' '),
toLowerCase
)
雖然只是寫作上的一個技巧,不過應該可以更突顯你想表達的意思。