immutable 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- require "immutable/foldable"
1
+ require_relative "foldable"
2
2
 
3
3
  module Immutable
4
4
  # +Immutable::Map+ represents an immutable map from keys to
@@ -133,12 +133,10 @@ module Immutable
133
133
  attr_reader :left, :key, :value, :right
134
134
 
135
135
  def initialize(left, key, value, right)
136
- @left, @key, @value, @right = left, key, value, right
137
- end
138
-
139
- def self.extract(val)
140
- accept_self_instance_only(val)
141
- [val.left, val.key, val.value, val.right]
136
+ @left = left
137
+ @key = key
138
+ @value = value
139
+ @right = right
142
140
  end
143
141
 
144
142
  class << self
@@ -150,12 +148,13 @@ module Immutable
150
148
  end
151
149
 
152
150
  def [](key)
153
- if key < self.key
154
- left[key]
155
- elsif key > self.key
156
- right[key]
151
+ x = key <=> @key
152
+ if x < 0
153
+ @left[key]
154
+ elsif x > 0
155
+ @right[key]
157
156
  else
158
- value
157
+ @value
159
158
  end
160
159
  end
161
160
 
@@ -342,12 +341,13 @@ module Immutable
342
341
  end
343
342
 
344
343
  def ins(key, value)
345
- if key < self.key
346
- RedFork[left.ins(key, value), self.key, self.value, right]
347
- elsif key > self.key
348
- RedFork[left, self.key, self.value, right.ins(key, value)]
344
+ x = key <=> @key
345
+ if x < 0
346
+ RedFork[@left.ins(key, value), @key, @value, @right]
347
+ elsif x > 0
348
+ RedFork[@left, @key, @value, @right.ins(key, value)]
349
349
  else
350
- RedFork[left, key, value, right]
350
+ RedFork[@left, key, value, @right]
351
351
  end
352
352
  end
353
353
  end
@@ -370,12 +370,13 @@ module Immutable
370
370
  end
371
371
 
372
372
  def ins(key, value)
373
- if key < self.key
374
- balance(left.ins(key, value), self.key, self.value, right)
375
- elsif key > self.key
376
- balance(left, self.key, self.value, right.ins(key, value))
373
+ x = key <=> @key
374
+ if x < 0
375
+ balance(@left.ins(key, value), @key, @value, @right)
376
+ elsif x > 0
377
+ balance(@left, @key, @value, @right.ins(key, value))
377
378
  else
378
- BlackFork[left, key, value, right]
379
+ BlackFork[@left, key, value, @right]
379
380
  end
380
381
  end
381
382
  end
@@ -0,0 +1,20 @@
1
+ require_relative "queue"
2
+
3
+ module Immutable
4
+ # +Immutable::OutputRestrictedDeque+ is an implementation of
5
+ # output-restricted deques described in "Purely Functional Data
6
+ # Structures" by Chris Okasaki.
7
+ class OutputRestrictedDeque < Queue
8
+ include Consable
9
+
10
+ # Adds a new element at the head of +self+.
11
+ #
12
+ # @param [Object] x the element to add.
13
+ # @return [Queue] a new queue.
14
+ def cons(x)
15
+ self.class.new(Stream.cons(->{x}, ->{@front}), @rear,
16
+ Stream.cons(->{x}, ->{@schedule}))
17
+ end
18
+ alias prepend cons
19
+ end
20
+ end
@@ -1,10 +1,10 @@
1
- require "immutable/stream"
1
+ require_relative "stream"
2
2
 
3
3
  module Immutable
4
4
  # +Immutable::Queue+ is an implementation of real-time queues described in
5
5
  # "Purely Functional Data Structures" by Chris Okasaki.
6
6
  class Queue
7
- include Enumerable
7
+ include Headable
8
8
 
9
9
  # +Queue.new+ is for internal use only. Use {Queue.empty} or {Queue.[]}
10
10
  # instead.
@@ -18,13 +18,13 @@ module Immutable
18
18
  #
19
19
  # @return [Queue] the empty queue.
20
20
  def self.empty
21
- Queue.new(Stream.null, Nil, Stream.null)
21
+ new(Stream.empty, Nil, Stream.empty)
22
22
  end
23
23
 
24
24
  # Creates a new queue populated with the given objects.
25
25
  #
26
26
  # @param [Array<Object>] elements the elements of the queue.
27
- # @return [List] the new queue.
27
+ # @return [Queue] the new queue.
28
28
  def self.[](*elements)
29
29
  elements.inject(empty, &:snoc)
30
30
  end
@@ -33,33 +33,9 @@ module Immutable
33
33
  #
34
34
  # @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
35
35
  def empty?
36
- @front.null?
36
+ @front.empty?
37
37
  end
38
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
39
  # Adds a new element at the end of +self+.
64
40
  #
65
41
  # @param [Object] x the element to add.
@@ -67,10 +43,11 @@ module Immutable
67
43
  def snoc(x)
68
44
  queue(@front, Cons[x, @rear], @schedule)
69
45
  end
46
+
70
47
  alias push snoc
71
48
 
72
49
  # Returns the first element of +self+. If +self+ is empty,
73
- # +Immutable::List::EmptyError+ is raised.
50
+ # +Immutable::EmptyError+ is raised.
74
51
  #
75
52
  # @return [Object] the first element of +self+.
76
53
  def head
@@ -78,22 +55,38 @@ module Immutable
78
55
  end
79
56
 
80
57
  # Returns the elements after the head of +self+. If +self+ is empty,
81
- # +Immutable::List::EmptyError+ is raised.
58
+ # +Immutable::EmptyError+ is raised.
82
59
  #
83
60
  # @return [Queue] the elements after the head of +self+.
84
61
  def tail
85
- if @front.null?
86
- raise List::EmptyError
62
+ if @front.empty?
63
+ raise EmptyError
87
64
  else
88
65
  queue(@front.tail, @rear, @schedule)
89
66
  end
90
67
  end
91
68
 
92
- # Calls +block+ once for each element in +self+.
93
- def each(&block)
94
- unless @front.null?
95
- yield(head)
96
- tail.each(&block)
69
+ private
70
+
71
+ def rotate(front, rear, accumulator)
72
+ Stream.lazy {
73
+ if front.empty?
74
+ Stream.cons(->{rear.head}, ->{accumulator})
75
+ else
76
+ Stream.cons(->{front.head}, ->{
77
+ rotate(front.tail, rear.tail,
78
+ Stream.cons(->{rear.head}, ->{accumulator}))
79
+ })
80
+ end
81
+ }
82
+ end
83
+
84
+ def queue(front, rear, schedule)
85
+ if schedule.empty?
86
+ f = rotate(front, rear, Stream.empty)
87
+ self.class.new(f, Nil, f)
88
+ else
89
+ self.class.new(front, rear, schedule.tail)
97
90
  end
98
91
  end
99
92
  end
@@ -1,6 +1,5 @@
1
- require "immutable/foldable"
2
- require "immutable/list"
3
- require "immutable/promise"
1
+ require_relative "list"
2
+ require_relative "promise"
4
3
 
5
4
  module Immutable
6
5
  # +Immutable::Stream+ represents a stream, also known as a lazy list.
@@ -22,17 +21,16 @@ module Immutable
22
21
  # }
23
22
  # p primes.take_while { |n| n < 10 }.to_list #=> List[2, 3, 5, 7]
24
23
  class Stream < Promise
25
- include Enumerable
26
- include Foldable
24
+ include Consable
27
25
 
28
26
  NULL = Object.new
29
27
 
30
28
  def NULL.head
31
- raise List::EmptyError
29
+ raise EmptyError
32
30
  end
33
31
 
34
32
  def NULL.tail
35
- raise List::EmptyError
33
+ raise EmptyError
36
34
  end
37
35
 
38
36
  def NULL.inspect
@@ -51,18 +49,18 @@ module Immutable
51
49
  # Returns an empty stream.
52
50
  #
53
51
  # @return [Stream] an empty stream.
54
- def self.null
52
+ def self.empty
55
53
  delay { NULL }
56
54
  end
57
55
 
58
56
  # Creates a new stream.
59
57
  #
60
58
  # @example A Stream which has 123 as the only element.
61
- # s = Stream.cons(->{123}, ->{Stream.null})
59
+ # s = Stream.cons(->{123}, ->{Stream.empty})
62
60
  # p s.to_list #=> List[123]
63
61
  # @example A Stream which has two elements: "abc" and "def".
64
62
  # s = Stream.cons(->{"abc"},
65
- # ->{Stream.cons(->{"def"}, ->{Stream.null})})
63
+ # ->{Stream.cons(->{"def"}, ->{Stream.empty})})
66
64
  # p s.to_list #=> List["abc", "def"]
67
65
  #
68
66
  # @param [Proc] head a +Proc+ whose value is the head of +self+.
@@ -72,19 +70,23 @@ module Immutable
72
70
  Stream.eager(Pair.new(Stream.delay(&head), Stream.lazy(&tail)))
73
71
  end
74
72
 
75
- # Creates a new stream whose head is the value of +block+ and whose tail
76
- # is +self+.
73
+ # Creates a new stream whose head is the value of +x+ or +block+ and
74
+ # whose tail is +self+.
77
75
  #
78
76
  # @example A Stream which has 123 as the only element.
79
- # s = Stream.null.prepend {123}
77
+ # s = Stream.empty.cons(123)
80
78
  # p s.to_list #=> List[123]
81
79
  # @example A Stream which has two elements: "abc" and "def".
82
- # s = Stream.null.prepend {"def"}.prepend {"abc"}
80
+ # s = Stream.empty.cons {"def"}.cons {"abc"}
83
81
  # p s.to_list #=> List["abc", "def"]
84
82
  #
85
83
  # @return [Stream] the new stream.
86
- def prepend(&block)
87
- Stream.eager(Pair.new(Stream.delay(&block), self))
84
+ def cons(x = nil, &block)
85
+ if block
86
+ Stream.eager(Pair.new(Stream.delay(&block), self))
87
+ else
88
+ Stream.eager(Pair.new(Stream.eager(x), self))
89
+ end
88
90
  end
89
91
 
90
92
  # Creates a new stream. Note that the arguments are evaluated eagerly.
@@ -115,7 +117,7 @@ module Immutable
115
117
  x = e.next
116
118
  cons ->{ x }, ->{ from_enumerator(e) }
117
119
  rescue StopIteration
118
- null
120
+ empty
119
121
  end
120
122
  }
121
123
  end
@@ -133,26 +135,24 @@ module Immutable
133
135
  # Returns whether +self+ is empty.
134
136
  #
135
137
  # @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
136
- def null?
138
+ def empty?
137
139
  force == NULL
138
140
  end
139
- alias empty? null?
140
141
 
141
142
  # Returns the first element of +self+. If +self+ is empty,
142
- # +Immutable::List::EmptyError+ is raised.
143
+ # +Immutable::EmptyError+ is raised.
143
144
  #
144
145
  # @return [Object] the first element of +self+.
145
146
  def head
146
147
  force.head.force
147
148
  end
148
- alias first head
149
149
 
150
150
  # Returns the last element of +self+. If +self+ is empty,
151
- # +Immutable::List::EmptyError+ is raised.
151
+ # +Immutable::EmptyError+ is raised.
152
152
  #
153
153
  # @return [Object] the last element of +self+.
154
154
  def last
155
- if tail.null?
155
+ if tail.empty?
156
156
  head
157
157
  else
158
158
  tail.last
@@ -160,7 +160,7 @@ module Immutable
160
160
  end
161
161
 
162
162
  # Returns the stream stored in the tail of +self+. If +self+ is empty,
163
- # +Immutable::List::EmptyError+ is raised.
163
+ # +Immutable::EmptyError+ is raised.
164
164
  #
165
165
  # @return [Stream] the stream stored in the tail of +self+.
166
166
  def tail
@@ -168,17 +168,17 @@ module Immutable
168
168
  end
169
169
 
170
170
  # Returns all the elements of +self+ except the last one.
171
- # If +self+ is empty, +Immutable::List::EmptyError+ is
171
+ # If +self+ is empty, +Immutable::EmptyError+ is
172
172
  # raised.
173
173
  #
174
174
  # @return [Stream] the elements of +self+ except the last one.
175
175
  def init
176
176
  Stream.lazy {
177
- if null?
178
- raise List::EmptyError
177
+ if empty?
178
+ raise EmptyError
179
179
  else
180
- if tail.null?
181
- Stream.null
180
+ if tail.empty?
181
+ Stream.empty
182
182
  else
183
183
  Stream.cons(->{head}, ->{tail.init})
184
184
  end
@@ -195,7 +195,7 @@ module Immutable
195
195
 
196
196
  def inspect_i(s = nil)
197
197
  if eager?
198
- if null?
198
+ if empty?
199
199
  s || ""
200
200
  else
201
201
  h = force.head.eager? ? head.inspect : "?"
@@ -215,88 +215,13 @@ module Immutable
215
215
  end
216
216
  protected :inspect_i
217
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
218
  # Appends two streams +self+ and +s+.
294
219
  #
295
220
  # @param [Stream] s the stream to append.
296
221
  # @return [Stream] the new stream.
297
222
  def +(s)
298
223
  Stream.lazy {
299
- if null?
224
+ if empty?
300
225
  s
301
226
  else
302
227
  Stream.cons ->{head}, ->{tail + s}
@@ -304,33 +229,14 @@ module Immutable
304
229
  }
305
230
  end
306
231
 
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
232
  # Returns the stream obtained by applying the given block to each
327
233
  # element in +self+.
328
234
  #
329
235
  # @return [Stream] the obtained stream.
330
236
  def map(&block)
331
237
  Stream.lazy {
332
- if null?
333
- Stream.null
238
+ if empty?
239
+ Stream.empty
334
240
  else
335
241
  Stream.cons ->{ yield(head) }, ->{ tail.map(&block) }
336
242
  end
@@ -341,7 +247,7 @@ module Immutable
341
247
  #
342
248
  # @return [Stream] the reversed stream.
343
249
  def reverse
344
- foldl(Stream.null) { |x, y| Stream.cons(->{y}, ->{x}) }
250
+ foldl(Stream.empty) { |x, y| Stream.cons(->{y}, ->{x}) }
345
251
  end
346
252
 
347
253
  # Returns a new stream obtained by inserting +sep+ in between the
@@ -351,7 +257,7 @@ module Immutable
351
257
  # @return [Stream] the new stream.
352
258
  def intersperse(sep)
353
259
  Stream.lazy {
354
- if null?
260
+ if empty?
355
261
  self
356
262
  else
357
263
  Stream.cons(->{head}, ->{tail.prepend_to_all(sep)})
@@ -361,7 +267,7 @@ module Immutable
361
267
 
362
268
  def prepend_to_all(sep)
363
269
  Stream.lazy {
364
- if null?
270
+ if empty?
365
271
  self
366
272
  else
367
273
  Stream.cons ->{sep}, ->{
@@ -383,54 +289,26 @@ module Immutable
383
289
  intersperse(xs).flatten
384
290
  end
385
291
 
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.
292
+ # Concatenates a stream of streams.
405
293
  #
406
- # @return [Stream] the elements that satisfies the condition.
407
- def filter(&block)
294
+ # @return [Stream] the concatenated stream.
295
+ def flatten
408
296
  Stream.lazy {
409
- if null?
410
- Stream.null
297
+ if empty?
298
+ self
411
299
  else
412
- if yield(head)
413
- Stream.cons ->{ head }, ->{ tail.filter(&block) }
300
+ if head.empty?
301
+ tail.flatten
414
302
  else
415
- tail.filter(&block)
303
+ Stream.cons ->{head.head}, ->{
304
+ Stream.cons(->{head.tail}, ->{tail}).flatten
305
+ }
416
306
  end
417
307
  end
418
308
  }
419
309
  end
420
310
 
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
311
+ alias concat flatten
434
312
 
435
313
  # Returns the first +n+ elements of +self+, or +self+ itself if
436
314
  # +n > self.length+.
@@ -439,8 +317,8 @@ module Immutable
439
317
  # @return [Stream] the first +n+ elements of +self+.
440
318
  def take(n)
441
319
  Stream.lazy {
442
- if n <= 0 || null?
443
- Stream.null
320
+ if n <= 0 || empty?
321
+ Stream.empty
444
322
  else
445
323
  Stream.cons ->{ head }, ->{ tail.take(n - 1) }
446
324
  end
@@ -454,7 +332,7 @@ module Immutable
454
332
  # @return [Stream] the suffix of +self+ after the first +n+ elements.
455
333
  def drop(n)
456
334
  Stream.lazy {
457
- if n <= 0 || null?
335
+ if n <= 0 || empty?
458
336
  self
459
337
  else
460
338
  tail.drop(n - 1)
@@ -468,8 +346,8 @@ module Immutable
468
346
  # @return [Stream] the prefix of the elements of +self+.
469
347
  def take_while(&block)
470
348
  Stream.lazy {
471
- if null? || !yield(head)
472
- Stream.null
349
+ if empty? || !yield(head)
350
+ Stream.empty
473
351
  else
474
352
  Stream.cons ->{ head }, ->{ tail.take_while(&block) }
475
353
  end
@@ -482,7 +360,7 @@ module Immutable
482
360
  # @return [Stream] the suffix of the elements of +self+.
483
361
  def drop_while(&block)
484
362
  Stream.lazy {
485
- if null? || !yield(head)
363
+ if empty? || !yield(head)
486
364
  self
487
365
  else
488
366
  tail.drop_while(&block)
@@ -490,11 +368,22 @@ module Immutable
490
368
  }
491
369
  end
492
370
 
493
- # Converts +self+ to a list.
371
+ # Returns the elements in +self+ for which the given block evaluates to
372
+ # true.
494
373
  #
495
- # @return [List] a list.
496
- def to_list
497
- foldr(List[]) { |x, xs| Cons[x, xs] }
374
+ # @return [Stream] the elements that satisfies the condition.
375
+ def filter(&block)
376
+ Stream.lazy {
377
+ if empty?
378
+ Stream.empty
379
+ else
380
+ if yield(head)
381
+ Stream.cons ->{ head }, ->{ tail.filter(&block) }
382
+ else
383
+ tail.filter(&block)
384
+ end
385
+ end
386
+ }
498
387
  end
499
388
 
500
389
  # Builds a stream from the seed value +e+ and the given block. The block
@@ -514,7 +403,7 @@ module Immutable
514
403
  Stream.lazy {
515
404
  x = yield(e)
516
405
  if x.nil?
517
- Stream.null
406
+ Stream.empty
518
407
  else
519
408
  y, z = x
520
409
  Stream.cons ->{ y }, ->{ unfoldr(z, &block) }
@@ -530,11 +419,11 @@ module Immutable
530
419
  # @return [Stream] the new stream.
531
420
  def zip(*xss)
532
421
  Stream.lazy {
533
- if null?
422
+ if empty?
534
423
  self
535
424
  else
536
- heads = xss.map { |xs| xs.null? ? nil : xs.head }
537
- tails = xss.map { |xs| xs.null? ? Stream.null : xs.tail }
425
+ heads = xss.map { |xs| xs.empty? ? nil : xs.head }
426
+ tails = xss.map { |xs| xs.empty? ? Stream.empty : xs.tail }
538
427
  Stream.cons ->{ [head, *heads] }, ->{ tail.zip(*tails) }
539
428
  end
540
429
  }
@@ -550,11 +439,11 @@ module Immutable
550
439
  # @return [Stream] the new stream.
551
440
  def zip_with(*xss, &block)
552
441
  Stream.lazy {
553
- if null?
442
+ if empty?
554
443
  self
555
444
  else
556
- heads = xss.map { |xs| xs.null? ? nil : xs.head }
557
- tails = xss.map { |xs| xs.null? ? Stream.null : xs.tail }
445
+ heads = xss.map { |xs| xs.empty? ? nil : xs.head }
446
+ tails = xss.map { |xs| xs.empty? ? Stream.empty : xs.tail }
558
447
  h = yield(head, *heads)
559
448
  Stream.cons ->{ h }, ->{ tail.zip_with(*tails, &block) }
560
449
  end