我在一些文章和博客中看到了curry函数的引用,但我找不到一个好的解释(或者至少一个有意义的解释!)


当前回答

下面是JavaScript中的一个curry示例,这里的multiply返回用于将x乘以2的函数。

const multiply = (presetConstant) => {
  return (x) => {
    return presetConstant * x;
  };
};

const multiplyByTwo = multiply(2);

// now multiplyByTwo is like below function & due to closure property in JavaScript it will always be able to access 'presetConstant' value
// const multiplyByTwo = (x) => {
//   return presetConstant * x;
// };

console.log(`multiplyByTwo(8) : ${multiplyByTwo(8)}`);

输出

multiplyByTwo(8): 16

其他回答

我发现这篇文章,以及它引用的文章,有助于更好地理解咖喱: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

正如其他人所提到的,它只是一种具有单参数函数的方法。

这很有用,因为你不需要假设有多少参数将被传入,所以你不需要2个参数,3个参数和4个参数函数。

在这里,您可以找到c#中curry实现的简单解释。在评论中,我试图展示咖喱是如何有用的:

public static class FuncExtensions {
    public static Func<T1, Func<T2, TResult>> Curry<T1, T2, TResult>(this Func<T1, T2, TResult> func)
    {
        return x1 => x2 => func(x1, x2);
    }
}

//Usage
var add = new Func<int, int, int>((x, y) => x + y).Curry();
var func = add(1);

//Obtaining the next parameter here, calling later the func with next parameter.
//Or you can prepare some base calculations at the previous step and then
//use the result of those calculations when calling the func multiple times 
//with different input parameters.

int result = func(1);

curry函数应用于多个参数列表,而不仅仅是 一个。

这是一个常规的、非咖喱的函数,它加了两个Int 参数x和y:

scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3

这是一个类似的咖喱函数。而不是 对于一个包含两个Int形参的列表,您将此函数应用于两个包含一个Int形参的列表 Int参数each:

scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)Intscala> second(2)
res6: Int = 3
scala> curriedSum(1)(2)
res5: Int = 3

这里发生的事情是,当您调用curriedSum时,实际上会得到两个背对背的传统函数调用。第一个函数 调用接受一个名为x的Int形参,并返回一个函数 为第二个函数。第二个函数接受Int形参 y。

这里有一个名为first的函数,它在精神上完成了第一个传统函数 函数调用curriedSum会做:

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

对第一个函数应用1——换句话说,调用第一个函数 而传入1 -会得到第二个函数:

scala> val second = first(1)
second: (Int) => Int = <function1>

对第二个函数应用2得到的结果是:

scala> second(2)
res6: Int = 3

在函数代数中,处理带有多个参数的函数(或相当于一个n元组的参数)有点不优雅——但是,正如Moses Schönfinkel(以及Haskell Curry)所证明的那样,这是不需要的:您所需要的只是带有一个参数的函数。

那么如何处理自然表示为f(x,y)的式子呢?好吧,你把它等价于f(x)(y)——f(x),叫它g,是一个函数,你把这个函数应用到y上。换句话说,你只有带一个参数的函数——但其中一些函数返回其他函数(也带一个参数;-)。

像往常一样,维基百科对此有一个很好的总结条目,有许多有用的指针(可能包括关于你最喜欢的语言的;-),以及稍微更严格的数学处理。

其他答案已经说明了curry是什么:向curry函数传递的参数比它预期的要少,这不是错误,而是返回一个函数,该函数预期其余的参数,并返回相同的结果,就好像您一次性将它们全部传入一样。

我会试着解释为什么它有用。这是一种你从未意识到你需要的工具,直到你真正使用它。curry首先是一种让你的程序更具表现力的方法——你可以用更少的代码把操作组合在一起。

For example, if you have a curried function add, you can write the equivalent of JS x => k + x (or Python lambda x: k + x or Ruby { |x| k + x } or Lisp (lambda (x) (+ k x)) or …) as just add(k). In Haskelll you can even use the operator: (k +) or (+ k) (The two forms let you curry either way for non-commutative operators: (/ 9) is a function that divides a number by 9, which is probably the more common use case, but you also have (9 /) for a function that divides 9 by its argument.) Besides being shorter, the curried version contains no made-up parameter name like the x found in all the other versions. It’s not needed. You’re defining a function that adds some constant k to a number, and you don’t need to give that number a name just to talk about the function. Or even to define it. This is an example of what’s called “point-free style”. You can combine operations together given nothing but the operations themselves. You don’t have to declare anonymous functions that do nothing but apply some operation to their argument, because *that’s what the operations already are.

当以咖喱友好的方式定义高阶函数时,这变得非常方便。例如,curried map(fn, list)让您定义一个只使用map(fn)的映射器,可以稍后将其应用于任何列表。但是将定义为map(list, fn)的映射curry化只能让您定义一个将其他函数应用到常量列表的函数,这在一般情况下可能不太有用。

Currying reduces the need for things like pipes and threading. In Clojure, you might define a temperature conversion function using the threading macro ->: (defn f2c (deg) (-> deg (- 32) (* 5) (/ 9)). That’s cool, it reads nicely left to right (“subtract 32, multiply by 5 and divide by 9.”) and you only have to mention the parameter twice instead of once for every suboperation… but it only works because -> is a macro that transforms the whole form syntactically before anything is evaluated. It turns into a regular nested expression behind the scenes: (/ (* (- deg 32) 5) 9). If the math ops were curried, you wouldn’t need a macro to combine them so nicely, as in Haskell let f2c = (subtract 32) & (* 5) & (/ 9). (Although it would admittedly be more idiomatic to use function composition, which reads right to left: (/ 9) . (* 5) . (subtract 32).)

同样,很难找到好的演示例子;在复杂的情况下,咖喱是最有用的,因为它确实有助于解决方案的可读性,但这些需要太多的解释才能让您理解问题,以至于关于咖喱的整个课程可能会淹没在噪音中。