了解 JavaScript 函数式编程 - 柯里化
栏目: JavaScript · 发布时间: 5年前
内容简介:在这个多彩的世界,有些事物对与我们来说并不是非必须的,就像我们早已习惯存在但是又非必须的东西:互联网,移动手机,微波炉,电梯等等。当他们不存在的时候我们也能正常的快乐的生存下去,但是一旦拥有了以后他们的存在就变得不可或缺。就像我们的 curry 工具一样。
curry
就是咖喱一样美好的 工具 性的拌料让我们的函数更加的易用耦合性低。
curry
的概念很简单: 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
。
你可以一次性地调用 curry
函数,也可以每次只传一个参数分多次调用。
var add = function(x) { return function(y) { return x + y; }; }; var increment = add(1); var addTen = add(10); increment(2); // 3 addTen(2); // 12 复制代码
curry 的重要性
在这个多彩的世界,有些事物对与我们来说并不是非必须的,就像我们早已习惯存在但是又非必须的东西:互联网,移动手机,微波炉,电梯等等。当他们不存在的时候我们也能正常的快乐的生存下去,但是一旦拥有了以后他们的存在就变得不可或缺。就像我们的 curry 工具一样。
- 我们来创建一个普通的 curry ,for your enjoyment 吧。这里用到了 lodash 函数库,不熟悉的朋友可以看一下 lodash 的官网
var curry = require('lodash').curry; var match = curry(function(what, str) { return str.match(what); }); var replace = curry(function(what, replacement, str) { return str.replace(what, replacement); }); var filter = curry(function(f, ary) { return ary.filter(f); }); var map = curry(function(f, ary) { return ary.map(f); }); 复制代码
我在上面的代码中遵循的是一种简单,同时也非常重要的模式。即策略性地把要操作的数据(String, Array)放到最后一个参数里。到使用它们的时候你就明白这样做的原因是什么了。
- 下面我们开始使用上面的代码,看看为什么会这么去处理我们的函数。
// 匹配空格 match(/\s+/g, "hello world"); // [ ' ' ] match(/\s+/g)("hello world"); // [ ' ' ] // 引出一个 hasSpace 的函数变量,暂存用 var hasSpaces = match(/\s+/g); // function(x) { return x.match(/\s+/g) } // 使用这个 hasSpace 去做一些相关的处理 hasSpaces("hello world"); // [ ' ' ] hasSpaces("spaceless"); // null // 现在我们知道了它的返回值,我们可以通过其他函数做进一步的处理。比如筛选出一个有空格的数组值 filter(hasSpaces, ["tori_spelling", "tori amos"]); // ["tori amos"] // 保存这个 findSpace 的函数变量 var findSpaces = filter(hasSpaces); // function(xs) { return xs.filter(function(x) { return x.match(/\s+/g) }) } // 轻松的使用吧 findSpaces(["tori_spelling", "tori amos"]); // ["tori amos"] var noVowels = replace(/[aeiou]/ig); // function(replacement, x) { return x.replace(/[aeiou]/ig, replacement) } var censored = noVowels("*"); // function(x) { return x.replace(/[aeiou]/ig, "*") } censored("Chocolate Rain"); // 'Ch*c*l*t* R**n' 复制代码
==我们为甚要这么多繁琐的步骤,而不是一步到位?==
这里表明的是一种“预加载”函数的能力,通过传递一到两个参数调用函数,就能得到一个记住了这些参数的新函数。分解的使用的函数,让每个函数更具有一定的独立性,使用导出的时候,做到纯净无污染的传递。
扩展我们的 curry
curry
的用处非常广泛,就像在 hasSpaces
、 findSpaces
和 censored
看到的那样,只需传给函数一些参数,就能得到一个新函数。
-
下面我们用
map
包裹一下我们的函数
var getChildren = function(x) { return x.childNodes; }; var allTheChildren = map(getChildren); 复制代码
只传给函数一部分参数这样的操作通常被称为局部调用(partial application),因为只需要内联调用能够大量减少样板文件代码(boilerplate code)。
当我们谈论纯函数的时候,我们说它们接受一个输入返回一个输出。curry 函数所做的正是这样:每传递一个参数调用函数,就返回一个新函数处理剩余的参数。这就是一个输入对应一个输出啊。
练习一下
-
这里引用了 ramda,如果没有的话可以手动引入安装一下和引用。
-
npm install ramda
-
var _ = require('ramda'); // 练习 1(局部调用的使用) //============== // 通过局部调用(partial apply)移除所有参数 var words = function(str) { return split(' ', str); }; // 练习 1a(组合使用函数) //============== // 使用 `map` 创建一个新的 `words` 函数,使之能够操作字符串数组 var sentences = undefined; // 练习 2 //============== // 通过局部调用(partial apply)移除所有参数 var filterQs = function(xs) { return filter(function(x){ return match(/q/i, x); }, xs); }; // 练习 3(柯里化~) //============== // 使用帮助函数 `_keepHighest` 重构 `max` 使之成为 curry 函数 // 无须改动: var _keepHighest = function(x,y){ return x >= y ? x : y; }; // 重构这段代码: var max = function(xs) { return reduce(function(acc, x){ return _keepHighest(acc, x); }, -Infinity, xs); }; // use curry 1: // ============ // 包裹数组的 `slice` 函数使之成为 curry 函数 // //[1,2,3].slice(0, 2) var slice = undefined; // use curry 2: // ============ // 借助 `slice` 定义一个 `take` curry 函数,该函数调用后可以取出字符串的前 n 个字符。 var take = undefined; 复制代码
这是上面的答案,先别急着看答案,让我们先思考一下 链接
总结
通过简单地传递几个参数,就能动态创建实用的新函数;而且还能带来一个额外好处,那就是保留了数学的函数定义,尽管参数不止一个。
参考
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 了解 JavaScript 函数式编程 - 声明式函数
- 了解 JavaScript 函数式编程 -- 什么是纯函数
- 了解 JavaScript 函数式编程-类型签名
- 想要确保架构目标达成?适合度函数了解一下
- 了解 JavaScript 函数式编程 - 代码组合的优势
- 了解递归的几种姿势 - 函数式编程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
并行算法的设计与分析
陈国良 / 2009-8 / 66.00元
第3版在修订版的基础上进行了大幅度的修订,新增加3章、重写3章,改写8章。《普通高等教育十一五国家级规划教材·并行算法的设计与分析(第3版)》系统深入地讨论了计算机领域中诸多计算问题的并行算法的设计和分析方法。在着重介绍各种并行计算模型上的常用和典型的并行算法的同时,也力图反映本学科的最新成就、学科前沿和发展趋势。 全书共分二十章,包括基础篇4章(绪论、设计技术、前缀计算、排序和选择网络),......一起来看看 《并行算法的设计与分析》 这本书的介绍吧!