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 相关文章推荐
用Greasemonkey 脚本收藏网站会员信息到本地
Oct 26 Javascript
Ajax同步与异步传输的示例代码
Nov 21 Javascript
jQuery往返城市和日期查询实例讲解
Oct 09 Javascript
浅谈使用MVC模式进行JavaScript程序开发
Nov 10 Javascript
JS实时弹出新消息提示框并有提示音响起的实现代码
Apr 20 Javascript
Vue ElementUI之Form表单验证遇到的问题
Aug 21 Javascript
jQuery插件jsonview展示json数据
May 26 jQuery
在Vue methods中调用filters里的过滤器实例
Aug 30 Javascript
Vue框架里使用Swiper的方法示例
Sep 20 Javascript
jquery插件开发模式实例详解
Jul 20 jQuery
JS Thunk 函数的含义和用法实例总结
Apr 08 Javascript
vue 实现tab切换保持数据状态
Jul 21 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 中的输出缓冲
2006/12/21 PHP
基于php iconv函数的使用详解
2013/06/09 PHP
js判断浏览器的比较全的代码
2007/02/13 Javascript
JQUERY的属性选择符和自定义选择符使用方法(二)
2011/04/07 Javascript
Jquery右下角抖动、浮动 实例代码(兼容ie6、FF)
2013/08/15 Javascript
Google (Local) Search API的简单使用介绍
2013/11/28 Javascript
js禁止回车提交表单的示例代码
2013/12/23 Javascript
jQuery防止click双击多次提交及传递动态函数或多参数
2014/04/02 Javascript
javascript搜索框效果实现方法
2015/05/14 Javascript
JS实现弹出浮动窗口(支持鼠标拖动和关闭)实例详解
2015/08/06 Javascript
5个最顶级jQuery图表类库插件【jquery插件库】
2016/05/05 Javascript
js将滚动条滚动到指定位置的简单实现方法
2016/06/25 Javascript
AngularJS 表达式详细讲解及实例代码
2016/07/26 Javascript
通过sails和阿里大于实现短信验证
2017/01/04 Javascript
微信小程序开发经验总结(推荐)
2017/01/11 Javascript
Vue中的数据监听和数据交互案例解析
2017/07/12 Javascript
深入浅析javascript函数中with
2018/10/28 Javascript
js最实用string(字符串)类型的使用及截取与拼接详解
2019/04/26 Javascript
使用vue脚手架(vue-cli)搭建一个项目详解
2019/05/09 Javascript
ES6 Iterator接口和for...of循环用法分析
2019/07/31 Javascript
vue学习笔记之Vue中css动画原理简单示例
2020/02/29 Javascript
JS数据类型判断的几种常用方法
2020/07/07 Javascript
python自动化测试实例解析
2014/09/28 Python
基于python 字符编码的理解
2017/09/02 Python
Python 异常处理Ⅳ过程图解
2019/10/18 Python
mac使用python识别图形验证码功能
2020/01/10 Python
Python第三方库的几种安装方式(小结)
2020/04/03 Python
python3列表删除大量重复元素remove()方法的问题详解
2021/01/04 Python
试述DBMS的主要功能
2016/11/13 面试题
汇科协同Java笔试题
2012/03/31 面试题
师范教师大学生职业生涯规划范文
2014/01/05 职场文书
2014年保管员工作总结
2014/11/18 职场文书
一般纳税人申请报告
2015/05/18 职场文书
2019年自助餐厅创业计划书模板
2019/08/22 职场文书
六年级情感作文之500字
2019/10/23 职场文书
SpringBoot整合JWT的入门指南
2021/06/29 Java/Android