当前位置:  开发笔记 > 编程语言 > 正文

currying和部分应用有什么区别?

如何解决《currying和部分应用有什么区别?》经验,为你挑选了8个好方法。

我经常在互联网上看到各种各样的抱怨,其他人的currying例子并不是currying,但实际上只是部分应用.

我没有找到关于部分应用是什么的合理解释,或者它与currying有何不同.似乎存在普遍的混淆,在某些地方将等效的例子描述为currying,在其他地方描述为部分应用.

有人可以向我提供这两个术语的定义,以及它们如何区别的细节吗?



1> Mark Cidade..:

Currying将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);

许多函数式语言都可以让你写f x y z.如果你只调用f x 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


你是说部分应用程序是在你讨论一个函数时,并使用一些但不是所有结果函数?
或多或少,是的.如果只提供参数的子集,则会返回一个接受其余参数的函数
@JasonBunting,关于你的第一个评论,你所谈论的是*谴责*.Currying将多参数函数作为输入并返回一组1-arg函数作为输出.De-currying将1-arg函数链作为输入并返回多arg函数作为输出.正如http://stackoverflow.com/a/23438430/632951所述
@Mark:我想这只是那些在我身上引出迂腐的概念之一 - 但对权威来源的吸引力几乎无法满足,因为它们似乎都相互指向.维基百科几乎不是我认为的权威来源,但我知道很难找到其他的东西.我只想说,无论我们是否同意(或不同意)白话的细节,我认为我们都知道我们所说的话及其权力!:)谢谢马克!

2> Pacerier..:

了解它们之间差异的最简单方法是考虑一个真实的例子.假设我们有一个函数Add,它将2个数字作为输入并返回一个数字作为输出,例如Add(7, 5)返回12.在这种情况下:

使用Add部分应用函数7将为我们提供一个新函数作为输出.该函数本身需要1个数字作为输入并输出一个数字.因此:

Partial(Add, 7); // returns a function f2 as output

                 // f2 takes 1 number as input and returns a number as output

所以我们可以这样做:

f2 = Partial(Add, 7);
f2(5); // returns 12;
       // f2(7)(5) is just a syntactic shortcut

哗众取宠的功能Add会给我们一个新的功能输出.该功能本身需要1号作为输入,并输出另一个新的功能.然后第三个函数将1个数作为输入并返回一个数作为输出.因此:

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

所以我们可以这样做:

f2 = Curry(Add);
f3 = f2(7);
f3(5); // returns 12

换句话说,"currying"和"partial application"是两个完全不同的功能.Currying只需1个输入,而部分应用需要2个(或更多)输入.

即使它们都返回一个函数作为输出,返回的函数也是完全不同的形式,如上所示.


部分应用程序将函数从"n-ary"转换为"(x-n)-ary",从"n-ary"到"n*1-ary".部分应用的函数具有**缩小范围**(应用程序),即,"Add7"的表达力低于"Add".另一方面,curry函数与原始函数一样富有表现力.
我相信更有特色的特性是当我们讨论f(x,y,z)=> R时,我们得到f(x),它返回g(y)=> h(z)=> R,每个消耗一个参数; 但是当我们将f(x,y,z)部分地应用为f(x)时,我们得到g(y,z)=> R,即有两个参数.如果不是那个特性,我们可以说currying就像是对0参数的部分应用,从而使所有参数都不受限制; 但是实际上f()部分应用于0参数是一个消耗3个args的函数,与curried f()不同.
再一次,正确的答案不是第一个或最多的投票:在这个答案的最后,咖喱与部分签名的简单解释实际上是解决问题的最简单方法.
注释“ f2(7)(5)仅仅是句法捷径”是什么意思?(我了解得很少。)`f2`是否已经包含/“了解” 7?

3> dodgy_coder..:

注意:这是从F#Basics中获取的,这是.NET开发人员进入函数式编程的优秀介绍性文章.

Currying意味着将具有许多参数的函数分解为一系列函数,每个函数都接受一个参数并最终产生与原始函数相同的结果.对于功能编程新手来说,Currying可能是最具挑战性的话题,特别是因为它经常与部分应用混淆.您可以在此示例中看到两者都在工作:

let multiply x y = x * y    
let double = multiply 2
let ten = double 5

您应该立即看到与大多数命令式语言不同的行为.第二个语句通过将一个参数传递给一个带两个的函数来创建一个名为double的新函数.结果是一个函数,它接受一个int参数并产生相同的输出,就好像你已经调用了multiply,x等于2,y等于那个参数.在行为方面,它与此代码相同:

let double2 z = multiply 2 z

通常,人们错误地认为乘法是形成双重的.但这只是有点真实.乘法函数是curry,但是在定义时会发生这种情况,因为F#中的函数默认是curry.当创建双重函数时,更准确地说,部分应用了乘法函数.

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

要创建double,将计算乘法函数链中的第一个函数以部分应用乘法.结果函数的名称为double.当计算double时,它使用其参数以及部分应用的值来创建结果.



4> Jon Skeet..:

有趣的问题.经过一番搜索后,"部分功能应用程序没有进行调整"给出了我发现的最佳解释.我不能说实际差异对我来说特别明显,但后来我不是FP专家......

另一个有用的页面(我承认我尚未完全阅读)是"使用Java闭包进行Currying和部分应用程序".

看起来这看起来像是一对广泛混淆的术语,请注意.


无法相信你有20个upvotes的几个链接和一个录取,你真的不知道咖喱和部分应用之间的区别.好好玩,先生.
您发布的@Jon链接信息丰富,但最好扩展您的答案并在此处添加更多信息.
关于差异的第一个环节是现场点.这是我发现的另一个有用的:http://bit.ly/CurryingVersusPartialApplication
Currying与元组有关(将一个带有元组参数的函数转换为一个带有n个独立参数的函数,反之亦然).部分应用程序是将函数应用于某些参数的能力,为剩余的参数产生新函数.如果您只是考虑cur = =与元组一起做,那么很容易记住.
@Jon见http://meta.stackoverflow.com/questions/275464/why-was-my-flag-for-a-link-only-answer-from-2008-declined

5> 小智..:

我在另一个主题/sf/ask/17360801/中回答了这个问题.简而言之,部分函数应用程序是关于修复给定多变量函数的一些参数以产生具有较少参数的另一个函数,而Currying是关于将N个参数的函数转换为返回一元函数的一元函数... [示例在这篇文章的末尾显示了Currying.]

Currying主要是理论上的兴趣:人们可以仅使用一元函数来表达计算(即每个函数都是一元的).在实践中和作为副产品,如果语言具有curried功能,它是一种可以使许多有用(但不是全部)部分功能应用程序变得微不足道的技术.同样,它不是实现部分应用程序的唯一方法.因此,您可能会遇到以其他方式完成部分应用程序的情况,但人们将其误认为是Currying.

(Currying的例子)

在实践中,人们不会只写

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

为了Currying.



6> Roland..:

Currying是一个参数的函数,它接受一个函数f并返回一个新函数h.请注意,h从中获取参数X并返回映射到的函数:YZ

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)

双方将产生相同的单参数函数.

对于更高的arity函数,相等性不正确,因为在这种情况下currying将返回单参数函数,而部分应用程序将返回多参数函数.

差异也在于行为,而currying递归地转换整个原始函数(每个参数一次),部分应用只是一步替换.

来源:维基百科Currying.



7> gsklee..:

通过以下JavaScript示例可以最好地说明curry和部分应用程序之间的区别:

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

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

6 === partial(2, 3);

部分应用导致更小的功能; 在上面的例子中,farity为3,而partial只有2的arity.更重要的是,部分应用的函数会在调用时立即返回结果,而不是currying链中的另一个函数.因此,如果你看到类似的东西partial(2)(3),它实际上并不是部分应用.

进一步阅读:

功能编程在5分钟内完成

Currying:与部分功能应用的对比



8> Kamafeather..:
简单的答案

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

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


简单提示

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

当函数具有两个以上的参数时,可以看到真正的区别。


简单e(c)(样本)

(用Javascript)

function process(context, success_callback, error_callback, subject) {...}

为什么总是传递参数,例如上下文和回调,如果它们总是相同的呢?只需为函数绑定一些值

processSubject = _.partial(process, my_context, my_success, my_error)

并调用它subject1foob​​ar的

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

舒服,不是吗?

使用currying,您每次需要传递一个参数

curriedProcess = _.curry(process);
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');

免责声明

我跳过了所有的学术/数学解释。因为我不知道。也许有帮助

推荐阅读
惬听风吟jyy_802
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有