• 每天看一片代码系列(一):stream.js


    简介

    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又可以进一步地划分,所以不可避免地通过递归来实现各种操作。对于应对无限长的数列来说是一个有效的方法,如果把它和数据结构中的链表关联起来,我们就明朗多了。

  • 相关阅读:
    leetcode 78. 子集 JAVA
    leetcode 91. 解码方法 JAVA
    leetcode 75. 颜色分类 JAVA
    leetcode 74 搜索二维矩阵 java
    leetcode 84. 柱状图中最大的矩形 JAVA
    last occurance
    first occurance
    classical binary search
    LC.234.Palindrome Linked List
    LC.142. Linked List Cycle II
  • 原文地址:https://www.cnblogs.com/cubika/p/4437402.html
Copyright © 2020-2023  润新知