Javascript中transducer的应用
2017-12-01 22:25
204 查看
本文假定你对下列知识有一定了解
函数式编程高阶函数
柯里化
ES6语法
需求背景
假定有一数组,const testArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
要筛选出所有大于3的元素,然后再加1,组成新的数组[5, 6, 7, 8, 9, 10].
用命令式编程很容易实现:
// 算法1 let result = []; testArray.forEach(x => { if (greaterThanThree(x)) { result.push(increaseOne(x)); } }); console.log(result);
用函数式编程,最简单的方式是这样:
// 算法2 const result = testArray.filter(greaterThanThree).map(increaseOne); console.log(result);
看似代码少了很多,但是效率却下降了。算法1的时间复杂度是O(n), 算法2的时间复杂度是O(2*n).
对于这种情况,如何用函数式编程实现O(n)的算法?
首先,filter和map都可以用reduce来实现,算法2可以转化为如下代码:
// 算法3 const greaterThanThree = x => x > 3; const filterReducer = (acc, element) => { return greaterThanThree(element) ? acc.concat(element) : acc; }; const increaseOne = x => x + 1; const mapReducer = (acc, element) => { return acc.concat(increaseOne(element)); }; let result = restArray.reduce(filterReducer, []).reduce(mapReducer, []); console.log(result);
于是,思路是将filterReducer和mapReducer组装成一个Reducer,作为参数传给restArray.reduce。
第一步
将函数greaterThanThree和increaseOne作为参数提出来,于是filterReducer和mapReducer转化为:const filterReducer = (acc, element, predicate) => { return predicate(element) ? acc.concat(element) : acc; }; const mapReducer = (acc, element, transform) => { return acc.concat(transform(element)); };
但是,因为需要先组装filterReducer和mapReducer,再传给reduce,所以参数是分开传入的。并且acc和element是在执行reduce时最后传入,所以需要柯里化:
const filterReducer = predicate => (acc, element) => { return predicate(element) ? acc.concat(element) : acc; }; const mapReducer = transform => (acc, element) => { return acc.concat(transform(element)); };
第二步
进一步抽象。filterReducer和mapReducer都有concat。如果要把他们组装成一个Reducer,必须只有一个concat,所以concat也要当参数传入。目前是调用数组concat方法,为了能参数化,必须重写:const concat = (acc, element) => acc.concat(element);
然后,就可以参数化concat:
const filterReducer = predicate => concatReducer => (acc, element) => { return predicate(element) ? concatReducer(acc, element) : acc; }; const mapReducer = transform => concatReducer => (acc, element) => { return concatReducer(acc, transform(element)); };
至此,filterReducer和mapReducer就转化为了transducer。
第三步
引入compose:var compose = (f, g) => x => { return f(g(x)); };
由于需求是先筛选后转化(加1),所以思路是将mapReducer作为concatReducer传入filterReducer:
const newReducer = compose(filterReducer(greaterThanThree), mapReducer(increaseOne));
上文说过,必须只有一个concat,所以concat在组装后再传入:
const result = testArray.reduce(newReducer(concat), []);
完整代码
// 算法4
var compose = (f, g) => x => f(g(x));
const greaterThanThree = x => x > 3;
const increaseOne = x => x + 1;
const concat = (acc, element) => acc.concat(element);
const filterReducer = predicate => concatReducer => (acc, element) => { return predicate(element) ? concatReducer(acc, element) : acc; }; const mapReducer = transform => concatReducer => (acc, element) => { return concatReducer(acc, transform(element)); };
const newReducer = compose(filterReducer(greaterThanThree), mapReducer(increaseOne));
const result = testArray.reduce(newReducer(concat), []);
console.log(result);
如何理解newReducer(concat)
newReducer(concat)等价于compose(filterReducer(greaterThanThree), mapReducer(increaseOne))(concat)
代入compose等价于
filterReducer(greaterThanThree)(mapReducer(increaseOne)(concat))
代入filterReducer等价于
(acc, element) => { return greaterThanThree(element) ? mapReducer(increaseOne)(concat)(acc, element) : acc; };
代入mapReducer等价于
(acc, element) => { return greaterThanThree(element) ? ((acc, element) => { return concat(acc, increaseOne(element)); })(acc, element) : acc; };
等价于
(acc, element) => { return greaterThanThree(element) ? concat(acc, increaseOne(element)) : acc; };
到这里,也许你会恍然大悟。如果不考虑各种扩展重用,只是要快点解决这个性能问题,但又要守住函数式编程的底线,你只需要直接用这个reducer即可。
相关文章推荐
- JavaScript的应用在什么地方?
- JavaScript 应用开发 #1:理解模型与集合
- 如何组织大型JavaScript应用中的代码?
- javaScript介绍与应用
- Javascript基础应用
- JavaScript 应用开发 #5:为完成的任务添加样式
- Javascript ActiveX 简单应用--Windows管理
- 最新的Javascript和CSS应用技巧荟萃
- JavaScript事件冒泡简介及应用
- JavaScript:键盘keyCode值列表,及keyCode的应用
- javascript是widgets应用的瓶颈吗?
- 提高 web 应用性能之 JavaScript 性能调优
- 超越Web,Javascript在物联网的应用
- 如何组织大型JavaScript应用中的代码?
- 精通JavaScript开发课时13-14(DOM操作应用)笔记
- AJAX工作原理及其优缺点 1.什么是AJAX? AJAX全称为“Asynchronous JavaScript and XML”(异步JavaScript和XML),是一种创建交互式网页应用的网页
- JavaScript应用:Iframe自适应其加载的内容高度
- JavaScript 应用技巧集合[推荐]
- 在XSL里应用javascript/C# Script等
- 使用ArcGIS API for JavaScript创建第一个应用