/stream.coffee
CoffeeScript | 153 lines | 147 code | 5 blank | 1 comment | 29 complexity | 4b23158117f8c4addac903ee6fdddc81 MD5 | raw file
1plus = (a,b) -> a+b 2eq = (a,b) -> a==b 3mirror = (x) -> x 4nonce=[] 5class 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 137output = exports or window 138output.Stream = Stream 139 140primeStream = new Stream 2, -> 141 Stream.range(3).filter (primeCandidate) -> 142 primeStream.until((prime)-> 143 prime*prime > primeCandidate 144 ).all (prime)-> 145 primeCandidate%prime != 0 146output.primeStream = primeStream 147 148#in general, it is not possible to tell if a stream is finite. 149paradoxicalStream = new Stream 1, -> 150 if paradoxicalStream.is_finite() 151 Stream.repeat(1) 152 else 153 new Stream()