函数式编程-什么是“固化”?

我在几篇文章和博客中都看到过关于咖喱函数的引用,但是我找不到很好的解释(或者至少有一个合理的解释!)

17个解决方案
820 votes

咖喱化是指将一个包含多个参数的函数分解为一系列仅包含一个参数的函数。 这是JavaScript中的示例:

function add (a, b) {
  return a + b;
}

add(3, 4); // returns 7

该函数接受两个参数a和b并返回它们的总和。 现在,我们将使用此函数:

function add (a) {
  return function (b) {
    return a + b;
  }
}

这是一个接受一个参数a的函数,并返回接受另一个参数b的函数,该函数返回其和。

add(3)(4);

var add3 = add(3);

add3(4);

第一条语句返回7,就像add(3,4)语句一样。 第二条语句定义了一个名为add3的新函数,该函数会将3加到其参数中。 这就是某些人所谓的关闭。 第三条语句使用add3操作将3加到4,结果再次产生7。

Kyle Cronin answered 2020-08-09T14:29:14Z
124 votes

在函数的代数中,处理带有多个参数(或等效的一个参数为N元组)的函数有些微不足道-但是,正如MosesSchönfinkel(以及独立的Haskell Curry)证明的那样,不需要它:所有人 需要的是带有一个参数的函数。

那么,如何处理您自然表达的东西,例如f(x,y)? 好吧,您认为它等同于f(x)(y)-f(x),称为g,是一个函数,然后将该函数应用于y。换句话说,您只有带有一个参数的函数-但其中一些函数返回另一个 函数(也要接受一个参数;-)。

像往常一样,维基百科对此有一个不错的摘要条目,其中包含许多有用的指针(可能包括与您喜欢的语言有关的指针;-)以及更严格的数学处理方法。

Alex Martelli answered 2020-08-09T14:29:44Z
96 votes

这是一个具体的例子:

假设您有一个计算作用在物体上的重力的函数。 如果您不知道公式,则可以在这里找到。 此函数将三个必要参数作为参数。

现在,在地球上,您只想计算该星球上物体的力。 用功能语言,您可以将地球质量传递给功能,然后对其进行部分评估。 您会得到的是另一个仅包含两个参数并计算地球上物体的引力的函数。 这称为“咖喱”。

Shea Daniels answered 2020-08-09T14:30:14Z
46 votes

Currying是一种可以应用于函数的转换,以使它们比以前少使用一个参数。

例如,在F#中,您可以这样定义一个函数:

let f x y z = x + y + z

在这里,函数f接受参数x,y和z并将它们求和在一起,因此:

f 1 2 3

返回6。

因此,根据我们的定义,我们可以为f:-定义curry函数:

let curry f = fun x -> f x

其中'fun x-> f x'是Lambda函数,等效于C#中的x => f(x)。 该函数输入您要咖喱的函数,并返回一个带有单个参数的函数,并返回将第一个参数设置为输入参数的指定函数。

使用前面的示例,我们可以得到f的咖喱:-

let curryf = curry f

然后,我们可以执行以下操作:

let f1 = curryf 1

它为我们提供了一个函数f1,它等效于f1 y z = 1 + yz。 这意味着我们可以执行以下操作:

f1 2 3

返回6。

此过程通常与“部分功能应用程序”混淆,后者可以这样定义:

let papply f x = f x

尽管我们可以将其扩展为多个参数,即:-

let papply2 f x y = f x y
let papply3 f x y z = f x y z
etc.

部分应用程序将使用该函数和参数,并返回一个需要一个或多个更少参数的函数,并且如前两个示例所示,该函数直接在标准F#函数定义中实现,因此我们可以获得以下结果:

let f1 = f 1
f1 2 3

这将返回结果6。

结论:-

currying和部分函数应用程序之间的区别在于:

Currying具有一个函数,并提供一个接受单个参数的新函数,并返回其第一个参数设置为该参数的指定函数。 这使我们可以将具有多个参数的函数表示为一系列单参数函数。 例:-

let f x y z = x + y + z
let curryf = curry f
let f1 = curryf 1
let f2 = curryf 2
f1 2 3
6
f2 1 3
6

局部函数应用程序更直接-它接受一个函数和一个或多个参数,然后返回将前n个参数设置为指定的n个参数的函数。 例:-

let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
ljs answered 2020-08-09T14:31:51Z
37 votes

它可以是使用功能来实现其他功能的一种方式。

在javascript中:

let add = function(x){
  return function(y){ 
   return x + y
  };
};

将允许我们这样称呼它:

let addTen = add(10);

运行此命令时,addTen()作为x传入;

let add = function(10){
  return function(y){
    return 10 + y 
  };
};

这意味着我们将返回此函数:

function(y) { return 10 + y };

所以当你打电话

 addTen();

你真的在打电话:

 function(y) { return 10 + y };

因此,如果您这样做:

 addTen(4)

它与:

function(4) { return 10 + 4} // 14

因此,我们的addTen()总是在传入的内容中增加十。我们可以通过相同的方式进行类似的功能:

let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
let addSeventy = add(70)  // ... and so on...
Adzz answered 2020-08-09T14:32:51Z
27 votes

curried函数是重写了多个参数的函数,以便它接受第一个参数,并返回一个接受第二个参数的函数,依此类推。 这允许几个参数的函数部分地应用其某些初始参数。

Jon Harrop answered 2020-08-09T14:33:13Z
6 votes

这是Python中的一个玩具示例:

>>> from functools import partial as curry

>>> # Original function taking three parameters:
>>> def display_quote(who, subject, quote):
        print who, 'said regarding', subject + ':'
        print '"' + quote + '"'


>>> display_quote("hoohoo", "functional languages",
           "I like Erlang, not sure yet about Haskell.")
hoohoo said regarding functional languages:
"I like Erlang, not sure yet about Haskell."

>>> # Let's curry the function to get another that always quotes Alex...
>>> am_quote = curry(display_quote, "Alex Martelli")

>>> am_quote("currying", "As usual, wikipedia has a nice summary...")
Alex Martelli said regarding currying:
"As usual, wikipedia has a nice summary..."

(只需通过+使用串联,以避免非Python程序员分心。)

编辑添加:

参见[http://docs.python.org/library/functools.html?highlight=partial#functools.partial,]这也显示了Python实现此方法的部分对象与函数的区别。

Anon answered 2020-08-09T14:33:46Z
5 votes

咖喱化将函数从可调用的sum(1)(2)转换为可调用的function(b)

否则,当您分解一个将多个参数合并为一部分参数的函数时,就会遇到麻烦。

从字面上看,currying是功能的转换:从一种调用方式转换为另一种调用方式。 在JavaScript中,我们通常做一个包装来保留原始功能。

咖喱不会调用函数。 它只是改变了它。

让我们创建咖喱函数,对两个参数的函数执行curring。 换句话说,用于两个参数的sum(1)(2) function(b)将其转换为f(a)(b)

function curry(f) { // curry(f) does the currying transform
  return function(a) {
    return function(b) {
      return f(a, b);
    };
  };
}

// usage
function sum(a, b) {
  return a + b;
}

let carriedSum = curry(sum);

alert( carriedSum(1)(2) ); // 3

如您所见,实现是一系列包装器。

  • sum(1)(2)的结果是包装器function(b)
  • 当它被称为sum(1)(2)时,参数将保存在词法环境中,并返回一个新的包装器function(b)
  • 然后sum(1)(2)最终调用提供2的function(b),并将调用传递给原始的多参数和。
MidhunKrishna answered 2020-08-09T14:34:42Z
4 votes

如果您了解add,您已经中途了。 inc的想法是将参数预先应用于函数,并返回仅需要其余参数的新函数。 调用此新函数时,它将包括预加载的参数以及提供给它的任何参数。

在Clojure中,add是一个函数,但要使事情清晰明了:

(defn add [a b] (+ a b))

您可能会意识到add函数只是在传递的任何数字上加1。

(inc 7) # => 8

让我们使用add自己构建它:

(def inc (partial add 1))

在这里,我们返回另一个函数,该函数在add的第一个参数中加载了1。由于inc接受了两个参数,因此新的partial函数仅需要b参数-而不是像以前那样使用2个参数,因为已经部分应用了1。 因此,partial是一种工具,可通过该工具创建具有默认值的新功能。 这就是为什么在函数式语言中,函数通常将参数从通用到特定进行排序。 这使得重用此类功能以构造其他功能更加容易。

现在,假设该语言足够聪明以至于能够内省地理解add想要两个参数。 当我们传递一个参数而不是阻止它时,如果函数部分应用了该参数,那么我们代表我们传递了它,而又意识到我们可能打算稍后再提供另一个参数? 然后,我们可以定义inc,而无需显式使用partial

(def inc (add 1)) #partial is implied

这是某些语言的行为方式。 当人们希望将功能组合成更大的转换时,它特别有用。 这将导致换能器。

Mario answered 2020-08-09T14:35:30Z
3 votes

我发现本文及其引用的文章对更好地理解curring很有用:[http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx]

正如其他人提到的那样,这只是拥有一个参数函数的一种方式。

这很有用,因为您不必假设要传入多少个参数,因此您不需要2个参数,3个参数和4个参数函数。

James Black answered 2020-08-09T14:35:59Z
3 votes

正如所有其他答案一样,currying有助于创建部分应用的功能。 Javascript不提供对自动执行的本机支持。 因此,上面提供的示例可能对实际编码没有帮助。 livescript中有一个很好的例子(本质上是编译成js)[http://livescript.net/]

times = (x, y) --> x * y
times 2, 3       #=> 6 (normal use works as expected)
double = times 2
double 5         #=> 10

在上面的示例中,当您没有给出任何参数时,livescript为您生成新的咖喱函数(双精度)

user3804449 answered 2020-08-09T14:36:24Z
3 votes

Curry可以简化您的代码。 这是使用此功能的主要原因之一。 咖喱化是将接受n个参数的函数转换为仅接受一个参数的n个函数的过程。

原理是使用闭包(closure)属性传递传递的函数的参数,将其存储在另一个函数中并将其视为返回值,这些函数形成一个链,最后的参数传递给完成 操作。

这样做的好处是它可以通过一次处理一个参数来简化参数的处理,这也可以提高程序的灵活性和可读性。 这也使程序更易于管理。 同样,将代码分成较小的部分将使其易于重用。

例如:

function curryMinus(x) 
{
  return function(y) 
  {
    return x - y;
  }
}

var minus5 = curryMinus(1);
minus5(3);
minus5(5);

我也可以

var minus7 = curryMinus(7);
minus7(3);
minus7(5);

这对于使复杂的代码整齐并处理不同步的方法等非常有用。

Marcus Thornton answered 2020-08-09T14:37:06Z
2 votes

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

这是一个常规的非咖喱函数,它添加了两个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参数:

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

这里发生的是,当您调用first时,实际上是背对背进行了两次传统的函数调用。 第一项功能调用采用一个名为curriedSum的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
nazar_art answered 2020-08-09T14:37:53Z
2 votes

currying的一个示例是拥有函数时,您目前仅知道其中一个参数:

例如:

func aFunction(str: String) {
    let callback = callback(str) // signature now is `NSData -> ()`
    performAsyncRequest(callback)
}

func callback(str: String, data: NSData) {
    // Callback code
}

func performAsyncRequest(callback: NSData -> ()) {
    // Async code that will call callback with NSData as parameter
}

在这里,由于在将第二个参数发送给performAsyncRequest(_:)时不知道回调的第二个参数,因此您必须创建另一个lambda /闭包才能将其发送给函数。

S2dent answered 2020-08-09T14:38:22Z
1 votes

在这里,您可以找到有关C#中的currying实现的简单说明。 在评论中,我试图展示如何使用curring:

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);
Vadim S. answered 2020-08-09T14:38:42Z
0 votes

这是使用n no的函数的通用和最短版本的示例。 的参数。

const add = a => b => b ? add(a + b) : a; 

const add = a => b => b ? add(a + b) : a; 
console.log(add(1)(2)(3)(4)());

Prashant Andani answered 2020-08-09T14:39:02Z
-1 votes

这等效于partial在python中所做的工作。 当我们想将通用函数用于特定目的时,可以使用这种机制来修复参数的子集。

pr-pal answered 2020-08-09T14:39:22Z
translate from https://stackoverflow.com:/questions/1352855/in-functional-programming-what-is-currying