用 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 “是指函数所接收的参数个数”。例如,

  1. function fn (a, b) {
  2. // ...
  3. }
  4. function _fn(a, b, c) {
  5. // ...
  6. }
Js

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

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

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

  1. function multiply(a, b, c) {
  2. return a * b * c;
  3. }
Js

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

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

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

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

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

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

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

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

  1. let mul1 = multiply(1);
Js

它返回这个函数:

  1. return (b) => {
  2. return (c) => {
  3. return a * b * c;
  4. }
  5. }
Js

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

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

  1. let mul2 = mul1(2);
Js

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

  1. return (c) => {
  2. return a * b * c;
  3. }
Js

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

从本质上讲,mul2 将是:

  1. mul2 = (c) => {
  2. return a * b * c;
  3. }
Js

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

  1. const result = mul2(3);
Js

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

  1. log(result); // 6
Js

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

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

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

让我们看另一个例子:

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

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

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

  1. function volume(l) {
  2. return (w) => {
  3. return (h) => {
  4. return l * w * h;
  5. }
  6. }
  7. }
  8. 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. Java面试——微服务

    1.什么是微服务?    就目前而言,对于微服务业界并没有一个统一的,标准的定义. 但通常而言,微服务架构是一种架构模式或者说是一种架构风格,它提倡将单一应用程序划分一组小的服务,每个服务运行在其独立 ...

  2. 【转】BASE64编码简介

    BASE64是一种编码方式,通常用于把二进制数据编码为可写的字符形式的数据. 这是一种可逆的编码方式. 编码后的数据是一个字符串,其中包含的字符为:A-Z.a-z.0-9.+./ 共64个字符:26 ...

  3. .NET Core、Xamarin、.NET Standard和.NET Framework四者之间的区别

    前段时日微软(Microsoft)正式发布了.NET Core 2.0,在很多开发社区中反响不错.但还是有一些开发者发出了疑问,.NET Core.Xamarin..NET Standard和.NET ...

  4. Java内存区域划分、内存分配原理(转)

    文章引用自 http://blog.csdn.net/OyangYujun/article/details/41173747 运行时数据区域 Java虚拟机在执行Java的过程中会把管理的内存划分为若 ...

  5. mysql编码问题:

    在my.ini文件改为: [client]default-character-set = utf8port=3306 [mysql] default-character-set=utf8 [mysql ...

  6. iRedMail退信问题的解决(转)

    安装完iRedMail之后发现可以给外网发邮件但是收不到外网发来的邮件,查看log发现这么一句话:postfix/postscreen[11355]: NOQUEUE: reject: RCPT fr ...

  7. Java多线程编程模式实战指南之Promise模式

    Promise模式简介(转) Promise模式是一种异步编程模式 .它使得我们可以先开始一个任务的执行,并得到一个用于获取该任务执行结果的凭据对象,而不必等待该任务执行完毕就可以继续执行其他操作.等 ...

  8. Luogu3199 HNOI2009 最小圈 分数规划、SPFA

    传送门 可以发现它的式子是一个分数规划的式子,所以可以二分答案,将所有边权减掉当前二分值之后跑一边$SPFA$判断负环即可. 然而这道题把$BFS-SPFA$卡掉了却没卡$DFS-SPFA$ 出题人: ...

  9. git lg 配置

    git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d ...

  10. KMeans算法分析以及实现

    KMeans KMeans是一种无监督学习聚类方法, 目的是发现数据中数据对象之间的关系,将数据进行分组,组内的相似性越大,组间的差别越大,则聚类效果越好. 无监督学习,也就是没有对应的标签,只有数据 ...