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 相关文章推荐
jquery 简单图片导航插件jquery.imgNav.js
Mar 17 Javascript
jquery中的 $(&quot;#jb51&quot;)与document.getElementById(&quot;jb51&quot;) 的区别
Jul 26 Javascript
jQuery数据显示插件整合实现代码
Oct 24 Javascript
js获取url中的参数且参数为中文时通过js解码
Mar 19 Javascript
JavaScript仿支付宝密码输入框
Dec 29 Javascript
canvas实现钟表效果
Feb 13 Javascript
javascript滚轮事件基础实例讲解(37)
Feb 14 Javascript
微信分享调用jssdk实例
Jun 08 Javascript
用JS实现简单的登录验证功能
Jul 28 Javascript
Vue路由管理器Vue-router的使用方法详解
Feb 05 Javascript
小程序跳转到的H5页面再跳转回跳小程序的方法
Mar 06 Javascript
Vue实现圆环进度条的示例
Feb 06 Vue.js
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
用Zend Encode编写开发PHP程序
2006/10/09 PHP
php常用字符串比较函数实例汇总
2014/11/24 PHP
PHP验证码生成原理和实现
2016/01/24 PHP
js利用div背景,做一个竖线的效果。
2008/11/22 Javascript
捕获键盘事件(且兼容各浏览器)
2013/07/03 Javascript
JS实现向表格中动态添加行的方法
2015/03/30 Javascript
js网页滚动条滚动事件实例分析
2015/05/05 Javascript
基于Javascript实现倒计时功能
2016/02/22 Javascript
AngularJs  E2E Testing 详解
2016/09/02 Javascript
javascript的document中的动态添加标签实现方法
2016/10/24 Javascript
微信小程序 WXDropDownMenu组件详解及实例代码
2016/10/24 Javascript
基于Three.js插件制作360度全景图
2016/11/29 Javascript
smartupload实现文件上传时获取表单数据(推荐)
2016/12/12 Javascript
vue mint-ui 实现省市区街道4级联动示例(仿淘宝京东收货地址4级联动)
2017/10/16 Javascript
浅谈Angular HttpClient简单入门
2018/05/04 Javascript
js实现录音上传功能
2019/11/22 Javascript
Vue+Element实现网页版个人简历系统(推荐)
2019/12/31 Javascript
[01:07:34]DOTA2-DPC中国联赛定级赛 RNG vs Aster BO3第二场 1月9日
2021/03/11 DOTA
python求斐波那契数列示例分享
2014/02/14 Python
Python实现字典的遍历与排序功能示例
2017/12/23 Python
Python实现PS滤镜中马赛克效果示例
2018/01/20 Python
Python中偏函数用法示例
2018/06/07 Python
python命名空间(namespace)简单介绍
2019/08/10 Python
python实现加密的方式总结
2020/01/19 Python
django 文件上传功能的相关实例代码(简单易懂)
2020/01/22 Python
Pyecharts地图显示不完成问题解决方案
2020/05/11 Python
Python中过滤字符串列表的方法
2020/12/22 Python
硕士研究生自我鉴定
2013/11/08 职场文书
业务代表的岗位职责
2013/11/16 职场文书
毕业生个人求职信范文分享
2014/01/05 职场文书
培训讲师岗位职责
2014/04/13 职场文书
爱护草坪标语
2014/06/24 职场文书
2016秋季运动会前导词
2015/11/25 职场文书
mysq启动失败问题及场景分析
2021/07/15 MySQL
SQL之各种join小结详细讲解
2021/08/04 MySQL
java实现web实时消息推送的七种方案
2022/07/23 Java/Android