百度百科里对柯里化的定义是
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术由 Christopher Strachey 以逻辑学家 Haskell Curry 命名的,尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的。
简直看不懂哦,Talk is Cheap,我们拿一个简单的haskell程序说明什么叫
自动柯里化
两数相加,返回结果:
fun add x y = x + y
这是一个典型的柯里化的版本,当我输入 (add 1) 2
时与add 1 2
的返回结果一模一样,都是3
Prelude> (add 1) 2
3
但是,换个角度上讲,我明明设置了两个“变量”,在haskell里却把它们的结果又封装成了一个函数,这就是haskell的强大特性:自动柯里化,haskell全部帮你自动处理了。
简单来说,柯里化指的是函数里只接收一个余下的参数,再调用原函数,返回出相应的结果。
而同样的,没有柯里化的普通版本却是这样的:
fun add(x, y) = x + y
这个版本里,只能通过add(1,2)
进行相加操作。
柯里化实际上是将高阶函数进行降阶处理,没理解?接着看~
JavaScript的柯里化
JavaScript存在闭包的特性,能够很好的为我们将函数柯里化,但是很可惜JavaScript不支持自动柯里化,这意味着,我们要自己写柯里化的过程,不过还好,这能有效的让我们理解,到底什么是柯里化。
同样的,还是那个add的简单函数,用JavaScript实现如下:
function add(x, y){
if(y===undefined){
return (z) => x + z;
}else{
return x+y;
}
}
测试一下!通过执行add(1)(2)
,果然返回了3
,是的,这是一个手动构造柯里化函数的过程。对于大多数函数,我们有通用的柯里化方法:
function curry(func) {
var fixedArgs = [].slice.call(arguments,1);
return function() {
args = fixedArgs.concat([].slice.call(arguments))
return func.apply(null, args);
};
}
也就是说,对于add
函数来说,我们用一个函数生成了另一个范围更小的函数,一层接一层的解决问题。这符合函数式编程的思想,就是我们更关注函数与函数之间的关系,而非状态本身,更在乎局部而非整体。
这个例子体现不出这种思想的精妙之处,那么我们再举一个例子🌰
如果,我们想让一个已知的数组每个值都做加1操作,传统的方法该怎么做?
let ar = [1,2,3];
ar.map((x)=> x+1);
对吧?那么我们下面可以用柯里化的思想把这个问题实现:
const add = ar => ar.map(x => x + 1);
add([1, 2, 3]);
首先我们在add
里定义了一个函数,并且传入参数为ar
,ar
进行Array.prototype.map()
操作,对每一个数进行遍历,并且返回这个数字+1后的值给ar
,这看起来挺玄乎的,但确实可行,我们只关心局部,请试图把add
当做数学里中的函数去思考理解,或许会好懂的多。可是这只是方便了我们理解,那么到底有什么好处呢?
再举一个例子🌰
如果我们想写一个函数,用来对已知数组的已知量进行相加n的操作,怎么写?传统方法
let ar= [1,2,3];
function addToArray(ar,n){
return ar.map((x)=> x+n)
};
console.log(addToArray(ar,1));
但是,我们可以这样写:
const add = a => b => a+b;
const addToArray = (ar,n) => ar.map(add(n));
addToArray([2,3],1)
结果都一样,其实过程是不一样的,你可能真的憋不住了,到底有什么用呢?
柯里化的好处
就像上面的例子一样,你能看出不同吗?对,第一个的实质是新写了一个函数,对数组进行map操作,而第二个则是在直接在map
里进行add
操作,这很通用,一旦你写了很多类似于add
的函数,就会方便起来,可以让我们方便的在map
,reduce
,filter
里调用。
- 能够进行延迟计算,就像add(1)(2)一样,1比2先传入,2就会被延迟计算,在特定的场景里,有一定的应用意义。
- 当你发现你要调用一个函数,并且调用参数都是一样的情况下,这个参数就可以被柯里化,以便更好的完成任务。
- 优雅的写法,允许你写出来的代码更干净、更有表达力。
虽然柯里化在日常应用不多,但仍然是一个不可不了解的好概念。
相关阅读
Currying and ES6 Arrow Functions
浅析 JavaScript 中的 函数 currying 柯里化
Currying - Part 6 of Functional Programming in JavaScript(需梯子)
网友评论
```js
function curry(func) {
var fixedArgs = [].slice.call(arguments,1);
return function() {
args = fixedArgs.concat([].slice.call(arguments))
return func.apply(null, args);
};
}
```
这样的curry化只能延迟一次,第二次调用函数就会执行了。
我自己研究了以下,应该写成这样吧。。。
```
function curry(func){
var args = [].slice.call(arguments,1);
var _func = function(){
[].push.apply(args,arguments);
return _func;
}
_func.end = function(){
return func.apply(this,args.concat([].slice.call(arguments)));
}
return _func;
}
```
只要不执行返回值的.end()方法,就不会输出,可以一直延迟。。或者把参数长度为0作为执行的标准是不是合理一点?