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

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

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


当前回答

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会递归地转换整个原始函数(每个参数一次),而局部应用只是一步替换。

来源:维基百科。

我在另一个帖子https://stackoverflow.com/a/12846865/1685865中回答了这个问题。简而言之,部分函数应用是关于固定一个给定的多变量函数的一些参数,以产生另一个具有更少参数的函数,而Currying是关于将一个有N个参数的函数转换为一个返回一元函数的一元函数…[一个curry的例子显示在这篇文章的最后。]

curry主要是理论方面的兴趣:可以只用一元函数来表示计算(即每个函数都是一元函数)。在实践中,它是一种技术,可以使许多有用的(但不是全部)部分函数式应用程序变得微不足道,如果语言有咖喱函数的话。同样,它不是实现部分应用程序的唯一方法。所以你可能会遇到这样的情况,部分应用程序是用其他方式完成的,但人们会把它误认为是curry。

(以咖喱为例)

在实践中,人们不只是写

lambda x: lambda y: lambda z: x + y + z

或者等价的javascript

function (x) { return function (y){ return function (z){ return x + y + z }}}

而不是

lambda x, y, z: x + y + z

为了柯里林。

局部套用

维基百科上说

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)

注意:本文摘自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.

在写这篇文章时,我混淆了咖喱和不咖喱。它们是函数的逆变换。不管你怎么称呼它,只要你知道这个变换和它的逆代表什么。

不咖喱的定义不是很清楚(或者说,有“冲突”的定义都抓住了这个想法的精神)。基本上,这意味着将一个接受多个参数的函数转换为一个接受单个参数的函数。例如,

(+) :: Int -> Int -> Int

现在,你如何把它变成一个只有一个参数的函数呢?你当然作弊了!

plus :: (Int, Int) -> Int

注意,加号现在只有一个参数(由两件事组成)。超级!

这有什么意义?好吧,如果你有一个带两个参数的函数,你有一对参数,知道你可以把函数应用到参数上,并且仍然得到你想要的是很好的。事实上,这样做的管道已经存在,所以你不需要做像显式模式匹配这样的事情。你所要做的就是:

(uncurry (+)) (1,2)

什么是偏函数应用?将有两个参数的函数转换为只有一个参数的函数是另一种方法。但它的工作方式不同。同样,让我们以(+)为例。我们如何将它转换为一个以单个Int作为参数的函数?我们作弊!

((+) 0) :: Int -> Int

这是一个函数,将0加到任何Int。

((+) 1) :: Int -> Int

对任意Int值加1。等。在每一种情况下,(+)是“部分应用”。