immutable 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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