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

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

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


当前回答

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分钟 咖喱:与部分函数应用的对比

其他回答

注意:本文摘自f# Basics,这是一篇非常好的介绍性文章,供。net开发人员学习函数式编程。

Currying means breaking a function with many arguments into a series of functions that each take one argument and ultimately produce the same result as the original function. Currying is probably the most challenging topic for developers new to functional programming, particularly because it is often confused with partial application. You can see both at work in this example: let multiply x y = x * y let double = multiply 2 let ten = double 5 Right away, you should see behavior that is different from most imperative languages. The second statement creates a new function called double by passing one argument to a function that takes two. The result is a function that accepts one int argument and yields the same output as if you had called multiply with x equal to 2 and y equal to that argument. In terms of behavior, it’s the same as this code: let double2 z = multiply 2 z Often, people mistakenly say that multiply is curried to form double. But this is only somewhat true. The multiply function is curried, but that happens when it is defined because functions in F# are curried by default. When the double function is created, it’s more accurate to say that the multiply function is partially applied. The multiply function is really a series of two functions. The first function takes one int argument and returns another function, effectively binding x to a specific value. This function also accepts an int argument that you can think of as the value to bind to y. After calling this second function, x and y are both bound, so the result is the product of x and y as defined in the body of double. To create double, the first function in the chain of multiply functions is evaluated to partially apply multiply. The resulting function is given the name double. When double is evaluated, it uses its argument along with the partially applied value to create the result.

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和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分钟 咖喱:与部分函数应用的对比

有趣的问题。经过一番搜索,“部分函数应用程序不是咖喱”给出了我找到的最好的解释。我不能说实际的区别对我来说特别明显,但我不是一个FP专家……

另一个看起来很有用的页面(我承认我还没有完全读完)是“用Java闭包curry和Partial Application”。

注意,这看起来确实是一对被广泛混淆的术语。

这里还有其他很好的答案,但我相信Java中的这个例子(根据我的理解)可能对一些人有益:

public static <A,B,X> Function< B, X > partiallyApply( BiFunction< A, B, X > aBiFunction, A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< X > partiallyApply( Function< A, X > aFunction, A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  A, Function< B, X >  > curry( BiFunction< A, B, X > bif ){
    return a -> partiallyApply( bif, a );
}

因此curry为您提供了一个单参数函数来创建函数,而partial-application则创建一个包装器函数,该包装器函数硬编码一个或多个参数。

如果你想复制和粘贴,下面的文件比较吵闹,但是使用起来比较友好,因为类型比较宽松:

public static <A,B,X> Function< ? super B, ? extends X > partiallyApply( final BiFunction< ? super A, ? super B, X > aBiFunction, final A aValue ){
    return b -> aBiFunction.apply( aValue, b );
}

public static <A,X> Supplier< ? extends X > partiallyApply( final Function< ? super A, X > aFunction, final A aValue ){
    return () -> aFunction.apply( aValue );
}

public static <A,B,X> Function<  ? super A,  Function< ? super B, ? extends X >  > curry( final BiFunction< ? super A, ? super B, ? extends X > bif ){
    return a -> partiallyApply( bif, a );
}