什么是“柯里化”?

发布于 2024-07-04 18:22:41 字数 58 浏览 10 评论 0原文

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

I've seen references to curried functions in several articles and blogs but I can't find a good explanation (or at least one that makes sense!)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(25

以往的大感动 2024-07-11 18:22:41

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

"Currying" is the process of taking the function of multiple arguments and converting it into a series of functions that each take a single argument and return a function of a single argument, or in the case of the final function, return the actual result.

浅暮の光 2024-07-11 18:22:41

这就是著名的闭包

function add() {
  let a = 0;
  return function(b) {   
    return a + b;
  };
}

这就是神奇的柯里化

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

This one is the the famous closure:

function add() {
  let a = 0;
  return function(b) {   
    return a + b;
  };
}

And this one is the magical currying

function add(a) {
  return function(b) {
    return a + b;
  };
}
骑趴 2024-07-11 18:22:41

有一个“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));
  };

There is an example of "Currying in 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));
  };
﹂绝世的画 2024-07-11 18:22:41

下面是 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)}`);

输出

multiplyByTwo(8) : 16

Below is one of currying example in JavaScript, here the multiply return the function which is used to multiply x by two.

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

Output

multiplyByTwo(8) : 16

有深☉意 2024-07-11 18:22:41

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

我会尽力解释为什么它有用。 它是您从未意识到自己需要的工具之一,直到您意识到。 柯里化首先是一种让你的程序更具表现力的方法——你可以用更少的代码将操作组合在一起。

例如,如果您有一个柯里化函数 add,您可以编写相当于 JS x =>; 的代码。 k + x (或 Python lambda x: k + x 或 Ruby { |x| k + x } 或 Lisp (lambda (x) (+ kx)) 或 ...) 就像 add(k) 一样。 在 Haskelll 中,您甚至可以使用运算符:(k +)(+ k) (这两种形式允许您对非交换运算符进行柯里化:(/ 9) 是一个将数字除以 9 的函数,这可能是更常见的用例,但您也可以使用 (9 /) 来表示将 9 除以它的值的函数参数。)除了更短之外,柯里化版本不包含像所有其他版本中的 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)。)

同样,很难找到好的演示示例;柯里化在复杂的情况下最有用,它确实有助于提高解决方案的可读性,但这些解释只是为了让您理解问题,以至于可能会丢失有关柯里化的整体课程。喧嚣中。

The other answers have said what currying is: passing fewer arguments to a curried function than it expects is not an error, but instead returns a function that expects the rest of the arguments and returns the same result as if you had passed them all in at once.

I’ll try to motivate why it’s useful. It’s one of those tools that you never realized you needed until you do. Currying is above all a way to make your programs more expressive - you can combine operations together with less code.

For example, if you have a curried function add, you can write the equivalent of JS x => k + x (or Python lambda x: k + x or Ruby { |x| k + x } or Lisp (lambda (x) (+ k x)) or …) as just add(k). In Haskelll you can even use the operator: (k +) or (+ k) (The two forms let you curry either way for non-commutative operators: (/ 9) is a function that divides a number by 9, which is probably the more common use case, but you also have (9 /) for a function that divides 9 by its argument.) Besides being shorter, the curried version contains no made-up parameter name like the x found in all the other versions. It’s not needed. You’re defining a function that adds some constant k to a number, and you don’t need to give that number a name just to talk about the function. Or even to define it. This is an example of what’s called “point-free style”. You can combine operations together given nothing but the operations themselves. You don’t have to declare anonymous functions that do nothing but apply some operation to their argument, because that’s what the operations already are.

This becomes very handy with higher-order functions when they’re defined in a currying-friendly way. For instance, a curried map(fn, list) lets you define a mapper with just map(fn) that can be applied it to any list later. But currying a map defined instead as map(list, fn) just lets you define a function that will apply some other function to a constant list, which is probably less generally useful.

Currying reduces the need for things like pipes and threading. In Clojure, you might define a temperature conversion function using the threading macro ->: (defn f2c (deg) (-> deg (- 32) (* 5) (/ 9)). That’s cool, it reads nicely left to right (“subtract 32, multiply by 5 and divide by 9.”) and you only have to mention the parameter twice instead of once for every suboperation… but it only works because -> is a macro that transforms the whole form syntactically before anything is evaluated. It turns into a regular nested expression behind the scenes: (/ (* (- deg 32) 5) 9). If the math ops were curried, you wouldn’t need a macro to combine them so nicely, as in Haskell let f2c = (subtract 32) & (* 5) & (/ 9). (Although it would admittedly be more idiomatic to use function composition, which reads right to left: (/ 9) . (* 5) . (subtract 32).)

Again, it’s hard to find good demo examples; currying is most useful in complex cases where it really helps the readability of the solution, but those take so much explanation just to get you to understand the problem that the overall lesson about currying can get lost in the noise.

我的痛♀有谁懂 2024-07-11 18:22:41

更聪明的柯里化方法是使用 < code>Function.prototype.bind,即在函数上调用 bind())。 这样,该功能就不需要像其他人所演示的那样进行修改。

function h(a, b) {
  return a*a + b*b;
}

var a = f.bind(this, 3);

a(4) == 5*5  // true

A smarter way of currying would be to use Function.prototype.bind, i.e invoking bind() on a function). That way, the function needs not to be modified as others have demonstrated.

function h(a, b) {
  return a*a + b*b;
}

var a = f.bind(this, 3);

a(4) == 5*5  // true
遥远的她 2024-07-11 18:22:41

柯里化的一个例子是,当您拥有目前只知道其中一个参数的函数时:

例如:

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(_:) 时不知道回调的第二个参数code> 您必须创建另一个 lambda / 闭包才能将其发送到函数。

An example of currying would be when having functions you only know one of the parameters at the moment:

For example:

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
}

Here, since you don't know the second parameter for callback when sending it to performAsyncRequest(_:) you would have to create another lambda / closure to send that one to the function.

聽兲甴掵 2024-07-11 18:22:41

在这里您可以找到 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);

Here you can find a simple explanation of currying implementation in C#. In the comments, I have tried to show how currying can be useful:

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);
新一帅帅 2024-07-11 18:22:41

与所有其他答案一样,柯里化有助于创建部分应用的函数。 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 会生成新的柯里化函数你(双)

As all other answers currying helps to create partially applied functions. Javascript does not provide native support for automatic currying. So the examples provided above may not help in practical coding. There is some excellent example in livescript (Which essentially compiles to js)
http://livescript.net/

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

In above example when you have given less no of arguments livescript generates new curried function for you (double)

巾帼英雄 2024-07-11 18:22:41

该线程中的大多数示例都是人为的(添加数字)。 这些对于说明概念很有用,但当您可能在应用程序中实际使用柯里化时,这些并不会激发您的兴趣。

这是 JavaScript 用户界面库 React 的一个实际示例。 这里的柯里化说明了闭包性质。

正如大多数用户界面库中的典型情况一样,当用户单击按钮时,将调用一个函数来处理该事件。 处理程序通常会修改应用程序的状态并触发界面重新呈现。

项目列表是常见的用户界面组件。 每个项目可能有一个与其关联的标识符(通常与数据库记录相关)。 例如,当用户单击按钮以“喜欢”列表中的某个项目时,处理程序需要知道单击了哪个按钮。

柯里化是实现 id 和 handler 之间绑定的一种方法。 在下面的代码中,makeClickHandler 是一个函数,它接受 id 并返回一个在其范围内具有该 id 的处理函数。

内部函数的工作原理对于本次讨论并不重要。 但如果您好奇,它会搜索项目数组以通过 id 查找项目并增加其“喜欢”,通过设置状态触发另一个渲染。 React 中的状态是不可变的,因此修改一个值需要比您预期的更多工作。

您可以将调用柯里化函数视为“剥离”外部函数以公开可供调用的内部函数。 这个新的内部函数是传递给 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.createRoot(document.querySelector("#app"))
  .render(<List />);
button {
  font-family: monospace;
  font-size: 2em;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

Most of the examples in this thread are contrived (adding numbers). These are useful for illustrating the concept, but don't motivate when you might actually use currying in an app.

Here's a practical example from React, the JavaScript user interface library. Currying here illustrates the closure property.

As is typical in most user interface libraries, when the user clicks a button, a function is called to handle the event. The handler typically modifies the application's state and triggers the interface to re-render.

Lists of items are common user interface components. Each item might have an identifier associated with it (usually related to a database record). When the user clicks a button to, for example, "like" an item in the list, the handler needs to know which button was clicked.

Currying is one approach for achieving the binding between id and handler. In the code below, makeClickHandler is a function that accepts an id and returns a handler function that has the id in its scope.

The inner function's workings aren't important for this discussion. But if you're curious, it searches through the array of items to find an item by id and increments its "likes", triggering another render by setting the state. State is immutable in React so it takes a bit more work to modify the one value than you might expect.

You can think of invoking the curried function as "stripping" off the outer function to expose an inner function ready to be called. That new inner function is the actual handler passed to React's onClick. The outer function is a closure for the loop body to specify the id that will be in scope of a particular inner handler function.

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.createRoot(document.querySelector("#app"))
  .render(<List />);
button {
  font-family: monospace;
  font-size: 2em;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="app"></div>

天煞孤星 2024-07-11 18:22:41

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

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

A curried function is applied to multiple argument lists, instead of just
one.

Here is a regular, non-curried function, which adds two Int
parameters, x and y:

scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int,y: Int)Int
scala> plainOldSum(1, 2)
res4: Int = 3

Here is similar function that’s curried. Instead
of one list of two Int parameters, you apply this function to two lists of one
Int parameter 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

What’s happening here is that when you invoke curriedSum, you actually get two traditional function invocations back to back. The first function
invocation takes a single Int parameter named x , and returns a function
value for the second function. This second function takes the Int parameter
y.

Here’s a function named first that does in spirit what the first traditional
function invocation of curriedSum would do:

scala> def first(x: Int) = (y: Int) => x + y
first: (x: Int)(Int) => Int

Applying 1 to the first function—in other words, invoking the first function
and passing in 1 —yields the second function:

scala> val second = first(1)
second: (Int) => Int = <function1>

Applying 2 to the second function yields the result:

scala> second(2)
res6: Int = 3
眼角的笑意。 2024-07-11 18:22:41

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

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

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

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

(inc 7) # => 8

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

(def inc (partial add 1))

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

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

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

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

If you understand partial you're halfway there. The idea of partial is to preapply arguments to a function and give back a new function that wants only the remaining arguments. When this new function is called it includes the preloaded arguments along with whatever arguments were supplied to it.

In Clojure + is a function but to make things starkly clear:

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

You may be aware that the inc function simply adds 1 to whatever number it's passed.

(inc 7) # => 8

Let's build it ourselves using 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.

Now imagine if the language were smart enough to understand introspectively that add wanted two arguments. When we passed it one argument, rather than balking, what if the function partially applied the argument we passed it on our behalf understanding that we probably meant to provide the other argument later? We could then define inc without explicitly using partial.

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

This is the way some languages behave. It is exceptionally useful when one wishes to compose functions into larger transformations. This would lead one to transducers.

枫林﹌晚霞¤ 2024-07-11 18:22:41

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

正如其他人提到的,这只是一种拥有单参数函数的方法。

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

I found this article, and the article it references, useful, to better understand currying:
http://blogs.msdn.com/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx

As the others mentioned, it is just a way to have a one parameter function.

This is useful in that you don't have to assume how many parameters will be passed in, so you don't need a 2 parameter, 3 parameter and 4 parameter functions.

离去的眼神 2024-07-11 18:22:41

柯里化是 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

很酷吧?

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

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

我挑战你!

希望您了解什么是柯里化。 如果您需要任何说明,请随时在这里发表评论。

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

Currying is one of the higher-order functions of Java Script.

Currying is a function of many arguments which is rewritten such that it takes the first argument and return a function which in turns uses the remaining arguments and returns the value.

Confused?

Let see an example,

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

This is similar to the following currying function,

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

So what does this code means?

Now read the definition again,

Currying is a function of many arguments which is rewritten such that it takes first argument and return a function which in turns uses the remaining arguments and returns the value.

Still, Confused?
Let me explain in deep!

When you call this function,

var curryAdd = add(5);

It will return you a function like this,

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

So, this is called higher-order functions. Meaning, Invoking one function in turns returns another function is an exact definition for higher-order function. This is the greatest advantage for the legend, Java Script.
So come back to the currying,

This line will pass the second argument to the curryAdd function.

curryAdd(6);

which in turns results,

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

Hope you understand the usage of currying here.
So, Coming to the advantages,

Why Currying?

It makes use of code reusability.
Less code, Less Error.
You may ask how it is less code?

I can prove it with ECMA script 6 new feature arrow functions.

Yes! ECMA 6, provide us with the wonderful feature called arrow functions,

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

With the help of the arrow function, we can write the above function as follows,

x=>y=>x+y

Cool right?

So, Less Code and Fewer bugs!!

With the help of these higher-order function one can easily develop a bug-free code.

I challenge you!

Hope, you understood what is currying. Please feel free to comment over here if you need any clarifications.

Thanks, Have a nice day!

梦归所梦 2024-07-11 18:22:41

这是通用的示例,也是带有 n 的函数柯里化的最短版本。 参数。

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

Here is the example of generic and the shortest version for function currying with n no. of params.

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

掌心的温暖 2024-07-11 18:22:41

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

Here's a toy example in 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..."

(Just using concatenation via + to avoid distraction for non-Python programmers.)

Editing to add:

See http://docs.python.org/library/functools.html?highlight=partial#functools.partial,
which also shows the partial object vs. function distinction in the way Python implements this.

帅气称霸 2024-07-11 18:22:41

Curry 可以简化你的代码。 这是使用它的主要原因之一。 柯里化 (Currying) 是将接受 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);

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

Curry can simplify your code. This is one of the main reasons to use this. Currying is a process of converting a function that accepts n arguments into n functions that accept only one argument.

The principle is to pass the arguments of the passed function, using the closure (closure) property, to store them in another function and treat it as a return value, and these functions form a chain, and the final arguments are passed in to complete the operation.

The benefit of this is that it can simplify the processing of parameters by dealing with one parameter at a time, which can also improve the flexibility and readability of the program. This also makes the program more manageable. Also dividing the code into smaller pieces would make it reuse-friendly.

For example:

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

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

I can also do...

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

This is very great for making complex code neat and handling of unsynchronized methods etc.

∝单色的世界 2024-07-11 18:22:41

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

A curried function is a function of several arguments rewritten such that it accepts the first argument and returns a function that accepts the second argument and so on. This allows functions of several arguments to have some of their initial arguments partially applied.

固执像三岁 2024-07-11 18:22:41

柯里化是将函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)(c)

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

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

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

让我们创建一个对双参数函数执行柯里化的柯里化函数。 换句话说,两个参数 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

As你可以看到,实现是一系列包装器。

  • curry(func) 的结果是一个包装器 function(a)
  • 当像 sum(1) 那样调用它时,参数将保存在词法环境中,并返回一个新的包装器 function(b)
  • 然后 sum(1)(2) 最终调用 function(b) 提供 2,并将调用传递给原始多参数 sum。

Currying is translating a function from callable as f(a, b, c) into callable as f(a)(b)(c).

Otherwise currying is when you break down a function that takes multiple arguments into a series of functions that take part of the arguments.

Literally, currying is a transformation of functions: from one way of calling into another. In JavaScript, we usually make a wrapper to keep the original function.

Currying doesn’t call a function. It just transforms it.

Let’s make curry function that performs currying for two-argument functions. In other words, curry(f) for two-argument f(a, b) translates it into 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

As you can see, the implementation is a series of wrappers.

  • The result of curry(func) is a wrapper function(a).
  • When it is called like sum(1), the argument is saved in the Lexical Environment, and a new wrapper is returned function(b).
  • Then sum(1)(2) finally calls function(b) providing 2, and it passes the call to the original multi-argument sum.
国粹 2024-07-11 18:22:41

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

这是正式的定义:

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

这是一个有意义的现实世界示例:

您去 ATM 取钱。 您刷卡,输入密码并做出选择,然后按 Enter 键提交“金额”以及请求。

这是正常的取款功能。

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

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

这是带有柯里化函数的 Atm:

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

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

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

Currying means to convert a function of N arity into N functions of arity 1. The arity of the function is the number of arguments it requires.

Here is the formal definition:

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

Here is a real world example that makes sense:

You go to ATM to get some money. You swipe your card, enter pin number and make your selection and then press enter to submit the "amount" alongside the request.

here is the normal function for withdrawing money.

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

In this implementation function expects us entering all arguments at once. We were going to swipe the card, enter the pin and make the request, then function would run. If any of those steps had issue, you would find out after you enter all the arguments. With curried function, we would create higher arity, pure and simple functions. Pure functions will help us easily debug our code.

this is Atm with curried function:

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.

Also, each function here is reusable, so you can use the same functions in different parts of your project.

夏雨凉 2024-07-11 18:22:41

它可以是一种使用函数来创建其他函数的方法。

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

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

It can be a way to use functions to make other functions.

In javascript:

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

Would allow us to call it like so:

let addTen = add(10);

When this runs the 10 is passed in as x;

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

which means we are returned this function:

function(y) { return 10 + y };

So when you call

 addTen();

you are really calling:

 function(y) { return 10 + y };

So if you do this:

 addTen(4)

it's the same as:

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

So our addTen() always adds ten to whatever we pass in. We can make similar functions in the same way:

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

Now the obvious follow up question is why on earth would you ever want to do that? It turns what was an eager operation x + y into one that can be stepped through lazily, meaning we can do at least two things
1. cache expensive operations
2. achieve abstractions in the functional paradigm.

Imagine our curried function looked like this:

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

We could call this function once, then pass around the result to be used in lots of places, meaning we only do the computationally expensive stuff once:

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

We can get abstractions in a similar way.

戏舞 2024-07-11 18:22:41

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

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

let f x y z = x + y + z

这里函数 f 接受参数 x、y 和 z 并将它们相加:-

f 1 2 3

返回 6。

根据我们的定义,我们可以为 f 定义柯里函数:-

let curry f = fun x -> f x

其中 '有趣的x-> f x' 是一个 lambda 函数,相当于 x => C# 中的 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.

结论:-

柯里化和部分函数应用之间的区别在于:-

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

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

Currying is a transformation that can be applied to functions to allow them to take one less argument than previously.

For example, in F# you can define a function thus:-

let f x y z = x + y + z

Here function f takes parameters x, y and z and sums them together so:-

f 1 2 3

Returns 6.

From our definition we can can therefore define the curry function for f:-

let curry f = fun x -> f x

Where 'fun x -> f x' is a lambda function equivilent to x => f(x) in C#. This function inputs the function you wish to curry and returns a function which takes a single argument and returns the specified function with the first argument set to the input argument.

Using our previous example we can obtain a curry of f thus:-

let curryf = curry f

We can then do the following:-

let f1 = curryf 1

Which provides us with a function f1 which is equivilent to f1 y z = 1 + y + z. This means we can do the following:-

f1 2 3

Which returns 6.

This process is often confused with 'partial function application' which can be defined thus:-

let papply f x = f x

Though we can extend it to more than one parameter, i.e.:-

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

A partial application will take the function and parameter(s) and return a function that requires one or more less parameters, and as the previous two examples show is implemented directly in the standard F# function definition so we could achieve the previous result thus:-

let f1 = f 1
f1 2 3

Which will return a result of 6.

In conclusion:-

The difference between currying and partial function application is that:-

Currying takes a function and provides a new function accepting a single argument, and returning the specified function with its first argument set to that argument. This allows us to represent functions with multiple parameters as a series of single argument functions. Example:-

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

Partial function application is more direct - it takes a function and one or more arguments and returns a function with the first n arguments set to the n arguments specified. Example:-

let f x y z = x + y + z
let f1 = f 1
let f2 = f 2
f1 2 3
6
f2 1 3
6
浅笑依然 2024-07-11 18:22:41

这是一个具体的例子:

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

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

Here's a concrete example:

Suppose you have a function that calculates the gravitational force acting on an object. If you don't know the formula, you can find it here. This function takes in the three necessary parameters as arguments.

Now, being on the earth, you only want to calculate forces for objects on this planet. In a functional language, you could pass in the mass of the earth to the function and then partially evaluate it. What you'd get back is another function that takes only two arguments and calculates the gravitational force of objects on earth. This is called currying.

随梦而飞# 2024-07-11 18:22:41

在函数代数中,处理采用多个参数(或相当于 N 元组的一个参数)的函数有些不雅——但是,正如 Moses Schönfinkel(以及独立的 Haskell Curry)所证明的那样,这不是必需的:所有你need 是带有一个参数的函数。

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

像往常一样,维基百科对此有一个很好的摘要条目,其中有许多有用的指示(可能包括有关您最喜欢的语言的指示;- )以及稍微严格的数学处理。

In an algebra of functions, dealing with functions that take multiple arguments (or equivalent one argument that's an N-tuple) is somewhat inelegant -- but, as Moses Schönfinkel (and, independently, Haskell Curry) proved, it's not needed: all you need are functions that take one argument.

So how do you deal with something you'd naturally express as, say, f(x,y)? Well, you take that as equivalent to f(x)(y) -- f(x), call it g, is a function, and you apply that function to y. In other words, you only have functions that take one argument -- but some of those functions return other functions (which ALSO take one argument;-).

As usual, wikipedia has a nice summary entry about this, with many useful pointers (probably including ones regarding your favorite languages;-) as well as slightly more rigorous mathematical treatment.

扭转时空 2024-07-11 18:22:41

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

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

add(3, 4); // returns 7

这是一个接受两个参数 ab 的函数,并返回它们的总和。 我们现在将对这个函数进行柯里化:

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

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

add(3)(4); // returns 7

var add3 = add(3); // returns a function

add3(4); // returns 7
  • 第一个语句返回 7,与 add(3, 4) 语句类似。
  • 第二条语句定义了一个名为 add3 的新函数,该函数会将 3 添加到其参数中。 这就是有些人所说的闭包。
  • 第三条语句使用 add3 操作将 3 添加到 4,再次生成结果 7。

Currying is when you break down a function that takes multiple arguments into a series of functions that each take only one argument. Here's an example in JavaScript:

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

add(3, 4); // returns 7

This is a function that takes two arguments, a and b, and returns their sum. We will now curry this function:

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

This is a function that takes one argument, a, and returns a function that takes another argument, b, and that function returns their sum.

add(3)(4); // returns 7

var add3 = add(3); // returns a function

add3(4); // returns 7
  • The first statement returns 7, like the add(3, 4) statement.
  • The second statement defines a new function called add3 that will add 3 to its argument. This is what some may call a closure.
  • The third statement uses the add3 operation to add 3 to 4, again producing 7 as a result.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文