什么是函数柯里化(Currying)
维基百科中的定义:
在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。
换句话说就是把一个有n个参数的函数转换成n个嵌套的函数,每个函数只接受一个参数,并返回一个新函数。也就是把f(a,b,c)
转化为f(a)(b)(c)
。
例子
例如一个函数接受a, b, c三个参数,返回它们的乘积:
function multiply(a, b, c) {
return a * b * c;
}
复制代码
运行此函数,我们在参数中依次传入1,2,3,返回6。
multiply(1,2,3); // 6
复制代码
让我们看一下柯里化之后的样子:
function multiply(a) {
return (b) => {
return (c) => {
return a * b * c
}
}
}
multiply(1)(2)(3) // 6
复制代码
我们把multiply(1,2,3)
变成了multiply(1)(2)(3)
的形式。通过把一个多参函数转换成一系列嵌套的函数,每个函数依次接受一个参数,这就是函数柯里化。
或者换一种写法可以更容易理解:
function multiply(a) {
return (b) => {
return (c) => {
return a * b * c
}
}
}
const mul1 = multiply(1);
const mul2 = mul1(2);
const result = mul2(3); // 6
复制代码
multiply函数接受一个a参数,我们传入1。mult1
等于multiply(1)
就等于
(b) => {
return (c) => {
return a * b * c
}
}
复制代码
并且接受一个参数b。以此类推,mul1(2)
传入2,然后赋值给mult2
等于
(c) => {
return a * b * c
}
复制代码
最后mul2(3)
返回a * b * c
。由于闭包的特性会保存之前传入的值1和2,最后得出6。
函数柯里化的应用
- 柯里化函数避免我们重复传参,实现复用
假设我们有一个函数用来计算三个物体的体积。
function volume(l, w, h) {
return l * w * h;
}
volume(200,30,100) // 2003000
volume(32,45,100); //144000
volume(2322,232,100) // 53870400
复制代码
从上面代码可以看出,如果每个物体的高度都是100,我们需要重复传三次。 如果使用柯里化函数可以避免:
function volume(h) {
return (w) => {
return (l) => {
return l * w * h
}
}
}
//固定高度
const itemHeight = volume(100);
//计算其他不同情况
itemHeight(30)(200);
itemHeight(45)(32);
itemHeight(232)(2322);
复制代码
- 函数的合成
合成两个函数的简单代码如下:
var compose = function(f,g) {
return function(x) {
return f(g(x));
};
};
复制代码
f(x)
和g(x)
合成为f(g(x))
,有一个隐藏的前提,就是f和g都只能接受一个参数, 其中就运用了函数柯里化。
什么是偏函数应用(Partial Application)
Partial Application(偏函数应用)很容易和函数柯里化混淆,它是指使用一个函数并将其应用一个或多个参数,但不是全部参数,在这个过程中创建一个新函数,这个函数用于接受剩余的参数。这段话很不容易理解,具体看下面的例子:
例子
function multiply(a,b,c){
return a * b * c;
}
//生产偏函数的工厂
function partial(fn,a){
return function(b,c){
return fn(a,b,c);
}
}
//变量parMulti接受返回的新函数
var parMulti = partial(multiply,1);
//在调用的时候传入剩余的参数
parMulti(2,3); // 6
复制代码
partial
函数可以帮我们生成一个偏函数。- 调用
partial
函数生成一个偏函数并赋值给parMulti
,其中预设一个值1
。 parMulti
接受剩余参数2,3
结合前面预设的1
得出最终结果。
偏函数的应用
例如bind函数可以让我们传入一个或多个想要预设的参数,之后返回一个新函数,并拥有指定的this值和预设参数。当绑定函数被调用时,这些参数会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们后面。
function addition(x, y) {
return x + y;
}
const plus5 = addition.bind(null, 5)
plus5(10) // output -> 15
plus5(20) // output -> 25
复制代码
我们预先传入了参数5
,并返回了一个新函数赋值给plus5
,此函数可以接受剩余的参数。调用plus5
传入剩余参数10
得出最终结果15
,如传入20
得出25
。偏函数通过设定预设值,帮我们实现代码上的复用。
总结
柯里化和偏函数都是用于将多个参数函数,转化为接受更少参数函数的方法。传入部分参数后,处于中间状态的函数可以作为固定值进行复用。但是其中不同之处在于:
- 柯里化是将函数转化为多个嵌套的一元函数,也就是每个函数只接受一个参数。
- 偏函数可以接受不只一个参数,它被固定了部分参数作为预设,并可以接受剩余的参数。