简介

stream.js是一个小型的js库,用于处理stream相关的操作。这里的stream是指一种数据结构,它像数组一样,可以放置多个类型的数据,但是并不限制长度,甚至可以达到无限长。可以对该数据结构进行检索、修改、追加等种种操作。由于其长度不限这一特性,使得它与通常意义下的数据结构有明显的区别。

API

stream提供的API包含三种。

第一种是创建类。包括:

  1. new Stream(head, functionReturingTail) 第二个参数是一个放回除第一个元素之外剩下的元素的方法,所以它可能也返回一个Stream,即通过一个或多个现有的Stream来构造一个新的Stream。当然,这两个参数都不是必须的,如果都不填,那就是一个空的stresam。
  2. Stream.make() 相当于一个静态方法,比较简单,直接将数据填入里面就行了。
  3. Stream.range(from, to) 返回从from到to中间所有数字组成的Stream,如果不填,默认为自然数。

第二种是查询类,包括:

  1. head() 返回流中的第一个item
  2. item(index) 返回index位置上的item
  3. tail() 返回除了第一个位置上剩下的元素
  4. empty() 是否为空

第三种是遍历操作类型,包括:

  1. map() 类似于数组中的map,其实就是一个映射的过程
  2. walk() 同样是遍历,但是并不会对元素产生直接的影响
  3. filter() 过滤掉某些元素,这几个都接受参数为函数的情形
  4. take(n)  取前n个元素
  5. 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的更多相关文章

  1. 每天看一片代码系列(四):layzr.js,处理图片懒加载的库

    所谓图片的懒加载,即只有当图片处于或者接近于当前视窗时才开始加载图片.该库的使用方法非常简单: var layzr = new Layzr({ attr: 'data-layzr', // attr和 ...

  2. 每天看一片代码系列(三):codepen上一个音乐播放器的实现

    今天我们看的是一个使用纯HTML+CSS+JS实现音乐播放器的例子,效果还是很赞的: codePen地址 HTML部分 首先我们要思考一下,一个播放器主要包含哪些元素.首先要有播放的进度信息,还有播放 ...

  3. 每天看一片代码系列(二):WebSocket-Node

    简介 我们都知道,websocket主要是通过在浏览器和服务端建立长连接,继而实现二者的相互数据通信.不同于HTTP的轮询,它不会有大量无效的HTTP消息交换,从而节省了花销.websocket其实就 ...

  4. Java 8系列之Stream的基本语法详解

    本文转至:https://blog.csdn.net/io_field/article/details/54971761 Stream系列: Java 8系列之Stream的基本语法详解 Java 8 ...

  5. Spring Cloud 系列之 Stream 消息驱动(二)

    本篇文章为系列文章,未读第一集的同学请猛戳这里:Spring Cloud 系列之 Stream 消息驱动(一) 本篇文章讲解 Stream 如何实现消息分组和消息分区. 消息分组 如果有多个消息消费者 ...

  6. gulp入坑系列(2)——初试JS代码合并与压缩

    在上一篇里成功安装了gulp到项目中,现在来测试一下gulp的合并与压缩功能 gulp入坑系列(1)--安装gulp(传送门):http://www.cnblogs.com/YuuyaRin/p/61 ...

  7. 从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码

    首发于公众号:计算机视觉life 旗下知识星球「从零开始学习SLAM」 这可能是最清晰讲解g2o代码框架的文章 理解图优化,一步步带你看懂g2o框架 小白:师兄师兄,最近我在看SLAM的优化算法,有种 ...

  8. 看图写代码---看图写代码 阅读<<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 ...

  9. 【前端模板之路】一、重构的兄弟说:我才不想看你的代码!把HTML给我交出来!

    写在前面 随着前端领域的发展和社会化分工的需要,继前端攻城湿之后,又一重要岗位横空出世——重构攻城湿!所谓的重构攻城湿,他们的一大特点之一,就是精通CSS配置文件的编写...前端攻城湿跟重构攻城湿是一 ...

随机推荐

  1. 使用Vue-cli脚手架

    使用vue-cli脚手架开发vue项目,有以下好处: (1)成熟的Vue项目架构设计. (2)本地测试服务器(热更新). (3)集成打包上线方案. Vue-cli系统要求: Node.js(>= ...

  2. 更新UI的几种方式

    在学习Handler的过程中牵涉到UI的更新,在这里就总结一下更新UI的四种方式吧,用法都比较简单,直接看代码就可以了. 一.使用Handler的post方法 新建项目,修改MainActivity代 ...

  3. 智能机器人“小昆”的实现(五)MainActivty的实现及项目结束

    好了,一切准备工作都完成了,下面我们就可以真正的编写MainActivity了.在MainActivity中,我们要为ListView设定适配器,并为发送按钮设定点击事件.我们的逻辑就是点击发送按钮, ...

  4. 面试准备——(二)专业知识(1)Linux

    面试的问题: 腾讯: 1. 查看进程的命令 美团: 1. 常用的命令——美团/滴滴 2. 如何在性能测试的时候查看进程 3. kill -9/-15区别 滴滴: 1.如何找到一个特定文件 2. 如何替 ...

  5. HTML CSS STYLE USER Font Awesome Definition Content Attribute

    在css样式表中写样式: 其中content使用图标类对应的字符编码如下

  6. plsql中特殊字符的处理

    --去除换行chr(10) update zhzl_address t set t.add_administration_num=replace(t.add_administration_num,ch ...

  7. 协作开发中常用的Git命令小结

    先提一下最基础的git命令用法: git clone   从远端克隆到本地仓库 git add . (注意add和. 之间有一个空格)将全部改动添加到暂存区 git checkout xxx 撤销更改 ...

  8. 557. Reverse Words in a String III (5月25日)

    解答 class Solution { public: string reverseWords(string s) { string temp,result; while(1){ if(s.find( ...

  9. MySQL语句整理(二)

    数据库操作前的准备 -- 创建数据库 -- create database python_test_1 charset=utf8; -- 使用数据库 -- use python_test_1; -- ...

  10. IO流,字节流

    /** * IO流,字节流 */ import java.io.FileInputStream; import java.io.FileOutputStream; public class ByStr ...