Ramda 的 call() 乍看很不起眼,但若搭配 converge() 之後,就能動態產生 Converging Function。
Version
macOS Mojave 10.14.5
VS Code 1.32.3
Quokka 1.0.195
Ramda 0.26.1
call()
import { call,add} from'ramda';call(add, 1, 2); // ?
最簡單的 call() ,第一個 argument 為 function,第二個 argument 之後為該 function 的 argument list,相當於執行 add(1, 2) 。
call()
(*… → a),*… → a
執行第一個 argument 的 function,並將之後 argument 當作該 function 的 argument list
(*… → a) :任意 function
*… :任意 argument list
a :回傳 function 執行的結果
Function
letproduct = {discount:5000,price:30000};// calPrice :: (Number, Number) -> NumberletcalPrice =(discount, price) =>price - discount -1000;// finalPrice :: Number -> NumberletfinalPrice =product=>calPrice(product.discount, product.price);finalPrice(product);// ?
calPrice() 傳入 discount 與 price argument,會計算出折扣後價錢。
fn() 則傳入 product object,再呼叫 calPrice() 。
Point-free
import{ converge, prop }from'ramda';letproduct = {discount:5000,price:30000};// calPrice :: (Number, Number) -> NumberletcalPrice =(discount, price) =>price - discount -1000;// finalPrice :: Number -> NumberletfinalPrice = converge( calPrice, [ prop('discount'), prop('price') ]);finalPrice(product);// ?
首先將 finalPrice() point-free。
11 行
// finalPrice :: Number -> NumberletfinalPrice = converge( calPrice, [ prop('discount'), prop('price') ]);
finalPrice() 的 argument 為 product object,包含 discount 與 price property,分別傳入 折扣 與 價錢 。
最後要執行的是 calPrice() ,分別有 discount 與 price 兩個 argument。
由於傳入 finalPrice() 的是 object,因此勢必要先使用 prop() 拆解後才能傳給 calPrice()執行。
這正是使用 finalPrice() 時機,將 calPrice() 當作 converging function,將 prop() 當作 branching function。
就功能而言沒問題,唯 calPrice() 並非 point-free,是否有繼續優化空間呢 ?
import { converge, prop, call,add, pipe, subtract, flip, compose }from'ramda';letproduct = { discount:5000, price:30000};// calPrice :: (Number, Number) -> NumberletcalPrice = pipe(add(1000), flip(subtract));// getDiscount :: Object -> NumberletgetDiscount = compose(calPrice, prop('discount'));// getPrice :: Object -> NumberletgetPrice = prop('price');// finalPrice :: Number -> NumberletfinalPrice = converge( call, [ getDiscount, getPrice ]);finalPrice(product);// ?
由於 calPrice() 有兩個 argument: discount 與 price ,基於 currying 特性,可將 calPrice() 視為由 discount 產生的 function,其 argument 只剩下 price 。
第 8 行
//calPrice ::(Number, Number)->Numberlet calPrice = pipe( add(1000), flip(subtract));
calPrice() 最直覺會想用 pipe() ,多減 1000 元就相當於 discount 多加 1000 元,所以先套用 add(1000) 將 discount 加 1000 ,再傳到下一個 subtract() 。
但 subtract() 第一個 argument 為 被減數 ,而 add(1000) 為 減數 ,因此必須使用 flip()將 subtract() 的 signature 反轉後才能使用。
我們發現 calPrice() 已經 point-free 了。
21 行
// finalPrice :: Number -> NumberletfinalPrice = converge( call, [ getDiscount, getPrice ]);
由於 calPrice() 由 discount 產生,因此 converge() 的 converging function 就不能再是 calPrice() ,而要改用 call() ,如此才會執行第一個 branching function。
第一個 branching function getDiscount() 以取得 discount 為目標。
第二個 branching function getPrice() 以取得 price 為目標,再傳入第一個 branching function 執行,這是因為 call() 的緣故。
14 行
// getDiscount :: Object -> NumberletgetDiscount = compose(calPrice, prop('discount'));
從 object 取得 discount property,然後透過 calPrice() 計算。
17 行
// getPrice :: Object -> NumberletgetPrice = prop('price');
從 object 取得 price property。
如果有想学习编程的初学者,可来我们的前端直播授课群的哦:571671034里面免费送整套系统的前端教程!