點燈坊

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

使用 zipFunc() 將 Array 中 Function 套用到另一 Array

Sam Xiao's Avatar 2020-01-08

若想將 Array 中一系列 Function 套用到另一 Array,Ramda 並沒有提供適當 Function,我們可自行組合出 zipFunc()

Version

macOS Mojave 10.15.2
VS Code 1.41.1
Quokka 1.0.271
Ramda 0.26.1
Wink-fp 1.20.44

Imperative

import { inc, dec, negate } from 'ramda'

let data1 = [inc, dec, negate]
let data2 = [1, 2, 3]

let zipFunc = fns => arr => {
  let result = []

  for (let i = 0; i < arr.length; i++) {
    result[i] = fns[i](arr[i])
  }

  return result
}

zipFunc(data1)(data2) // ?

data1 有一系列 function,會依序套用到 data2 array。

inc()dec()negate() 只是借用 Ramda 的 function,這不是重點,你也可以使用自己的 function。

Imperative 會使用 for loop 並搭配 i 變數,同時套用 argsarr 兩個 array。

zipfunc000

Array.prototype.reduce()

import { inc, dec, negate } from 'ramda'

let data1 = [inc, dec, negate]
let data2 = [1, 2, 3]

let zipFunc = fns => arr => arr.reduce((a, x, i) => [...a, fns[i](x)], [])

zipFunc(data1)(data2) // ?

for loop 也可使用 Array.prototype.reduce() 改寫。

zipfunc003

Ramda

import { zipWith, call, inc, dec, negate } from 'ramda'

let data1 = [inc, dec, negate]
let data2 = [1, 2, 3]

let zipFunc = zipWith(call)

zipFunc(data1)(data2) // ?

Ramda 並沒有提供 zipFunc(),但我們可以自行組合。

由於要將兩個 array 配對合併成新 array,因此會想到 Ramda 的 zip 系列,該系列共有 zip()zipWith()zipObj(),其中 zipObj() 結果為 object,因此不予考慮,而 zip() 又過於制式,也無法使用,最後只剩下 zipWith()

zipWith() 原意是要我們傳入兩個 array 經過 callback 運算,但有趣的是目前 function 已經在 array 中了,根本不需要 callback,所以 callback 只剩下一個目的:執行 array 中的 function。

第 6 行

let zipFunc = zipWith(call);

因此 zipWith() 的 callback 並不是傳入 arrow function,而是傳入 Ramda 的 call(),它將會執行 array 中的 function,至於第二個 argument 已經被 point-free,因此不用提供。

zipfunc001

Wink-fp

import { inc, dec, negate } from 'ramda'
import { zipFunc } from 'wink-fp'

let data1 = [inc, dec, negate]
let data2 = [1, 2, 3]

zipFunc(data1)(data2) // ?

Wink-fp 已經內建 zipFunc(),可直接使用。

zipFunc()
[a -> b] -> [a] -> [b]
將 array 中 function 套用到另一 array

zipfunc002

Conclusion

  • call() 看似不起眼,但搭配 zipWith() 卻出現很神奇效果,其實這也正是 FP 精神:將很多小 function 組合成大 function
  • FP 強調 function as data,但 function 最終還是要執行,因此 call() 是 function 的啟動器

Reference

Ramda Cookbook, Apply a list of functions in a specific order into a list of values
Ramda, zipWith()
Ramda, call()
Ramda, inc()
Ramda, dec()
Ramda, negate()