Ramda 常見的 Point-free 技巧

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

内容简介:若只求結果正確,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,因此 id 與 title 兩個 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() 之後, v 與 k 都不見了,所有 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 技巧》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

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

Learning JavaScript

Learning JavaScript

Shelley Powers / Oreilly & Associates Inc / 2006-10-17 / $29.99

As web browsers have become more capable and standards compliant, JavaScript has grown in prominence. JavaScript lets designers add sparkle and life to web pages, while more complex JavaScript has led......一起来看看 《Learning JavaScript》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具