我经常在网上看到各种各样的抱怨,说其他人的套用例子并不是套用,而实际上只是部分应用。

我还没有找到一个像样的解释来解释什么是部分应用,或者它与咖喱有什么不同。这似乎是一种普遍的混淆,类似的例子在一些地方被描述为套用,在另一些地方被描述为部分应用。

谁能给我提供这两个术语的定义,以及它们之间的区别?


当前回答

curry是一个只有一个参数的函数,它接受一个函数f,并返回一个新函数h。注意,h接受一个X的参数,并返回一个将Y映射到Z的函数:

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

部分应用是一个有两个(或多个)参数的函数,它接受一个函数f和一个或多个附加参数,并返回一个新函数g:

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

出现混淆是因为对于一个双参数函数,下面的等式成立:

partial(f, a) = curry(f)(a)

两边都将产生相同的单参数函数。

对于更高的函数,相等性不成立,因为在这种情况下,curry将返回一个单参数函数,而partial应用将返回一个多参数函数。

不同之处在于行为上,curry会递归地转换整个原始函数(每个参数一次),而局部应用只是一步替换。

来源:维基百科。

其他回答

要了解它们的不同之处,最简单的方法是考虑一个真实的例子。让我们假设我们有一个函数Add,它接受2个数字作为输入,并返回一个数字作为输出,例如Add(7,5)返回12。在这种情况下:

Partial applying the function Add with a value 7 will give us a new function as output. That function itself takes 1 number as input and outputs a number. As such: Partial(Add, 7); // returns a function f2 as output // f2 takes 1 number as input and returns a number as output So we can do this: f2 = Partial(Add, 7); f2(5); // returns 12; // f2(7)(5) is just a syntactic shortcut Currying the function Add will give us a new function as output. That function itself takes 1 number as input and outputs yet another new function. That third function then takes 1 number as input and returns a number as output. As such: Curry(Add); // returns a function f2 as output // f2 takes 1 number as input and returns a function f3 as output // i.e. f2(number) = f3 // f3 takes 1 number as input and returns a number as output // i.e. f3(number) = number So we can do this: f2 = Curry(Add); f3 = f2(7); f3(5); // returns 12

换句话说,“套用”和“局部应用”是两种完全不同的功能。curry只需要1个输入,而partial应用程序需要2个(或更多)输入。

尽管它们都返回一个函数作为输出,但返回的函数是完全不同的形式,如上所述。

curry是将一个有n个参数的函数转换为n个每个函数都有一个参数的函数。给定以下函数:

function f(x,y,z) { z(x(y));}

咖喱后,变成:

function f(x) { lambda(y) { lambda(z) { z(x(y)); } } }

为了得到f(x,y,z)的完整应用,你需要这样做:

f(x)(y)(z);

许多函数式语言允许你写fx y z。如果你只调用fx y或f(x)(y),那么你会得到一个部分应用的函数——返回值是lambda(z){z(x(y))}的闭包,将x和y的值传递给f(x,y)。

使用部分应用的一种方法是将函数定义为广义函数的部分应用,如fold:

function fold(combineFunction, accumulator, list) {/* ... */}
function sum     = curry(fold)(lambda(accum,e){e+accum}))(0);
function length  = curry(fold)(lambda(accum,_){1+accum})(empty-list);
function reverse = curry(fold)(lambda(accum,e){concat(e,accum)})(empty-list);

/* ... */
@list = [1, 2, 3, 4]
sum(list) //returns 10
@f = fold(lambda(accum,e){e+accum}) //f = lambda(accumulator,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10

curry是一个只有一个参数的函数,它接受一个函数f,并返回一个新函数h。注意,h接受一个X的参数,并返回一个将Y映射到Z的函数:

curry(f) = h 
f: (X x Y) -> Z 
h: X -> (Y -> Z)

部分应用是一个有两个(或多个)参数的函数,它接受一个函数f和一个或多个附加参数,并返回一个新函数g:

part(f, 2) = g
f: (X x Y) -> Z 
g: Y -> Z

出现混淆是因为对于一个双参数函数,下面的等式成立:

partial(f, a) = curry(f)(a)

两边都将产生相同的单参数函数。

对于更高的函数,相等性不成立,因为在这种情况下,curry将返回一个单参数函数,而partial应用将返回一个多参数函数。

不同之处在于行为上,curry会递归地转换整个原始函数(每个参数一次),而局部应用只是一步替换。

来源:维基百科。

curry和partial application之间的区别可以通过下面的JavaScript示例来最好地说明:

function f(x, y, z) {
    return x + y + z;
}

var partial = f.bind(null, 1);

6 === partial(2, 3);

局部应用的结果是一个更小的函数;在上面的例子中,f的arity是3,而partial的arity只有2。更重要的是,部分应用的函数将在被调用时立即返回结果,而不是沿着curry链向下的另一个函数。所以如果你看到的是偏(2)偏(3),实际上这不是偏应用。

进一步阅读:

函数式编程5分钟 咖喱:与部分函数应用的对比

局部套用

维基百科上说

curry是一种将带有多个参数的函数转换为带有单个参数的函数序列的技术。

例子

const add = (a, b) => a + b

const addC = (a) => (b) => a + b // curried function. Where C means curried

部分应用程序

文章刚刚好FP:部分应用

部分应用是将部分(而不是全部)参数应用到函数,并返回一个新函数等待其余参数。这些应用的参数存储在闭包中,并且将来对任何部分应用的返回函数仍然可用。

例子

const add = (a) => (b) => a + b

const add3 = add(3) // add3 is a partially applied function

add3(5) // 8

区别在于

咖喱是一种技术(模式) Partial application是一个带有一些预定义参数的函数(如前面示例中的add3)