immutable 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,20 @@
1
- # -*- tailcall-optimization: true; trace-instruction: false -*-
2
- # ported from http://www.cs.kent.ac.uk/people/staff/smk/redblack/Untyped.hs
1
+ require "immutable/foldable"
3
2
 
4
3
  module Immutable
5
- # <code>Immutable::Map</code> represents an immutable map from keys to
4
+ # +Immutable::Map+ represents an immutable map from keys to
6
5
  # values.
7
6
  #
8
- # <code>Immutable::Map</code> is an abstract class and
9
- # <code>Immutable::Map.[]</code> should be used instead of
10
- # <code>Immutable::Map.new</code>. For example:
7
+ # +Immutable::Map+ is an abstract class and
8
+ # {Immutable::Map.[]} should be used instead of
9
+ # {Immutable::Map.new}. For example:
11
10
  #
12
11
  # include Immutable
13
12
  # p Map[] #=> Map[]
14
13
  # p Map[a: 1, b: 2] #=> Map[:a => 1, :b => 2]
15
14
  #
16
- # <code>Immutable::Map#insert</code> inserts a key/value pair and
17
- # returns a new <code>Immutable::Map</code>. The original map never be
18
- # changed by <code>Immutable::Map#insert</code>. For example:
15
+ # {#insert} inserts a key/value pair and
16
+ # returns a new +Immutable::Map+. The original map never be
17
+ # changed by {#insert}. For example:
19
18
  #
20
19
  # m = Map[a: 1]
21
20
  # p m #=> Map[:a => 1]
@@ -23,6 +22,9 @@ module Immutable
23
22
  # p m2 #=> Map[:a => 1, :b => 2]
24
23
  # p m #=> Map[:a => 1]
25
24
  class Map
25
+ include Enumerable
26
+ include Foldable
27
+
26
28
  class InvarianceViolationError < StandardError
27
29
  end
28
30
 
@@ -38,7 +40,7 @@ module Immutable
38
40
  end
39
41
 
40
42
  # Returns a map that has the same key/value pairs as the
41
- # <code>Hash</code> object +h+.
43
+ # +Hash+ object +h+.
42
44
  def self.[](h = {})
43
45
  h.inject(Leaf) { |m, (k, v)| m.insert(k, v) }
44
46
  end
@@ -48,7 +50,7 @@ module Immutable
48
50
  ins(key, value).make_black
49
51
  end
50
52
 
51
- # Returns the value at +key+ in +self+, or <code>nil</code> if +key+
53
+ # Returns the value at +key+ in +self+, or +nil+ if +key+
52
54
  # isn't in +self+.
53
55
  def [](key)
54
56
  raise ScriptError, "this method should be overriden"
@@ -75,6 +77,11 @@ module Immutable
75
77
  } + "]"
76
78
  end
77
79
 
80
+ # Calls +block+ once for each key/value in +self+.
81
+ def each(&block)
82
+ foldl_with_key(nil) { |x, k, v| yield([k, v]) }
83
+ end
84
+
78
85
  # Folds the values in +self+ from right to left.
79
86
  def foldr(e)
80
87
  foldr_with_key(e) { |k, v, x| yield(v, x) }
@@ -122,9 +129,6 @@ module Immutable
122
129
  Leaf
123
130
  end
124
131
 
125
- def Leaf.each
126
- end
127
-
128
132
  class Fork < Map #:nodoc:
129
133
  attr_reader :left, :key, :value, :right
130
134
 
@@ -165,12 +169,6 @@ module Immutable
165
169
  end
166
170
  end
167
171
 
168
- def each(&block)
169
- left.each(&block)
170
- yield key, value
171
- right.each(&block)
172
- end
173
-
174
172
  def Leaf.foldr_with_key(e)
175
173
  e
176
174
  end
@@ -0,0 +1,116 @@
1
+ module Immutable
2
+ # +Immutable::Promise+ represents a promise to evaluate an expression
3
+ # later.
4
+ #
5
+ # @example Delayed computation
6
+ # promise = Promise.delay { puts "hello"; 1 + 2 }
7
+ # x = promise.force #=> hello
8
+ # p x #=> 3
9
+ # y = promise.force #=> (no output; the value is memoized)
10
+ # p y #=> 3
11
+ # @example Infinite streams
12
+ # def from(n)
13
+ # Promise.delay {
14
+ # Cons[n, from(n + 1)]
15
+ # }
16
+ # end
17
+ #
18
+ # def stream_ref(s, n)
19
+ # xs = s.force
20
+ # if xs.empty?
21
+ # nil
22
+ # else
23
+ # n == 0 ? xs.head : stream_ref(xs.tail, n - 1)
24
+ # end
25
+ # end
26
+ #
27
+ # nats = from(0)
28
+ # p stream_ref(nats, 0) #=> 0
29
+ # p stream_ref(nats, 3) #=> 3
30
+ class Promise
31
+ # :nodoc:
32
+ Content = Struct.new(:type, :value)
33
+
34
+ def initialize(type, value)
35
+ @content = Content.new(type, value)
36
+ end
37
+
38
+ private_class_method :new
39
+
40
+ # Takes a block which evaluates to a promise, and returns a promise
41
+ # which at some point in the future may be asked (by +Promise#force+)
42
+ # to evaluate the block and deliver the value of the resulting promise.
43
+ #
44
+ # @return [Promise] the created promise.
45
+ def self.lazy(&block)
46
+ new(:lazy, block)
47
+ end
48
+
49
+ # Takes an argument, and returns a promise which deliver the value of
50
+ # the argument.
51
+ #
52
+ # <code>Promise.eager(expresion)</code> is equivalent to
53
+ # <code>(value = Promise.eager; Promise.delay { value })</code>.
54
+ #
55
+ # @param [Object] value the value to be returned by +Promise#force+.
56
+ # @return [Promise] the created promise.
57
+ def self.eager(value)
58
+ new(:eager, value)
59
+ end
60
+
61
+ # Returns whether +self+ is lazy.
62
+ #
63
+ # @return [true, false] +true+ if +self+ is lazy; otherwise, +false+.
64
+ def lazy?
65
+ content.type == :lazy
66
+ end
67
+
68
+ # Returns whether +self+ is eager.
69
+ #
70
+ # @return [true, false] +true+ if +self+ is eager; otherwise, +false+.
71
+ def eager?
72
+ content.type == :eager
73
+ end
74
+
75
+ # Takes a block, and returns a promise which at some point in the future
76
+ # may be asked (by +Promise#force+) to evaluate the block and deliver
77
+ # the resulting value.
78
+ #
79
+ # <code>Promise.delay { expression }</code> is equivalent to
80
+ # <code>Promise.lazy { Promise.eager(expression) }</code>.
81
+ #
82
+ # @return [Promise] the created promise.
83
+ def self.delay
84
+ lazy {
85
+ eager(yield)
86
+ }
87
+ end
88
+
89
+ # Returns the value of +self+.
90
+ # If a value of +self+ has already been computated, the value is
91
+ # returned. Otherwise, the promise is first evaluated, and the resulting
92
+ # value is returned.
93
+ #
94
+ # @return [Object] the value of +self+.
95
+ def force
96
+ case content.type
97
+ when :eager
98
+ content.value
99
+ when :lazy
100
+ promise = content.value.call
101
+ if content.type != :eager
102
+ content.type = promise.content.type
103
+ content.value = promise.content.value
104
+ promise.content = content
105
+ end
106
+ force
107
+ else
108
+ raise ScriptError, "should not reach here"
109
+ end
110
+ end
111
+
112
+ protected
113
+
114
+ attr_accessor :content
115
+ end
116
+ end
@@ -0,0 +1,100 @@
1
+ require "immutable/stream"
2
+
3
+ module Immutable
4
+ # +Immutable::Queue+ is an implementation of real-time queues described in
5
+ # "Purely Functional Data Structures" by Chris Okasaki.
6
+ class Queue
7
+ include Enumerable
8
+
9
+ # +Queue.new+ is for internal use only. Use {Queue.empty} or {Queue.[]}
10
+ # instead.
11
+ def initialize(front, rear, schedule)
12
+ @front = front
13
+ @rear = rear
14
+ @schedule = schedule
15
+ end
16
+
17
+ # Returns an empty queue.
18
+ #
19
+ # @return [Queue] the empty queue.
20
+ def self.empty
21
+ Queue.new(Stream.null, Nil, Stream.null)
22
+ end
23
+
24
+ # Creates a new queue populated with the given objects.
25
+ #
26
+ # @param [Array<Object>] elements the elements of the queue.
27
+ # @return [List] the new queue.
28
+ def self.[](*elements)
29
+ elements.inject(empty, &:snoc)
30
+ end
31
+
32
+ # Returns whether +self+ is empty.
33
+ #
34
+ # @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
35
+ def empty?
36
+ @front.null?
37
+ end
38
+
39
+ def rotate(front, rear, accumulator)
40
+ Stream.lazy {
41
+ if front.null?
42
+ Stream.cons(->{rear.head}, ->{accumulator})
43
+ else
44
+ Stream.cons(->{front.head}, ->{
45
+ rotate(front.tail, rear.tail,
46
+ Stream.cons(->{rear.head}, ->{accumulator}))
47
+ })
48
+ end
49
+ }
50
+ end
51
+ private :rotate
52
+
53
+ def queue(front, rear, schedule)
54
+ if schedule.null?
55
+ f = rotate(front, rear, Stream.null)
56
+ Queue.new(f, Nil, f)
57
+ else
58
+ Queue.new(front, rear, schedule.tail)
59
+ end
60
+ end
61
+ private :queue
62
+
63
+ # Adds a new element at the end of +self+.
64
+ #
65
+ # @param [Object] x the element to add.
66
+ # @return [Queue] a new queue.
67
+ def snoc(x)
68
+ queue(@front, Cons[x, @rear], @schedule)
69
+ end
70
+ alias push snoc
71
+
72
+ # Returns the first element of +self+. If +self+ is empty,
73
+ # +Immutable::List::EmptyError+ is raised.
74
+ #
75
+ # @return [Object] the first element of +self+.
76
+ def head
77
+ @front.head
78
+ end
79
+
80
+ # Returns the elements after the head of +self+. If +self+ is empty,
81
+ # +Immutable::List::EmptyError+ is raised.
82
+ #
83
+ # @return [Queue] the elements after the head of +self+.
84
+ def tail
85
+ if @front.null?
86
+ raise List::EmptyError
87
+ else
88
+ queue(@front.tail, @rear, @schedule)
89
+ end
90
+ end
91
+
92
+ # Calls +block+ once for each element in +self+.
93
+ def each(&block)
94
+ unless @front.null?
95
+ yield(head)
96
+ tail.each(&block)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,564 @@
1
+ require "immutable/foldable"
2
+ require "immutable/list"
3
+ require "immutable/promise"
4
+
5
+ module Immutable
6
+ # +Immutable::Stream+ represents a stream, also known as a lazy list.
7
+ # A stream is similar to a list. However the evaluation of a stream
8
+ # element is delayed until its value is needed
9
+ #
10
+ # @example Natural numbers
11
+ # def from(n)
12
+ # Stream.cons ->{n}, ->{from(n + 1)}
13
+ # end
14
+ #
15
+ # nats = from(0)
16
+ # p from(0).take(10).to_list #=> List[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
17
+ # @example Prime numbers
18
+ # primes = Stream.from(2).filter { |n|
19
+ # (2 ... n / 2 + 1).all? { |x|
20
+ # n % x != 0
21
+ # }
22
+ # }
23
+ # p primes.take_while { |n| n < 10 }.to_list #=> List[2, 3, 5, 7]
24
+ class Stream < Promise
25
+ include Enumerable
26
+ include Foldable
27
+
28
+ NULL = Object.new
29
+
30
+ def NULL.head
31
+ raise List::EmptyError
32
+ end
33
+
34
+ def NULL.tail
35
+ raise List::EmptyError
36
+ end
37
+
38
+ def NULL.inspect
39
+ "NULL"
40
+ end
41
+
42
+ class Pair
43
+ attr_reader :head, :tail
44
+
45
+ def initialize(head, tail)
46
+ @head = head
47
+ @tail = tail
48
+ end
49
+ end
50
+
51
+ # Returns an empty stream.
52
+ #
53
+ # @return [Stream] an empty stream.
54
+ def self.null
55
+ delay { NULL }
56
+ end
57
+
58
+ # Creates a new stream.
59
+ #
60
+ # @example A Stream which has 123 as the only element.
61
+ # s = Stream.cons(->{123}, ->{Stream.null})
62
+ # p s.to_list #=> List[123]
63
+ # @example A Stream which has two elements: "abc" and "def".
64
+ # s = Stream.cons(->{"abc"},
65
+ # ->{Stream.cons(->{"def"}, ->{Stream.null})})
66
+ # p s.to_list #=> List["abc", "def"]
67
+ #
68
+ # @param [Proc] head a +Proc+ whose value is the head of +self+.
69
+ # @param [Proc] tail a +Proc+ whose value is the tail of +self+.
70
+ # @return [Stream] the new stream.
71
+ def self.cons(head, tail)
72
+ Stream.eager(Pair.new(Stream.delay(&head), Stream.lazy(&tail)))
73
+ end
74
+
75
+ # Creates a new stream whose head is the value of +block+ and whose tail
76
+ # is +self+.
77
+ #
78
+ # @example A Stream which has 123 as the only element.
79
+ # s = Stream.null.prepend {123}
80
+ # p s.to_list #=> List[123]
81
+ # @example A Stream which has two elements: "abc" and "def".
82
+ # s = Stream.null.prepend {"def"}.prepend {"abc"}
83
+ # p s.to_list #=> List["abc", "def"]
84
+ #
85
+ # @return [Stream] the new stream.
86
+ def prepend(&block)
87
+ Stream.eager(Pair.new(Stream.delay(&block), self))
88
+ end
89
+
90
+ # Creates a new stream. Note that the arguments are evaluated eagerly.
91
+ #
92
+ # @param [Array<Object>] elements the elements of the stream.
93
+ # @return [Stream] the new stream.
94
+ def self.[](*elements)
95
+ from_enum(elements)
96
+ end
97
+
98
+ # Creates a new stream from an +Enumerable+ object.
99
+ #
100
+ # @param [Enumerable] e an +Enumerable+ object.
101
+ # @return [Stream] the new stream.
102
+ def self.from_enum(e)
103
+ from_enumerator(e.each)
104
+ end
105
+
106
+ # Creates a new stream from an +Enumerator+ object.
107
+ # Note that +from_enumerator+ has side effects because it calls
108
+ # +Enumerator#next+.
109
+ #
110
+ # @param [Enumerator] e an +Enumerator+ object.
111
+ # @return [Stream] the new stream.
112
+ def self.from_enumerator(e)
113
+ lazy {
114
+ begin
115
+ x = e.next
116
+ cons ->{ x }, ->{ from_enumerator(e) }
117
+ rescue StopIteration
118
+ null
119
+ end
120
+ }
121
+ end
122
+
123
+ # Creates an infinite stream which starts from +first+ and increments
124
+ # each succeeding element by +step+.
125
+ #
126
+ # @param [#+] first the first element.
127
+ # @param [#+] step the step for succeeding elements.
128
+ # @return [Stream] the new stream.
129
+ def self.from(first, step = 1)
130
+ cons ->{ first }, ->{ from(first + step, step) }
131
+ end
132
+
133
+ # Returns whether +self+ is empty.
134
+ #
135
+ # @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
136
+ def null?
137
+ force == NULL
138
+ end
139
+ alias empty? null?
140
+
141
+ # Returns the first element of +self+. If +self+ is empty,
142
+ # +Immutable::List::EmptyError+ is raised.
143
+ #
144
+ # @return [Object] the first element of +self+.
145
+ def head
146
+ force.head.force
147
+ end
148
+ alias first head
149
+
150
+ # Returns the last element of +self+. If +self+ is empty,
151
+ # +Immutable::List::EmptyError+ is raised.
152
+ #
153
+ # @return [Object] the last element of +self+.
154
+ def last
155
+ if tail.null?
156
+ head
157
+ else
158
+ tail.last
159
+ end
160
+ end
161
+
162
+ # Returns the stream stored in the tail of +self+. If +self+ is empty,
163
+ # +Immutable::List::EmptyError+ is raised.
164
+ #
165
+ # @return [Stream] the stream stored in the tail of +self+.
166
+ def tail
167
+ force.tail
168
+ end
169
+
170
+ # Returns all the elements of +self+ except the last one.
171
+ # If +self+ is empty, +Immutable::List::EmptyError+ is
172
+ # raised.
173
+ #
174
+ # @return [Stream] the elements of +self+ except the last one.
175
+ def init
176
+ Stream.lazy {
177
+ if null?
178
+ raise List::EmptyError
179
+ else
180
+ if tail.null?
181
+ Stream.null
182
+ else
183
+ Stream.cons(->{head}, ->{tail.init})
184
+ end
185
+ end
186
+ }
187
+ end
188
+
189
+ # Creates a string representation of +self+.
190
+ #
191
+ # @return [String] a stream representation of +self+.
192
+ def inspect
193
+ "Stream[" + inspect_i + "]"
194
+ end
195
+
196
+ def inspect_i(s = nil)
197
+ if eager?
198
+ if null?
199
+ s || ""
200
+ else
201
+ h = force.head.eager? ? head.inspect : "?"
202
+ if s
203
+ tail.inspect_i(s + ", " + h)
204
+ else
205
+ tail.inspect_i(h)
206
+ end
207
+ end
208
+ else
209
+ if s
210
+ s + ", ..."
211
+ else
212
+ "..."
213
+ end
214
+ end
215
+ end
216
+ protected :inspect_i
217
+
218
+ # Calls +block+ once for each element in +self+.
219
+ def each(&block)
220
+ unless null?
221
+ yield(head)
222
+ tail.each(&block)
223
+ end
224
+ end
225
+
226
+ # Reduces +self+ using +block+ from right to left. +e+ is used as the
227
+ # starting value. For example:
228
+ #
229
+ # Stream[1, 2, 3].foldr(9) { |x, y| x + y } #=> 1 - (2 - (3 - 9)) = -7
230
+ #
231
+ # @param [Object] e the start value.
232
+ # @return [Object] the reduced value.
233
+ def foldr(e, &block)
234
+ if null?
235
+ e
236
+ else
237
+ yield(head, tail.foldr(e, &block))
238
+ end
239
+ end
240
+
241
+ # Reduces +self+ using +block+ from right to left. If +self+ is empty,
242
+ # +Immutable::List::EmptyError+ is raised.
243
+ #
244
+ # @return [Object] the reduced value.
245
+ def foldr1(&block)
246
+ if tail.null?
247
+ head
248
+ else
249
+ yield(head, tail.foldr1(&block))
250
+ end
251
+ end
252
+
253
+ # Reduces +self+ using +block+ from left to right. +e+ is used as the
254
+ # starting value. For example:
255
+ #
256
+ # Stream[1, 2, 3].foldl(9) { |x, y| x + y } #=> ((9 - 1) - 2) - 3 = 3
257
+ #
258
+ # @param [Object] e the start value.
259
+ # @return [Object] the reduced value.
260
+ def foldl(e, &block)
261
+ if null?
262
+ e
263
+ else
264
+ tail.foldl(yield(e, head), &block)
265
+ end
266
+ end
267
+
268
+ # Reduces +self+ using +block+ from left to right. If +self+ is empty,
269
+ # +Immutable::List::EmptyError+ is raised.
270
+ #
271
+ # @return [Object] the reduced value.
272
+ def foldl1(&block)
273
+ tail.foldl(head, &block)
274
+ end
275
+
276
+ # Returns whether +self+ equals to +s+.
277
+ #
278
+ # @param [Stream] s the stream to compare.
279
+ # @return [true, false] +true+ if +self+ equals to +s+; otherwise,
280
+ # +false+.
281
+ def ==(s)
282
+ if !s.is_a?(Stream)
283
+ false
284
+ else
285
+ if null?
286
+ s.null?
287
+ else
288
+ !s.null? && head == s.head && tail == s.tail
289
+ end
290
+ end
291
+ end
292
+
293
+ # Appends two streams +self+ and +s+.
294
+ #
295
+ # @param [Stream] s the stream to append.
296
+ # @return [Stream] the new stream.
297
+ def +(s)
298
+ Stream.lazy {
299
+ if null?
300
+ s
301
+ else
302
+ Stream.cons ->{head}, ->{tail + s}
303
+ end
304
+ }
305
+ end
306
+
307
+ # Concatenates a stream of streams.
308
+ #
309
+ # @return [Stream] the concatenated stream.
310
+ def flatten
311
+ Stream.lazy {
312
+ if null?
313
+ self
314
+ else
315
+ if head.null?
316
+ tail.flatten
317
+ else
318
+ Stream.cons ->{head.head}, ->{
319
+ Stream.cons(->{head.tail}, ->{tail}).flatten
320
+ }
321
+ end
322
+ end
323
+ }
324
+ end
325
+
326
+ # Returns the stream obtained by applying the given block to each
327
+ # element in +self+.
328
+ #
329
+ # @return [Stream] the obtained stream.
330
+ def map(&block)
331
+ Stream.lazy {
332
+ if null?
333
+ Stream.null
334
+ else
335
+ Stream.cons ->{ yield(head) }, ->{ tail.map(&block) }
336
+ end
337
+ }
338
+ end
339
+
340
+ # Returns the elements of +self+ in reverse order.
341
+ #
342
+ # @return [Stream] the reversed stream.
343
+ def reverse
344
+ foldl(Stream.null) { |x, y| Stream.cons(->{y}, ->{x}) }
345
+ end
346
+
347
+ # Returns a new stream obtained by inserting +sep+ in between the
348
+ # elements of +self+.
349
+ #
350
+ # @param [Object] sep the object to insert between elements.
351
+ # @return [Stream] the new stream.
352
+ def intersperse(sep)
353
+ Stream.lazy {
354
+ if null?
355
+ self
356
+ else
357
+ Stream.cons(->{head}, ->{tail.prepend_to_all(sep)})
358
+ end
359
+ }
360
+ end
361
+
362
+ def prepend_to_all(sep)
363
+ Stream.lazy {
364
+ if null?
365
+ self
366
+ else
367
+ Stream.cons ->{sep}, ->{
368
+ Stream.cons ->{head}, ->{tail.prepend_to_all(sep)}
369
+ }
370
+ end
371
+ }
372
+ end
373
+ protected :prepend_to_all
374
+
375
+ # Returns a new stream obtained by inserting +xs+ in between the streams
376
+ # in +self+ and concatenates the result.
377
+ # +xss.intercalate(xs)+ is equivalent to
378
+ # +xss.intersperse(xs).flatten+.
379
+ #
380
+ # @param [Stream] xs the stream to insert between streams.
381
+ # @return [Stream] the new stream.
382
+ def intercalate(xs)
383
+ intersperse(xs).flatten
384
+ end
385
+
386
+ # Returns the first element in +self+ for which the given block
387
+ # evaluates to true. If such an element is not found, it
388
+ # returns +nil+.
389
+ #
390
+ # @return [Object] the found element.
391
+ def find(&block)
392
+ if null?
393
+ nil
394
+ else
395
+ if yield(head)
396
+ head
397
+ else
398
+ tail.find(&block)
399
+ end
400
+ end
401
+ end
402
+
403
+ # Returns the elements in +self+ for which the given block evaluates to
404
+ # true.
405
+ #
406
+ # @return [Stream] the elements that satisfies the condition.
407
+ def filter(&block)
408
+ Stream.lazy {
409
+ if null?
410
+ Stream.null
411
+ else
412
+ if yield(head)
413
+ Stream.cons ->{ head }, ->{ tail.filter(&block) }
414
+ else
415
+ tail.filter(&block)
416
+ end
417
+ end
418
+ }
419
+ end
420
+
421
+ # Returns the +n+th element of +self+. If +n+ is out of range, +nil+ is
422
+ # returned.
423
+ #
424
+ # @return [Object] the +n+th element.
425
+ def [](n)
426
+ if n < 0 || null?
427
+ nil
428
+ elsif n == 0
429
+ head
430
+ else
431
+ tail[n - 1]
432
+ end
433
+ end
434
+
435
+ # Returns the first +n+ elements of +self+, or +self+ itself if
436
+ # +n > self.length+.
437
+ #
438
+ # @param [Integer] n the number of elements to take.
439
+ # @return [Stream] the first +n+ elements of +self+.
440
+ def take(n)
441
+ Stream.lazy {
442
+ if n <= 0 || null?
443
+ Stream.null
444
+ else
445
+ Stream.cons ->{ head }, ->{ tail.take(n - 1) }
446
+ end
447
+ }
448
+ end
449
+
450
+ # Returns the suffix of +self+ after the first +n+ elements, or
451
+ # +Stream[]+ if +n > self.length+.
452
+ #
453
+ # @param [Integer] n the number of elements to drop.
454
+ # @return [Stream] the suffix of +self+ after the first +n+ elements.
455
+ def drop(n)
456
+ Stream.lazy {
457
+ if n <= 0 || null?
458
+ self
459
+ else
460
+ tail.drop(n - 1)
461
+ end
462
+ }
463
+ end
464
+
465
+ # Returns the longest prefix of the elements of +self+ for which +block+
466
+ # evaluates to true.
467
+ #
468
+ # @return [Stream] the prefix of the elements of +self+.
469
+ def take_while(&block)
470
+ Stream.lazy {
471
+ if null? || !yield(head)
472
+ Stream.null
473
+ else
474
+ Stream.cons ->{ head }, ->{ tail.take_while(&block) }
475
+ end
476
+ }
477
+ end
478
+
479
+ # Returns the suffix remaining after
480
+ # +self.take_while(&block)+.
481
+ #
482
+ # @return [Stream] the suffix of the elements of +self+.
483
+ def drop_while(&block)
484
+ Stream.lazy {
485
+ if null? || !yield(head)
486
+ self
487
+ else
488
+ tail.drop_while(&block)
489
+ end
490
+ }
491
+ end
492
+
493
+ # Converts +self+ to a list.
494
+ #
495
+ # @return [List] a list.
496
+ def to_list
497
+ foldr(List[]) { |x, xs| Cons[x, xs] }
498
+ end
499
+
500
+ # Builds a stream from the seed value +e+ and the given block. The block
501
+ # takes a seed value and returns +nil+ if the seed should
502
+ # unfold to the empty stream, or returns +[a, b]+, where
503
+ # +a+ is the head of the stream and +b+ is the
504
+ # next seed from which to unfold the tail. For example:
505
+ #
506
+ # xs = List.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
507
+ # p xs #=> List[3, 2, 1]
508
+ #
509
+ # +unfoldr+ is the dual of +foldr+.
510
+ #
511
+ # @param [Object] e the seed value.
512
+ # @return [Stream] the stream built from the seed value and the block.
513
+ def self.unfoldr(e, &block)
514
+ Stream.lazy {
515
+ x = yield(e)
516
+ if x.nil?
517
+ Stream.null
518
+ else
519
+ y, z = x
520
+ Stream.cons ->{ y }, ->{ unfoldr(z, &block) }
521
+ end
522
+ }
523
+ end
524
+
525
+ # Takes zero or more streams and returns a new stream in which each
526
+ # element is an array of the corresponding elements of +self+ and the
527
+ # input streams.
528
+ #
529
+ # @param [Array<Stream>] xss the input streams.
530
+ # @return [Stream] the new stream.
531
+ def zip(*xss)
532
+ Stream.lazy {
533
+ if null?
534
+ self
535
+ else
536
+ heads = xss.map { |xs| xs.null? ? nil : xs.head }
537
+ tails = xss.map { |xs| xs.null? ? Stream.null : xs.tail }
538
+ Stream.cons ->{ [head, *heads] }, ->{ tail.zip(*tails) }
539
+ end
540
+ }
541
+ end
542
+
543
+ # Takes zero or more streams and returns the stream obtained by applying
544
+ # the given block to an array of the corresponding elements of +self+
545
+ # and the input streams.
546
+ # +xs.zip_with(*yss, &block)+ is equivalent to
547
+ # +xs.zip(*yss).map(&block)+.
548
+ #
549
+ # @param [Array<Stream>] xss the input streams.
550
+ # @return [Stream] the new stream.
551
+ def zip_with(*xss, &block)
552
+ Stream.lazy {
553
+ if null?
554
+ self
555
+ else
556
+ heads = xss.map { |xs| xs.null? ? nil : xs.head }
557
+ tails = xss.map { |xs| xs.null? ? Stream.null : xs.tail }
558
+ h = yield(head, *heads)
559
+ Stream.cons ->{ h }, ->{ tail.zip_with(*tails, &block) }
560
+ end
561
+ }
562
+ end
563
+ end
564
+ end