點燈坊

失くすものさえない今が強くなるチャンスよ

使用 reduce() 取代 For Loop

Sam Xiao's Avatar 2020-01-15

reduce() 為 FP 代表性 Function,與 Array.prototype.reduce() 功能相同;唯內建的 reduce() 是以 OOP 掛在 Array 上,而 Ramda 的 reduce() 是以 FP 將 Array 傳入最後一個參數。

Version

macOS Mojave 10.15.2
VS Code 1.41.1
Quokka 1.0.274
Ramda 0.26.1

Imperative

let data = [30, 80, 20, 40, 25]

let reduce = fn => init => arr => {
  let a = init

  for(let x of arr)
    a = fn(a, x)

  return a
}

let reducer = (a, x) => a >= x ? a - x : a
reduce(reducer)(100)(data) // ?

帳戶內有 100 元,data 為陸續所提出的金額。

  • 若提出後 餘額 大於等於 0,則 提出該金額,並繼續下一筆
  • 若提出後 餘額 小於 0,則 不可 提出該金額,並繼續下一筆
  • 最後回傳帳戶剩餘金額

Imperative 會使用 for loop,以初始值 init 建立好回傳的 a,並使用傳入的 function 計算結果,最後回傳 a 結束執行。

reduce000

reduce()

let data = [30, 80, 20, 40, 25]

let reduce = fn => init => arr => arr.reduce(fn, init)

let reducer = (a, x) => a >= x ? a - x : a
reduce(reducer)(100)(data) // ?

Array.prototype 已經內建 reduce(),可直接使用。

reduce001

Ramda

import { reduce } from 'ramda'

let data = [30, 80, 20, 40, 25]

let reducer = (a, x) => a > x ? a - x : a
reduce(reducer)(100)(data) // ?

Ramda 也提供了 reduce(),可使其 point-free。

reduce()
((a, b) → a) → a → [b] → a
將 array 轉變為單一 value 或 object

((a, b) → a):reducer function,運算 accumulator 與 value 關係

a:accumulator,每個 iteration 的累計值

[b]:data 為 array

a:最後累計值

reduce002

Conclusion

  • 很多初學者視 reduce() 為畏途,其實只要自己用 imperative 實作一次 reduce(),就可發現它只是將重複的部分抽成 higher order function,不重複部分以 function 傳入而已
  • 只要 for loop 寫的出來,理論上 reduce() 都寫得出來,但應優先使用 higher order function 組合,除非真的組合不出來,最後才使用 reduce()
  • Ramda 的 reduce()Array.prototype.reduce() 相同,差異只在 data 位置而已

Reference

MDN, Array.prototype.reduce()
Ramda, reduce()