
前一篇算是站在一個 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
)
雖然只是寫作上的一個技巧,不過應該可以更突顯你想表達的意思。