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 26 Javascript
扩展jQuery 键盘事件的几个基本方法
Oct 30 Javascript
js 页面传参数时 参数值含特殊字符的问题
Dec 13 Javascript
ExtJs的Date格式字符代码
Dec 30 Javascript
jquery ready函数、css函数及text()使用示例
Sep 27 Javascript
BootStrap智能表单实战系列(三)分块表单配置详解
Jun 13 Javascript
BootStrap tab选项卡使用小结
Aug 09 Javascript
借助node实战JSONP跨域实例
Mar 30 Javascript
Node.js Buffer模块功能及常用方法实例分析
Jan 05 Javascript
JavaScript函数式编程(Functional Programming)高阶函数(Higher order functions)用法分析
May 22 Javascript
Javascript如何实现扩充基本类型
Aug 26 Javascript
Node使用koa2实现一个简单JWT鉴权的方法
Jan 26 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 构造函数construct的前下划线是双的_
2009/12/08 PHP
推荐5款跨平台的PHP编辑器
2014/12/25 PHP
PHP开发中csrf攻击的简单演示和防范
2017/05/07 PHP
php实现获取农历(阴历)、节日、节气的类与用法示例
2017/11/20 PHP
ThinkPHP框架获取最后一次执行SQL语句及变量调试简单操作示例
2018/06/13 PHP
Laravel 关联模型-关联新增和关联更新的方法
2019/10/10 PHP
js 图片轮播(5张图片)
2008/12/30 Javascript
jQuery中fadeIn、fadeOut、fadeTo的使用方法(图片显示与隐藏)
2013/05/08 Javascript
加载远程图片时,经常因为缓存而得不到更新的解决方法(分享)
2013/06/26 Javascript
js 三级关联菜单效果实例
2013/08/13 Javascript
javascript四舍五入函数代码分享(保留后几位)
2013/12/10 Javascript
js函数在frame中的相互调用详解
2014/03/03 Javascript
JavaScript中双叹号!!作用示例介绍
2014/09/21 Javascript
兼容主流浏览器的JS复制内容到剪贴板
2014/12/12 Javascript
分享9个最好用的JavaScript开发工具和代码编辑器
2015/03/24 Javascript
js验证真实姓名与身份证号是否匹配
2015/10/13 Javascript
JavaScript设计模式初探
2016/01/07 Javascript
JS密码生成与强度检测完整实例(附demo源码下载)
2016/04/06 Javascript
基于js实现二级下拉联动
2016/12/17 Javascript
JS实现非首屏图片延迟加载的示例
2018/01/06 Javascript
Angular 4.x+Ionic3踩坑之Ionic3.x pop反向传值详解
2018/03/13 Javascript
[00:43]DOTA2小紫本全民票选福利PA至宝全方位展示
2014/11/25 DOTA
[46:03]LGD vs VGJ.T 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
Python实现决策树C4.5算法的示例
2018/05/30 Python
python从内存地址上加载python对象过程详解
2020/01/08 Python
使用HTML5在网页中嵌入音频和视频播放的基本方法
2016/02/22 HTML / CSS
商务英语专业毕业生自荐信
2013/11/05 职场文书
30年同学聚会邀请函
2014/01/25 职场文书
导游个人求职信范文
2014/03/23 职场文书
广告艺术设计专业自荐书
2014/07/08 职场文书
诚实守信演讲稿
2014/09/01 职场文书
五五普法心得体会
2014/09/04 职场文书
幼儿园感恩节活动方案2014
2014/10/11 职场文书
2014年店长工作总结
2014/11/17 职场文书
springboot中rabbitmq实现消息可靠性机制详解
2021/09/25 Java/Android
使用 Apache 反向代理的设置技巧
2022/01/18 Servers