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技术很烂的五个原因分析
Oct 28 Javascript
详解JavaScript的Polymer框架中的通知交互
Jul 29 Javascript
JSON字符串转JSON对象
Jul 31 Javascript
JavaScript基础篇(6)之函数表达式闭包
Dec 11 Javascript
JQuery控制DIV的选取实现方法
Sep 18 Javascript
实例解析Array和String方法
Dec 14 Javascript
浅谈mint-ui 填坑之路
Nov 06 Javascript
vue中eventbus被多次触发以及踩过的坑
Dec 02 Javascript
JavaScript 中使用 Generator的方法
Dec 29 Javascript
微信网页授权并获取用户信息的方法
Jul 30 Javascript
node.js 基于cheerio的爬虫工具的实现(需要登录权限的爬虫工具)
Apr 10 Javascript
解决LayUI加上form.render()下拉框和单选以及复选框不出来的问题
Sep 27 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实现mysql封装类示例
2014/05/07 PHP
php中实现获取随机数组列表的自定义函数
2015/04/02 PHP
Laravel 5框架学习之Laravel入门和新建项目
2015/04/07 PHP
简介AngularJS中使用factory和service的方法
2015/06/17 Javascript
jQuery模拟黑客帝国矩阵效果实例
2015/06/28 Javascript
jQuery垂直多级导航菜单代码分享
2015/08/18 Javascript
关于webuploader插件使用过程遇到的小问题
2016/11/07 Javascript
文件上传插件SWFUpload的使用指南
2016/11/29 Javascript
jQuery无刷新上传之uploadify简单代码
2017/01/17 Javascript
javascript实现非常简单的小数取整功能示例
2017/06/13 Javascript
详解webpack3如何正确引用并使用jQuery库
2017/08/26 jQuery
JS实现十分钟倒计时代码实例
2018/10/18 Javascript
实现Vue的markdown文档可以在线运行的方法示例
2018/12/11 Javascript
微信小程序如何调用图片接口API并居中显示
2019/06/29 Javascript
微信小程序实现星级评价
2019/11/20 Javascript
Vue常用的全选/反选的示例代码
2020/02/19 Javascript
vuejs element table 表格添加行,修改,单独删除行,批量删除行操作
2020/07/18 Javascript
python如何在终端里面显示一张图片
2016/08/17 Python
Python读取Json字典写入Excel表格的方法
2018/01/03 Python
Python简单实现查找一个字符串中最长不重复子串的方法
2018/03/26 Python
Python实现从log日志中提取ip的方法【正则提取】
2018/03/31 Python
python搭建服务器实现两个Android客户端间收发消息
2018/04/12 Python
在NumPy中创建空数组/矩阵的方法
2018/06/15 Python
Numpy中矩阵matrix读取一列的方法及数组和矩阵的相互转换实例
2018/07/02 Python
Python面向对象之接口、抽象类与多态详解
2018/08/27 Python
详解如何为eclipse安装合适版本的python插件pydev
2018/11/04 Python
解析HTML5中的新功能本地存储localStorage
2016/03/01 HTML / CSS
美国值得信赖的婚恋交友网站:eHarmony
2018/10/04 全球购物
护士辞职信模板
2014/01/20 职场文书
公司门卫岗位职责
2014/03/15 职场文书
庆六一文艺汇演活动方案
2014/08/26 职场文书
群众路线自我剖析材料
2014/10/08 职场文书
三年级学生期末评语
2014/12/26 职场文书
承诺保证书格式
2015/02/28 职场文书
导游词之嵊泗列岛
2019/10/30 职场文书
图文详解Nginx版本平滑升级方案
2021/09/15 Servers