我在一些文章和博客中看到了curry函数的引用,但我找不到一个好的解释(或者至少一个有意义的解释!)
当前回答
curry是一种可以应用于函数的转换,允许它们比以前少接受一个参数。
例如,在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 - >fx '是一个lambda函数,在c#中等价于x => f(x)。此函数输入您希望curry的函数,并返回一个接受单个参数的函数,并返回指定的函数,其中第一个参数设置为输入参数。
使用前面的例子,我们可以得到f的curry值:-
let curryf = curry f
然后我们可以做以下的事情:-
let f1 = curryf 1
这为我们提供了一个函数f1,它等价于f1 y z = 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的结果。
结论:-
咖喱和部分函数应用的区别是:-
curry接受一个函数,并提供一个接受单个参数的新函数,并返回指定函数,并将其第一个参数设置为该参数。这允许我们将具有多个形参的函数表示为一系列单实参函数。例子:-
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
其他回答
curry的一个例子是当你有一个函数时,你现在只知道其中一个参数:
例如:
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 /闭包来发送给函数。
它可以是一种用函数生成其他函数的方法。
在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)
我们可以用类似的方式得到抽象。
如果你理解了部分,你就成功了一半。partial的思想是将参数预先应用到一个函数,并返回一个只需要剩余参数的新函数。当这个新函数被调用时,它包括预加载的参数以及提供给它的任何参数。
在Clojure +中是一个函数,但要明确一点:
(defn add [a b] (+ a b))
您可能已经意识到inc函数只是简单地将1加到它传递的任何数字上。
(inc 7) # => 8
让我们自己使用partial来构建它:
(def inc (partial add 1))
Here we return another function that has 1 loaded into the first argument of add. As add takes two arguments the new inc function wants only the b argument -- not 2 arguments as before since 1 has already been partially applied. Thus partial is a tool from which to create new functions with default values presupplied. That is why in a functional language functions often order arguments from general to specific. This makes it easier to reuse such functions from which to construct other functions.
现在想象一下,如果语言足够聪明,能够自省地理解add需要两个参数。当我们向它传递一个参数时,如果函数部分应用了我们代表它传递的参数,并理解我们可能打算稍后提供另一个参数呢?然后,我们可以在不显式使用partial的情况下定义inc。
(def inc (add 1)) #partial is implied
这是一些语言的表现方式。当希望将函数组合成更大的转换时,它特别有用。这将把人们引向传感器。
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
下面是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实现的部分对象和函数的区别。
推荐文章
- 一元加/数字(x)和parseFloat(x)之间的区别是什么?
- angularjs中的compile函数和link函数有什么区别
- 删除绑定中添加的事件监听器
- 很好的初学者教程socket.io?
- HtmlSpecialChars在JavaScript中等价于什么?
- React: 'Redirect'没有从' React -router-dom'中导出
- 如何在React中使用钩子强制组件重新渲染?
- 我如何使用Jest模拟JavaScript的“窗口”对象?
- 我如何等待一个承诺完成之前返回一个函数的变量?
- 在JavaScript中根据键值查找和删除数组中的对象
- 使嵌套JavaScript对象平放/不平放的最快方法
- 如何以及为什么'a'['toUpperCase']()在JavaScript工作?
- 有Grunt生成index.html不同的设置
- 文档之间的区别。addEventListener和window。addEventListener?
- 如何检查动态附加的事件监听器是否存在?