Curry(局部套用) or Partial Application(局部应用)?

名词解释

Partial Application,也译作“偏应用”或“部分应用”。
Curring,也译作“柯里化”。
arity,元。表示函数的参数数量。如,unary(一元),binary(二元),ternary(三元),polyadic(多元)。

完全应用

假设有一个函数A,其需要n个参数来应用,而当我们调用函数A时,给它传入了n个参数。则A函数完全应用了我们传入的参数。

局部应用

即函数调用的时候只提供部分参数供其应用。例如以下:

    function mapWith(fn){
        return function(list){
            return map(list,fn);
        }
    }
如上,当我们调用mapWith函数时,先传入一个参数,其返回一个函数结果,执行该结果的时候再传入第二个参数,返回最终结果。类似于:
    mapWith(square)([1,2,3]);

局部套用

上面的局部应用,其中的map函数为一个我们已知的具体函数,我们可以稍加修整,如下:
第一步,先将map用一个更抽象的函数binaryFn取代,暗示此处可以做任何一种二元函数的处理。同时,最外层的函数名,也就可以做相应的调整了,我们可以使用wrapper取代之。
第二步,既然用来最终处理的函数都抽象化了,那传入的参数自然也没有必要限定其类型。
于是,我们可以将其稍微调整为如下状态:

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

到这里,会突然发现其中的binaryFn函数并未做初始化,无法灵活的使用,于是我们可以再包装一层:


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

到现在,我们可以试着调用一下rightmostCurry函数。


var rightmostCurriedMap = rightmostCurry(map);
var squareAll = rightmostCurriedMap(square);
squareAll([2, 3, 5]); // => [4, 9, 25]
squareAll([1, 4, 7, 6]); // => [1, 16, 49, 36]

对比局部应用会发现,在局部应用中,最内层的处理函数是确定的。而在局部套用中,最内层的实际处理函数,是未知的,其最终结果是在最外层函数调用后,分批传入其他参数来获取的。
由此可以做如下解释:

  • 局部应用: 返回最终结果的处理方式是限定的,每一层的函数调用所传入
    的参数都将逐次参与最终处理过程中去;
  • 局部套用: 返回最终结果的处理方式是未知的,需要我们在使用的时候将
    其作为参数传入。

leftmost(最左形式)

仔细观察上面的函数,可能会有疑问,在中间层函数中,把参数调换会出现什么情况呢?先把函数改造下,如下:


function leftmostCurry(binaryFn) {
return function (firstArg) {
return function (secondArg) {
return binaryFn(firstArg, secondArg);
};
};
}
var leftmostCurriedMap = leftmostCurry(map);

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

var oneToThreeEach = leftmostCurriedMap([1, 2, 3]);
oneToThreeEach(square);   // => [1, 4, 9]
oneToThreeEach(double);   // => [2, 4, 6]


上面就是最左形式的局部套用示例用法。
由于一般代码逻辑都是“从左向右”,因此在其他项目中见过比较多的应该是leftmost形式。习惯上直接把leftmost形式叫做curry。

现实使用

现实中用到局部套用情况一般可能做如下处理:


function rightmostUnaryPartialApplication(binaryFn, secondArg) {
return function (firstArg) {
return binaryFn(firstArg, secondArg);
};
}
var applyLast = rightmostUnaryPartialApplication;

var squareAll = applyLast(map, square);
var doubleAll = applyLast(map, double);
squareAll([1,2,3]);
doubleAll([1,2,3]);


以上用法,仔细观察会发现有很多开发者都在多次使用,希望在用到或者看到这类用法时,能真正理解其区别及联系。

参考文献

函数式编程中局部应用(Partial Application)和局部套用(Currying)的区别
Curry or Partial Application? Eric ElliottFollow Jul 20, 2015



money☜☜☜ wechat 『『『 reward 点击扫码打赏 ~~~ ^_^ 』』』alipay ☞☞☞ money