什么是函数柯里化(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

 

  1. partial函数可以帮我们生成一个偏函数。
  2. 调用partial函数生成一个偏函数并赋值给parMulti,其中预设一个值1
  3. 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。偏函数通过设定预设值,帮我们实现代码上的复用。

 

总结


柯里化和偏函数都是用于将多个参数函数,转化为接受更少参数函数的方法。传入部分参数后,处于中间状态的函数可以作为固定值进行复用。但是其中不同之处在于:

  • 柯里化是将函数转化为多个嵌套的一元函数,也就是每个函数只接受一个参数。
  • 偏函数可以接受不只一个参数,它被固定了部分参数作为预设,并可以接受剩余的参数。