├── README ├── LICENSE ├── tests.coffee └── stream.coffee /README: -------------------------------------------------------------------------------- 1 | re-implementation of stream.js: 2 | 3 | https://winterbe.github.io/streamjs/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012 Michael Blume 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests.coffee: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env coffee 2 | {Stream, primeStream} = require "./stream" 3 | 4 | assert = (b) -> 5 | if not b 6 | console.log 'FUCK' 7 | throw 'fuck' 8 | 9 | eq = (a,b) -> 10 | if a==b 11 | return 12 | console.log a 13 | console.log b 14 | throw 'fuck' 15 | 16 | eqList = (s,l) -> 17 | if s.equal Stream.make l... 18 | return 19 | console.log s.list() 20 | console.log l 21 | throw 'fuck' 22 | 23 | 24 | #before we start, make sure this equal thing works 25 | one_to_five = Stream.range(1,5) 26 | one_to_ten = Stream.range(1,10) 27 | six_to_ten = Stream.range(6,10) 28 | empty = new Stream() 29 | assert not one_to_five.equal one_to_ten 30 | assert not empty.equal one_to_five 31 | assert not one_to_five.equal empty 32 | assert not one_to_five.equal six_to_ten 33 | 34 | assert one_to_five.member(1) 35 | assert one_to_five.member(5) 36 | assert not one_to_five.member(6) 37 | 38 | s = Stream.make(10, 20, 30) 39 | eqList s, [10,20,30] 40 | eq s.length(), 3 41 | eq s.head(), 10 42 | eq s.item(0), 10 43 | eq s.item(1), 20 44 | eq s.item(2), 30 45 | eq s.sum(), 60 46 | 47 | t = s.tail() 48 | eq t.head(), 20 49 | u = t.tail() 50 | eq u.head(), 30 51 | v = u.tail() 52 | assert v.empty() 53 | 54 | 55 | s = Stream.range(10, 20) 56 | eqList s, [10..20] 57 | 58 | 59 | doubleNumber = (x) -> 60 | 2 * x 61 | 62 | numbers = Stream.range(10, 15) 63 | doubles = numbers.map(doubleNumber) 64 | eqList doubles, [20,22,24,26,28,30] 65 | 66 | checkIfOdd = (x) -> 67 | if x % 2 == 0 68 | false 69 | else 70 | true 71 | numbers = Stream.range(10, 15) 72 | onlyOdds = numbers.filter(checkIfOdd) 73 | eqList onlyOdds, [11,13,15] 74 | 75 | isFive = (x) -> x==5 76 | 77 | eqList Stream.range(1).until(isFive), [1..4] 78 | eqList Stream.range(1).up_until(isFive), [1..5] 79 | 80 | printItem = (x) -> 81 | console.log "The element is: " + x 82 | numbers = Stream.range(10, 12) 83 | #test this better 84 | numbers.walk printItem 85 | 86 | numbers = Stream.range(10, 100) 87 | fewerNumbers = numbers.take(10) 88 | eqList fewerNumbers, [10..19] 89 | numbers = Stream.range(1, 3) 90 | multiplesOfTen = numbers.scale(10) 91 | eqList multiplesOfTen, [10,20,30] 92 | eqList numbers.add(multiplesOfTen), [11,22,33] 93 | 94 | naturalNumbers = Stream.range() 95 | oneToTen = naturalNumbers.take(10) 96 | eqList oneToTen, [1..10] 97 | 98 | evenNumbers = naturalNumbers.map((x) -> 99 | 2 * x 100 | ) 101 | oddNumbers = naturalNumbers.filter((x) -> 102 | x % 2 != 0 103 | ) 104 | eqList evenNumbers.take(3), [2,4,6] 105 | eqList oddNumbers.take(3), [1,3,5] 106 | 107 | s = new Stream(10, -> 108 | new Stream() 109 | ) 110 | eqList s, [10] 111 | t = new Stream(10, -> 112 | new Stream(20, -> 113 | new Stream(30, -> 114 | new Stream() 115 | ) 116 | ) 117 | ) 118 | eqList t, [10,20,30] 119 | 120 | ones = -> 121 | new Stream(1, ones) 122 | 123 | s = ones() 124 | eqList s.take(3), [1,1,1] 125 | 126 | a = Stream.make 1,2,3 127 | b = Stream.make 4,5,6 128 | 129 | eqList a.append(b), [1,2,3,4,5,6] 130 | eqList a.cycle().take(8), [1,2,3,1,2,3,1,2] 131 | eqList Stream.repeat(1).take(5), [1,1,1,1,1] 132 | 133 | naturalNumbers = -> 134 | new Stream(1, -> 135 | ones().add naturalNumbers() 136 | ) 137 | 138 | eqList naturalNumbers().take(5), [1..5] 139 | 140 | 141 | 142 | sieve = (s) -> 143 | h = s.head() 144 | new Stream(h, -> 145 | sieve s.tail().filter((x) -> 146 | x % h != 0 147 | ) 148 | ) 149 | tenPrimes = [2,3,5,7,11,13,17,19,23,29] 150 | eqList sieve(Stream.range(2)).take(10), tenPrimes 151 | 152 | 153 | eqList primeStream.take(10), tenPrimes 154 | 155 | fib = Stream.recursive ((a,b) -> a+b), 1, 1 156 | 157 | eqList fib.take(10), [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 158 | 159 | 160 | assert Stream.make(false, false, true).any() 161 | assert not Stream.make(false, false, false).any() 162 | assert Stream.make(true, true, true).all() 163 | assert not Stream.make(true, true, false).all() 164 | -------------------------------------------------------------------------------- /stream.coffee: -------------------------------------------------------------------------------- 1 | plus = (a,b) -> a+b 2 | eq = (a,b) -> a==b 3 | mirror = (x) -> x 4 | nonce=[] 5 | class Stream 6 | constructor: (@_head=nonce, @_tail) -> 7 | if not @empty() and not @_tail? 8 | @_tail = new Stream() 9 | 10 | @make: (head=nonce, rest...) -> 11 | if head != nonce 12 | new Stream head, -> Stream.make rest... 13 | else 14 | new Stream() 15 | @repeat: (x) -> 16 | s=new Stream x, -> s 17 | @recursive: (f, inits...) -> 18 | n = inits.length 19 | helper = (index) -> 20 | if n > index 21 | new Stream inits[index], -> helper index+1 22 | else 23 | midstreamhelper result 24 | midstreamhelper = (s) -> 25 | next = f(s.take(n).list()...) 26 | new Stream next, -> midstreamhelper s.tail() 27 | result = new Stream inits[0], -> helper 1 28 | @range: (min=1, max='never') -> 29 | if min == max 30 | Stream.make(min) 31 | else new Stream min, -> Stream.range(min+1, max) 32 | 33 | empty: -> @_head == nonce 34 | throw_if_empty: -> 35 | if @empty() 36 | throw error: "end of stream" 37 | head: -> 38 | @throw_if_empty() 39 | @_head 40 | tail: -> 41 | @throw_if_empty() 42 | if @_tail not instanceof Stream 43 | @_tail = @_tail() 44 | @_tail 45 | item: (n) -> @skip(n).head() 46 | skip: (n) -> 47 | s = this 48 | while n != 0 49 | n-- 50 | s = s.tail() 51 | s 52 | walk: (f) -> 53 | s = this 54 | until s.empty() 55 | f s.head() 56 | s = s.tail() 57 | null 58 | print: -> @walk console.log 59 | force: -> @walk -> 60 | map: (f) -> 61 | if @empty() 62 | new Stream() 63 | else 64 | new Stream f(@head()), => @tail().map f 65 | filter: (f) -> 66 | s = this 67 | until s.empty() or f s.head() 68 | s = s.tail() 69 | return new Stream() if s.empty() 70 | new Stream s.head(), -> s.tail().filter f 71 | any: (f=mirror) -> not @filter(f).empty() 72 | all: (f=mirror) -> 73 | negated = (x) -> not f x 74 | not @any(negated) 75 | take: (n) -> 76 | return new Stream() if n==0 77 | new Stream @head(), => 78 | return new Stream() if n==1 79 | @tail().take n-1 80 | scale: (factor) -> 81 | scaleone = (n) -> factor*n 82 | @map(scaleone) 83 | zip: (zipper, otherstream) -> 84 | mismatch = -> throw error: "length mismatch" 85 | if @empty() 86 | if otherstream.empty() 87 | new Stream() 88 | else 89 | mismatch() 90 | else if otherstream.empty() 91 | mismatch() 92 | else 93 | new Stream zipper(@head(), otherstream.head()), => 94 | @tail().zip(zipper, otherstream.tail()) 95 | append: (s) -> 96 | if @empty() 97 | s 98 | else 99 | new Stream @head(), => @tail().append(s) 100 | cycle: -> 101 | if @empty() 102 | throw error: "zero times infinity" 103 | s = new Stream @head(), => @tail().append(s) 104 | add: (otherstream) -> @zip plus, otherstream 105 | reduce: (reducer, initial) -> 106 | @walk (element) -> initial = reducer initial, element 107 | initial 108 | sum: -> @reduce(plus, 0) 109 | length: -> 110 | l = 0 111 | @walk -> l++ 112 | l 113 | list: -> 114 | out = [] 115 | @walk (n) -> out.push n 116 | out 117 | until: (f) -> 118 | return new Stream() if @empty() or f @head() 119 | new Stream @head(), => @tail().until f 120 | up_until: (f) -> 121 | return new Stream() if @empty() 122 | new Stream @head(), => 123 | if f @head() 124 | new Stream() 125 | else 126 | @tail().up_until f 127 | equal: (s) -> 128 | try 129 | bstream = @zip eq, s 130 | bstream.all() 131 | catch err 132 | if err.error != "length mismatch" 133 | throw err 134 | false 135 | member: (x) -> @any (m) -> m==x 136 | 137 | output = exports or window 138 | output.Stream = Stream 139 | 140 | primeStream = new Stream 2, -> 141 | Stream.range(3).filter (primeCandidate) -> 142 | primeStream.until((prime)-> 143 | prime*prime > primeCandidate 144 | ).all (prime)-> 145 | primeCandidate%prime != 0 146 | output.primeStream = primeStream 147 | 148 | #in general, it is not possible to tell if a stream is finite. 149 | paradoxicalStream = new Stream 1, -> 150 | if paradoxicalStream.is_finite() 151 | Stream.repeat(1) 152 | else 153 | new Stream() 154 | --------------------------------------------------------------------------------