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的事件描述
Sep 08 Javascript
使用JQuery和s3captche实现一个水果名字的验证
Aug 14 Javascript
为jquery.ui.dialog 增加“自动记住关闭时的位置”的功能
Nov 24 Javascript
jQuery插件 tabBox实现代码
Feb 09 Javascript
js中的string.format函数代码
Aug 11 Javascript
jQuery右键菜单contextMenu使用实例
Sep 28 Javascript
js使用数组判断提交数据是否存在相同数据
Nov 27 Javascript
jQuery产品间断向下滚动效果核心代码
May 08 Javascript
JS原型对象的创建方法详解
Jun 16 Javascript
vue 使某个组件不被 keep-alive 缓存的方法
Sep 21 Javascript
浅谈js闭包理解
Apr 01 Javascript
vue 公共列表选择组件,引用Vant-UI的样式方式
Nov 02 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编程语言开发动态WAP页面
2006/10/09 PHP
用PHP 快速生成 Flash 动画的方法
2007/03/06 PHP
php 数组的创建、调用和更新实现代码
2009/03/09 PHP
那些年一起学习的PHP(二)
2012/03/21 PHP
php+xml编程之SimpleXML的应用实例
2015/01/24 PHP
php文件系统处理方法小结
2016/05/23 PHP
JS中简单的实现像C#中using功能(有源码下载)
2007/01/09 Javascript
Javascript动画的实现原理浅析
2015/03/02 Javascript
jQuery实现购物车数字加减效果
2015/03/14 Javascript
详解javascript事件冒泡
2016/01/09 Javascript
Bootstrap编写导航栏和登陆框
2016/05/30 Javascript
Websocket协议详解及简单实例代码
2016/12/12 Javascript
详解Vue-基本标签和自定义控件
2017/03/24 Javascript
JavaScript Canvas绘制圆形时钟效果
2020/08/20 Javascript
vue组件实现弹出框点击显示隐藏效果
2020/10/26 Javascript
angularJs中跳转到指定的锚点实例($anchorScroll)
2018/08/31 Javascript
JavaScript 反射和属性赋值实例解析
2019/10/28 Javascript
原生javascript的ajax请求及后台PHP响应操作示例
2020/02/24 Javascript
JavaScript冒泡算法原理与实现方法深入理解
2020/06/04 Javascript
js前端传json后台接收‘‘被转为quot的问题解决
2020/11/12 Javascript
[57:22]完美世界DOTA2联赛PWL S2 FTD vs PXG 第二场 11.27
2020/12/01 DOTA
Python实现去除代码前行号的方法
2015/03/10 Python
python3中函数参数的四种简单用法
2018/07/09 Python
Flask框架响应、调度方法和蓝图操作实例分析
2018/07/24 Python
pygame游戏之旅 添加游戏介绍
2018/11/20 Python
python 实现读取一个excel多个sheet表并合并的方法
2019/02/12 Python
python中dict使用方法详解
2019/07/17 Python
在Python中使用filter去除列表中值为假及空字符串的例子
2019/11/18 Python
python中count函数简单的实例讲解
2020/02/06 Python
canvas之自定义头像功能实现代码示例
2017/09/29 HTML / CSS
Kingsoft金山公司C/C++笔试题
2016/05/10 面试题
社区关爱留守儿童活动方案
2014/08/22 职场文书
2019年最新借条范本!
2019/07/08 职场文书
Nginx下配置Https证书详细过程
2021/04/01 Servers
Python OpenCV快速入门教程
2021/04/17 Python
python3 hdf5文件 遍历代码
2021/05/19 Python