柯里化和部分应用有什么区别?
我经常在互联网上看到各种抱怨,认为其他人的柯里化示例不是柯里化,而实际上只是部分应用。
我还没有找到关于什么是部分应用或者它与柯里化有何不同的合理解释。 似乎存在普遍的混乱,等效的示例在某些地方被描述为柯里化,而在其他地方则被描述为部分应用。
有人能给我提供这两个术语的定义以及它们有何不同的详细信息吗?
I quite often see on the Internet various complaints that other peoples examples of currying are not currying, but are actually just partial application.
I've not found a decent explanation of what partial application is, or how it differs from currying. There seems to be a general confusion, with equivalent examples being described as currying in some places, and partial application in others.
Could someone provide me with a definition of both terms, and details of how they differ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
柯里化是将 n 个参数的单个函数转换为 n 个函数,每个函数只有一个参数。 给定以下函数:
当柯里化时,变为:
为了获得 f(x,y,z) 的完整应用,您需要执行以下操作:
许多函数式语言允许您编写
fxy z
。 如果您只调用fx y
或 f(x)(y),那么您将得到一个部分应用的函数 - 返回值是 lambda(z) 的闭包){z(x(y))},将 x 和 y 的值传递给f(x,y)
。使用部分应用的一种方法是将函数定义为广义函数的部分应用,例如 fold:
Currying is converting a single function of n arguments into n functions with a single argument each. Given the following function:
When curried, becomes:
In order to get the full application of f(x,y,z), you need to do this:
Many functional languages let you write
f x y z
. If you only callf x y
or f(x)(y) then you get a partially-applied function—the return value is a closure oflambda(z){z(x(y))}
with passed-in the values of x and y tof(x,y)
.One way to use partial application is to define functions as partial applications of generalized functions, like fold:
了解它们有何不同的最简单方法是考虑一个真实示例。 假设我们有一个函数
Add
,它接受 2 个数字作为输入并返回一个数字作为输出,例如Add(7, 5)
返回12
。 在本例中:部分应用值为
7
的函数Add
将为我们提供一个新函数作为输出。 该函数本身接受 1 个数字作为输入并输出一个数字。 因此:所以我们可以这样做:
柯里化函数
Add
将为我们提供一个新函数作为输出。 该函数本身接受 1 个数字作为输入并输出另一个新函数。 然后,第三个函数将 1 个数字作为输入并返回一个数字作为输出。 因此:所以我们可以这样做:
换句话说,“柯里化”和“偏应用”是两个完全不同的函数。 柯里化仅需要 1 个输入,而部分应用需要 2 个(或更多)输入。
尽管它们都返回一个函数作为输出,但返回的函数具有完全不同的形式,如上所示。
The easiest way to see how they differ is to consider a real example. Let's assume that we have a function
Add
which takes 2 numbers as input and returns a number as output, e.g.Add(7, 5)
returns12
. In this case:Partial applying the function
Add
with a value7
will give us a new function as output. That function itself takes 1 number as input and outputs a number. As such:So we can do this:
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:So we can do this:
In other words, "currying" and "partial application" are two totally different functions. Currying takes exactly 1 input, whereas partial application takes 2 (or more) inputs.
Even though they both return a function as output, the returned functions are of totally different forms as demonstrated above.
注意:本文摘自 F# 基础知识,这是一篇针对 .NET 开发人员进入函数式编程的优秀介绍性文章。
Note: this was taken from F# Basics an excellent introductory article for .NET developers getting into functional programming.
有趣的问题。 经过一番搜索,"部分功能应用是not currying” 给出了我发现的最好的解释。 我不能说实际的差异对我来说特别明显,但我不是 FP 专家......
另一个看起来有用的页面(我承认我还没有完全阅读) ) 是“使用 Java 闭包进行柯里化和部分应用程序”。
请注意,这看起来确实是一对被广泛混淆的术语。
Interesting question. After a bit of searching, "Partial Function Application is not currying" gave the best explanation I found. I can't say that the practical difference is particularly obvious to me, but then I'm not an FP expert...
Another useful-looking page (which I confess I haven't fully read yet) is "Currying and Partial Application with Java Closures".
It does look like this is widely-confused pair of terms, mind you.
我已经在另一个线程 https://stackoverflow.com/a/12846865/1685865 中回答了这个问题。 简而言之,偏函数应用是关于修复给定多变量函数的一些参数,以产生另一个参数更少的函数,而柯里化是将 N 个参数的函数转换为返回一元函数的一元函数...[的例子柯里化在这篇文章的末尾展示。]
柯里化主要具有理论意义:人们可以仅使用一元函数来表达计算(即每个函数都是一元的)。 在实践中,作为副产品,如果语言具有柯里化函数,那么这种技术可以使许多有用的(但不是全部)部分功能应用程序变得微不足道。 再次强调,它并不是实现部分应用程序的唯一方法。 因此,您可能会遇到以其他方式完成部分应用的情况,但人们将其误认为是柯里化。
(柯里化示例)
在实践中,人们不会只是
编写或等效的 javascript 。
而
为了柯里化
I have answered this in another thread https://stackoverflow.com/a/12846865/1685865 . In short, partial function application is about fixing some arguments of a given multivariable function to yield another function with fewer arguments, while Currying is about turning a function of N arguments into a unary function which returns a unary function...[An example of Currying is shown at the end of this post.]
Currying is mostly of theoretical interest: one can express computations using only unary functions (i.e. every function is unary). In practice and as a byproduct, it is a technique which can make many useful (but not all) partial functional applications trivial, if the language has curried functions. Again, it is not the only means to implement partial applications. So you could encounter scenarios where partial application is done in other way, but people are mistaking it as Currying.
(Example of Currying)
In practice one would not just write
or the equivalent javascript
instead of
for the sake of Currying.
柯里化是一个一个参数的函数,它接受一个函数
f
并返回一个新函数h
。 请注意,h
从X
获取参数并返回将Y
映射到Z 的函数 code>:
偏应用是一个具有两个(或更多)参数的函数,它接受一个函数
f
和一个或多个附加参数f
并且返回一个新函数g
:之所以会出现混乱,是因为对于双参数函数,以下等式成立:
双方都会产生相同的单参数函数。
对于更高数量的函数来说,等式并不成立,因为在这种情况下,柯里化将返回一个单参数函数,而部分应用程序将返回一个多参数函数。
区别还在于行为,而柯里化会递归地转换整个原始函数(每个参数一次),部分应用只是一步替换。
资料来源:维基百科柯里化。
Currying is a function of one argument which takes a function
f
and returns a new functionh
. Note thath
takes an argument fromX
and returns a function that mapsY
toZ
:Partial application is a function of two(or more) arguments which takes a function
f
and one or more additional arguments tof
and returns a new functiong
:The confusion arises because with a two-argument function the following equality holds:
Both sides will yield the same one-argument function.
The equality is not true for higher arity functions because in this case currying will return a one-argument function, whereas partial application will return a multiple-argument function.
The difference is also in the behavior, whereas currying transforms the whole original function recursively(once for each argument), partial application is just a one step replacement.
Source: Wikipedia Currying.
简单的答案
Curry:允许您调用一个函数,将其拆分为多个调用,每次调用提供一个参数。
部分:允许您调用一个函数,将其拆分为多个调用,并为每个调用提供多个参数。
简单提示
两者都允许您调用提供较少参数的函数(或者更好的是,累积地提供它们)。 实际上,它们(在每次调用时)都将特定值绑定到函数的特定参数。
当函数有超过 2 个参数时,可以看到真正的区别。
简单的 e(c)(sample)
(Javascript)
我们希望在不同的
主题
上运行以下process
函数(例如,假设我们的主题是"subject1"
和"foobar"
字符串):如果参数始终相同,为什么总是传递参数(例如上下文和回调)?
只需为函数绑定一些值:
并在 subject1 和 foobar 上调用它,省略前 3 个参数的重复,使用:
舒适,不是吗?
Simple answer
Curry: lets you call a function, splitting it in multiple calls, providing one argument per-call.
Partial: lets you call a function, splitting it in multiple calls, providing multiple arguments per-call.
Simple hints
Both allow you to call a function providing less arguments (or, better, providing them cumulatively). Actually both of them bind (at each call) a specific value to specific arguments of the function.
The real difference can be seen when the function has more than 2 arguments.
Simple e(c)(sample)
(in Javascript)
We want to run the following
process
function on differentsubject
s (e.g. let's say our subjects are"subject1"
and"foobar"
strings):why always passing the arguments, like context and the callbacks, if they will be always the same?
Just bind some values for the the function:
and call it on subject1 and foobar, omitting the repetition of the first 3 arguments, with:
Comfy, isn't it? ????
With currying you'd instead need to pass one argument per time
Disclaimer
I skipped all the academic/mathematical explanation. Cause I don't know it. Maybe it helped ????
EDIT:
As added by @basickarl, a further slight difference in use of the two functions (see Lodash for examples) is that:
partial
returns a pre-cooked function that can be called once with the missing argument(s) and return the final result;curry
is being called multiple times (one for each argument), returning a pre-cooked function each time; except in the case of calling with the last argument, that will return the actual result from the processing of all the arguments.With ES6:
here's a quick example of how immediate Currying and Partial-application are in ECMAScript 6.
柯里化和偏应用之间的区别可以通过以下 JavaScript 示例得到最好的说明:
偏应用导致函数的数量较小; 在上面的示例中,
f
的元数为 3,而partial
的元数仅为 2。更重要的是,部分应用的函数将立即返回结果在被调用时,而不是柯里化链中的另一个函数。 因此,如果您看到类似partial(2)(3)
的内容,实际上它并不是部分应用程序。进一步阅读:
The difference between curry and partial application can be best illustrated through this following JavaScript example:
Partial application results in a function of smaller arity; in the example above,
f
has an arity of 3 whilepartial
only has an arity of 2. More importantly, a partially applied function would return the result right away upon being invoke, not another function down the currying chain. So if you are seeing something likepartial(2)(3)
, it's not partial application in actuality.Further reading:
我在学习的过程中经常遇到这个问题,并且已经被问过很多次了。 我描述差异的最简单方法是两者相同:)让我解释一下......显然存在差异。
部分应用和柯里化都涉及向函数提供参数,也许不是一次全部提供。 一个相当典型的例子是两个数字相加。 在伪代码(实际上是没有关键字的 JS)中,基本函数可能如下:
如果我想要一个“addOne”函数,我可以部分应用它或柯里化它:
现在使用它们很清楚:
那么有什么区别呢? 嗯,这很微妙,但是部分应用程序涉及提供一些参数,然后返回的函数将在下一次调用时执行主函数,而柯里化将一直等待,直到它拥有所有必需的参数:
简而言之,使用部分应用程序预填充一些值,知道下次调用该方法时,它将执行,使所有未提供的参数保持未定义; 当您想要根据需要多次返回部分应用的函数来满足函数签名时,请使用柯里化。 最后一个人为的例子:
希望这有帮助!
更新:某些语言或 lib 实现将允许您将 arity(最终评估中的参数总数)传递给部分应用程序实现,这可能会将我的两个描述混为一团……但在这一点上,这两种技术是很大程度上可以互换。
I had this question a lot while learning and have since been asked it many times. The simplest way I can describe the difference is that both are the same :) Let me explain...there are obviously differences.
Both partial application and currying involve supplying arguments to a function, perhaps not all at once. A fairly canonical example is adding two numbers. In pseudocode (actually JS without keywords), the base function may be the following:
If I wanted an "addOne" function, I could partially apply it or curry it:
Now using them is clear:
So what's the difference? Well, it's subtle, but partial application involves supplying some arguments and the returned function will then execute the main function upon next invocation whereas currying will keep waiting till it has all the arguments necessary:
In short, use partial application to prefill some values, knowing that the next time you call the method, it will execute, leaving undefined all unprovided arguments; use currying when you want to continually return a partially-applied function as many times as necessary to fulfill the function signature. One final contrived example:
Hope this helps!
UPDATE: Some languages or lib implementations will allow you to pass an arity (total number of arguments in final evaluation) to the partial application implementation which may conflate my two descriptions into a confusing mess...but at that point, the two techniques are largely interchangeable.
这里很多人都没有正确解决这个问题,也没有人谈论重叠。
简单答案
柯里化:让您调用一个函数,将其拆分为多个调用,每次调用提供一个参数。
部分应用程序:允许您调用一个函数,将其拆分为多个调用,并为每个调用提供多个参数。
这意味着什么? 这意味着最多有两次对部分函数的调用。 柯里化有多少参数就有多少。 如果柯里化函数只有两个参数,那么它本质上与偏函数相同。
示例
部分应用和柯里化
部分应用
柯里化
命名约定
等我有时间的时候我会写这个,很快就会的。
A lot of people here do not address this properly, and no one has talked about overlaps.
Simple answer
Currying: Lets you call a function, splitting it in multiple calls, providing one argument per-call.
Partial Application: Lets you call a function, splitting it in multiple calls, providing multiple arguments per-call.
What does that mean? That means that there are max two calls for a partial function. Currying has as many as the amount of arguments. If the currying function only has two arguments, then it is essentially the same as a partial function.
Examples
Partial Application and Currying
Partial Application
Currying
Naming conventions
I'll write this when I have time, which is soon.
对我来说,部分应用程序必须创建一个新函数,其中使用的参数完全集成到结果函数中。
大多数函数式语言通过返回闭包来实现柯里化:部分应用时不要在 lambda 下求值。 因此,为了让部分应用变得有趣,我们需要区分柯里化和部分应用,并将部分应用视为柯里化加上 lambda 下的求值。
For me partial application must create a new function where the used arguments are completely integrated into the resulting function.
Most functional languages implement currying by returning a closure: do not evaluate under lambda when partially applied. So, for partial application to be interesting, we need to make a difference between currying and partial application and consider partial application as currying plus evaluation under lambda.
我在这里可能是非常错误的,因为我在理论数学或函数式编程方面没有很强的背景,但从我对 FP 的短暂涉足来看,柯里化似乎倾向于将 N 个参数的函数变成一个参数的 N 个函数,而部分应用[在实践中]对于参数数量不确定的可变参数函数效果更好。 我知道以前的答案中的一些示例无法解释这一解释,但它对我区分概念的帮助最大。 考虑这个例子(为简洁起见,用 CoffeeScript 编写,如果它进一步令人困惑,我很抱歉,但如果需要,请要求澄清):
这显然是一个人为的示例,但请注意,部分应用接受任意数量参数的函数允许我们执行一个函数但有一些初步数据。 柯里化函数是类似的,但允许我们分段执行一个 N 参数函数,直到(但仅直到)所有 N 个参数都被考虑在内。
再说一次,这是我从我读过的内容中得出的结论。 如果有人不同意,我希望能评论一下原因,而不是立即投反对票。 另外,如果 CoffeeScript 难以阅读,请访问 CoffeeScript.org,单击“尝试 CoffeeScript”并粘贴我的代码以查看编译版本,这可能(希望)更有意义。 谢谢!
I could be very wrong here, as I don't have a strong background in theoretical mathematics or functional programming, but from my brief foray into FP, it seems that currying tends to turn a function of N arguments into N functions of one argument, whereas partial application [in practice] works better with variadic functions with an indeterminate number of arguments. I know some of the examples in previous answers defy this explanation, but it has helped me the most to separate the concepts. Consider this example (written in CoffeeScript for succinctness, my apologies if it confuses further, but please ask for clarification, if needed):
This is obviously a contrived example, but notice that partially applying a function that accepts any number of arguments allows us to execute a function but with some preliminary data. Currying a function is similar but allows us to execute an N-parameter function in pieces until, but only until, all N parameters are accounted for.
Again, this is my take from things I've read. If anyone disagrees, I would appreciate a comment as to why rather than an immediate downvote. Also, if the CoffeeScript is difficult to read, please visit coffeescript.org, click "try coffeescript" and paste in my code to see the compiled version, which may (hopefully) make more sense. Thanks!
我假设大多数提出这个问题的人已经熟悉基本概念,所以他们没有必要谈论这个。 重叠部分是令人困惑的部分。
您也许能够充分使用这些概念,但您将它们一起理解为这种伪原子无定形概念模糊。 缺少的是知道它们之间的边界在哪里。
与其定义每一个是什么,更容易强调它们的差异——边界。
柯里化是指定义函数。
部分应用是当您调用函数时。
应用程序是调用函数的数学术语。
部分应用程序需要调用柯里化函数并获取函数作为返回类型。
I'm going to assume most people who ask this question are already familiar with the basic concepts so their is no need to talk about that. It's the overlap that is the confusing part.
You might be able to fully use the concepts, but you understand them together as this pseudo-atomic amorphous conceptual blur. What is missing is knowing where the boundary between them is.
Instead of defining what each one is, it's easier to highlight just their differences—the boundary.
Currying is when you define the function.
Partial Application is when you call the function.
Application is math-speak for calling a function.
Partial application requires calling a curried function and getting a function as the return type.
这里还有其他很好的答案,但我相信 Java 中的这个示例(根据我的理解)可能对某些人有益:
因此柯里化为您提供了一个单参数函数来创建函数,其中部分应用程序创建了一个包装器硬编码一个或多个参数的函数。
如果你想复制和粘贴,下面的代码会更吵闹,但使用起来更友好,因为类型更宽松:
There are other great answers here but I believe this example (as per my understanding) in Java might be of benefit to some people:
So currying gives you a one-argument function to create functions, where partial-application creates a wrapper function that hard codes one or more arguments.
If you want to copy&paste, the following is noisier but friendlier to work with since the types are more lenient:
柯里化
维基百科说
文章
部分应用示例
Just Enough FP:部分应用
示例
区别在于
currying
是一种技术(模式)部分应用
是一个带有一些预定义参数的函数(如上一个示例中的add3
)Currying
Wikipedia says
Example
Partial application
Article Just Enough FP: Partial Application
Example
The difference is
currying
is a technique (pattern)partial application
is a function with some predefined arguments (likeadd3
from the previous example)在写这篇文章时,我混淆了柯里化和非柯里化。 它们是函数的逆变换。 只要你得到变换及其逆表示的内容,你叫什么并不重要。
非柯里化的定义不是很清楚(或者更确切地说,存在“冲突”的定义,但都抓住了这个想法的精神)。 基本上,这意味着将带有多个参数的函数转换为带有单个参数的函数。 例如,
现在,如何将其转换为采用单个参数的函数? 当然是你作弊啦!
请注意,plus 现在采用单个参数(由两个事物组成)。 极好的!
这有什么意义呢? 好吧,如果您有一个接受两个参数的函数,并且有一对参数,那么很高兴知道您可以将该函数应用于参数,并且仍然得到您所期望的结果。 事实上,执行此操作的管道已经存在,因此您不必执行诸如显式模式匹配之类的操作。 你所要做的就是:
那么什么是偏函数应用呢? 将两个参数的函数转换为只有一个参数的函数是另一种不同的方法。 但它的工作原理有所不同。 再次以(+)为例。 我们如何将它变成一个以单个 Int 作为参数的函数? 我们作弊!
这是给任何 Int 加零的函数。
对任何 Int 加 1。 等等。在每种情况下,(+) 都是“部分应用”。
In writing this, I confused currying and uncurrying. They are inverse transformations on functions. It really doesn't matter what you call which, as long as you get what the transformation and its inverse represent.
Uncurrying isn't defined very clearly (or rather, there are "conflicting" definitions that all capture the spirit of the idea). Basically, it means turning a function that takes multiple arguments into a function that takes a single argument. For example,
Now, how do you turn this into a function that takes a single argument? You cheat, of course!
Notice that plus now takes a single argument (that is composed of two things). Super!
What's the point of this? Well, if you have a function that takes two arguments, and you have a pair of arguments, it is nice to know that you can apply the function to the arguments, and still get what you expect. And, in fact, the plumbing to do it already exists, so that you don't have to do things like explicit pattern matching. All you have to do is:
So what is partial function application? It is a different way to turn a function in two arguments into a function with one argument. It works differently though. Again, let's take (+) as an example. How might we turn it into a function that takes a single Int as an argument? We cheat!
That's the function that adds zero to any Int.
adds 1 to any Int. Etc. In each of these cases, (+) is "partially applied".