Ramda 常見的 Point-free 技巧

栏目: 编程语言 · 发布时间: 5年前

内容简介:若只求結果正確,Ramda 其實不難,而是難在如何湊 Point-free,很多看似簡單,卻會讓你想破頭,本文整理出幾個常見的技巧,可以視為 Pattern 使用。WebStorm 2018.3.3Quokka 1.0.134

若只求結果正確,Ramda 其實不難,而是難在如何湊 Point-free,很多看似簡單,卻會讓你想破頭,本文整理出幾個常見的技巧,可以視為 Pattern 使用。

Version

WebStorm 2018.3.3

Quokka 1.0.134

Ramda 0.26.1

Callback 只有一個參數

__

import { map } from 'ramda';

const data = [1, 2, 3];

const bookLut = {
  1: 'Functional Programming in JavaScript',
  2: 'RxJS in Action',
  3: 'Speaking JavaScript',
};

const getBooks = map(x => bookLut[x]);
console.log(getBooks(data));

data 為 book 的 id ,而實際 id書名 的對照表在 bookLut object,若需求只要顯示 書名 ,我們會使用 map()

若以需求而言,這樣寫就很棒了,唯 map() 的 callback 不是 Point-free,還帶著一個 x 參數,若能將 x 也拿掉就更精簡了。

Ramda 常見的 Point-free 技巧

import { map, prop, __ } from 'ramda';

const data = [1, 2, 3];

const bookLut = {
  1: 'Functional Programming in JavaScript',
  2: 'RxJS in Action',
  3: 'Speaking JavaScript',
};

const getBooks = map(prop(__, bookLut));
console.log(getBooks(data));

回顧一下 map()prop() 的 signature

map()

Functor f => (a -> b) -> f a -> f b

將 a 格式資料轉換成 b 格式資料,且筆數不變

map() 的 callback 必須是 a -> b

prop()

s -> {s: a} -> a | undefined

傳入 key 與 object,傳回 value

prop() 原本的設計是當你只傳入 s key 時,回傳 {s: a} -> a ,這剛好與 map() callback 的 signature 一樣,可取代 callback 將原本 x 參數拿掉成 Point-free。

但很不幸的,我們需求反而是要對 prop() 傳入 {s: a} ,也就是 object 部分,而非 key,這在實務上經常遇到,很多人在這裡卡關不知道怎麼繼續 Point-free 了。

Ramda 提供了 __ ,能讓我們延遲給參數,讓 prop() 依然回傳 {s: a} -> a ,原本要傳入的 key 先用 __ 取代,等 map() 的 callback 傳進 x 參數。

Ramda 常見的 Point-free 技巧

flip()

import { map, prop, flip } from 'ramda';

const data = [1, 2, 3];

const bookLut = {
  1: 'Functional Programming in JavaScript',
  2: 'RxJS in Action',
  3: 'Speaking JavaScript',
};

const getBooks = map(flip(prop)(bookLut));
console.log(getBooks(data));

Ramda 另外提供了 flip() ,可以將 function 的前兩個參數做調換,讓原本無法 Point-free 的 function 變成可以 Point-free。

flip()

((a, b, c, …) → z) → (b → a → c → … → z)

將 function 的前兩個參數順序調換,以配合 Point-free

與其使用 prop(__, bookLut) ,若能將變成 prop(bookLut, __) 那就太好了,這樣 data 可以放在最後一參數,可以安心的做 Point-free。

先使用 flip(prop) 產生新的 function,使其 signature 成為 {s: a} -> s -> a | undefined ,如此 s key 成為第二個參數,就能直接 Point-free 將 x 參數拿掉了。

Ramda 常見的 Point-free 技巧

Callback 有兩個參數

import { includes, map, pickBy } from 'ramda';

const data = [
  { id: 1, title: 'Functional Programming in JavaScript', year: 2016 },
  { id: 2, title: 'RxJS in Action', year: 2017 },
  { id: 3, title: 'Speaking JavaScript', year: 2014 },
];

const getBooks = map(pickBy((v, k) => includes('i', k)));
console.dir(getBooks(data));

當 callback 有兩個參數,且要 Point-free 的 data 又在第二個參數時。

pickBy() 原本的設計,就是希望你提供 (v, k) → boolean 這種 signature 的 callback,而我們的需求是顯示 key 的部分包含 i 的所有 object 與 property,因此 idtitle 兩個 property 都會被 pick 出來。

pickBy()

((v, k) → boolean) → {k: v} → {k: v}

根據傳入條件的 function,回傳新的符合條件 key / value 的 object

Ramda 常見的 Point-free 技巧

若以需求而言,這樣就打完收工了。

但若仔細看,會發現 pickBy() 的 callback 還有 (v, k) 並沒有 Point-free,是否有繼續優化的空間呢 ?

import { map, pickBy, includes, flip } from 'ramda';

const data = [
  { id: 1, title: 'Functional Programming in JavaScript', year: 2016 },
  { id: 2, title: 'RxJS in Action', year: 2017 },
  { id: 3, title: 'Speaking JavaScript', year: 2014 },
];

const getBooks = map(pickBy(flip(includes('i'))));
console.dir(getBooks(data));

使用 flip() 之後, vk 都不見了,所有 function 都 Point-free 了,且結果依然正確。

Ramda 常見的 Point-free 技巧

pickBy() callback 的 signature 只有一個參數 k

pickBy(k => includes('i', k))

我們能夠輕易的 Point-free 沒問題:

pickBy(includes('i'));

但偏偏目前 pickBy() callback 為兩個參數 (v, k) ,因此無法 Point-free 將 (v, k) 拿掉。

(v, k) => includes('i', k)

所以我們希望能產生新的 function 取代 (v, k) => includes('i', k)

includes()

a -> [a] -> boolean

當 value 存在於 array 內時,則傳回 true ,否則傳回 false

includes('i') 時,回傳 [a] -> boolean ,我們也可以視為

([a], __) -> boolean

其中 [a] 就是我們的 k__ 為 Ramda 的 placeholder,表示多一個參數也無妨,但此時 ([a], __) 對應的是 pickBy()(v, k) ,也就是我們的 k 對應到的是 v ,會變成判斷 property value 是否包含 i ,這不是我們要的。

flip(includes('i'));
// (__, [a]) -> boolean

透過 flip() 翻轉 includes('i') 的參數後, (__, [a]) 對應到 (v, k) ,也就是我們的 k 對應到 callback 的 k 參數,這是 property key,正是我們要的,因此可將 k => includes('i', k) 完全用 flip(includes('i')) 取代,順利達成 Point-free 目標。

Conclusion

  • 本文共介紹 2 種 pattern,當 callback 只有一個參數時,使用 __flip() 取代 callback,可視程式碼可讀性自行靈活運用
  • 當 callback 時有兩個參數時,若我們要 Point-free 的 data 是第一個參數,可以無視第二個參數直接 Point-free,但若要 Point-free 的 data 是第二個參數時,只好先將 data flip() 成第二個參數,如此才能 Point-free 取代 callback

Reference

Ramda , flip()

Ramda , map()

Ramda , pickBy()

Ramda , includes()


以上所述就是小编给大家介绍的《Ramda 常見的 Point-free 技巧》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

使用HTML5和Node构建超媒体API

使用HTML5和Node构建超媒体API

【美】Mike Amundsen(麦克.阿蒙森) / 臧秀涛 / 电子工业出版社 / 2014-5 / 55.00元

《使用HTML5和Node构建超媒体API》探讨了超媒体API 的设计,介绍了作为超媒体API 的构件块的超媒体因子,并讲解了基本格式、状态转移、领域风格和应用流程这4 种超媒体设计元素;之后作者结合具体的场景,通过3个动手实验章节,从超媒体因子和超媒体设计元素入手,用实际的代码向我们详细地演示了超媒体API 的设计;最后介绍了超媒体设计的文档编写、注册与发布等内容。 《使用HTML5和No......一起来看看 《使用HTML5和Node构建超媒体API》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具