柯里化和部分应用有什么区别?

发布于 2024-07-07 04:50:56 字数 181 浏览 9 评论 0原文

我经常在互联网上看到各种抱怨,认为其他人的柯里化示例不是柯里化,而实际上只是部分应用。

我还没有找到关于什么是部分应用或者它与柯里化有何不同的合理解释。 似乎存在普遍的混乱,等效的示例在某些地方被描述为柯里化,而在其他地方则被描述为部分应用。

有人能给我提供这两个术语的定义以及它们有何不同的详细信息吗?

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

梦年海沫深 2024-07-14 04:50:56

柯里化是将 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);

许多函数式语言允许您编写 fxy z。 如果您只调用 fx yf(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

Currying is converting a single function of n arguments into n functions with a single argument each. Given the following function:

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

When curried, becomes:

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

In order to get the full application of f(x,y,z), you need to do this:

f(x)(y)(z);

Many functional languages let you write f x y z. If you only call f x y or f(x)(y) then you get a partially-applied function—the return value is a closure of lambda(z){z(x(y))} with passed-in the values of x and y to f(x,y).

One way to use partial application is to define functions as partial applications of generalized functions, like 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
南城旧梦 2024-07-14 04:50:56

了解它们有何不同的最简单方法是考虑一个真实示例。 假设我们有一个函数 Add,它接受 2 个数字作为输入并返回一个数字作为输出,例如 Add(7, 5) 返回 12。 在本例中:

  • 部分应用值为 7 的函数 Add 将为我们提供一个新函数作为输出。 该函数本身接受 1 个数字作为输入并输出一个数字。 因此:

    部分(添加,7);   // 返回函数 f2 作为输出 
    
                       // f2 接受 1 个数字作为输入并返回一个数字作为输出 
      

    所以我们可以这样做:

    f2 = 部分(添加, 7); 
      f2(5);   // 返回 12; 
             // f2(7)(5) 只是一个语法快捷方式 
      
  • 柯里化函数Add将为我们提供一个新函数作为输出。 该函数本身接受 1 个数字作为输入并输出另一个新函数。 然后,第三个函数将 1 个数字作为输入并返回一个数字作为输出。 因此:

    咖喱(添加);   // 返回函数 f2 作为输出 
    
                  // f2 接受 1 个数字作为输入并返回函数 f3 作为输出 
                  // 即 f2(数字) = f3 
    
                  // f3 接受 1 个数字作为输入并返回一个数字作为输出 
                  // 即 f3(number) = number 
      

    所以我们可以这样做:

    f2 = 咖喱(添加); 
      f3 = f2(7); 
      f3(5);   // 返回 12 
      

换句话说,“柯里化”和“偏应用”是两个完全不同的函数。 柯里化仅需要 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) returns 12. In this case:

  • 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
    

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.

一杆小烟枪 2024-07-14 04:50:56

注意:本文摘自 F# 基础知识,这是一篇针对 .NET 开发人员进入函数式编程的优秀介绍性文章。

柯里化意味着将具有多个参数的函数分解为一系列
每个函数都有一个参数并最终产生
与原始函数的结果相同。 柯里化可能是最多的
对于刚接触函数式编程的开发人员来说,这是一个具有挑战性的话题,特别是因为它
经常与部分应用混淆。 您可以在工作中看到两者
在此示例中:

让 xy = x * y 相乘     
  让 double = 乘以 2 
  让 10 = 双 5 
  

马上,您应该会看到与大多数人不同的行为
命令式语言。 第二条语句创建一个新函数
通过将一个参数传递给一个需要两个参数的函数来调用 double。
结果是一个接受一个 int 参数并产生以下结果的函数
输出与您使用 x 等于 2 和 y 调用相乘相同
等于该论证。 就行为而言,与此相同
代码:

令 double2 z = 乘以 2 z 
  

人们常常错误地认为乘法被柯里化以形成双精度。
但这只是部分正确。 乘法函数是柯里化的,但是
定义时会发生这种情况,因为 F# 中的函数是通过柯里化的
默认。 当创建 double 函数时,更准确的是
说乘法函数被部分应用。

乘法函数实际上是一系列两个函数。 首先
函数接受一个 int 参数并返回另一个函数,
有效地将 x 绑定到特定值。 该函数还接受
一个 int 参数,您可以将其视为绑定到 y 的值。 后
调用第二个函数,x 和 y 都是绑定的,所以结果是
double 主体中定义的 x 和 y 的乘积。

创建 double,即乘法链中的第一个函数
函数被评估以部分应用乘法。 所结果的
函数被命名为 double。 当计算 double 时,它​​使用
它的参数以及部分应用的值来创建
结果。

Note: this was taken from F# Basics an excellent introductory article for .NET developers getting into functional programming.

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.

薆情海 2024-07-14 04:50:56

有趣的问题。 经过一番搜索,"部分功能应用是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.

嘿看小鸭子会跑 2024-07-14 04:50:56

我已经在另一个线程 https://stackoverflow.com/a/12846865/1685865 中回答了这个问题。 简而言之,偏函数应用是关于修复给定多变量函数的一些参数,以产生另一个参数更少的函数,而柯里化是将 N 个参数的函数转换为返回一元函数的一元函数...[的例子柯里化在这篇文章的末尾展示。]

柯里化主要具有理论意义:人们可以仅使用一元函数来表达计算(即每个函数都是一元的)。 在实践中,作为副产品,如果语言具有柯里化函数,那么这种技术可以使许多有用的(但不是全部)部分功能应用程序变得微不足道。 再次强调,它并不是实现部分应用程序的唯一方法。 因此,您可能会遇到以其他方式完成部分应用的情况,但人们将其误认为是柯里化。

(柯里化示例)

在实践中,人们不会只是

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

为了柯里化

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

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

or the equivalent javascript

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

instead of

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

for the sake of Currying.

樱花坊 2024-07-14 04:50:56

柯里化是一个一个参数的函数,它接受一个函数f并返回一个新函数h。 请注意,hX 获取参数并返回将 Y 映射到 Z 的函数 code>:

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

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

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

之所以会出现混乱,是因为对于双参数函数,以下等式成立:

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

双方都会产生相同的单参数函数。

对于更高数量的函数来说,等式并不成立,因为在这种情况下,柯里化将返回一个单参数函数,而部分应用程序将返回一个多参数函数。

区别还在于行为,而柯里化会递归地转换整个原始函数(每个参数一次),部分应用只是一步替换。

资料来源:维基百科柯里化

Currying is a function of one argument which takes a function f and returns a new function h. Note that h takes an argument from X and returns a function that maps Y to Z:

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

Partial application is a function of two(or more) arguments which takes a function f and one or more additional arguments to f and returns a new function g:

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

The confusion arises because with a two-argument function the following equality holds:

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

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.

殊姿 2024-07-14 04:50:56

简单的答案

Curry:允许您调用一个函数,将其拆分为多个调用,每次调用提供一个参数。

部分:允许您调用一个函数,将其拆分为多个调用,并为每个调用提供多个参数。


简单提示

两者都允许您调用提供较少参数的函数(或者更好的是,累积地提供它们)。 实际上,它们(在每次调用时)都将特定值绑定到函数的特定参数。

当函数有超过 2 个参数时,可以看到真正的区别。


简单的 e(c)(sample)

(Javascript)

我们希望在不同的主题上运行以下process函数(例如,假设我们的主题是 "subject1""foobar" 字符串):

function process(context, successCallback, errorCallback, subject) {...}

如果参数始终相同,为什么总是传递参数(例如上下文和回调)?

只需为函数绑定一些值:

processSubject = _.partial(process, my_context, my_success, my_error)
// assign fixed values to the first 3 arguments of the `process` function

并在 subject1foobar 上调用它,省略前 3 个参数的重复,使用:

processSubject('subject1');
processSubject('foobar');

舒适,不是吗?

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 different subjects (e.g. let's say our subjects are "subject1" and "foobar" strings):

function process(context, successCallback, errorCallback, subject) {...}

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:

processSubject = _.partial(process, my_context, my_success, my_error)
// assign fixed values to the first 3 arguments of the `process` function

and call it on subject1 and foobar, omitting the repetition of the first 3 arguments, with:

processSubject('subject1');
processSubject('foobar');

Comfy, isn't it? ????


With currying you'd instead need to pass one argument per time

curriedProcess = _.curry(process);   // make the function curry-able
processWithBoundedContext = curriedProcess(my_context);
processWithCallbacks = processWithBoundedContext(my_success)(my_error); // note: these are two sequential calls

result1 = processWithCallbacks('subject1');
// same as: process(my_context, my_success, my_error, 'subject1');

result2 = processWithCallbacks('foobar'); 
// same as: process(my_context, my_success, my_error, 'foobar');

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;
  • while 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.

const partialSum = math => (eng, geo) => math + eng + geo;
const curriedSum = math => eng => geo => math + eng + geo;
﹏半生如梦愿梦如真 2024-07-14 04:50:56

柯里化和偏应用之间的区别可以通过以下 JavaScript 示例得到最好的说明:

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

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

6 === partial(2, 3);

偏应用导致函数的数量较小; 在上面的示例中,f 的元数为 3,而 partial 的元数仅为 2。更重要的是,部分应用的函数将立即返回结果在被调用时,而不是柯里化链中的另一个函数。 因此,如果您看到类似 partial(2)(3) 的内容,实际上它并不是部分应用程序。

进一步阅读:

The difference between curry and partial application can be best illustrated through this following JavaScript example:

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

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

6 === partial(2, 3);

Partial application results in a function of smaller arity; in the example above, f has an arity of 3 while partial 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 like partial(2)(3), it's not partial application in actuality.

Further reading:

无需解释 2024-07-14 04:50:56

我在学习的过程中经常遇到这个问题,并且已经被问过很多次了。 我描述差异的最简单方法是两者相同:)让我解释一下......显然存在差异。

部分应用和柯里化都涉及向函数提供参数,也许不是一次全部提供。 一个相当典型的例子是两个数字相加。 在伪代码(实际上是没有关键字的 JS)中,基本函数可能如下:

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

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

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

现在使用它们很清楚:

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

那么有什么区别呢? 嗯,这很微妙,但是部分应用程序涉及提供一些参数,然后返回的函数将在下一次调用时执行主函数,而柯里化将一直等待,直到它拥有所有必需的参数:

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

简而言之,使用部分应用程序预填充一些值,知道下次调用该方法时,它将执行,使所有未提供的参数保持未定义; 当您想要根据需要多次返回部分应用的函数来满足函数签名时,请使用柯里化。 最后一个人为的例子:

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

希望这有帮助!

更新:某些语言或 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:

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

If I wanted an "addOne" function, I could partially apply it or curry it:

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

Now using them is clear:

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

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:

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

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:

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

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.

云仙小弟 2024-07-14 04:50:56

这里很多人都没有正确解决这个问题,也没有人谈论重叠。

简单答案

柯里化:让您调用一个函数,将其拆分为多个调用,每次调用提供一个参数。

部分应用程序:允许您调用一个函数,将其拆分为多个调用,并为每个调用提供多个参数。

两者之间的显着区别之一是调用
部分应用函数立即返回结果,而不是另一个
沿着柯里化链向下运行; 这种区别可以说明
显然对于参数数大于二的函数。

这意味着什么? 这意味着最多有两次对部分函数的调用。 柯里化有多少参数就有多少。 如果柯里化函数只有两个参数,那么它本质上与偏函数相同。

示例

部分应用和柯里化

function bothPartialAndCurry(firstArgument) {
    return function(secondArgument) {
        return firstArgument + secondArgument;
    }
}

const partialAndCurry = bothPartialAndCurry(1);
const result = partialAndCurry(2);

部分应用

function partialOnly(firstArgument, secondArgument) {
    return function(thirdArgument, fourthArgument, fifthArgument) {
        return firstArgument + secondArgument + thirdArgument + fourthArgument + fifthArgument;
    }
}

const partial = partialOnly(1, 2);
const result = partial(3, 4, 5);

柯里化

function curryOnly(firstArgument) {
    return function(secondArgument) {
        return function(thirdArgument) {
            return function(fourthArgument ) {
                return function(fifthArgument) {
                    return firstArgument + secondArgument + thirdArgument + fourthArgument + fifthArgument;
                }
            }
        }
    }
}

const curryFirst = curryOnly(1);
const currySecond = curryFirst(2);
const curryThird = currySecond(3);
const curryFourth = curryThird(4);
const result = curryFourth(5);

// or...

const result = curryOnly(1)(2)(3)(4)(5);

命名约定

等我有时间的时候我会写这个,很快就会的。

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.

One of the significant differences between the two is that a call to a
partially applied function returns the result right away, not another
function down the currying chain; this distinction can be illustrated
clearly for functions whose arity is greater than two.

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

function bothPartialAndCurry(firstArgument) {
    return function(secondArgument) {
        return firstArgument + secondArgument;
    }
}

const partialAndCurry = bothPartialAndCurry(1);
const result = partialAndCurry(2);

Partial Application

function partialOnly(firstArgument, secondArgument) {
    return function(thirdArgument, fourthArgument, fifthArgument) {
        return firstArgument + secondArgument + thirdArgument + fourthArgument + fifthArgument;
    }
}

const partial = partialOnly(1, 2);
const result = partial(3, 4, 5);

Currying

function curryOnly(firstArgument) {
    return function(secondArgument) {
        return function(thirdArgument) {
            return function(fourthArgument ) {
                return function(fifthArgument) {
                    return firstArgument + secondArgument + thirdArgument + fourthArgument + fifthArgument;
                }
            }
        }
    }
}

const curryFirst = curryOnly(1);
const currySecond = curryFirst(2);
const curryThird = currySecond(3);
const curryFourth = curryThird(4);
const result = curryFourth(5);

// or...

const result = curryOnly(1)(2)(3)(4)(5);

Naming conventions

I'll write this when I have time, which is soon.

如梦亦如幻 2024-07-14 04:50:56

对我来说,部分应用程序必须创建一个新函数,其中使用的参数完全集成到结果函数中。

大多数函数式语言通过返回闭包来实现柯里化:部分应用时不要在 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.

久随 2024-07-14 04:50:56

我在这里可能是非常错误的,因为我在理论数学或函数式编程方面没有很强的背景,但从我对 FP 的短暂涉足来看,柯里化似乎倾向于将 N 个参数的函数变成一个参数的 N 个函数,而部分应用[在实践中]对于参数数量不确定的可变参数函数效果更好。 我知道以前的答案中的一些示例无法解释这一解释,但它对我区分概念的帮助最大。 考虑这个例子(为简洁起见,用 CoffeeScript 编写,如果它进一步令人困惑,我很抱歉,但如果需要,请要求澄清):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

这显然是一个人为的示例,但请注意,部分应用接受任意数量参数的函数允许我们执行一个函数但有一些初步数据。 柯里化函数是类似的,但允许我们分段执行一个 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):

# partial application
partial_apply = (func) ->
  args = [].slice.call arguments, 1
  -> func.apply null, args.concat [].slice.call arguments

sum_variadic = -> [].reduce.call arguments, (acc, num) -> acc + num

add_to_7_and_5 = partial_apply sum_variadic, 7, 5

add_to_7_and_5 10 # returns 22
add_to_7_and_5 10, 11, 12 # returns 45

# currying
curry = (func) ->
  num_args = func.length
  helper = (prev) ->
    ->
      args = prev.concat [].slice.call arguments
      return if args.length < num_args then helper args else func.apply null, args
  helper []

sum_of_three = (x, y, z) -> x + y + z
curried_sum_of_three = curry sum_of_three
curried_sum_of_three 4 # returns a function expecting more arguments
curried_sum_of_three(4)(5) # still returns a function expecting more arguments
curried_sum_of_three(4)(5)(6) # returns 15
curried_sum_of_three 4, 5, 6 # returns 15

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!

忘羡 2024-07-14 04:50:56

我假设大多数提出这个问题的人已经熟悉基本概念,所以他们没有必要谈论这个。 重叠部分是令人困惑的部分。

您也许能够充分使用这些概念,但您将它们一起理解为这种伪原子无定形概念模糊。 缺少的是知道它们之间的边界在哪里。

与其定义每一个是什么,更容易强调它们的差异——边界。

柯里化是指定义函数。

部分应用是当您调用函数时。

应用程序是调用函数的数学术语。

部分应用程序需要调用柯里化函数并获取函数作为返回类型。

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.

霓裳挽歌倾城醉 2024-07-14 04:50:56

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

因此柯里化为您提供了一个单参数函数来创建函数,其中部分应用程序创建了一个包装器硬编码一个或多个参数的函数。

如果你想复制和粘贴,下面的代码会更吵闹,但使用起来更友好,因为类型更宽松:

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 );
}

There are other great answers here but I believe this example (as per my understanding) in Java might be of benefit to some people:

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 );
}

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:

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 );
}
太阳公公是暖光 2024-07-14 04:50:56

柯里化

维基百科说

柯里化是将采用多个参数的函数转换为每个采用单个参数的函数序列的技术。

文章

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

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

部分应用示例

Just Enough FP:部分应用

部分应用是将部分(但不是全部)参数应用于函数并返回等待其余参数的新函数的行为。 这些应用的参数存储在闭包中,并且将来可用于任何部分应用的返回函数。

示例

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

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

add3(5) // 8

区别在于

  1. currying 是一种技术(模式)
  2. 部分应用 是一个带有一些预定义参数的函数(如上一个示例中的 add3

Currying

Wikipedia says

Currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a single argument.

Example

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

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

Partial application

Article Just Enough FP: Partial Application

Partial application is the act of applying some, but not all, of the arguments to a function and returning a new function awaiting the rest of the arguments. These applied arguments are stored in closure and remain available to any of the partially applied returned functions in the future.

Example

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

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

add3(5) // 8

The difference is

  1. currying is a technique (pattern)
  2. partial application is a function with some predefined arguments (like add3 from the previous example)
烟柳画桥 2024-07-14 04:50:56

在写这篇文章时,我混淆了柯里化和非柯里化。 它们是函数的逆变换。 只要你得到变换及其逆表示的内容,你叫什么并不重要。

非柯里化的定义不是很清楚(或者更确切地说,存在“冲突”的定义,但都抓住了这个想法的精神)。 基本上,这意味着将带有多个参数的函数转换为带有单个参数的函数。 例如,

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

现在,如何将其转换为采用单个参数的函数? 当然是你作弊啦!

plus :: (Int, Int) -> Int

请注意,plus 现在采用单个参数(由两个事物组成)。 极好的!

这有什么意义呢? 好吧,如果您有一个接受两个参数的函数,并且有一对参数,那么很高兴知道您可以将该函数应用于参数,并且仍然得到您所期望的结果。 事实上,执行此操作的管道已经存在,因此您不必执行诸如显式模式匹配之类的操作。 你所要做的就是:

(uncurry (+)) (1,2)

那么什么是偏函数应用呢? 将两个参数的函数转换为只有一个参数的函数是另一种不同的方法。 但它的工作原理有所不同。 再次以(+)为例。 我们如何将它变成一个以单个 Int 作为参数的函数? 我们作弊!

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

这是给任何 Int 加零的函数。

((+) 1) :: 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,

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

Now, how do you turn this into a function that takes a single argument? You cheat, of course!

plus :: (Int, Int) -> Int

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:

(uncurry (+)) (1,2)

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!

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

That's the function that adds zero to any Int.

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

adds 1 to any Int. Etc. In each of these cases, (+) is "partially applied".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文