在计算机科学中,柯里化(英语:Currying),又译为卡瑞化
或加里化
,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。这个技术由克里斯托弗·斯特雷奇
以逻辑学家哈斯凯尔·加里
命名的,尽管它是Moses Schönfinkel
和戈特洛布·弗雷格
发明的。
高阶函数的特点:
- 函数可以作为参数被传递;
- 函数可以作为返回值输出。
1 | var foo = function(a) { |
反柯里化:函数柯里化的对偶是Uncurrying,一种使用匿名单参数函数来实现多参数函数的方法。
理解
Currying概念其实很简单,只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
简单求和函数
a+b+c
1 | function add(a, b, c) { |
a+b+c Currying
1 | function add(x) { |
这里我们定义了一个add函数,它接受一个参数并返回一个新的函数。调用add之后,返回的函数就通过闭包的方式记住了add的第一个参数。一次性地调用它实在是有点繁琐,好在我们可以使用一个特殊的curry帮助函数(helper function)使这类函数的定义和调用更加容易。
用ES6的箭头函数,我们可以将上面的add实现成这样:
1 | const add = x => y => z => x + y + z; |
好像使用箭头函数更清晰了许多。
1 | function addFn(x) { |
1 | function Arguments() { |
场景使用
实际场景的运用能更好的加深我们的理解,所以我们用实际的生活场景来看看 柯里化 如何运用。
获取数据
- 默认参数
- 剩余参数(Array)
- arguments对象(非Array)
数组拼接
arguments转数组
1
2Array.from(arguments)
Array.prototype.slice.call(arguments)剩余参数
1 | [...[1,3],...[1,2]] => [1, 3, 1, 2] |
concat()
连接两个或更多的数组,并返回结果。1
2
3
4
5var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])
sum.concat(Array.from(arguments))
a.concat(4,[1, 2]) => [4, 1, 2]
[].concat(4, 5) => [4, 5]slice
方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。原始数组不会被修改。1
2[].slice.call([1, 2, 4], 1) => [2, 4]
[].slice.call([1, 2]) => [1, 2]
1 | Array.slice() |
除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。另外,你可以使用 bind 来简化该过程。
1 | var unboundSlice = Array.prototype.slice; |
push
向数组的末尾添加一个或更多元素,并返回新的长度。1
2
3
4
5
6
7[].push.apply(_args, [].slice.call(arguments));
Array.prototype.push.apply(_args, [].slice.call(arguments))
var a = [1];
var c = [1, 3, 2];
[].push.apply(a, c); // 返回length
console.log(a).apply()
a+b+c
x + y + z + d
1 | function add(x) { |
求和
这段代码实际上还是不能满足要求的,题主测试成功也只是因为所在环境的 console.log 会将结果转为 string 输出,为了 return tmp 不至于输出一个function,所以重新定义了函数 tmp 的 toString 方法,使其返回 sum 值。
比如如果使用以下代码测试会发现问题
1 | function add(a) { |
1 | function addCurry(x) { |
1 | function add() { |
1 | var add = function(x, y) { |
剩余参数
1 | function addReduce(...a){ |
1 | function add(...a){ |
arguments
1 | function add(){ |
1 | function add(){ |
自定义方法(偏函数)
1 | var currying = function (fn) { |
编写一个通用的 curry()
1 | function curry(fn) { |
1 | var currying = function (fn) { |
记账本
记账本,每天记录使用多少钱,一个月算一次总花费(或者在我想要知道的时候)
1 | function account(){ |
老婆的测试
1 | var currying = function(fn) { |
实现柯里化
1 | var currying = function(fn) { |
1 | var currying = function(fn) { |
f([‘1’,’2’]) => ‘12’
1 | var concatArray = function(chars) { |
固定易变因素
柯里化特性决定了它这应用场景。提前把易变因素,传参固定下来,生成一个更明确的应用函数。最典型的代表应用,是bind函数用以固定this这个易变对象。
1 | Function.prototype.bind = function(context) { |
Uncurrying
函数柯里化的对偶是Uncurrying,一种使用匿名单参数函数来实现多参数函数的方法。
1 | 可能遇到这种情况:拿到一个柯里化后的函数,却想要它柯里化之前的版本,这本质上就是想将类似f(1)(2)(3)的函数变回类似g(1,2,3)的函数。 |
偏函数
Partial Application(偏函数应用) 是指使用一个函数并将其应用一个或多个参数,但不是全部参数,在这个过程中创建一个新函数。
1 | function add3(a, b, c) { return a+b+c; } |
var toString=object.prototype.toString;
var isString=function(obj){
return toString.call(obj)==’[object String]’;
};
var isFunction=function(obj){
return toString.call(obj)==’[object Function]’;
};
……….
偏函数方法:
var isType=function(type){
return function(obj){
return tostring.call(obj)==’[object ‘ + type+ ‘ ]’;
}
}
1 | //生成偏函数的函数 |
拓展
-JavaScript函数柯里化
-我理解的函数柯里化
-高阶函数应用–currying
-从一道面试题谈谈函数柯里化 (Currying)
-Javascript中有趣的反柯里化技术
-为什么要柯里化(why-curry-helps)slide
-前端开发者进阶之函数柯里化Currying
-JS中的柯里化(currying)
-何为Curry化/柯里化?
-一道题看透函数柯里化(currying)
-掌握 JavaScript 函数的柯里化
-
-
-
-
-
ES6 中 curry 和 apply 的实现
apply() 在 ES6 中的实现:
1 | function curry(fn) { |
apply() 在 ES6 中的实现:
1 | // 应用左边任意数量的参数 |