.

iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
6
Software Development

Functional Programming in JS系列 第 3

Buzz Word 1 : Declarative vs. Imperative

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20200904/20106426bGnG71Oiho.jpg
前一篇算是站在一個 High Level 角度大略說明 FP 好處,這一篇開始會有一系列 Buzz Words for FP 的介紹,這些專有名詞也算是讓新手卻步的門檻,但其實沒有這麼難,我覺得比較像是用不同角度去解釋類似的事情。

Declarative 宣告式 vs. Imperative 命令式

Functional programming 是 Declarative Paradigm 的代表,邏輯為用較為抽象的程式碼,理解程式碼想要達到怎樣的目標,Function 之間不會互相共用 state 狀態,而 Imperative 代表 OOP 比較容易寫出狀態互相依賴的程式碼。
https://ithelp.ithome.com.tw/upload/images/20200903/20106426z5UUcte6kF.jpg

Imperative is How to do stuff

Imperative programming is a programming paradigm that uses statements that change a program's state. 

著重在 HOW,具體表達程式碼該做什麼才能達到目標,比較像我以前撰寫程式的思維,程式一步一步按著順序照著你給他指示執行。Imperative 比較常運用 Statement ,也就是 if , while , for , switch 等。

  • Example: OOP
  • Stateful
  • Can mutate state
  • 常會有 Side Effect 發生

Declarative is What's stuff

Declarative programming is a programming paradigm that expresses the logic of a computation without describing its control flow.

著重在該做什麼 WHAT ,並採取抽象化流程。Declarative 比較常運用表達式 expression,表達式特色是單純運算並一定會有返回值

  • Example: FP
  • Stateless: 你只要專心於當前算式,結束後再也不用管他
  • Constants, immutability
  • No Side Effect

舉一個例子,將 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
  • 輸出個別字串長度 > 3 字元的字串 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.


參考文章

如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您

歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。


上一篇
為什麼要學 Functional Programming?
下一篇
Do Everything with Function
系列文
Functional Programming in JS30
.
圖片
  直播研討會

2 則留言

1
listennn08
iT邦高手 5 級 ‧ 2020-09-03 08:54:56

例子的寫法不太對

const words = lower.split(' ') // ['welcome', 'to', 'fp', 'world ']
這裡會是產出 ['welcome', 'to', 'fp', 'world', '']

另外筆誤的地方

return keepers.join('-') // 'world welcome'

寫文章辛苦了/images/emoticon/emoticon32.gif

hannahpun iT邦新手 3 級 ‧ 2020-09-03 10:54:25 檢舉

真的是筆誤(汗),已更新,謝謝

8
良葛格
iT邦新手 2 級 ‧ 2020-09-04 08:01:08

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 中,算是比較高階的抽象了,以上例來說,若你將 sunhead + 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
)

雖然只是寫作上的一個技巧,不過應該可以更突顯你想表達的意思。

我要留言

立即登入留言