Currying,中文多翻译为柯里化,感觉这个音译还没有达到类似 Humor 之于幽默的传神地步,后面直接使用 Currying。

什么是 Currying

Currying 是这么一种机制,它将一个接收多个参数的函数,拆分成多个接收单个参数的函数。

考察下面的代码:

function add (a, b) {
return a + b;
} add(3, 4); // returns 7

add 接收两个参数 a,b,并返回它们的和 a+b。

经过 curry 化处理后,函数成了如下形式:

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

现在 add 接收一个参数 a,返回另一个接收一个参数 b 的函数。

add(3)(4);

var add3 = add(3);

add3(4);

现在当调用 add(3) 后,得到的不是和,而是另一个接收一个参数的函数,因此,add 的返回可以继续被调用,add(3)(4) 后面的这次调用才会将 4 加到 3 上得到和。

而 var add3 = add(3) 这样的单次调用,得到的函数效果相当于是将 3 保存在了新函数的闭包中,该函数会对传入的参数加 3。

注意这里提到了将入参 3 保存 到了闭包中后续使用,很容易联想到 Function.prototype.bind(),它就可以对传入的函数提前绑定一些预设的入参:

function.bind(thisArg[, arg1[, arg2[, ...]]])

后面会看到,正因为 bind 和 Currying 有点关系,在实现任意函数的 Currying 化时会用到它。

注意到 Currying 化的定义,其实是将多个参数打散到多个函数中,这个过程可通过代码来自动化,以达到将任意多入参函数进行 Currying 化的目的,后面讨论实现。

偏函数/Partial Application

区别与 Currying,如果在拆分入参的过程中,这些拆分出来的函数不是一次只应用其中的一个,而是任意多个,则这些函数就是部分应用(Parital application)了原函数中的入参,称作偏函数。

考察下面的 add 函数,其实是将前面示例中的 add 入参进行了扩充,由两个增加到四个:

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

那么如下的函数就都是偏函数,它们都部分应用了 add 的入参:

function partial1(a) {
return function(c) {
return a + b + c + d;
};
}
function partial2(a, b) {
return function(c, d) {
return a + b + c + d;
};
}
function partial3(a, b, c) {
return function(d) {
return a + b + c + d;
};
}

偏函数中这种入参的拆分和部分应用,并不仅限于一层的拆分,可以是任意多次的:

function partial1(a, b) {
return function partial2(c) {
return function partial3(d) {
return a + b + c + d;
};
};
} partial1(1)(2, 3)(4); // 10

其中,partial1、partial2、partial3 一起构成了原 add 函数的偏函数。

可以看到,偏函数是 Curring 更加一般(general)的形式,下面看如何实现将任意函数进行 Currying 化,或偏函数化。

将一般化函数进行 Currying 化

我们需要构造这么一个函数假设名叫 curry,

function curry(fn){
// 待实现
}

调用 curry 后,我们可以得到原函数 Curry 化后的版本,

function add (a, b) {
return a + b;
} var currified = curry(add);

即上述 currified 应该等效为:

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

首先,通过 Function.length 是可以知道一个给定函数其预期的入参个数的。

再加上前面提到的 bind 函数,可以得到如下的实现:

function curry(f) {
return function currify() {
const args = Array.prototype.slice.call(arguments);
return args.length >= f.length ?
f.apply(null, args) :
currify.bind(null, ...args)
}
}

下面测试一下:

function add(a, b) {
return a + b;
} var currified = curry(add); currified(1)(2); // 3

并且以上实现不只是简单的 Currying 化,可以是任意数量和任意次数的 parial application:

function add(a, b, c, d) {
return a + b + c + d;
} var currified = curry(add); currified(1)(2)(3)(4); // 10

currified(1)(2, 3)(4); // 10

currified(1, 2)(3, 4); // 10

总之就是各种形状和(hàn)姿势,各种颜色和(hàn)皮肤的组合。

自动化的 CurryIng 倒是实现了,可说了半天,它具体有什么实用价值。

函数的组合(function composition)

我们知道代数里面可以有函数的组合,譬如:

f(x) = x * x
g(y) = y + 1
g(f(x)) = x * x + 1 g(f(2)) = 2 * 2 + 1 = 5

上述代数表达转成 JavaScript 即:

function f(x) {
return x ** 2;
} function g(y) {

return y + 1;

} g(f(2)) // 5

这里用到了两个函数 f,g 联合起来得到一个结果,他们都分别只接收一个入参同时返回一个结果。

像这样只接收一个入参并返回一个结果的函数,便符合组装的需求,可像上面这样自由组合。通过上面的讨论我们知道,任意函数都可经过 Currying 化处理后变成多个只接收单个入参的函数。这就为函数的组合提供了基础。

因此我们可以将 f,g 的结合形成一个新的函数,这个函数作为对外的接口被调用即可。

const compose = fn1 => fn2 => input => fn1(fn2(input));

使用:

const myFn = compose(f)(g);
myFn(2); // 5

像上面的 compose 还不够一般化,他只接收两个函数并对其进行结合,下面来看更加一般化的函数组合,将实现接收任意多个函数。

const pipe = (...fns) => input => fns.reduce((mem, fn) => fn(mem), input)

const double = x => x  2

const addOne = x => x + 1

const square = x => x
x pipe(square, double, addOne)(2)

上面的 pipe 将对输入依次应用 入参中的各函数,所以取名 pipe 管道流。

以上,函数的组装。

相关资源

Currying 及应用的更多相关文章

  1. 前端开发者进阶之函数柯里化Currying

    穆乙:http://www.cnblogs.com/pigtail/p/3447660.html 在计算机科学中,柯里化(英语:Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接 ...

  2. 函数式中的 currying

    currying 是函数式语言中经常遇到的一个概念,翻译成 柯里化,不是库里化. currying 指的是将接收多个参数的函数变换成接收一个单一参数,并且返回接收余下的参数而且返回结果的新函数的技术. ...

  3. 函数柯里化(Currying)示例

    ”函数柯里化”是指将多变量函数拆解为单变量的多个函数的依次调用, 可以从高元函数动态地生成批量的低元的函数.可以看成一个强大的函数工厂,结合函数式编程,可以叠加出很BT的能力.下面给出了两个示例,说明 ...

  4. JavaScript中的Partial Application和Currying

    这篇文章是一篇学习笔记,记录我在JS学习中的一个知识点及我对它的理解,知识点和技巧本身并不是我原创的.(引用或参考到的文章来源在文末) 先不解释Partial Application(偏函数应用)和C ...

  5. call, apply && bind, currying

    简要概括: apply(): 将函数作为指定对象的方法来调用,传递给它的是指定的参数数组 ——function.apply(thisobj, args) 或者 function.apply(thiso ...

  6. [Javascript] What is JavaScript Function Currying?

    Currying is a core concept of functional programming and a useful tool for any developer's toolbelt. ...

  7. Function Currying in javascript 的一些注释

    理解函数柯里化(Function Currying ),最关键的是理解下面这个函数: function curry(fn){ var args = Array.prototype.slice.call ...

  8. 浅析 JavaScript 中的 函数 currying 柯里化

    原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字 ...

  9. JavaScript函数的柯里化(currying)

    转载请注明出处:http://www.cnblogs.com/shamoyuu/p/currying.html 什么是js函数的currying /柯里化? 说到js的柯里化,相信很多朋友都会头大.或 ...

  10. 函数式编程之-Currying

    这个系列涉及到了F#这门语言,也许有的人觉得这样的语言遥不可及,的确我几乎花了2-3年的时间去了解他:也许有人觉得学习这样的冷门语言没有必要,我也赞同,那么我为什么要花时间去学习呢?作为一门在Tiob ...

随机推荐

  1. Codeforces Round #480 (Div. 2) A. Links and Pearls

    题目地址:http://codeforces.com/contest/980/problem/A 官方题解: 我的理解:o表示珍珠,-表示链子,给一串字符串你可以任意重组这条项链(不能删去),判断这条 ...

  2. Gym 101964 题解

    B:Broken Watch (别问,问就是队友写的) 代码: import java.awt.List; import java.io.BufferedInputStream; import jav ...

  3. PAT 天梯杯 L3-008. 喊山 bfs

    L3-008. 喊山 时间限制 150 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 喊山,是人双手围在嘴边成喇叭状,对着远方高山发出“喂—喂喂 ...

  4. 刨死你系列——LinkedHashMap剖析(基于jdk1.8)

    一.概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,Linked ...

  5. Python远程连接MySQL数据库

    使用Python连接数据库首先需要安装Python的数据库驱动. 我的本地只装了Python,并没有装MySQL,当我使用命令: sudo pip install mysql-python 安装驱动( ...

  6. 如何设置FreePBX的数据库用户可以通过远程来连接Mysql数据库?

    要满足mysql允许通过除了本机外的主机进行使用客户端连接的方法: 要设置root用户允许通过外网用户进行连接访问的操作方法: 1.首先先改mysql的配置文件 将绑定的#bind-address = ...

  7. 054 Python程序设计思维

    目录 一.单元开篇 二.计算思维与程序设计 2.1 计算思维 2.1.1 第3种人类思维特征 2.1.2 抽象和自动化 2.1.3 计数求和:计算1-100的计数和 2.1.4 圆周率的计算 2.1. ...

  8. Java虚拟机原理和调优

    https://blog.csdn.net/sun1021873926/article/details/78002118 115道Java经典面试题(面中率最高.最全) 史上最全 40 道 Dubbo ...

  9. SSM框架——详细整合教程

    SSM框架——详细整合教程(Spring+SpringMVC+MyBatis) 1.基本概念   1.1.Spring Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Jav ...

  10. 开发必配的Finder设置

    1.显示标签页.显示路径栏.显示状态栏的设置位置,在访达->显示-> 显示状态栏 个人三个都设置了,但是觉得显示状态栏用的并不多,反而多一行,下面是显示状态栏的效果,主要可以一眼看出有多少 ...