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
了解一点js的Eval函数
Jul 26 Javascript
原生javascript实现addClass,removeClass,hasClass函数
Feb 25 Javascript
使用PHP+JavaScript将HTML页面转换为图片的实例分享
Apr 18 Javascript
基于bootstrap-datetimepicker.js不支持IE8的快速解决方法
Nov 07 Javascript
jquery中封装函数传递当前元素的方法示例
May 05 jQuery
Vue-Router实现组件间跳转的三种方法
Nov 07 Javascript
React中嵌套组件与被嵌套组件的通信过程
Jul 11 Javascript
JS实现获取数组中最大值或最小值功能示例
Mar 02 Javascript
jQuery实现简易聊天框
Feb 08 jQuery
Vue + element 实现多选框组并保存已选id集合的示例代码
Jun 03 Javascript
Postman参数化实现过程及原理解析
Aug 13 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
基于Linux调试工具strace与gdb的常用命令总结
2013/06/03 PHP
利用PHP+JS实现搜索自动提示(实例)
2013/06/09 PHP
PHP常用算法和数据结构示例(必看篇)
2017/03/15 PHP
php字符串函数 str类常见用法示例
2020/05/15 PHP
不用AJAX和IFRAME,说说真正意义上的ASP+JS无刷新技术
2008/09/25 Javascript
关于Jqzoom的使用心得 jquery放大镜效果插件
2010/04/12 Javascript
JQuery中判断一个元素下面是否有内容或者有某个标签的判断代码
2012/02/02 Javascript
Javascript中 关于prototype属性实现继承的原理图
2013/04/16 Javascript
js实时获取系统当前时间实例代码
2013/06/28 Javascript
JS 屏蔽按键效果与改变按键效果的示例代码
2013/12/24 Javascript
js动态往表格的td中添加图片并注册事件
2014/06/12 Javascript
javascript中返回顶部按钮的实现
2015/05/05 Javascript
vue.js+boostrap项目实践(案例详解)
2016/09/21 Javascript
jquery判断类型是不是number类型的实例代码
2016/10/07 Javascript
jquery对象和DOM对象的相互转换详解
2016/10/18 Javascript
关于js函数解释(包括内嵌,对象等)
2016/11/20 Javascript
JS中正则表达式全局匹配模式 /g用法详解
2017/04/01 Javascript
jQuery除指定区域外点击任何地方隐藏DIV功能
2017/11/13 jQuery
Vue使用json-server进行后端数据模拟功能
2018/04/17 Javascript
bootstrap 日期控件 datepicker被弹出框dialog覆盖的解决办法
2019/07/09 Javascript
Vue实现导航栏的显示开关控制
2019/11/01 Javascript
解决vue动态路由异步加载import组件,加载不到module的问题
2020/07/26 Javascript
Python正则表达式教程之一:基础篇
2017/03/02 Python
Python书单 不将就
2017/07/11 Python
Python中跳台阶、变态跳台阶与矩形覆盖问题的解决方法
2018/05/19 Python
python生成密码字典的方法
2018/07/06 Python
Tensorflow安装问题: Could not find a version that satisfies the requirement tensorflow
2020/04/20 Python
Python基于当前时间批量创建文件
2020/05/07 Python
解决Keras使用GPU资源耗尽的问题
2020/06/22 Python
解决keras使用cov1D函数的输入问题
2020/06/29 Python
canvas拼图功能实现代码示例
2018/11/21 HTML / CSS
英国儿童设计师服装的领先零售商:Base
2019/03/17 全球购物
2014年会演讲稿范文
2014/01/06 职场文书
《回乡偶书》教学反思
2014/04/12 职场文书
初中班主任培训心得体会
2016/01/07 职场文书
利用python进行数据加载
2021/06/20 Python