JavaScript 函数式编程 柯里化

My Dad once explained how there are certain things one can live without until one acquires them.

为什么要柯里化

想象一个场景,我们需要把播放次数、购买次数增加一,或者需要往购物车里面增加十个苹果。

本质的动作都是 add

// 核心操作
const add = (x, y) => x + y;

// 原本的播放次数是123,每次播放次数加一
let times = 123;
times = add(1, time);   // 124
times = add(1, time);   // 125
// ...

// 原有3个苹果,每次多放入10个苹果
let apple = 3;
apple = add(10, apple);   // 13
apple = add(10, apple);   // 23
// ...

但是同一组动作,第一个参数都是相同的,然而每次调用都需要重复传入,略显繁琐。

所以我们可以使用闭包的方式”记住”第一个参数 ↓ ↓ ↓

// 核心操作
const add = (x) => {
  return (y) => {
    return x + y
  }
};

// 偷懒的写法,但是可能一眼看去,不容易看出逻辑
// const add = x => y => x + y;

// 增加 1
const increment = add(1);
const addTen = add(10);

let times = 123;
times = increment(time);    // 124

let apple = 3;
apple = addTen(apple);      // 13

柯里化 (curry) 的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
—— JS 函数式编程指南

上面的例子中,核心的 add 经过修改后,只需传递部分参数,就能得到一个”记住”了这些参数的新函数。


更多柯里化的例子

借助 lodash, ramda 这些库的帮助,我们还能通过 curry 这个帮助函数,更方便地实现柯里化。

import { curry } from "lodash";

const match   = curry((what, str) => str.match(what));

const replace = curry((what, replacement, str) => str.replace(what, replacement));

const filter  = curry((f, ary) => ary.filter(f));

const map     = curry((f, ary) => ary.map(f));

match(/\s+/g, 'Hello World!');    // [ ' ' ]

match(/\s+/g)('Hello World!');    // [ ' ' ]

const hasSpaces = match(/\s+/g);  // x => x.match(/\s+/g)

hasSpaces('hello world');         // [ ' ' ]

hasSpaces('spaceless');           // null

filter(hasSpaces, ['tori_spelling', 'tori amos']);    // ['tori amos']

const findSpaces = filter(hasSpaces)                  // xs => xs.filter(x => x.match(/\s+/g))

findSpaces(['tori_spelling', 'tori amos']);           // ['tori amos']

可以一次性给一个 curried 函数传入所有的参数来返回我们最终所需的函数

也可以一步一步传入所需的参数,逐级构建我们所需的函数。


match → hasSpaces → findSpaces

一个完整的复杂的函数可以由多个小的、功能逐级健壮的参数 (函数) 构建而成,类似于搭积木一样。

同时,这种模块化的构建,细分了各个模块的功能,也更利于代码调试和单元测试。


胡说八道

之前是想看java的clojure来学一下FP的思想的…后来,一直都没看。

最近翻出了基于javascript的实现,然后就折腾折腾。还行吧,与命令式不同的函数式,从另一个角度看待这个世界,挺有趣的。


参考链接

第 4 章: 柯里化(curry) · JS 函数式编程指南
Chapter 4: Currying · mostly-adequate-guide

This blog is under a CC BY-NC-SA 4.0 Unported License
Link to this article: http://nhh.ink/2017/10/18/js-fp-curry/