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_03(ExtJs Grid的简单使用)
Oct 02 Javascript
Extjs4 GridPanel 的几种样式使用介绍
Apr 18 Javascript
JSON 数字排序多字段排序介绍
Sep 18 Javascript
js判断某个方法是否存在实例代码
Jan 10 Javascript
充分发挥Node.js程序性能的一些方法介绍
Jun 23 Javascript
基于javascript实现最简单的选项卡切换效果
May 16 Javascript
js判断手机号是否正确并返回的实现代码
Jan 17 Javascript
babel基本使用详解
Feb 17 Javascript
jQuery Ajax前后端使用JSON进行交互示例
Mar 17 Javascript
Vue+webpack+Element 兼容问题总结(小结)
Aug 16 Javascript
解决vue axios的封装 请求状态的错误提示问题
Sep 25 Javascript
VUE写一个简单的表格实例
Aug 06 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
人工智能开始玩《星际争霸2》 你的操作跟得上吗?
2017/08/11 星际争霸
php数组操作之键名比较与差集、交集赋值的方法
2014/11/10 PHP
PHP实现163邮箱自动发送邮件
2016/03/29 PHP
浅谈php处理后端&amp;接口访问超时的解决方法
2016/10/29 PHP
laravel实现批量更新多条记录的方法示例
2017/10/22 PHP
php进程daemon化的正确实现方法
2018/09/06 PHP
TFDN图片播放器 不错自动播放
2006/10/03 Javascript
js生成动态表格并为每个单元格添加单击事件的方法
2014/04/14 Javascript
JS根据年月获得当月天数的实现代码
2014/07/03 Javascript
简介BootStrap model弹出框的使用
2016/04/27 Javascript
jQuery插件EasyUI实现Layout框架页面中弹出窗体到最顶层效果(穿越iframe)
2016/08/05 Javascript
vue2中filter()的实现代码
2017/07/09 Javascript
boostrap模态框二次弹出清空原有内容的方法
2018/08/10 Javascript
彻底揭秘keep-alive原理(小结)
2019/05/05 Javascript
Vue记住滚动条和实现下拉加载的完美方法
2020/07/31 Javascript
如何在Vue项目中添加接口监听遮罩
2021/01/25 Vue.js
[15:56]Heroes18_暗影萨满(完美)
2014/10/31 DOTA
python返回昨天日期的方法
2015/05/13 Python
python查看FTP是否能连接成功的方法
2015/07/30 Python
python读写csv文件实例代码
2019/07/05 Python
Python表达式的优先级详解
2020/02/18 Python
Python基于smtplib模块发送邮件代码实例
2020/05/29 Python
Python中logging日志记录到文件及自动分割的操作代码
2020/08/05 Python
Grid 宫格常用布局的实现
2020/01/10 HTML / CSS
美国环保妈妈、儿童和婴儿用品购物网站:The Tot
2019/11/24 全球购物
如何用Lucene索引数据库
2016/02/23 面试题
涉外文秘个人求职的自我评价
2013/10/07 职场文书
刘胡兰的英雄事迹材料
2014/02/11 职场文书
《美丽的黄昏》教学反思
2014/02/28 职场文书
绩效考核实施方案
2014/03/18 职场文书
矿泉水广告词
2014/03/20 职场文书
2014年保洁员工作总结
2014/11/19 职场文书
2014年便民服务中心工作总结
2014/12/20 职场文书
2015年社区工会工作总结
2015/05/26 职场文书
会计主管竞聘书
2015/09/15 职场文书
pnpm对npm及yarn降维打击详解
2022/08/05 Javascript