介绍部分来自JS函数式编程指南
介绍
柯里化(curry)的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。回忆下之前讲作用域时提到的闭包概念,这里的一部分参数的缓存就是通过闭包实现的,仔细阅读下面的代码,你会发现无处不在的闭包。
curry函数可以一次性调用,也可以分多次调用,使用方式如下:1
2
3
4
5
6
7
8
9var add = function(x){
return function(y){
return x+y;
}
}
var increment = add(1);
var addTen = increment(10);
increment(2); // 3
addTen(2); // 3
这里我们定义了一个 add 函数,它接受一个参数并返回一个新的函数。调用 add 之后,返回的函数就通过闭包的方式记住了 add 的第一个参数。一次性地调用它实在是有点繁琐,好在我们可以使用一个特殊的 curry 帮助函数(helper function)使这类函数的定义和调用更加容易。
1 | var curry = require('lodash').curry; |
上面的代码中遵循的是一种简单,同时也非常重要的模式。即策略性地把要操作的数据(String, Array)放到最后一个参数里。到使用它们的时候你就明白这样做的原因是什么了。
1 | match(/\s+/g, "hello world"); |
这里表明的是一种“预加载”函数的能力(有些地方也把这个称作延迟计算的能力),通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。这是闭包的应用
curry 的用处非常广泛,就像在 hasSpaces、findSpaces 和 censored 看到的那样,只需传给函数一些参数,就能得到一个新函数。只传给函数一部分参数通常也叫做局部调用(partial application),能够大量减少样板文件代码(boilerplate code)。
当我们谈论纯函数的时候,我们说它们接受一个输入返回一个输出。curry 函数所做的正是这样:每传递一个参数调用函数,就返回一个新函数处理剩余的参数。这就是一个输入对应一个输出啊。哪怕输出是另一个函数,它也是纯函数。当然 curry 函数也允许一次传递多个参数,但这只是出于减少 () 的方便。
1 | // 曾经使用模板的时候写过这样的代码 |
封装一个curry函数的函数
通过上一节的介绍我们知道,函数柯里化的过程,其实就是一个收集参数的过程,我们将每一次传入的参数都收集起来,并在完成参数的收集后,完成计算处理,我们可以尝试借助这个思路来封装。
1 | // 简单实现,参数只能从右到左传递 |
柯里化与偏函数
当把已知函数的一些参数固定,结果函数被称为偏函数,通过使用bind获得偏函数,也有其他方式实现。当我们不想一次一次重复相同的参数时,偏函数是很便捷的。如我们有send(from,to)函数,如果from总是相同的,可以使用偏函数简化调用。
而柯里化是用来转换函数调用从f(a,b,c)至f(a)(b)(c)的一种思路,也可通过柯里化的方式来实现偏函数。
总结
curry 函数用起来非常得心应手,堪称手头必备工具,能够让函数式编程不那么繁琐和沉闷。