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 相关文章推荐
JavaScript Cookie 直接浏览网站分网址
Dec 08 Javascript
固定表格行列(expression)在IE下适用
Jul 25 Javascript
node.js中的fs.existsSync方法使用说明
Dec 17 Javascript
基于JQuery的$.ajax方法进行异步请求导致页面闪烁的解决办法
May 10 Javascript
JavaScript作用域示例详解
Jul 07 Javascript
Angular2 自定义validators的实现方法
Jul 05 Javascript
详解vue-cli + webpack 多页面实例配置优化方法
Jul 13 Javascript
vue.js声明式渲染和条件与循环基础知识
Jul 31 Javascript
node跨域转发 express+http-proxy-middleware的使用
May 31 Javascript
详解vue中的父子传值双向绑定及数据更新问题
Jun 13 Javascript
Layui实现数据表格默认全部显示(不要分页)
Oct 26 Javascript
Vue切换div显示隐藏,多选,单选代码解析
Jul 14 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
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
解析file_get_contents模仿浏览器头(user_agent)获取数据
2013/06/27 PHP
PHP自动识别字符集并完成转码详解
2013/08/02 PHP
PHP简单获取视频预览图的方法
2015/03/12 PHP
学习php设计模式 php实现装饰器模式(decorator)
2015/12/07 PHP
php魔术方法功能与用法实例分析
2016/10/19 PHP
thinkphp框架类库扩展操作示例
2019/11/26 PHP
PHP数组实际占用内存大小原理解析
2020/12/11 PHP
InnerHtml和InnerText的区别分析
2009/03/13 Javascript
javascript实现Table排序的方法
2015/05/15 Javascript
Jsonp post 跨域方案
2015/07/06 Javascript
javascript引用类型之时间Date和数组Array
2015/08/27 Javascript
详解Javascript中的Object对象
2016/02/28 Javascript
BootStrap的alert提示框的关闭后再显示怎么解决
2016/05/17 Javascript
详解jQuery中的getAll()和cleanData()
2019/04/15 jQuery
JS使用iView的Dropdown实现一个右键菜单
2019/05/06 Javascript
Vue表单提交点击事件只允许点击一次的实例
2020/10/23 Javascript
[42:36]DOTA2上海特级锦标赛B组败者赛 VG VS Spirit第二局
2016/02/26 DOTA
[01:11:32]VG vs FNATIC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
python 根据pid杀死相应进程的方法
2017/01/16 Python
Python3 Random模块代码详解
2017/12/04 Python
使用python将请求的requests headers参数格式化方法
2019/01/02 Python
Python Django模板之模板过滤器与自定义模板过滤器示例
2019/10/18 Python
Python多线程模块Threading用法示例小结
2019/11/09 Python
Matplotlib绘制雷达图和三维图的示例代码
2020/01/07 Python
Python3中小括号()、中括号[]、花括号{}的区别详解
2020/11/15 Python
adidas美国官网:adidas US
2016/09/21 全球购物
佛罗里达州印第安河新鲜水果:Hale Groves
2017/02/20 全球购物
Richards网上商店:当代时尚,遍布巴西
2019/11/03 全球购物
linux面试相关问题
2012/08/11 面试题
中专毕业生自我鉴定
2013/11/21 职场文书
中学生差生评语
2014/01/30 职场文书
乡领导班子四风问题对照检查材料
2014/09/25 职场文书
神龙架导游词
2015/02/11 职场文书
施工员岗位职责范本
2015/04/11 职场文书
离婚被告代理词
2015/05/23 职场文书