immutable 0.2.0 → 0.3.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,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