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

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

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


当前回答

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

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

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

其他回答

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是一个只有一个参数的函数,它接受一个函数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都涉及向函数提供参数,可能不是一次全部提供。一个相当典型的例子是两个数字相加。在伪代码(实际上是没有关键字的JS)中,基函数可能如下所示:

add = (x, y) => x + y

如果我想要一个“addOne”函数,我可以部分应用它或curry它:

addOneC = curry(add, 1)
addOneP = partial(add, 1)

现在使用它们是很清楚的:

addOneC(2) #=> 3
addOneP(2) #=> 3

那么有什么不同呢?好吧,这很微妙,但部分应用程序涉及提供一些参数,然后返回的函数将在下次调用时执行主函数,而curry将一直等待,直到它拥有所有必要的参数:

curriedAdd = curry(add) # notice, no args are provided
addOne = curriedAdd(1) # returns a function that can be used to provide the last argument
addOne(2) #=> returns 3, as we want

partialAdd = partial(add) # no args provided, but this still returns a function
addOne = partialAdd(1) # oops! can only use a partially applied function once, so now we're trying to add one to an undefined value (no second argument), and we get an error

简而言之,使用partial application预填充一些值,知道下次调用该方法时,它将执行,所有未提供的参数都未定义;当您希望连续多次返回部分应用的函数以实现函数签名时,请使用curry。最后一个人为的例子:

curriedAdd = curry(add)
curriedAdd()()()()()(1)(2) # ugly and dumb, but it works

partialAdd = partial(add)
partialAdd()()()()()(1)(2) # second invocation of those 7 calls fires it off with undefined parameters

希望这能有所帮助!

更新:一些语言或库实现将允许您传递一个arity(最终计算中的参数总数)到部分应用程序实现,这可能会将我的两个描述合并成令人困惑的混乱…但在这一点上,这两种技术在很大程度上是可以互换的。

这里还有其他很好的答案,但我相信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 );
}

局部套用

维基百科上说

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)