名词解释
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
☜☜☜ wechat 『『『 reward 点击扫码打赏 ~~~ ^_^ 』』』alipay ☞☞☞