我在一些文章和博客中看到了curry函数的引用,但我找不到一个好的解释(或者至少一个有意义的解释!)
当前回答
这个线程中的大多数例子都是虚构的(添加数字)。这些对于说明这个概念很有用,但当你在应用程序中实际使用咖喱时,它们就没有意义了。
下面是一个来自React (JavaScript用户界面库)的实例。这里的curry说明了闭包属性。
与大多数用户界面库中的典型情况一样,当用户单击按钮时,将调用一个函数来处理该事件。处理程序通常修改应用程序的状态并触发接口重新呈现。
项目列表是常见的用户界面组件。每个项目都可能有一个与之相关的标识符(通常与数据库记录相关)。例如,当用户单击按钮以“喜欢”列表中的某项时,处理程序需要知道单击了哪个按钮。
curry是实现id和处理程序之间绑定的一种方法。在下面的代码中,makeClickHandler是一个函数,它接受一个id,并返回一个在其作用域中包含id的处理程序函数。
内部函数的工作方式在本文中并不重要。但如果你好奇的话,它会在项目数组中搜索,通过id找到一个项目,并增加它的“喜欢”,通过设置状态触发另一次呈现。State在React中是不可变的,所以修改一个值所花费的工作比你想象的要多一些。
您可以将调用curry函数视为“剥离”外部函数以公开准备调用的内部函数。这个新的内部函数是传递给React的onClick的实际处理程序。外部函数用于循环体指定特定内部处理函数范围内的id。
const List = () => { const [items, setItems] = React.useState([ {name: "foo", likes: 0}, {name: "bar", likes: 0}, {name: "baz", likes: 0}, ].map(e => ({...e, id: crypto.randomUUID()}))); // .----------. outer func inner func // | currying | | | // `----------` V V const makeClickHandler = (id) => (event) => { setItems(prev => { const i = prev.findIndex(e => e.id === id); const cpy = {...prev[i]}; cpy.likes++; return [ ...prev.slice(0, i), cpy, ...prev.slice(i + 1) ]; }); }; return ( <ul> {items.map(({name, likes, id}) => <li key={id}> <button onClick={ /* strip off first function layer to get a click handler bound to `id` and pass it to onClick */ makeClickHandler(id) } > {name} ({likes} likes) </button> </li> )} </ul> ); }; ReactDOM.render( <List />, document.querySelector("#root") ); button { font-family: monospace; font-size: 2em; } <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script> <div id="root"></div>
其他回答
它可以是一种用函数生成其他函数的方法。
在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)
我们可以用类似的方式得到抽象。
我发现这篇文章,以及它引用的文章,有助于更好地理解咖喱: http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx
正如其他人所提到的,它只是一种具有单参数函数的方法。
这很有用,因为你不需要假设有多少参数将被传入,所以你不需要2个参数,3个参数和4个参数函数。
Curry可以简化代码。这是使用它的主要原因之一。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);
这对于使复杂的代码变得整洁和处理非同步方法等非常有用。
在这里,您可以找到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);
下面是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实现的部分对象和函数的区别。
推荐文章
- 在React Native中使用Fetch授权头
- 为什么我的球(物体)没有缩小/消失?
- 如何使用jQuery检测页面的滚动位置
- if(key in object)或者if(object. hasownproperty (key)
- 一元加/数字(x)和parseFloat(x)之间的区别是什么?
- angularjs中的compile函数和link函数有什么区别
- 删除绑定中添加的事件监听器
- 很好的初学者教程socket.io?
- HtmlSpecialChars在JavaScript中等价于什么?
- React: 'Redirect'没有从' React -router-dom'中导出
- 如何在React中使用钩子强制组件重新渲染?
- 我如何使用Jest模拟JavaScript的“窗口”对象?
- 我如何等待一个承诺完成之前返回一个函数的变量?
- 在JavaScript中根据键值查找和删除数组中的对象
- 使嵌套JavaScript对象平放/不平放的最快方法