每天看一片代码系列(一):stream.js
简介
stream.js是一个小型的js库,用于处理stream相关的操作。这里的stream是指一种数据结构,它像数组一样,可以放置多个类型的数据,但是并不限制长度,甚至可以达到无限长。可以对该数据结构进行检索、修改、追加等种种操作。由于其长度不限这一特性,使得它与通常意义下的数据结构有明显的区别。
API
stream提供的API包含三种。
第一种是创建类。包括:
- new Stream(head, functionReturingTail) 第二个参数是一个放回除第一个元素之外剩下的元素的方法,所以它可能也返回一个Stream,即通过一个或多个现有的Stream来构造一个新的Stream。当然,这两个参数都不是必须的,如果都不填,那就是一个空的stresam。
- Stream.make() 相当于一个静态方法,比较简单,直接将数据填入里面就行了。
- Stream.range(from, to) 返回从from到to中间所有数字组成的Stream,如果不填,默认为自然数。
第二种是查询类,包括:
- head() 返回流中的第一个item
- item(index) 返回index位置上的item
- tail() 返回除了第一个位置上剩下的元素
- empty() 是否为空
第三种是遍历操作类型,包括:
- map() 类似于数组中的map,其实就是一个映射的过程
- walk() 同样是遍历,但是并不会对元素产生直接的影响
- filter() 过滤掉某些元素,这几个都接受参数为函数的情形
- take(n) 取前n个元素
- scale(n) 将每一个元素都乘以n
栗子
递归调用构造Stream
如前所述,构造函数的第二个参数可以是一个返回Stream的方法,那如果该方法中调用了自身呢?也就构成了无限长的具有相同数字的流。
function ones() {
return new Stream(
// the first element of the stream of ones is 1...
1,
// and the rest of the elements of this stream are given by calling the function ones() (this same function!)
ones
);
}
Stream的相加
一个无限长的Stream和另一些无限长的Stream相加会出现什么情况?如下:
function ones() {
return new Stream( 1, ones );
}
function naturalNumbers() {
return new Stream(
// the natural numbers are the stream whose first element is 1...
1,
function () {
// and the rest are the natural numbers all incremented by one
// which is obtained by adding the stream of natural numbers...
// 1, 2, 3, 4, 5, ...
// to the infinite stream of ones...
// 1, 1, 1, 1, 1, ...
// yielding...
// 2, 3, 4, 5, 6, ...
// which indeed are the REST of the natural numbers after one
return ones().add( naturalNumbers() );
}
);
}
虽然从名字上可以看出这是自然数,但初次看上去确实有些confusing。不妨列一下式子:
设 result = (1, n1, n2, n3, ....)
又有(n1, n2, n3, ...) = (1, 1, 1, ...) + (1, n1, n2, n3, ...)
那么 n1 = 2, n2 = 1 + n1 = 3, n3 = 1 + n2 = 4, ... 所以下一个数总是比上一个数多1, 那么就是自然数了!
代码解析
初看代码时我都惊了,虽然API有那么多,但整个代码仅仅有两百来行,还真是挺小。
首先定义了一个类Stream, 设置了一下它的head和获取剩余部分用到的函数。
function Stream( head, tailPromise ) {
if ( typeof head != 'undefined' ) {
this.headValue = head;
}
if ( typeof tailPromise == 'undefined' ) {
tailPromise = function () {
return new Stream();
};
}
this.tailPromise = tailPromise;
}
接下来就直接在prototype上开放API了,注意由于它无限长的特性,因此不能像遍历数组一样去一个一个索引,而是像链表一样,通过next指针去获取,这里就是通过tail()方法去获取。
var s = this;
while ( n != 0 ) {
--n;
try {
s = s.tail();
}
catch ( e ) {
throw new Error('Item index does not exist in stream.');
}
}
当然,如果你对一个无限长的Stream做map,它当然不会真正的一个一个去无穷尽地map完,而是重新构造了一个Stream,定义了新的规则,即head = f(head), tail = tail().map(f),所以只有在取数据的时候才会执行这里面的函数。
map: function( f ) {
if ( this.empty() ) {
return this;
}
var self = this;
return new Stream( f( this.head() ), function () {
return self.tail().map( f );
} );
}
其他的API方法也是一样,我认为实现上最大的特点就是:递归调用!一个流无论再长,我都把它划分为两部分:一个元素为头部,剩余的为尾部。然后要做的就是对头部操作一下,对尾部操作一下即可。比如构造rarnge为low - high的流时只需制定头部为low,尾部为low+1到high的流就行了。
return new Stream( low, function () {
return Stream.range( low + 1, high );
} );
Stream 就是一种 headElement + tailStream的结构,由于tailStream又可以进一步地划分,所以不可避免地通过递归来实现各种操作。对于应对无限长的数列来说是一个有效的方法,如果把它和数据结构中的链表关联起来,我们就明朗多了。
每天看一片代码系列(一):stream.js的更多相关文章
- 每天看一片代码系列(四):layzr.js,处理图片懒加载的库
所谓图片的懒加载,即只有当图片处于或者接近于当前视窗时才开始加载图片.该库的使用方法非常简单: var layzr = new Layzr({ attr: 'data-layzr', // attr和 ...
- 每天看一片代码系列(三):codepen上一个音乐播放器的实现
今天我们看的是一个使用纯HTML+CSS+JS实现音乐播放器的例子,效果还是很赞的: codePen地址 HTML部分 首先我们要思考一下,一个播放器主要包含哪些元素.首先要有播放的进度信息,还有播放 ...
- 每天看一片代码系列(二):WebSocket-Node
简介 我们都知道,websocket主要是通过在浏览器和服务端建立长连接,继而实现二者的相互数据通信.不同于HTTP的轮询,它不会有大量无效的HTTP消息交换,从而节省了花销.websocket其实就 ...
- Java 8系列之Stream的基本语法详解
本文转至:https://blog.csdn.net/io_field/article/details/54971761 Stream系列: Java 8系列之Stream的基本语法详解 Java 8 ...
- Spring Cloud 系列之 Stream 消息驱动(二)
本篇文章为系列文章,未读第一集的同学请猛戳这里:Spring Cloud 系列之 Stream 消息驱动(一) 本篇文章讲解 Stream 如何实现消息分组和消息分区. 消息分组 如果有多个消息消费者 ...
- gulp入坑系列(2)——初试JS代码合并与压缩
在上一篇里成功安装了gulp到项目中,现在来测试一下gulp的合并与压缩功能 gulp入坑系列(1)--安装gulp(传送门):http://www.cnblogs.com/YuuyaRin/p/61 ...
- 从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码
首发于公众号:计算机视觉life 旗下知识星球「从零开始学习SLAM」 这可能是最清晰讲解g2o代码框架的文章 理解图优化,一步步带你看懂g2o框架 小白:师兄师兄,最近我在看SLAM的优化算法,有种 ...
- 看图写代码---看图写代码 阅读<<Audio/Video Connectivity Solutions for Virtex-II Pro and Virtex-4 FPGAs >>
看图写代码 阅读<<Audio/Video Connectivity Solutions for Virtex-II Pro and Virtex-4 FPGAs >> 1.S ...
- 【前端模板之路】一、重构的兄弟说:我才不想看你的代码!把HTML给我交出来!
写在前面 随着前端领域的发展和社会化分工的需要,继前端攻城湿之后,又一重要岗位横空出世——重构攻城湿!所谓的重构攻城湿,他们的一大特点之一,就是精通CSS配置文件的编写...前端攻城湿跟重构攻城湿是一 ...
随机推荐
- Steeltoe
谈谈Circuit Breaker在.NET Core中的简单应用 http://www.cnblogs.com/catcher1994/p/8975192.html 前言 由于微服务的盛行,不少公司 ...
- c++11 实现RAII特性
参考文章https://blog.csdn.net/pongba/article/details/7911997 什么是RAII 技术?(参见百度百科相关条目) RAII(Resource Acqui ...
- D3——散点图Scatterplot
散点图 //Width and height ; ; ; var dataset = [ [, ], [, ], [, ], [, ], [, ], [, ], [, ], [, ], [, ], [ ...
- luogu3368树状数组模板2
题目链接:https://www.luogu.org/problemnew/show/P3368 题意:与模板1不同的是这题的操作是树状数组并不在行的区间更新和单点查找,如果按照模板1那样写肯定会T. ...
- PHP----作业:查询数据显示在页面上
作业: 查询INFO表所有数据,显示在页面上(表格)性别要显示男女 民族 显示民族名称 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tra ...
- php中文正则匹配
今天接到一个需求,用户昵称系统需要将昵称输入的字符类型限定为 中文,英文,数字, -,_ 显然这个应该用正则来实现,那么最终的规则是怎么样的呢?示例代码如下: <?php $str = '我爱北 ...
- 17、配置嵌入式servlet容器(1)
SpringBoot默认使用Tomcat作为嵌入式的Servlet容器 1).如何定制和修改Servlet容器的相关配置 1.修改和server有关的配置 (Se ...
- Android客户端与服务器端通过DES加密认证
转载地址:http://blog.csdn.net/spring21st/article/details/6730283 由于Android应用没有像web开发中的session机制,所以采用PHPS ...
- Java项目排查cpu负载高
背景 我负责的其中一个项目在空负载的情况下,CPU占用率依然保持着100%左右,线上.测试.开发的服务都一样:是什么导致的呢?在开发环境我查看了请求流量,这个流量可以忽略但CPU占用率一直在60%-1 ...
- 每日笔记---使用@ConfigurationProperties读取yml配置
每日笔记---使用@ConfigurationProperties读取yml配置 参考地址 https://www.cnblogs.com/mycs-home/p/8352140.html 1.添加 ...