用 JS 理解柯里化

函数式编程风格,试图以函数作为参数传递(回调)和无副作用的返回函数(修改程序的状态)。

很多语言采用了这种编程风格。JavaScript,Haskell,Clojure,Erlang 和 Scala 是其中最受欢迎的几种语言。

数式编程风格具有传递和返回函数的能力,它带来了许多概念:

  • Pure Functions(纯函数)
  • Currying(柯里化)
  • Higher-Order functions(高阶函数)

我们将在这里介绍这些概念中的一种 Currying。

在本文中,我们将看到 currying 如何工作,以及它如何在软件开发人员的工作中发挥作用。

提示:您可以将其变为 Bit组件,而不是复制粘贴可重用的 JS 功能,并快速与团队共享项目。

Bit - 使用代码组件共享和构建
Bit 可帮助您在项目和应用程序之间共享,发现和使用代码组件,以构建新功能和...       bitsrc.io

什么是 Currying?

Currying 是函数式编程中的一个过程,我们可以将具有多个参数的函数转换为顺序嵌套的函数。它返回一个接收下一个参数的新函数。

它不断返回一个新函数(接收当前参数,就像我们之前所说的那样),直到所有参数都用完为止。保留参数 "alive"(通过闭包),并且当返回并执行 currying 链中的最终函数时,所有参数都在执行中使用。

Currying 是将具有多个 arity 的函数转换为具有较少 arity 的函数的过程 -  Kristina Brainwave

注意:术语 arity “是指函数所接收的参数个数”。例如,

function fn (a, b) {
// ...
} function _fn(a, b, c) {
// ...
}
Js

函数 fn 接受两个参数(2-arity 函数),_fn 接受三个参数(3-arity 函数)。

因此,currying 将具有多个参数的函数转换为一系列函数,每个函数都接收一个参数。

我们来看一个简单的例子:

function multiply(a, b, c) {
return a * b * c;
}
Js

此函数接收三个数字,将数字相乘并返回结果。

multiply(1, 2, 3) // 6
Js

请参阅我们如何使用完整参数调用 multiply 函数,来创建一个 curried 版本函数,看看我们如何在一系列调用中调用相同的函数(并得到相同的结果):

function multiply(a) {
return (b) => {
return (c) => {
return a * b * c;
}
}
} log(multiply(1)(2)(3)) // 6
Js

我们已将 multiply(1,2,3) 函数调用转为 multiply(1)(2)(3) 多个函数调用。

我们已经将一个函数转为一系列函数。为了得到三个数相乘的结果 12 和 3 三个数字一个接一个地传递,每个数字都将在下一个函数的内部调用。我们可以将 multiply(1)(2)(3) 分开以便更好地理解它:

const mul1 = multiply(1);
const mul2 = mul1(2);
const result = mul2(3);
log(result); // 6
Js

让我们一个接一个地接收参数。我们把 1 传递给了 multiply 函数:

let mul1 = multiply(1);
Js

它返回这个函数:

return (b) => {
return (c) => {
return a * b * c;
}
}
Js

现在,mul1 保持上面的函数定义,它带有一个参数 b

我们调用了 mul1 函数,传入 2

let mul2 = mul1(2);
Js

在 mul2 中将返回第三个函数:

return (c) => {
return a * b * c;
}
Js

返回的函数现在存储在 mul2 变量中。

从本质上讲,mul2 将是:

mul2 = (c) => {
return a * b * c;
}
Js

当 mul2 以 3 作为参数调用时,

const result = mul2(3);
Js

它把之前传入的参数 a = 1b = 2 带入计算并返回结果 6

log(result); // 6
Js

作为嵌套函数,mul2 可以访问外部函数 multiply 以及 mul1 作用域内的变量。

这就是为什么 mul2 能使用已经在的函数中定义的变量并执行乘法运算的原因。虽然这些函数早已在内存中被回收 garbage collected,但它的变量仍以某种方式保留 "alive"

您会看到三个数字一次一个地应用于该函数,并且每次都返回一个新函数,直到所有数字都用完为止。

让我们看另一个例子:

function volume(l, w, h) {
return l * w * h;
} const aCylinder = volume(100, 20, 90); // 180000
Js

我们有一个计算实心物体体积的函数 volume

柯里化后的版本将接受一个参数并返回一个函数,该函数也将接收一个参数并返回一个函数。这个过程将被循环/继续,直到到达最后一个参数并返回最后一个函数,这将执行与前一个参数和最后一个参数的乘法运算。

function volume(l) {
return (w) => {
return (h) => {
return l * w * h;
}
}
} const aCylinder = volume(100)(20)(90) // 180000
Js

与我们在 multiply 函数中所使用的一样,最后一个函数只接受 h ,但是它将执行的操作所用到的其他函数作用域内的变量早已被函数返回。因为闭包 Closure ,这些参数仍然有效。

currying 背后的想法是接收一个函数并返回一个特殊函数的函数。

数学中的 Currying

我有点喜欢数学例证

JS 柯里化 (curry)的更多相关文章

  1. Scala 基础(十二):Scala 函数式编程(四)高级(二)参数(类型)推断、闭包(closure)、函数柯里化(curry)、控制抽象

    1  参数(类型)推断 参数推断省去类型信息(在某些情况下[需要有应用场景],参数类型是可以推断出来的,如list=(1,2,3) list.map() map中函数参数类型是可以推断的),同时也可以 ...

  2. js 柯里化Currying

    今天读一篇博客的时候,看都有关柯里化的东西,由于好奇,特意查了一下,找到一篇比较好的文章,特意收藏. 引子先来看一道小问题:有人在群里出了到一道题目:var s = sum(1)(2)(3) .... ...

  3. JS - 柯里化

    一:what's this? 柯里化: 是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术.其实,柯里化就是用闭包原理实现函数 ...

  4. Javascript函数柯里化(curry)

    函数柯里化currying,是函数式编程非常重要的一个标志.它的实现需要满足以下条件,首先就是函数可以作为参数进行传递,然后就是函数可以作为返回值return出去.我们依靠这个特性编写很多优雅酷炫的代 ...

  5. js柯里化的一个应用

    听到同学说面试一道题目 add(1)(2)(3)(4); 查询了下资料  这是一个js里面的柯里化 现象 add_curry防范返回的是一个 retVal,并不是执行结果.这里的代码很想递归,但是不是 ...

  6. 函数柯里化 curry

    一.函数柯里化的特性: (1)参数复用 $.ajax // 示例一 function ajax(type,url,data) { var xhr = new XMLHttpRequest(); xhr ...

  7. js柯里化

    这篇文章讲的很好啊~例子很好 http://www.zhangxinxu.com/wordpress/2013/02/js-currying/ 这篇是讲函数式编程的,其中也有涉及到,说明了柯里化是一种 ...

  8. js 柯里化、深拷贝、浅拷贝

    curry const sum = (a, b, c, d) => a + b + c + d const curry = fn => (judge = (...args) => a ...

  9. js函数式编程(二)-柯里化

    这节开始讲的例子都使用简单的TS来写,尽量做到和es6差别不大,正文如下 我们在编程中必然需要用到一些变量存储数据,供今后其他地方调用.而函数式编程有一个要领就是最好不要依赖外部变量(当然允许通过参数 ...

随机推荐

  1. box-shadow的动效制作

    突然发现原来box-shadow的功能很强大,还能做动效,下面整理下box-shadow几个效果 案例1:hover效果 <a href="/app/list">WEB ...

  2. dp 动态规划 之C - Apple Catching 简单基础

    终于开始写dp了,还很不熟练 It is a little known fact that cows love apples. Farmer John has two apple trees (whi ...

  3. Hue添加Spark notebook

    参考自https://blogs.msdn.microsoft.com/pliu/2016/06/18/run-hue-spark-notebook-on-cloudera/ 说明 使用Clouder ...

  4. 转载 .Net多线程编程—并发集合 https://www.cnblogs.com/hdwgxz/p/6258014.html

    集合 1 为什么使用并发集合? 原因主要有以下几点: System.Collections和System.Collections.Generic名称空间中所提供的经典列表.集合和数组都不是线程安全的, ...

  5. 剑指offer从上往下打印二叉树 、leetcode102. Binary Tree Level Order Traversal(即剑指把二叉树打印成多行、层序打印)、107. Binary Tree Level Order Traversal II 、103. Binary Tree Zigzag Level Order Traversal(剑指之字型打印)

    从上往下打印二叉树这个是不分行的,用一个队列就可以实现 class Solution { public: vector<int> PrintFromTopToBottom(TreeNode ...

  6. 深入浅出的webpack构建工具---DevServer配置项(二)

    深入浅出的webpack构建工具---DevServer配置项(二) 阅读目录 DevServer配置项 1. contentBase 2. port 3. host 4. headers 5. hi ...

  7. VUE2第五天学习---自定义指令

    阅读目录 1.理解VUE中的自定义指令 回到顶部 1.理解VUE中的自定义指令 默认核心指令有 (v-model 和 v-show), 但是有时候我们需要用到自定义指令,在vue中,代码复用主要形式和 ...

  8. TCP/IP协议--TCP协议概括和TCP连接的建立和终止

    TCP提供一种面向连接的.可靠的字节流服务.面向连接指,发送和接收方在交换数据前必须建立一个TCP连接.顺便说下,一个TCP连接只有两方,因此广播和多播是不能应用于TCP的.字节流指,两个应用程序通过 ...

  9. 4《想成为黑客,不知道这些命令行可不行》(Learn Enough Command Line to Be Dangerous)—目录

    我们已经学习过许多处理文件的Unix工具,现在是时候来学习目录了,也就是文件夹(图20).正如我们所见,许多在文件中的开发思想也适用于目录,但同样也有许多区别.

  10. Luogu4622 COCI2012-2013#6 JEDAN 组合、DP

    传送门 题意:给出一个$N$个数的序列$a_i$,其中$a_i=-1$表示第$i$个位置数字未知,问有多少种用非负整数代替$a_i$中$-1$的方法使得从全$0$序列经过以下操作若干次得到序列$a_i ...