iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 17
1
Software Development

Functional Programming in JS系列 第 17

[補充] Ramda.js

https://ithelp.ithome.com.tw/upload/images/20200917/20106426MZGbno0Scj.jpg

接下來的練習想直接用 Ramda.js 因為會簡潔很多,所以這一篇先來簡單介紹一下 Ramda

What's Ramda?

A practical functional library for JavaScript programmers.

Ramda 是一個 Library ,可以把 Ramda 想成 functional programming 界的 lodash 或 underscore 。

Why use Ramda?

Ramda 的 API 很豐富,而且大部分 API 也很直覺,不需要花太多時間理解。但你可能會覺得我用 loadash 就好,為何要多學一個?最大的差別在於

  • Ramda 強調函式都是 pure 的。Immutability 跟不造成 side-effect 是他的設計理念,也就是說我們可以寫出更簡單優雅的程式碼
  • 任何 API 都會自動 curry 化,這特性提供了程式碼相當大的彈性
  • "Function first,Data last"

有著自動轉 Curry 與 "Function first,Data last" 特性好處非常非常多,也很符合 Pointfree style (之後文章會介紹),簡而言之就是能讓功能獨立切開再組合成想要的樣子。

It's very easy to build functions as sequences of simpler functions, each of which transforms the data and passes it along to the next.

就像前一篇曾經出現的例子,每一個 function都像是一個小水管獨立處理不同的事,只要有不同排列組合下就可以輕易得到不同結果

https://ithelp.ithome.com.tw/upload/images/20200917/20106426vcv0oxNHSX.jpg

What Ramda looks like?

如以上所說的 "function first,data last",Ramda 第一個參數一定為函式,後面才是資料

_.函式名稱(函式, 資料)

const _ = R;

const odd = x => x%2 === 1
const data = [3, 5, 6];
_.filter(odd, data); // [3, 5]

另外 Ramda 會幫你把所有函式 Curry 化,所以你要這樣寫也可以

_.filter(odd)(data); // [3, 5]

// 或是也可以這樣寫
const filter1 = _.filter(odd);
const filter2 = filter1(data)

若以上轉成一般寫法的話大概會是這樣,比較繁瑣些

const _curry = f => a => b => f(a, b)
const _filter = _curry( (fn, arr) => arr.filter(fn) );
const result = _filter(odd)

console.log(result([3, 5, 6])) // [3, 5]

常用 Ramda API

這邊只先介紹之後幾篇會用到的 Ramda API

List

filter: 過濾出符合條件的成員

const isEven = n => n % 2 === 0;

R.filter(isEven, [1, 2, 3, 4]); //=> [2, 4]

R.filter(isEven, {a: 1, b: 2, c: 3, d: 4}); //=> {b: 2, d: 4}

reverse: 反轉

R.reverse([1, 2, 3]);  //=> [3, 2, 1]
R.reverse([1, 2]);     //=> [2, 1]
R.reverse([1]);        //=> [1]
R.reverse([]);         //=> []

R.reverse('abc');      //=> 'cba'
R.reverse('ab');       //=> 'ba'
R.reverse('a');        //=> 'a'
R.reverse('');         //=> ''

map: 成員依次執行某個函式

const double = x => x * 2;

R.map(double, [1, 2, 3]); //=> [2, 4, 6]

R.map(double, {x: 1, y: 2, z: 3}); //=> {x: 2, y: 4, z: 6}

last: :返回最后一个成員

R.last(['fi', 'fo', 'fum']); //=> 'fum'
R.last([]); //=> undefined

R.last('abc'); //=> 'c'
R.last(''); //=> ''

Math

add: 返回兩個值的和

R.add(2, 3);       //=>  5
R.add(7)(10);      //=> 17

multiply: 返回兩個值的積

R.multiply(2)(5)  // 10

Object

prop: return 指定的屬性

R.prop('x', {x: 100}); //=> 100
R.prop('x', {}); //=> undefined
R.prop(0, [100]); //=> 100
R.compose(R.inc, R.prop('x'))({ x: 3 }) //=> 4

Function

compose: 將多個函式合併成一個函式,右到左執行

R.compose(Math.abs, R.add(1), R.multiply(2))(-4) // 7

pipe: 將多個函式合併成一個函式,左到右執行

var negative = x => -1 * x;
var increaseOne = x => x + 1;

var f = R.pipe(Math.pow, negative, increaseOne);
f(3, 4) // -80 => -(3^4) + 1

curry: 將多個參數轉變為單一參數

const addFourNumbers = (a, b, c, d) => a + b + c + d;

const curriedAddFourNumbers = R.curry(addFourNumbers);
const f = curriedAddFourNumbers(1, 2);
const g = f(3);
g(4); //=> 10

Ramda 本身的 API 相當豐富,也可以搭配 compose/pipe 自由組裝自己的 function,並且利用以上提到方法來簡化函式。


參考文章

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

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


上一篇
Composition
下一篇
[練習] Composition Exercise
系列文
Functional Programming in JS30

尚未有邦友留言

立即登入留言