我在一些文章和博客中看到了curry函数的引用,但我找不到一个好的解释(或者至少一个有意义的解释!)
当前回答
下面是泛型和最短版本的函数用n no curry的例子。的参数。
const add = a => b => b ? add(a + b) : a;
Const add = a => b => b ?Add (a + b): a; console.log(添加(1)(2)(3)(4)());
其他回答
curry是指将一个N次的函数转换为N次1次的函数。函数的元数是它所需要的参数的个数。
下面是正式的定义:
curry(f) :: (a,b,c) -> f(a) -> f(b)-> f(c)
下面是一个真实的例子:
你去自动取款机取钱。你刷你的卡,输入密码,做出选择,然后按enter键提交“金额”和请求。
这是取款的正常功能。
const withdraw=(cardInfo,pinNumber,request){
// process it
return request.amount
}
在这个实现函数中,我们希望一次性输入所有参数。我们将刷卡,输入密码并发出请求,然后函数将运行。如果这些步骤中有任何问题,在输入所有参数后就会发现。使用curry函数,我们可以创建更高的、纯粹的和简单的函数。纯函数将帮助我们轻松地调试代码。
这是带有咖喱功能的Atm机。
const withdraw=(cardInfo)=>(pinNumber)=>(request)=>request.amount
ATM, takes the card as input and returns a function that expects pinNumber and this function returns a function that accepts the request object and after the successful process, you get the amount that you requested. Each step, if you had an error, you will easily predict what went wrong. Let's say you enter the card and got error, you know that it is either related to the card or machine but not the pin number. Or if you entered the pin and if it does not get accepted you know that you entered the pin number wrong. You will easily debug the error.
此外,这里的每个函数都是可重用的,因此您可以在项目的不同部分使用相同的函数。
curry是将函数从可调用的f(a, b, c)转换为可调用的f(a)(b)(c)。
另外,curry是指将一个接受多个参数的函数分解为一系列接受部分参数的函数。
从字面上看,curry是函数的转换:从一种调用方式到另一种调用方式。在JavaScript中,我们通常创建一个包装器来保留原始函数。
curry不调用函数。它只是变换了它。
让我们创建一个curry函数,它对双实参函数执行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)一样被调用时,参数被保存在词法环境中,并返回一个新的包装器函数(b)。 然后sum(1)(2)最后调用函数(b)提供2,它将调用传递给原始的多参数sum。
这个线程中的大多数例子都是虚构的(添加数字)。这些对于说明这个概念很有用,但当你在应用程序中实际使用咖喱时,它们就没有意义了。
下面是一个来自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>
有一个“咖喱在理性ml”的例子。
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));
};
这里有一个具体的例子:
假设你有一个计算作用在物体上的引力的函数。如果你不知道公式,你可以在这里找到。这个函数接受三个必要的形参作为参数。
现在,在地球上,你只想计算这个星球上物体的力。在函数式语言中,你可以把地球的质量传递给函数,然后对它进行部分计算。你会得到另一个函数,它只接受两个参数,并计算地球上物体的引力。这叫做咖喱。