柯里化与偏应用

一、函数参数的个数

函数定义时会写明它所接收的参数个数。”一元参数”接收一个参数,”多元函数”接收多个参数。有些函数能够接收不定数量的参数,我们称之为”可变参数函数”。

二、偏应用

作为铺垫,我们首先实现一个map函数,用来将某个函数应用到数组的每个元素上:

    var _map = [].map;

    function map(list,unaryFn){
        return _map.call(list,unaryFn);
    }

    function square(n){
        return n*n;
    }

    map([1,2,3],square);
    //=> [1,4,9] 

显然,map是二元函数,square是一元函数。当我们使用[1,2,3]和square作为参数调用map时,我们是将这两个参数应用到map函数,并获得结果。
由于map函数接收两个参数,我们也提供了两个参数,所以说这是一次完整的应用。那何谓偏应用(或部分应用)呢?其实就是提供少于指定数量的参数。如仅提供一个参数来调用map函数。
如果我们只提供一个函数来调用map会怎么样?我们无法得到所要的结果,只能得到一个新的一元函数,通过调用这个函数并传递缺失的参数后,才能获得结果。
假设现在我们只提供一个参数给map,这个参数是unaryFn。我们从后往前逐步实现,首先map函数创建一个包装函数*

    var mapWrapper(list,unaryFn){
        return map(list,unaryFn);
    }

然后,我们将这个二元函数分割成两个嵌套的一元函数:

    function mapWrapper(unaryFn){
        return function(list){
            return map(list,unaryFn);
        }
    }  

这样一来,我们就能每次传递一个参数来进行调用:

    mapWrapper(square)([1,2,3]);
    //=>[1,4,9]

和之前的map函数相较,新的函数mapWrapper是一元函数,它的返回值是另一个一元函数,需要再次调用它才能获得返回值。那么偏应用要从何体现?让我们从第二个一元函数着手:

    var squareAll = mapWrapper(square);
    //=>[function]

    squareAll([1,2,3]);
    //=>[1,4,9]  

我们首先将square这个参数部分应用到了map函数,并获得一个一元函数squareAll,它能实现我们需要的功能。如果每次想要使用偏应用都需要手动编写这样一个包装函数,显然我们希望能自动化实现它。这就是下一节的内容:柯里化。

三、柯里化

首先,我们可以编写一个函数来返回包装器。我们仍以二元函数为例:

    function wrapper(unaryFn){
        return function(list){
            return map(list,unaryFn);
        }
    }  

将函数map和参数名称替换掉

    function wrapper(secondArg){
        return function(firstArg){
            return binaryFn(firstArg,secondArg);
        }
    }  

最后,我们再包装一层:

    function rightmostCurry(binaryFn){
        return function(secondArg){
            return function(firstArg){
                return binaryFn(firstArg,secondArg);
            }
        }
    }  

这样一来,我们之前使用的模式就抽象出来了。这个函数的用法是:

    var rightmostCurriedMap = rightmostCurry(map);
    var squareAll = rightmostCurriedMap(square);

    squareAll([1,4,9]);
    //=>[1,4,9]  

将一个多元函数转换成一系列一元函数的嵌套调用,这种转换称之为柯里化。

四、柯里化和偏应用的区别

1、柯里化是将一个多元函数分解为一系列嵌套调用的一元函数。分解后,你可以部分应用一个或多个参数。柯里化过程不会向函数传递参数。

2、偏应用是为一个多元函数提供部分参数,从而在调用时可以省略这些参数。

Fork me on GitHub