ChatGPT解决这个技术问题 Extra ChatGPT

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


n
nCardot

柯里化是将一个接受多个参数的函数分解为一系列函数,每个函数只接受一个参数。这是 JavaScript 中的一个示例:

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

add(3, 4); // returns 7

这是一个函数,它接受两个参数 a 和 b,并返回它们的总和。我们现在将 curry 这个函数:

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。


在实际意义上,我该如何利用这个概念?
@Strawberry,例如,假设您在 [1, 2, 3, 4, 5] 中有一个数字列表,您希望将其乘以任意数字。在 Haskell 中,我可以编写 map (* 5) [1, 2, 3, 4, 5] 将整个列表乘以 5,从而生成列表 [5, 10, 15, 20, 25]
我理解 map 函数的作用,但我不确定我是否理解你试图为我说明的观点。你是说map函数代表柯里化的概念吗?
@Strawberry map 的第一个参数必须是一个只接受一个参数的函数 - 列表中的一个元素。乘法——作为一个数学概念——是一种二元运算;它需要 2 个参数。但是,在 Haskell 中 * 是一个柯里化函数,类似于此答案中 add 的第二个版本。 (* 5) 的结果是一个函数,它接受一个参数并将其乘以 5,并允许我们将它与 map 一起使用。
@Strawberry 标准 ML 或 Haskell 等函数式语言的好处是您可以“免费”获得柯里化。您可以像在任何其他语言中一样定义多参数函数,并且您会自动获得它的柯里化版本,而无需自己投入一堆 lambda。因此,您可以生成从任何现有函数中获取更少参数的新函数,而无需大惊小怪或麻烦,并且可以轻松地将它们传递给其他函数。
A
Alex Martelli

在函数代数中,处理带有多个参数(或等效的 N 元组的一个参数)的函数有点不优雅——但是,正如 Moses Schönfinkel(以及独立的 Haskell Curry)所证明的那样,不需要:所有你need 是接受一个参数的函数。

那么,您如何处理您自然会表达为 f(x,y) 的事物?好吧,你把它当作 f(x)(y) -- f(x),称之为 g,是一个函数,然后你将该函数应用于 y。换句话说,你只有一个接受一个参数的函数——但其中一些函数返回其他函数(它们也接受一个参数;-)。

像往常一样,wikipedia 有一个很好的总结条目,其中包含许多有用的指针(可能包括关于您最喜欢的语言的指针;-)以及稍微更严格的数学处理。


我想与我上面的类似评论 - 我没有看到函数式语言将函数限制为采用单个 arg。我弄错了吗?
@hoohoo:函数式语言通常不会将函数限制为单个参数。然而,在更低、更数学的层面上,处理只接受一个参数的函数要容易得多。 (例如,在 lambda 演算中,函数一次只接受一个参数。)
好的。那么另一个问题。以下是真实的陈述吗? Lambda演算可以用作函数式编程的模型,但函数式编程不一定应用lambda演算。
正如维基百科页面所指出的,大多数 FP 语言“修饰”或“增强”lambda 演算(例如,使用一些常量和数据类型),而不仅仅是“应用”它,但它并不是那么接近。顺便说一句,是什么给你的印象,例如 Haskell 没有“将函数限制为采用单个 arg”?确实如此,尽管这与柯里化无关;例如 div :: Integral a => a -> a -> a - 注意那些多个箭头? “将 a 映射到函数,将 a 映射到 a”是一种读法;-)。您可以div &c 使用(单个)元组参数,但这在 Haskell 中确实是反惯用的。
@Alex - wrt Haskell & arg count,我没有花很多时间在 Haskell 上,这都是几周前的事了。所以这是一个容易犯的错误。
S
Shea Daniels

这是一个具体的例子:

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

现在,在地球上,你只想计算这个星球上物体的力。在函数式语言中,您可以将地球的质量传递给函数,然后对其进行部分评估。你会得到另一个函数,它只需要两个参数并计算地球上物体的引力。这称为柯里化。


出于好奇,JavaScript 的 Prototype 库提供了一个“curry”函数,它几乎完全按照您在此处解释的内容:prototypejs.org/api/function/curry
新的 PrototypeJS 咖喱函数链接。 prototypejs.org/doc/latest/language/Function/prototype/curry/…
这对我来说听起来像是部分应用。我的理解是,如果你应用柯里化,你可以用一个参数创建函数并将它们组合成更复杂的函数。我错过了什么吗?
@neontapir 是正确的。 Shea 所描述的不是柯里化。是部分应用。如果一个三参数函数被柯里化并且你把它称为 f(1),你得到的不是一个二参数函数。您将返回一个单参数函数,该函数返回另一个单参数函数。一个柯里化函数只能传递一个参数。 PrototypeJS 中的 curry 函数也不是 currying。是部分应用。
no(对部分评估)和 no(对柯里化)。这称为部分应用。需要柯里化才能启用它。
A
Adzz

它可以是一种使用功能来制作其他功能的方法。

在 JavaScript 中:

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

允许我们这样称呼它:

let addTen = add(10);

当它运行时,10 作为 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() 总是在我们传入的任何内容中添加 10。我们可以用相同的方式创建类似的函数:

let addTwo = add(2)       // addTwo(); will add two to whatever you pass in
let addSeventy = add(70)  // ... and so on...

现在显而易见的后续问题是,你到底为什么要这样做?它将一个急切的操作 x + y 变成了一个可以延迟执行的操作,这意味着我们至少可以做两件事 1. 缓存昂贵的操作 2. 在功能范式中实现抽象。

想象一下我们的柯里化函数看起来像这样:

let doTheHardStuff = function(x) {
  let z = doSomethingComputationallyExpensive(x)
  return function (y){
    z + y
  }
}

我们可以调用这个函数一次,然后将结果传递给很多地方,这意味着我们只做一次计算量大的东西:

let finishTheJob = doTheHardStuff(10)
finishTheJob(20)
finishTheJob(30)

我们可以通过类似的方式获得抽象。


我在这里看到的对固有顺序过程的最佳分步解释,也许是最好的,最具解释性的答案。
@jonsilver 我会说相反的,不是一个很好的解释。我同意它很好地解释了所提出的例子,但人们倾向于默认思考,“是的,非常清楚,但我可以用另一种方式做同样的事情,那么柯里化有什么好处?”换句话说,我希望它有足够的上下文或解释来说明柯里化是如何工作的,以及为什么与其他加十的方法相比,它不是一个无用和微不足道的观察。
最初的问题是“它是什么”,而不是它为什么有用。
curry 模式是一种将固定参数应用于现有函数的方法,目的是在不重新创建原始函数的情况下创建新的可重用函数。这个答案很好地证明了这一点。
“我们至少可以做两件事 1. 缓存昂贵的操作 2. 在功能范式中实现抽象。”这是其他答案所缺乏的“为什么有用”的解释。而且我认为这个答案也很好地解释了“什么”。
O
Orkhan Alikhanov

柯里化是一种可以应用于函数的转换,以允许它们比以前少一个参数。

例如,在 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 yz = 1 + y + z。这意味着我们可以执行以下操作:-

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 接受一个函数并提供一个接受单个参数的新函数,并返回指定的函数,并将其第一个参数设置为该参数。这允许我们将具有多个参数的函数表示为一系列单参数函数。例子:-

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

那么 C# 中的方法需要在部分应用之前进行柯里化吗?
“这允许我们将具有多个参数的函数表示为一系列单参数函数” - 完美,这对我来说很好。谢谢
J
J D

柯里化函数是一个重写了几个参数的函数,它接受第一个参数并返回一个接受第二个参数的函数,依此类推。这允许多个参数的函数部分应用它们的一些初始参数。


“这允许多个参数的函数部分应用它们的一些初始参数。” - 为什么这是有益的?
@acarlon 函数经常被重复调用,其中一个或多个参数相同。例如,如果您想在列表列表 xssmap 一个函数 f,您可以执行 map (map f) xss
谢谢,有道理。我做了更多的阅读,它已经到位。
我认为这个答案以一种简洁的方式正确。 “柯里化”是获取多个参数的函数并将其转换为一系列函数的过程,每个函数接受一个参数并返回单个参数的函数,或者在最终函数的情况下,返回实际结果.这可以由语言自动为您完成,或者您可以调用其他语言的 curry() 函数来生成 curry 版本。请注意,使用参数调用咖喱函数不是柯里化。咖喱已经发生了。
M
MidhunKrishna

Currying 将函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)

否则,柯里化就是将一个接受多个参数的函数分解为一系列接受部分参数的函数。

从字面上看,柯里化是函数的转换:从一种调用方式到另一种调用方式。在 JavaScript 中,我们通常会制作一个包装器来保留原始函数。

柯里化不调用函数。它只是改变它。

让我们制作 curry 函数来执行两个参数函数的柯里化。换句话说,两个参数 f(a, b)curry(f) 将其转换为 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

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

curry(func) 的结果是一个包装函数(a)。

当它像 sum(1) 那样调用时,参数保存在词法环境中,并返回一个新的包装器 function(b)。

然后 sum(1)(2) 最后调用 function(b) 提供 2,并将调用传递给原始的多参数 sum。


O
Orkhan Alikhanov

这是 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 实现此方法的部分对象与函数的区别。


我不明白 - 你这样做: >>> am_quote = curry(display_quote, "Alex Martelli") 但接下来你这样做: >>> am_quote("currying", "像往常一样,维基百科有一个很好的总结。 ..") 所以你有一个带有两个参数的函数。似乎柯里化应该给你三个不同的函数来组成?
我只使用部分来咖喱一个参数,产生一个有两个参数的函数。如果您愿意,您可以进一步 curry am_quote 以创建仅在特定主题上引用 Alex 的内容。数学背景可能集中在只包含一个参数的函数上——但我相信像这样修复任意数量的参数通常(如果从数学角度不精确的话)称为柯里化。
(顺便说一句 - '>>> 是 Python 交互式解释器中的提示符,而不是代码的一部分。)
好的,感谢您对 args 的澄清。我知道 Python 解释器提示符,我试图引用这些行但它不起作用;-)
在您发表评论后,我搜索并找到了其他参考资料,包括此处关于“currying”和“currying”之间的区别。 “部分应用程序”以响应我熟悉的许多不精确用法的实例。参见例如:stackoverflow.com/questions/218025/…
Y
Yilmaz

柯里化意味着将 N 元的函数转换为 N 元为 1 的函数。函数的 arity 是它需要的参数的数量。

这是正式的定义:

 curry(f) :: (a,b,c) -> f(a) -> f(b)-> f(c)

这是一个有意义的真实示例:

你去 ATM 取钱。您刷卡,输入密码并做出选择,然后按回车键提交请求旁边的“金额”。

这是取款的正常功能。

const withdraw=(cardInfo,pinNumber,request){
    // process it
       return request.amount
}

在这个实现中,函数期望我们一次输入所有参数。我们要刷卡,输入密码并提出请求,然后函数就会运行。如果这些步骤中的任何一个有问题,您会在输入所有参数后发现。使用 curried 函数,我们将创建更高级、更纯粹、更简单的函数。纯函数将帮助我们轻松调试代码。

这是带有咖喱功能的 Atm:

const withdraw=(cardInfo)=>(pinNumber)=>(request)=>request.amount

ATM,将卡作为输入并返回一个需要 pinNumber 的函数,该函数返回一个接受请求对象的函数,在成功处理后,您将获得您请求的金额。每一步,如果你有一个错误,你会很容易地预测出哪里出了问题。假设您输入卡并出现错误,您知道它与卡或机器有关,但与密码无关。或者,如果您输入了 pin 并且如果它没有被接受,您就知道您输入了错误的 pin 号。您将轻松调试错误。

此外,这里的每个函数都是可重用的,因此您可以在项目的不同部分使用相同的函数。


P
Prashant Andani

这是通用和最短版本的函数柯里化的示例,其中 n 没有。参数。

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

常量添加 = a => b => b ?添加(a + b):a;控制台日志(添加(1)(2)(3)(4)());


W
Will Ness

Currying 是 Java Script 的高阶函数之一。

柯里化是一个包含许多参数的函数,它被重写为它接受第一个参数并返回一个函数,该函数又使用剩余的参数并返回值。

使困惑?

来看一个例子,

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

这类似于下面的柯里化函数,

function add(a)
    {
        return function(b){
            return a+b;
        }
    }
var curryAdd = add(5);
curryAdd(6);

那么这段代码是什么意思呢?

现在再次阅读定义,

柯里化是一个包含许多参数的函数,它被重写为它接受第一个参数并返回一个函数,该函数又使用剩余的参数并返回值。

还是,糊涂?让我深入解释一下!

当你调用这个函数时,

var curryAdd = add(5);

它会返回一个这样的函数,

curryAdd=function(y){return 5+y;}

因此,这称为高阶函数。意思是,依次调用一个函数返回另一个函数是高阶函数的精确定义。这是传说中的最大优势,Java Script。所以回到咖喱,

此行会将第二个参数传递给 curryAdd 函数。

curryAdd(6);

这反过来导致,

curryAdd=function(6){return 5+6;}
// Which results in 11

希望你能理解这里柯里化的用法。所以,说到优势,

为什么要咖喱?

它利用了代码的可重用性。更少的代码,更少的错误。你可能会问代码少了怎么办?

我可以用 ECMA 脚本 6 个新特性箭头函数来证明这一点。

是的! ECMA 6,为我们提供了称为箭头函数的奇妙功能,

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

借助箭头函数,我们可以将上述函数编写如下,

x=>y=>x+y

酷吧?

所以,更少的代码和更少的错误!

借助这些高阶函数,可以轻松开发出无错误的代码。

我挑战你!

希望,你明白什么是咖喱。如果您需要任何澄清,请随时在此处发表评论。

谢谢,祝你有美好的一天!


M
Mario

如果您理解 partial,您就成功了一半。 partial 的想法是将参数预先应用到函数并返回一个只需要剩余参数的新函数。当这个新函数被调用时,它包括预加载的参数以及提供给它的任何参数。

在 Clojure 中 + 是一个函数,但为了让事情变得非常清楚:

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

您可能知道 inc 函数只是将 1 添加到它传递的任何数字。

(inc 7) # => 8

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

(def inc (partial add 1))

这里我们返回另一个函数,它在 add 的第一个参数中加载了 1。由于 add 采用两个参数,因此新的 inc 函数只需要 b 参数——而不是像以前那样的 2 个参数,因为 1 已经部分应用了。因此,partial 是一种工具,可用于创建具有预先提供的默认值的新函数。这就是为什么在函数式语言中,函数经常将参数从一般到特定排序。这使得重用这些函数来构造其他函数变得更加容易。

现在想象一下,如果语言足够聪明,可以内省地理解 add 需要两个参数。当我们传递一个参数而不是犹豫时,如果函数部分应用了我们代表我们传递它的参数,理解我们可能打算稍后提供另一个参数怎么办?然后我们可以在不显式使用 partial 的情况下定义 inc

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

这是某些语言的行为方式。当希望将函数组合成更大的转换时,它特别有用。这将导致一个传感器。


M
Marcus Thornton

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

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


J
James Black

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

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

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


u
user3804449

正如所有其他答案一样,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 会为您生成新的 curried 函数(double)


c
catch23

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

这是一个常规的非柯里化函数,它添加了两个 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

这里发生的情况是,当您调用 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

S
S2dent

柯里化的一个例子是当你现在只知道一个参数的函数时:

例如:

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 / 闭包以将其发送到函数。


func callback 是否返回自身?它被称为 @callback(str) 所以 let callback = callback(str),回调只是 func callback 的返回值
不,func callback(_:data:)接受两个参数,这里我只给它一个,String,所以它正在等待下一个(NSData),这就是为什么现在let callback是另一个等待数据的函数传入
V
V. S.

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

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

n
nCardot

“柯里化”是获取多个参数的函数并将其转换为一系列函数的过程,每个函数接受一个参数并返回单个参数的函数,或者在最终函数的情况下,返回实际结果。


M
Mark Reed

其他答案已经说了柯里化是什么:将比预期更少的参数传递给柯里化函数不是错误,而是返回一个函数,该函数期望其余参数并返回相同的结果,就好像你在一次。

我会尝试激励它为什么有用。它是您在做之前从未意识到自己需要的那些工具之一。柯里化首先是让您的程序更具表现力的一种方式——您可以将操作与更少的代码结合在一起。

例如,如果您有一个柯里化函数 add,您可以将 JS x => k + x(或 Python lambda x: k + x 或 Ruby { |x| k + x } 或 Lisp (lambda (x) (+ k x)) 或 ...)的等价物编写为 add(k)。在 Haskelll 中,您甚至可以使用运算符:(k +)(+ k)(这两种形式可以让您对非交换运算符进行任意一种柯里化:(/ 9) 是一个将数字除以 9 的函数,这可能是更常见的用例,但您也有 (9 /) 用于将 9 除以其参数的函数。)除了更短之外,curried 版本不包含像所有其他版本中的 x 那样的虚构参数名称。这不是必需的。您正在定义一个将一些常数 k 添加到数字的函数,并且您不需要为该数字命名只是为了讨论该函数。甚至可以定义它。这是所谓的“无点风格”的一个例子。除了操作本身,您可以将操作组合在一起。您不必声明只对它们的参数应用一些操作的匿名函数,因为 * 这就是操作已经是什么。

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

柯里化减少了对管道和线程等东西的需求。在 Clojure 中,您可以使用线程宏 ->(defn f2c (deg) (-> deg (- 32) (* 5) (/ 9)) 定义温度转换函数。这很酷,它从左到右读起来很好(“减去 32,乘以 5,然后除以 9。”)并且您只需为每个子操作提及参数两次而不是一次……但它之所以有效,是因为 -> 是在评估任何内容之前在语法上转换整个形式的宏。它在幕后变成了一个常规的嵌套表达式:(/ (* (- deg 32) 5) 9)。如果数学运算是柯里化的,您就不需要宏来将它们很好地组合起来,就像在 Haskell let f2c = (subtract 32) & (* 5) & (/ 9) 中那样。 (尽管使用函数组合无疑会更惯用,它从右到左读取:(/ 9) . (* 5) . (subtract 32)。)

同样,很难找到好的演示示例; currying 在复杂的情况下最有用,它确实有助于解决方案的可读性,但是为了让您理解问题,关于currying 的整体课程可能会迷失在噪音中。


你举了很多例子,但没有一个很好的论据来解释为什么。想解释一下这一点,因为我认为你在帖子开头提到了这一点?
m
madeinQuant

有一个“在 ReasonML 中进行柯里化”的例子。

let run = () => {
    Js.log("Curryed function: ");
    let sum = (x, y) => x + y;
    Printf.printf("sum(2, 3) : %d\n", sum(2, 3));
    let per2 = sum(2);
    Printf.printf("per2(3) : %d\n", per2(3));
  };

W
WasitShafi

下面是 JavaScript 中的柯里化示例之一,这里的 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)}`);

输出

乘以二(8):16