不可或缺的 curry
(译者注:原标题是“Can’t live if livin’ is without you”,为英国乐队 Badfinger 歌曲 Without You 中歌词。)
我父亲以前跟我说过,有些事物在你得到之前是无足轻重的,得到之后就不可或缺了。微波炉是这样,智能手机是这样,互联网也是这样——老人们在没有互联网的时候过得也很充实。对我来说,函数的柯里化(curry)也是这样。
curry 的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
你可以一次性地调用 curry 函数,也可以每次只传一个参数分多次调用。
var add = function(x) {
return function(y) {
return x + y;
};
};
var increment = add(1);
var addTen = add(10);
increment(2);
// 3
addTen(2);
// 12
这里我们定义了一个 add
函数,它接受一个参数并返回一个新的函数。调用 add
之后,返回的函数就通过闭包的方式记住了 add
的第一个参数。一次性地调用它实在是有点繁琐,好在我们可以使用一个特殊的 curry
帮助函数(helper function)使这类函数的定义和调用更加容易。
我们来创建一些 curry 函数享受下(译者注:此处原文是“for our enjoyment”,语出自圣经)。
var curry = require('lodash').curry;
var match = curry(function(what, str) {
return str.match(what);
});
var replace = curry(function(what, replacement, str) {
return str.replace(what, replacement);
});
var filter = curry(function(f, ary) {
return ary.filter(f);
});
var map = curry(function(f, ary) {
return ary.map(f);
});
我在上面的代码中遵循的是一种简单,同时也非常重要的模式。即策略性地把要操作的数据(String, Array)放到最后一个参数里。到使用它们的时候你就明白这样做的原因是什么了。
match(/\s+/g, "hello world");
// [ ' ' ]
match(/\s+/g)("hello world");
// [ ' ' ]
var hasSpaces = match(/\s+/g);
// function(x) { return x.match(/\s+/g) }
hasSpaces("hello world");
// [ ' ' ]
hasSpaces("spaceless");
// null
filter(hasSpaces, ["tori_spelling", "tori amos"]);
// ["tori amos"]
var findSpaces = filter(hasSpaces);
// function(xs) { return xs.filter(function(x) { return x.match(/\s+/g) }) }
findSpaces(["tori_spelling", "tori amos"]);
// ["tori amos"]
var noVowels = replace(/[aeiou]/ig);
// function(replacement, x) { return x.replace(/[aeiou]/ig, replacement) }
var censored = noVowels("*");
// function(x) { return x.replace(/[aeiou]/ig, "*") }
censored("Chocolate Rain");
// 'Ch*c*l*t* R**n'
这里表明的是一种“预加载”函数的能力,通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。
我鼓励你使用 npm install lodash
安装 lodash
,复制上面的代码放到 REPL 里跑一跑。当然你也可以在能够使用 lodash
或 ramda
的网页中运行它们。