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 相关文章推荐
如何快速的呈现我们的网页的技巧整理
Jul 01 Javascript
javascript Math.random()随机数函数
Nov 04 Javascript
JS 加入收藏夹的代码(主流浏览器通用)
May 13 Javascript
动态添加删除表格行的js实现代码
Feb 28 Javascript
node.js中的fs.createReadStream方法使用说明
Dec 17 Javascript
Javascript中的arguments与重载介绍
Mar 15 Javascript
Bootstrap网格系统详解
Apr 26 Javascript
从零开始学习Node.js系列教程三:图片上传和显示方法示例
Apr 13 Javascript
JS获取子、父、兄节点方法小结
Aug 14 Javascript
小程序实现选择题选择效果
Nov 04 Javascript
JS 设计模式之:单例模式定义与实现方法浅析
May 06 Javascript
原生JavaScript实现购物车
Jan 10 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 从数据库提取二进制图片的处理代码
2009/09/09 PHP
初次接触php抽象工厂模式(Elgg)
2010/03/21 PHP
解析用PHP读写音频文件信息的详解(支持WMA和MP3)
2013/05/10 PHP
php实现文件下载简单示例(代码实现文件下载)
2014/03/10 PHP
PHP5.3与5.5废弃与过期函数整理汇总
2014/07/10 PHP
php防止sql注入之过滤分页参数实例
2014/11/03 PHP
PHP实现正则表达式分组捕获操作示例
2018/02/03 PHP
自写的利用PDO对mysql数据库增删改查操作类
2018/02/19 PHP
Laravel中GraphQL接口请求频率实战记录
2020/09/01 PHP
jQuery 动画基础教程
2008/12/25 Javascript
中文路径导致unitpngfix.js不正常的解决方法
2013/06/26 Javascript
Google官方支持的NodeJS访问API,提供后台登录授权
2014/07/29 NodeJs
JavaScript中的无阻塞加载性能优化方案
2014/10/10 Javascript
javaScript的函数对象的声明详解
2015/02/06 Javascript
Node.js node-schedule定时任务隔多少分钟执行一次的方法
2015/02/10 Javascript
jquery性能优化高级技巧
2015/08/24 Javascript
第一次接触神奇的Bootstrap导航条
2016/08/09 Javascript
实例解析angularjs的filter过滤器
2016/12/14 Javascript
用POSTMAN发送JSON格式的POST请求示例
2018/09/04 Javascript
开发中常用的25个JavaScript单行代码(小结)
2019/06/28 Javascript
javascript 设计模式之组合模式原理与应用详解
2020/04/08 Javascript
antd form表单数据回显操作
2020/11/02 Javascript
[01:04]不如跳舞!DOTA2新英雄玛尔斯的欢乐日常
2019/03/11 DOTA
Python中getpass模块无回显输入源码解析
2018/01/11 Python
python实现飞机大战微信小游戏
2020/03/21 Python
python3 unicode列表转换为中文的实例
2018/10/26 Python
python实现基于信息增益的决策树归纳
2018/12/18 Python
python3.x+pyqt5实现主窗口状态栏里(嵌入)显示进度条功能
2019/07/04 Python
解决Ubuntu18中的pycharm不能调用tensorflow-gpu的问题
2020/09/17 Python
python利用appium实现手机APP自动化的示例
2021/01/26 Python
新西兰网上购物,折扣店:BestDeals.co.nz
2019/03/20 全球购物
Android面试宝典
2013/08/06 面试题
设置器与访问器的定义以及各自特点
2016/01/08 面试题
【魔兽争霸3重制版】原版画面与淬火MOD画面对比
2021/03/26 魔兽争霸
2014年汽车销售工作总结
2014/12/01 职场文书
老乡会致辞
2015/07/28 职场文书