JavaScript惰性求值的一种实现方法示例


Posted in Javascript onJanuary 11, 2019

前言

在学习 Haskell 时,我遇到了这种写法:

sum (takeWhile (<10000) (filter odd (map (^2) [1..])))

这段代码的意思是,找出自然整数中小于 10000 的同时是乘方数和奇数的数字,再把这些数加总。由于 Haskell 的懒运算特性,上面的程序并不会立马生成从 1 到 无限大的自然数列表,而是会等待 takeWhile 指令,再生成符合条件的列表。如果用 JS 来写,很难写出这么简洁高表达性的代码。一个可能的思路就是写个 while 循环,然后找到符合条件的数进行加总。这个比较简单,我就不演示了。

但是如果我们要用高阶函数来模拟 Haskell 的写法,就要想个办法实现懒运算了。提到懒,首先想到的就是 Iterator 。没人踢它一脚告诉它 next(),它会一直坐那儿不动的。

现在我们就来用 Iterator 来实现一个懒运算。

首先定义一个生成从 1 到无穷大自然数的 generator :

const numbers = function*() {
 let i = 1
 while (true) {
 yield i++
 }
}

由于只有在 generator 执行后生成的 iterable 上执行 next() 方法,yield 才会执行,所以我们要做的主要工作就是实现不同的 next 方法,达到目的。

我们需要先创建一个工厂函数 Lazy,Lazy 封装了我们的各种目标操作 :

const Lazy = iterator => {
 const next = iterable.next.bind(iterable)
 const map = () => {}
 const filter = () => {}
 const takeWhile = () => {}
 return {
  next,
  map,
  filter,
  takeWhile,
 }

我们先实现 map 方法,它会把每次 next 返回的值根据提供的回调函数进行修改:

const map = f => {
 const modifiedNext = () => {
 const item = next()
 const mappedValue = f(item.value)
 return {
  value: mappedValue,
  done: item.done,
 }
 }
 const newIter = { ...iterable, next: modifiedNext }
 return lazy(newIter)
}

再定义 filter 方法,它会让 next 只返回符合判断条件的值:

const filter = predicate => {
 const modifiedNext = () => {
 while (true) {
  const item = next()
  if (predicate(item.value)) {
  return item
  }
 }
 }
 const newIter = { ...iterable, next: modifiedNext }
 return lazy(newIter)
}

最后,定义 takeWhile,它会限制 next 执行的条件,一旦条件不满足,则停止执行 next 并返回历史执行结果:

const takeWhile = predicate => {
 const result = []
 let value = next().value
 while (predicate(value)) {
 result.push(value)
 value = next().value
 }
 return result
}

主要的方法都定义完了,现在把它们合并起来:

const Lazy = iterable => {
 const next = iterable.next.bind(iterable)

 const map = f => {
 const modifiedNext = () => {
  const item = next()
  const mappedValue = f(item.value)
  return {
  value: mappedValue,
  done: item.done,
  }
 }
 const newIter = { ...iterable, next: modifiedNext }
 return lazy(newIter)
 }

 const filter = predicate => {
 const modifiedNext = () => {
  while (true) {
  const item = next()
  if (predicate(item.value)) {
   return item
  }
  }
 }
 const newIter = { ...iterable, next: modifiedNext }
 return lazy(newIter)
 }

 const takeWhile = predicate => {
 const result = []
 let value = next().value
 while (predicate(value)) {
  result.push(value)
  value = next().value
 }
 return result
 }

 return Object.freeze({
 map,
 filter,
 takeWhile,
 next,
 })
}

const numbers = function*() {
 let i = 1
 while (true) {
 yield i++
 }
}

现在用我们写的 Lazy 和 numbers 函数来实现文章开头的 Haskell 代码:

Lazy(numbers())
 .map(x => x ** 2)
 .filter(x => x % 2 === 1)
 .takeWhile(x => x < 10000)
 .reduce((x, y) => x + y)
// => 16650

参考:

Lazy Evaluation in JavaScript with Generators, Map, Filter, and Reduce

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Javascript 相关文章推荐
JSON 和 JavaScript eval使用说明
Jun 13 Javascript
JavaScript对象创建及继承原理实例解剖
Feb 28 Javascript
js数值和和字符串进行转换时可以对不同进制进行操作
Mar 05 Javascript
jQuery自定义滚动条完整实例
Jan 08 Javascript
AngularJS 视图详解及示例代码
Aug 17 Javascript
JavaScript cookie详解及简单实例应用
Dec 31 Javascript
Vue.js学习笔记之修饰符详解
Jul 25 Javascript
iview给radio按钮组件加点击事件的实例
Sep 30 Javascript
详解Angular5/Angular6项目如何添加热更新(HMR)功能
Oct 10 Javascript
简述pm2常用命令集合及配置文件说明
May 30 Javascript
javascript设计模式 ? 模板方法模式原理与用法实例分析
Apr 23 Javascript
关于IDEA中的.VUE文件报错 Export declarations are not supported by current JavaScript version
Oct 17 Javascript
JavaScript创建对象的四种常用模式实例分析
Jan 11 #Javascript
详解Vue项目部署遇到的问题及解决方案
Jan 11 #Javascript
VeeValidate 的使用场景以及配置详解
Jan 11 #Javascript
JS函数节流和防抖之间的区分和实现详解
Jan 11 #Javascript
微信公众号H5支付接口调用方法
Jan 10 #Javascript
详解在Node.js中发起HTTP请求的5种方法
Jan 10 #Javascript
vue实现压缩图片预览并上传功能(promise封装)
Jan 10 #Javascript
You might like
便携利器 — TECSUN PL-365简评
2021/03/02 无线电
NOD32 v2.70.32 简体中文封装版 提供下载了
2007/02/27 PHP
PHP下几种删除目录的方法总结
2007/08/19 PHP
php面向对象全攻略 (四)构造方法与析构方法
2009/09/30 PHP
php学习笔记 php中面向对象三大特性之一[封装性]的应用
2011/06/13 PHP
Laravel框架集成UEditor编辑器的方法图文与实例详解
2019/04/17 PHP
Thinkphp 框架扩展之类库扩展操作详解
2020/04/23 PHP
超级退弹代码
2008/07/07 Javascript
基于jquery的表格排序
2010/09/11 Javascript
js实现点击图片将图片地址复制到粘贴板的方法
2015/02/16 Javascript
前端框架Vue.js中Directive知识详解
2016/09/12 Javascript
jQuery特殊符号转义的实现
2016/11/30 Javascript
JS中的phototype详解
2017/02/04 Javascript
带你了解session和cookie作用原理区别和用法
2017/08/14 Javascript
[原创]js实现保存文本框内容为本地文件兼容IE,chrome,火狐浏览器
2018/02/14 Javascript
angular1.x ui-route传参的三种写法小结
2018/08/31 Javascript
动态内存分配导致影响Javascript性能的问题
2018/12/18 Javascript
微信小程序搭建自己的Https服务器
2019/05/02 Javascript
[04:16]DOTA2英雄梦之声_第09期_斧王
2014/06/21 DOTA
python中for用来遍历range函数的方法
2018/06/08 Python
pyqt5 删除layout中的所有widget方法
2019/06/25 Python
Python3实现二叉树的最大深度
2019/09/30 Python
PYTHON如何读取和写入EXCEL里面的数据
2019/10/28 Python
Python实现Word文档转换Markdown的示例
2020/12/22 Python
Python 中Operator模块的使用
2021/01/30 Python
python链表类中获取元素实例方法
2021/02/23 Python
优秀生推荐信范文
2013/11/28 职场文书
煤矿安全生产责任书
2014/04/15 职场文书
审计专业自荐信范文
2014/04/21 职场文书
质量月口号
2014/06/20 职场文书
驾驶员安全责任书
2014/07/22 职场文书
房屋所有权证明
2014/10/20 职场文书
张家口市高新区党工委群众路线教育实践活动整改方案
2014/10/25 职场文书
拾金不昧表扬信
2015/01/16 职场文书
在职人员跳槽求职信
2015/03/20 职场文书
javascript的setTimeout()使用方法总结
2021/11/20 Javascript