每天看一片代码系列(一):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配置文件的编写...前端攻城湿跟重构攻城湿是一 ...
随机推荐
- 关于Hibernate懒加载问题的最终解决方案
看到一篇Hibernate懒加载的文章,所以转载,原地址如下: http://tuoxie007.iteye.com/blog/334853 Hibernate的强大之处之一是懒加载功能,可以有效的降 ...
- 手把手教你自定义attr
最近在学习的过程中遇到了自定义的attr和自定义的style.因此各种百度,各种博客的学习,算是有了一个系统的了解.在这里记录下自己的收获. 一.为什么要使用自定义attr以及本文定位 在androi ...
- 系统架构的定义(与系统)-architecture
architecture⟨system⟩ fundamental concepts or properties of a system in its environment embodied in i ...
- 【JQ】鼠标经过一组button,弹出各自的气泡图片
HTML <div id="bubble1" class="bubble"><img src="../image/p_bubble1 ...
- 六.安装jdk(基于Centos7安装)
1.我把java安装到/usr/local/jdk目录下面,所以,新建文件夹如下 2.把下载到的文件上传至Linux服务器 笔者使用wget命令直接把文件下载到服务器"wget http:/ ...
- PHP------面向对象的特性
面向对象的特性 一.面向对象有三大特性: 封装.继承.多态.(非常重要,第一要记住!!!!!!!!!!) 二.封装 封装就是用来做类的,把一个类封装起来.做类不能随便的做.我们做类不能随便去写一个类, ...
- 剑指offer5 从尾到头打印链表
错误代码: class Solution { public: vector<int> printListFromTailToHead(ListNode* head){ vector< ...
- OpenFlow, SDN, and NFV
OpenFlow An open standard to innovative protocols in production networks provides a standardized hoo ...
- Oracle 体系结构一 概述
Oracle服务器由两个实体组成:实例和数据库. 实例由内存结构和进程组成. 它暂时存在于RAM和CPU中.当关闭运行的实例时,实例将消失的无影无踪. 数据库由磁盘上的文件组成.不管在运行状态还是停止 ...
- UIButtonType各个类型的解释:
UIButtonType各个类型的解释: typedef NS_ENUM(NSInteger, UIButtonType) { UIButtonTypeCustom = , UIButtonTypeS ...