Language agnostic 咖喱和部分涂抹有什么区别?

Language agnostic 咖喱和部分涂抹有什么区别?,language-agnostic,terminology,definition,currying,partial-application,Language Agnostic,Terminology,Definition,Currying,Partial Application,我经常在互联网上看到各种各样的抱怨,说其他人的例子不是在讨好别人,实际上只是部分应用 我还没有找到一个像样的解释来解释什么是部分应用程序,或者它与咖喱有什么不同。这似乎是一种普遍的混淆,在某些地方,等效的例子被描述为currying,而在其他地方则被部分应用 有人能告诉我这两个术语的定义,以及它们之间的区别吗?有趣的问题。经过一番搜索,他给出了我找到的最好的解释。我不能说实际的差异对我来说特别明显,但我不是一个FP专家 另一个有用的页面(我承认我还没有完全阅读)是 请注意,这看起来确实是一对被广

我经常在互联网上看到各种各样的抱怨,说其他人的例子不是在讨好别人,实际上只是部分应用

我还没有找到一个像样的解释来解释什么是部分应用程序,或者它与咖喱有什么不同。这似乎是一种普遍的混淆,在某些地方,等效的例子被描述为currying,而在其他地方则被部分应用


有人能告诉我这两个术语的定义,以及它们之间的区别吗?

有趣的问题。经过一番搜索,他给出了我找到的最好的解释。我不能说实际的差异对我来说特别明显,但我不是一个FP专家

另一个有用的页面(我承认我还没有完全阅读)是


请注意,这看起来确实是一对被广泛混淆的术语。

Currying是将一个由n个参数组成的函数转换成n个函数,每个函数都有一个参数。鉴于以下功能:

function f(x,y,z) { z(x(y));}
processSubject = _.partial(process, my_context, my_success, my_error)
// assign fixed values to the first 3 arguments of the `process` function
咖喱时,变成:

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, 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
注意:这篇文章摘自一篇优秀的介绍性文章,供.NET开发人员学习函数式编程

curry意味着将具有多个参数的函数分解为一个系列 函数的集合,每个函数接受一个参数并最终生成 与原始函数的结果相同。咖喱可能是最重要的 对于刚接触函数式编程的开发人员来说,这是一个极具挑战性的话题,尤其是因为 经常与局部应用相混淆。你可以在工作中看到两者 在本例中:

let multiply x y = x * y    
let double = multiply 2
let ten = double 5
马上,你就会看到与大多数人不同的行为 命令式语言。第二条语句创建一个新函数 通过将一个参数传递给接受两个参数的函数来调用double。 结果是一个函数,它接受一个int参数并生成 输出与调用x等于2和y的乘法相同 等于那个论点。就行为而言,与此相同 代码:

通常,人们错误地说乘法是用来形成双精度的。 但这只是有些道理。乘法函数是curry函数,但是 当它被定义时就会发生这种情况,因为F#中的函数是由 违约创建double函数时,更准确地 假设乘法函数部分应用

乘法函数实际上是一系列两个函数。第一 函数接受一个int参数并返回另一个函数, 有效地将x绑定到特定值。此函数也接受 可以将其视为绑定到y的值的int参数。之后 调用第二个函数时,x和y都是绑定的,因此结果是 双精度体中定义的x和y的乘积

要创建double,乘法链中的第一个函数 对函数求值以部分应用乘法。结果 函数名为double。当计算double时,它使用 它的参数和部分应用的值一起创建 结果


我已经用另一条线索回答了这个问题。简言之,部分函数应用程序是关于固定给定多变量函数的某些参数,以生成另一个参数较少的函数,而Curry是关于将一个N个参数的函数转换为一元函数,该函数返回一元函数…[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:当部分应用时,不要在lambda下求值。因此,对于部分应用程序来说,有趣的是,我们需要在Currand和Plus应用之间有所区别,并考虑在LAMBDA下部分应用作为Currus Plus评估。

< P> Cury和部分应用之间的差异可以通过下面的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)
的内容,那么实际上它并不是部分应用程序

进一步阅读:


    • 在写这篇文章时,我把咖喱和不咖喱混淆了。它们是函数的反变换。不管你怎么称呼它,只要你得到变换和它的逆表示什么

      未结婚没有定义
      (+) :: Int -> Int -> Int
      
      plus :: (Int, Int) -> Int
      
      (uncurry (+)) (1,2)
      
      ((+) 0) :: Int -> Int
      
      ((+) 1) :: Int -> Int
      
      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
      
      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
      
      # 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
      
      curry(f) = h 
      f: (X x Y) -> Z 
      h: X -> (Y -> Z)
      
      part(f, 2) = g
      f: (X x Y) -> Z 
      g: Y -> Z
      
      partial(f, a) = curry(f)(a)
      
      add = (x, y) => x + y
      
      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
      
      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
      
      processSubject('subject1');
      processSubject('foobar');
      
      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);