Functional Programming
解決各自的小問題就解決了大問題
FP的好處
大師們怎麼說?
FLOLAC’14 唐鳳 函數設計程式的商業用途 part 1
FLOLAC’14 唐鳳 函數設計程式的商業用途 part 2
FLOLAC’14 唐鳳 函數設計程式的商業用途 part 3
那些 Functional Programming 教我的事 ihower
閃亮亮 Refactoring to Collections , 從陣列重構談物件導向程式設計
小節: FP的好處有
- 多線程程式很難寫,原因是都有
mutable shared state
,純粹的FP中只有immutable
變數,本身就滿足thread-safe
,提供了多線程程式良好的基底
function可以當作參數傳遞,function也可以當作function的回傳值
,很容易基於數學理論寫出更多靈活的函數組合,如curry和合成函數,達到lazy evaluation並省下資源
- 操作collection程式碼可讀性佳
Immutable變數
使用Object.freeze
ES5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| var o = Object.freeze({ foo: { greeting: 'Hello' }, bar: 'world', baz: '!' }) o.foo.greeting = 'Goodbye' o.bar = 'Ben' console.log(o.foo.greeting + ', ' + o.bar + o.baz) o = 5
|
ES6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const o = Object.freeze({ foo: { greeting: 'Hello' }, bar: 'world', baz: '!' }) o.foo.greeting = 'Goodbye' o.bar = 'Ben' console.log(`${ o.foo.greeting }, ${ o.bar }${o.baz}`) o = 5
|
都還是有地方能更動,怎麼辦?
Immutable.js來拯救
Function當參數 / Function當回傳
Function當參數的常見場景
- setTimeout / setInterval
- 後端API
- Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| setTimeout(function () { console.log(new Date().getTime()) }, 1000) router.get('/wallet/addresses', function (req, res, next) { ... }) let myFirstPromise = new Promise((resolve, reject) => { setTimeout(function(){ resolve("Success!") }, 250) }) myFirstPromise.then((successMessage) => { console.log("Yay! " + successMessage) })
|
Function當回傳值
最經典的案例是柯里化(Curry)和合成函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var add = function (x) { return function (y) { return x + y } } var increment = add(1) var addTen = add(10) increment(2) addTen(2)
|
合成函數
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var compose = function (f, g) { return function(x) { return f(g(x)) } } var toUpperCase = function (x) { return x.toUpperCase() } var exclaim = function (x) { return x + '!' } var shout = compose(exclaim, toUpperCase) shout("send in the clowns")
|
合成函數的結合率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| var compose = function(f, g) { return function(x) { return f(g(x)) } } var toUpperCase = function(x) { return x.toUpperCase() }; var head = function(x) { return x[0] } var reverse = function(x) { return x.reverse() } var test1 = compose(toUpperCase, compose(head, reverse)) var test2 = compose(compose(toUpperCase, head), reverse) console.log(test1(['jumpkick', 'roundhouse', 'uppercut'])) console.log(test2(['jumpkick', 'roundhouse', 'uppercut']))
|
相關的FP library:
- lodash
- ramdajs
- underscorejs
操作Collection程式碼可讀性佳
幾個常用FP方法
- map()
- filter()
- reduce()
練習題
找出[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
中
每個元素乘以3後,大於10且小於20,可以被2整除中,最大值是?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] var threeTimes = [] var inInterval = [] var dividedByTwo = [] var max = 0; for (var i = 0; i < array.length; i++) { threeTimes.push(array[i] * 3) } for (i = 0; i < threeTimes.length; i++) { if ((threeTimes[i] > 10) && (threeTimes[i] < 20)) { inInterval.push(threeTimes[i]) } } for (i = 0; i < inInterval.length; i++) { if ((inInterval[i] % 2) === 0) { dividedByTwo.push(inInterval[i]) } } for (i = 0; i < dividedByTwo.length; i++) { if (max < dividedByTwo[i]) { max = dividedByTwo[i] } } console.log(max)
|
邏輯一多就很難一眼看出要做什麼
FP來拯救!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] var max = array.map((number) => { return number * 3 }).filter((number) => { return number > 10 && number < 20 }).filter((number) => { return number % 2 === 0 }).reduce((target, next) => { return Math.max(target, next) }) console.log(max)
|
是不是一目瞭然!
這樣的做法很常出現在後端API從資料庫中取出collection後做資料再處理
檢驗這樣的程式是不是Immutable
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] var max = array.map((number) => { array = [0] return number * 3 }).filter((number) => { return number > 10 && number < 20 }).filter((number) => { return number % 2 === 0 }).reduce((target, next) => { return Math.max(target, next) }) console.log(max)
|
使用const更嚴謹
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] const max = array.map((number) => { return number * 3 }).filter((number) => { return number > 10 && number < 20 }).filter((number) => { return number % 2 === 0 }).reduce((target, next) => { return Math.max(target, next) }) console.log(max)
|
其他FP延伸閱讀資料
Gitbook