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

我经常在互联网上看到各种抱怨,其他人的卷曲的例子不是卷曲的,而实际上只是部分应用。

我还没有找到一个适当的解释部分应用程序是什么,或者它如何不同于咖喱。 似乎有一个普遍的混乱,相当的例子被描述为在一些地方卷曲,并在其他地方部分应用。

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


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

许多功能语言让你写fxyz 。 如果你只调用fxy或f(x)(y),那么你会得到一个部分应用的函数 - 返回值是lambda(z){z(x(y))}的闭包,并传入x和y到f(x,y)

使用部分应用程序的一种方法是将函数定义为泛化函数的部分应用程序,如fold

function fold(combineFunction, accumalator, 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(accumaltor,list) {/*...*/}
f(0,list) //returns 10
@g = f(0) //same as sum
g(list)  //returns 10

了解它们的不同之处的最简单方法就是考虑一个真实的例子 。 让我们假设我们有一个函数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只需要1个输入,而部分应用需要2个(或更多)输入。

    尽管它们都返回一个函数作为输出,但是返回的函数与上面演示的完全不同。


    注意:这是从F#Basics中为.NET开发人员开发函数式编程的绝佳入门文章。

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

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

    马上,你会看到与大多数命令式语言不同的行为。 第二个语句通过将一个参数传递给一个带有两个参数的函数来创建一个名为double的新函数。 结果是一个函数,它接受一个int参数,并产生相同的输出,就好像你用x等于2和y等于该参数来调用multiply一样。 就行为而言,它与此代码相同:

    let double2 z = multiply 2 z
    

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

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

    为了创建双精度,评估乘法函数链中的第一个函数以部分应用乘法。 结果函数的名字是double。 当评估double时,它使用它的参数以及部分应用的值来创建结果。

    链接地址: http://www.djcxy.com/p/24039.html

    上一篇: What is the difference between currying and partial application?

    下一篇: Encapsulation vs Abstraction?