Currying 及应用
Currying,中文多翻译为柯里化,感觉这个音译还没有达到类似 Humor 之于幽默的传神地步,后面直接使用 Currying。 什么是 CurryingCurrying 是这么一种机制,它将一个接收多个参数的函数,拆分成多个接收单个参数的函数。 考察下面的代码: 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.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( aec 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; } } 首先,通过 再加上前面提到的 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管道流。 以上,函数的组装。 相关资源
|
- 函数式编程中局部应用(Partial Application)和局部套用(Currying)的区别
- 偏函数应用(Partial Application)和函数柯里化(Currying)
- Scala新手指南中文版 -第十一篇 Currying and partially applied functions(科里化和部分应用函数)
- 函数加里化(Currying)和偏函数应用(Partial Application)的比较
- 函数加里化(Currying)和偏函数应用(Partial Application)的比较
- Microsoft IIS6.0实现WAP应用
- 70个AJAX应用的演示和源码下载
- “Ruby on Rails 之 Oracle 应用”常见问题解答
- 对象统计分析技术应用
- COM原理及应用之COM特性
- 在asp.net的web应用系统中导出申请表等文档
- nsIIdleService 的实现与应用
- 分享9个非常有趣的JavaScript应用
- Linux下插件开发应用案例
- 最老程序员创业札记:全文检索、数据挖掘、推荐引擎应用1
- Excel 2010高级应用-气泡图(八)
- nginx+lua+redis构建高并发应用
- 公众平台关注用户达到5万即可开通流量主功能 可以推广APP应用
- iOS8设置应用图标红点的权限问题
- ios-day03-01(使用NSDictionary存储应用信息,九宫格形式显示应用信息)