iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 15
1
Software Development

Functional Programming in JS系列 第 15

[練習] Currying Exercise

https://ithelp.ithome.com.tw/upload/images/20200915/20106426PgDqmCtO57.jpg

Practice 1

接下來題目都會像以下先列出範例 input、output 跟對應的型別

Input: str = 'Jingle bells Batman'
Output: ['Jingle', 'bells', 'Batman']

/**
 * @param {string} str
 * @return {array}
 */
const words = function(str) {
    // 請把 code 寫在這
};

一般函式寫法

const split = (delimiter, string) => string.split(delimiter)
const words = function(str) {
  return split(' ', str);
}

words('Jingle bells Batman'); 
// => ['Jingle', 'bells', 'Batman']

Currying

const _curry = f => a => b => f(a, b) 
const _split = _curry((delimiter, string) => string.split(delimiter))

const words = _split(' ')

words('Jingle bells Batman'); 
// => ['Jingle', 'bells', 'Batman']

Ramda

平常寫程式時,為了方便我們會用一些函式庫例如 lodash 或 underscore,而 Ramda 是FP 界最有名的函式庫,後面會有篇章介紹,這邊只是想強調 Ramda 所有函式都會自動 Curry 相當方便,例如同樣例子用 Ramda 寫會像以下,非常簡潔

const _ = R;
const words = _.split(' ')

Practice 2

Input: str = ['Jingle bells Batman', 'Robin laid an egg']
Output: [['Jingle', 'bells', 'Batman'], ['Robin', 'laid', 'an', 'egg']]

/**
 * @param {array} arr
 * @return {array}
 */
const sentences = function(arr) {
    
};

一般函式寫法

const map = (f, arr) => arr.map(f)
const sentences = arr => map(words, arr)
// words 是 practice 1 的函式

sentences(['Jingle bells Batman', 'Robin laid an egg']); 

Curry

const _map = f => arr => arr.map(f)
const sentences = _map(words)
// words 是 practice 1 的函式

/* 改成 Ramda 一行結束 ---- */
const sentences = _.map(words)

sentences(['Jingle bells Batman', 'Robin laid an egg']); 

Practice 3

Input: xs = ['quick', 'camels', 'quarry', 'over', 'quails']
Output: ['quick', 'quarry', 'quails']

/**
 * @param {array} arr
 * @return {arr}
 */
const filterQs = function(arr) {
    
};

一般函式寫法

const filter = (f, arr) => arr.filter(f)
const filterQs = arr => filter(x => x.match(/q/ig), arr)

filterQs(['quick', 'camels', 'quarry', 'over', 'quails']); 
// => ["quick", "quarry", "quails"]

Curry

const _filter = f => arr => arr.filter(f)
const filterQs = _filter(x => x.match(/q/ig))

/* 改成 Ramda 一行結束 ---- */
const filterQs =  _filter(x => _.test(/q/ig, x))

filterQs(['quick', 'camels', 'quarry', 'over', 'quails']); 
// => ["quick", "quarry", "quails"]

Practice 4

Input: xs = [323,523,554,123,5234]
Output: 5234

/**
 * @param {array} arr
 * @return {number}
 */
const _keepHighest = (x,y) => x >= y ? x : y // <- leave be
const max = function(xs) {
    
};

一般函式寫法

const max = function(xs) {
  return xs.reduce(function(acc, x){
    return _keepHighest(acc, x);
  }, 0);
}

max([323,523,554,123,5234])
// => 5234

Currying

// 直接用 Ramda
const max = _.reduce(_keepHighest, 0);

max([323,523,554,123,5234])
// => 5234

原始碼 codepen 在這裡

變一下花樣

運用 Currying 的 pattern 其實也可以變出其他花樣

Q: 寫出 curry 函式讓 add(x)(y) 等同 add(x, y)

const add = (x, y) => x + y;
const curry = f => x => y => f(x, y)

const result = curry(add)(3)(4)
console.log(result) // 7

Q: 寫出 toPair 函式讓 add([x, y]) 等同 add(x, y)

const add = (x, y) => x + y;
const toPair = f => ([x,y]) => f(x, y)

const result = toPair(add)([1, 2])
console.log(result) // 3

Q 寫出 flip 函式讓 add(y, x) = add(x, y)

const add = (x, y) => x + y;
const flip = f => (y, x) => f(x, y)

const result = flip(add)(1, 3)
console.log(result) // 4

Q 改寫 ['a', 'b', 'c'].slice(0, 2) 成 slice(1)(3)(['a', 'b', 'c'])

const slice = start => end => array => array.slice(start, end)

Observations

  • Ramda 理念是 "function first,data last" 所以你會發現 Curry 第一個參數都是 function,data 會放在後面,這樣也有助建立 Pointfree 模式 (先別管這是什麼,後面篇章會提到)

參考文章

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

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


上一篇
Currying
下一篇
Composition
系列文
Functional Programming in JS30

尚未有邦友留言

立即登入留言