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.
- data/README.md +0 -9
- data/benchmark/benchmark_deque.rb +59 -0
- data/benchmark/benchmark_map.rb +5 -5
- data/benchmark/benchmark_parallel_fib.rb +73 -0
- data/benchmark/benchmark_queue.rb +3 -3
- data/lib/immutable.rb +7 -5
- data/lib/immutable/consable.rb +344 -0
- data/lib/immutable/deque.rb +196 -0
- data/lib/immutable/foldable.rb +10 -0
- data/lib/immutable/headable.rb +235 -0
- data/lib/immutable/list.rb +23 -487
- data/lib/immutable/map.rb +23 -22
- data/lib/immutable/output_restricted_deque.rb +20 -0
- data/lib/immutable/queue.rb +31 -38
- data/lib/immutable/stream.rb +75 -186
- data/test/immutable/test_deque.rb +116 -0
- data/test/immutable/test_list.rb +62 -11
- data/test/immutable/test_map.rb +2 -11
- data/test/immutable/test_promise.rb +2 -2
- data/test/immutable/test_queue.rb +31 -4
- data/test/immutable/test_stream.rb +26 -16
- data/test/test_helper.rb +2 -0
- metadata +9 -2
data/lib/immutable/map.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
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
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
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
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
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
|
data/lib/immutable/queue.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
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
|
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
|
-
|
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 [
|
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.
|
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::
|
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::
|
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.
|
86
|
-
raise
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
data/lib/immutable/stream.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
|
2
|
-
|
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
|
26
|
-
include Foldable
|
24
|
+
include Consable
|
27
25
|
|
28
26
|
NULL = Object.new
|
29
27
|
|
30
28
|
def NULL.head
|
31
|
-
raise
|
29
|
+
raise EmptyError
|
32
30
|
end
|
33
31
|
|
34
32
|
def NULL.tail
|
35
|
-
raise
|
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.
|
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.
|
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.
|
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
|
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.
|
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.
|
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
|
87
|
-
|
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
|
-
|
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
|
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::
|
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::
|
151
|
+
# +Immutable::EmptyError+ is raised.
|
152
152
|
#
|
153
153
|
# @return [Object] the last element of +self+.
|
154
154
|
def last
|
155
|
-
if tail.
|
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::
|
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::
|
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
|
178
|
-
raise
|
177
|
+
if empty?
|
178
|
+
raise EmptyError
|
179
179
|
else
|
180
|
-
if tail.
|
181
|
-
Stream.
|
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
|
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
|
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
|
333
|
-
Stream.
|
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.
|
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
|
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
|
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
|
-
#
|
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
|
407
|
-
def
|
294
|
+
# @return [Stream] the concatenated stream.
|
295
|
+
def flatten
|
408
296
|
Stream.lazy {
|
409
|
-
if
|
410
|
-
|
297
|
+
if empty?
|
298
|
+
self
|
411
299
|
else
|
412
|
-
if
|
413
|
-
|
300
|
+
if head.empty?
|
301
|
+
tail.flatten
|
414
302
|
else
|
415
|
-
|
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
|
-
|
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 ||
|
443
|
-
Stream.
|
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 ||
|
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
|
472
|
-
Stream.
|
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
|
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
|
-
#
|
371
|
+
# Returns the elements in +self+ for which the given block evaluates to
|
372
|
+
# true.
|
494
373
|
#
|
495
|
-
# @return [
|
496
|
-
def
|
497
|
-
|
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.
|
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
|
422
|
+
if empty?
|
534
423
|
self
|
535
424
|
else
|
536
|
-
heads = xss.map { |xs| xs.
|
537
|
-
tails = xss.map { |xs| xs.
|
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
|
442
|
+
if empty?
|
554
443
|
self
|
555
444
|
else
|
556
|
-
heads = xss.map { |xs| xs.
|
557
|
-
tails = xss.map { |xs| xs.
|
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
|