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.
- data/README.md +33 -23
- data/benchmark/benchmark_queue.rb +41 -0
- data/lib/immutable.rb +9 -0
- data/lib/immutable/foldable.rb +29 -0
- data/lib/immutable/list.rb +247 -77
- data/lib/immutable/map.rb +18 -20
- data/lib/immutable/promise.rb +116 -0
- data/lib/immutable/queue.rb +100 -0
- data/lib/immutable/stream.rb +564 -0
- data/test/immutable/test_list.rb +50 -2
- data/test/immutable/test_map.rb +15 -2
- data/test/immutable/test_promise.rb +202 -0
- data/test/immutable/test_queue.rb +43 -0
- data/test/immutable/test_stream.rb +359 -0
- data/test/test_helper.rb +14 -0
- metadata +12 -2
data/README.md
CHANGED
@@ -3,37 +3,47 @@ immutable - immutable data structures for Ruby
|
|
3
3
|
|
4
4
|
This project aims to provide immutable data structures for Ruby.
|
5
5
|
|
6
|
-
Currently, immutable provides the following classes:
|
6
|
+
Currently, immutable provides the following classes and modules:
|
7
7
|
|
8
|
+
* Immutable::Foldable
|
8
9
|
* Immutable::List
|
9
10
|
* Immutable::Map
|
11
|
+
* Immutable::Promise
|
12
|
+
* Immutable::Stream
|
13
|
+
* Immutable::Queue
|
10
14
|
|
11
15
|
Install
|
12
16
|
========================
|
13
17
|
|
14
18
|
$ gem install immutable
|
15
19
|
|
20
|
+
Documentation
|
21
|
+
========================
|
22
|
+
|
23
|
+
* [API Reference](http://rubydoc.info/github/shugo/immutable/frames)
|
24
|
+
|
16
25
|
License
|
17
26
|
========================
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
27
|
+
|
28
|
+
(The MIT License)
|
29
|
+
|
30
|
+
Copyright (c) 2012 Shugo Maeda
|
31
|
+
|
32
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
33
|
+
a copy of this software and associated documentation files (the
|
34
|
+
'Software'), to deal in the Software without restriction, including
|
35
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
36
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
37
|
+
permit persons to whom the Software is furnished to do so, subject to
|
38
|
+
the following conditions:
|
39
|
+
|
40
|
+
The above copyright notice and this permission notice shall be
|
41
|
+
included in all copies or substantial portions of the Software.
|
42
|
+
|
43
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
44
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
45
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
46
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
47
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
48
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
49
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'immutable/queue'
|
3
|
+
require 'immutable/map'
|
4
|
+
|
5
|
+
TIMES = 100000
|
6
|
+
key_size = 10
|
7
|
+
|
8
|
+
def snoc(queue)
|
9
|
+
TIMES.times do
|
10
|
+
queue.snoc(0)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def head(queue)
|
15
|
+
TIMES.times do
|
16
|
+
queue.head
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def tail(queue)
|
21
|
+
TIMES.times do
|
22
|
+
queue.tail
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def run(bm, queue, num, method)
|
27
|
+
bm.report("#{method} for #{num}-elements queue") do
|
28
|
+
send(method, queue)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Benchmark.bmbm do |bm|
|
33
|
+
queues = [10, 1000, 100000].inject(Immutable::Map.empty) { |m, n|
|
34
|
+
m.insert(n, (1..n).inject(Immutable::Queue.empty, &:snoc))
|
35
|
+
}
|
36
|
+
for method in [:snoc, :head, :tail]
|
37
|
+
for num, queue in queues
|
38
|
+
run(bm, queue, num, method)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/immutable.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Immutable
|
2
|
+
module Foldable
|
3
|
+
# Returns the number of elements in +self+. May be zero.
|
4
|
+
#
|
5
|
+
# @return [Integer] the number of elements in +self+.
|
6
|
+
def length
|
7
|
+
foldl(0) { |x, y| x + 1 }
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns the number of elements in +self+. May be zero.
|
11
|
+
#
|
12
|
+
# @return [Integer] the number of elements in +self+.
|
13
|
+
alias size length
|
14
|
+
|
15
|
+
# Computes the sum of the numbers in +self+.
|
16
|
+
#
|
17
|
+
# @return [#+] the sum of the numbers.
|
18
|
+
def sum
|
19
|
+
foldl(0, &:+)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Computes the product of the numbers in +self+.
|
23
|
+
#
|
24
|
+
# @return [#*] the product of the numbers.
|
25
|
+
def product
|
26
|
+
foldl(1, &:*)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/immutable/list.rb
CHANGED
@@ -1,121 +1,167 @@
|
|
1
|
-
|
1
|
+
require "immutable/foldable"
|
2
2
|
|
3
3
|
module Immutable
|
4
|
-
#
|
4
|
+
# +Immutable::List+ represents an immutable list.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# +Immutable::List+ is an abstract class and
|
7
|
+
# {Immutable::List.[]} should be used instead of
|
8
|
+
# {Immutable::List.new}. For example:
|
9
9
|
#
|
10
10
|
# include Immutable
|
11
11
|
# p List[] #=> List[]
|
12
12
|
# p List[1, 2] #=> List[1, 2]
|
13
13
|
#
|
14
|
-
#
|
15
|
-
#
|
14
|
+
# +Immutable::Nil+ represents an empty list, and
|
15
|
+
# +Immutable::Cons+ represents a cons cell, which is a node in
|
16
16
|
# a list. For example:
|
17
17
|
#
|
18
18
|
# p Nil #=> List[]
|
19
19
|
# p Cons[1, Cons[2, Nil]] #=> List[1, 2]
|
20
20
|
class List
|
21
|
+
include Enumerable
|
22
|
+
include Foldable
|
23
|
+
|
21
24
|
class EmptyError < StandardError
|
22
25
|
def initialize(msg = "list is empty")
|
23
26
|
super(msg)
|
24
27
|
end
|
25
28
|
end
|
26
29
|
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
+
# Creates a new list populated with the given objects.
|
31
|
+
#
|
32
|
+
# @param [Array<Object>] elements the elements of the list.
|
33
|
+
# @return [List] the new list.
|
34
|
+
def self.[](*elements)
|
35
|
+
from_array(elements)
|
30
36
|
end
|
31
37
|
|
32
|
-
# Converts the given array
|
38
|
+
# Converts the given array to a list.
|
39
|
+
#
|
40
|
+
# @param [Array, #reverse_each] ary the array to convert.
|
41
|
+
# @return [List] the list converted from +ary+.
|
33
42
|
def self.from_array(ary)
|
34
43
|
ary.reverse_each.inject(Nil) { |x, y|
|
35
44
|
Cons.new(y, x)
|
36
45
|
}
|
37
46
|
end
|
38
47
|
|
39
|
-
# Converts
|
48
|
+
# Converts the given Enumerable object to a list.
|
49
|
+
#
|
50
|
+
# @param [#inject] enum the Enumerable object to convert.
|
51
|
+
# @return [List] the list converted from +enum+.
|
40
52
|
def self.from_enum(enum)
|
41
53
|
enum.inject(Nil) { |x, y|
|
42
54
|
Cons.new(y, x)
|
43
55
|
}.reverse
|
44
56
|
end
|
45
57
|
|
58
|
+
# Calls +block+ once for each element in +self+.
|
59
|
+
def each(&block)
|
60
|
+
end
|
61
|
+
|
46
62
|
# Appends two lists +self+ and +xs+.
|
63
|
+
#
|
64
|
+
# @param [List] xs the list to append.
|
65
|
+
# @return [List] the new list.
|
47
66
|
def +(xs)
|
48
67
|
foldr(xs) { |y, ys| Cons[y, ys] }
|
49
68
|
end
|
50
69
|
|
70
|
+
# Returns whether +self+ equals to +xs+.
|
71
|
+
#
|
72
|
+
# @param [List] xs the list to compare.
|
73
|
+
# @return [true, false] +true+ if +self+ equals to +xs+; otherwise,
|
74
|
+
# +false+.
|
75
|
+
def ==(xs)
|
76
|
+
# this method should be overriden
|
77
|
+
end
|
78
|
+
|
51
79
|
# Returns the first element of +self+. If +self+ is empty,
|
52
|
-
#
|
80
|
+
# +Immutable::List::EmptyError+ is raised.
|
81
|
+
#
|
82
|
+
# @return [Object] the first element of +self+.
|
53
83
|
def head
|
54
|
-
|
84
|
+
# this method should be overriden
|
55
85
|
end
|
56
86
|
|
57
87
|
# Returns the first element of +self+. If +self+ is empty,
|
58
|
-
#
|
88
|
+
# +Immutable::List::EmptyError+ is raised.
|
89
|
+
#
|
90
|
+
# @return [Object] the first element of +self+.
|
59
91
|
def first
|
60
92
|
head
|
61
93
|
end
|
62
94
|
|
63
95
|
# Returns the last element of +self+. If +self+ is empty,
|
64
|
-
#
|
96
|
+
# +Immutable::List::EmptyError+ is raised.
|
97
|
+
#
|
98
|
+
# @return [Object] the last element of +self+.
|
65
99
|
def last
|
66
|
-
|
100
|
+
# this method should be overriden
|
67
101
|
end
|
68
102
|
|
69
|
-
# Returns the
|
70
|
-
#
|
103
|
+
# Returns the elements after the head of +self+. If +self+ is empty,
|
104
|
+
# +Immutable::List::EmptyError+ is raised.
|
105
|
+
#
|
106
|
+
# @return [List] the elements after the head of +self+.
|
71
107
|
def tail
|
72
|
-
|
108
|
+
# this method should be overriden
|
73
109
|
end
|
74
110
|
|
75
|
-
# Returns all the
|
76
|
-
# If +self+ is empty,
|
111
|
+
# Returns all the elements of +self+ except the last one.
|
112
|
+
# If +self+ is empty, +Immutable::List::EmptyError+ is
|
77
113
|
# raised.
|
114
|
+
#
|
115
|
+
# @return [List] the elements of +self+ except the last one.
|
78
116
|
def init
|
79
|
-
|
117
|
+
# this method should be overriden
|
80
118
|
end
|
81
119
|
|
82
|
-
# Returns
|
120
|
+
# Returns whether +self+ is empty.
|
121
|
+
#
|
122
|
+
# @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
|
83
123
|
def empty?
|
84
|
-
|
124
|
+
# this method should be overriden
|
85
125
|
end
|
86
126
|
|
87
|
-
# Returns
|
127
|
+
# Returns whether +self+ is empty.
|
128
|
+
#
|
129
|
+
# @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
|
88
130
|
def null?
|
89
131
|
empty?
|
90
132
|
end
|
91
133
|
|
92
|
-
# Returns the number of elements in +self+. May be zero.
|
93
|
-
def length
|
94
|
-
foldl(0) { |x, y| x + 1 }
|
95
|
-
end
|
96
|
-
|
97
|
-
alias size length
|
98
|
-
|
99
134
|
# Returns the list obtained by applying the given block to each element
|
100
135
|
# in +self+.
|
136
|
+
#
|
137
|
+
# @return [List] the obtained list.
|
101
138
|
def map
|
102
139
|
foldr(Nil) { |x, xs| Cons[yield(x), xs] }
|
103
140
|
end
|
104
141
|
|
105
142
|
# Returns the elements of +self+ in reverse order.
|
143
|
+
#
|
144
|
+
# @return [List] the reversed list.
|
106
145
|
def reverse
|
107
146
|
foldl(Nil) { |x, y| Cons[y, x] }
|
108
147
|
end
|
109
148
|
|
110
|
-
#
|
111
|
-
|
112
|
-
|
149
|
+
# Returns a new list obtained by inserting +sep+ in between the elements
|
150
|
+
# of +self+.
|
151
|
+
#
|
152
|
+
# @param [Object] sep the object to insert between elements.
|
153
|
+
# @return [List] the new list.
|
154
|
+
def intersperse(sep)
|
155
|
+
# this method should be overriden
|
113
156
|
end
|
114
157
|
|
115
|
-
#
|
116
|
-
# result.
|
117
|
-
#
|
118
|
-
#
|
158
|
+
# Returns a new list obtained by inserting +xs+ in between the lists in
|
159
|
+
# +self+ and concatenates the result.
|
160
|
+
# +xss.intercalate(xs)+ is equivalent to
|
161
|
+
# +xss.intersperse(xs).flatten+.
|
162
|
+
#
|
163
|
+
# @param [List] xs the list to insert between lists.
|
164
|
+
# @return [List] the new list.
|
119
165
|
def intercalate(xs)
|
120
166
|
intersperse(xs).flatten
|
121
167
|
end
|
@@ -124,78 +170,91 @@ module Immutable
|
|
124
170
|
#
|
125
171
|
# p List[List[1, 2, 3], List[4, 5, 6]].transpose
|
126
172
|
# #=> List[List[1, 4], List[2, 5], List[3, 6]]
|
173
|
+
#
|
174
|
+
# @return [List] the transposed list.
|
127
175
|
def transpose
|
128
|
-
|
176
|
+
# this method should be overriden
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns the list of all subsequences of +self+.
|
180
|
+
#
|
181
|
+
# @return [List<List>] the list of subsequences.
|
182
|
+
def subsequences
|
183
|
+
Cons[List[], nonempty_subsequences]
|
129
184
|
end
|
130
185
|
|
131
186
|
# Reduces +self+ using +block+ from right to left. +e+ is used as the
|
132
187
|
# starting value. For example:
|
133
188
|
#
|
134
189
|
# List[1, 2, 3].foldr(9) { |x, y| x + y } #=> 1 - (2 - (3 - 9)) = -7
|
190
|
+
#
|
191
|
+
# @param [Object] e the start value.
|
192
|
+
# @return [Object] the reduced value.
|
135
193
|
def foldr(e, &block)
|
136
|
-
|
194
|
+
# this method should be overriden
|
137
195
|
end
|
138
196
|
|
139
197
|
# Reduces +self+ using +block+ from right to left. If +self+ is empty,
|
140
|
-
#
|
198
|
+
# +Immutable::List::EmptyError+ is raised.
|
199
|
+
#
|
200
|
+
# @return [Object] the reduced value.
|
141
201
|
def foldr1(&block)
|
142
|
-
|
202
|
+
# this method should be overriden
|
143
203
|
end
|
144
204
|
|
145
205
|
# Reduces +self+ using +block+ from left to right. +e+ is used as the
|
146
206
|
# starting value. For example:
|
147
207
|
#
|
148
208
|
# List[1, 2, 3].foldl(9) { |x, y| x + y } #=> ((9 - 1) - 2) - 3 = 3
|
209
|
+
#
|
210
|
+
# @param [Object] e the start value.
|
211
|
+
# @return [Object] the reduced value.
|
149
212
|
def foldl(e, &block)
|
150
|
-
|
213
|
+
# this method should be overriden
|
151
214
|
end
|
152
215
|
|
153
216
|
# Reduces +self+ using +block+ from left to right. If +self+ is empty,
|
154
|
-
#
|
217
|
+
# +Immutable::List::EmptyError+ is raised.
|
218
|
+
#
|
219
|
+
# @return [Object] the reduced value.
|
155
220
|
def foldl1(&block)
|
156
|
-
|
221
|
+
# this method should be overriden
|
157
222
|
end
|
158
223
|
|
159
224
|
# Concatenates a list of lists.
|
225
|
+
#
|
226
|
+
# @return [List] the concatenated list.
|
160
227
|
def flatten
|
161
228
|
foldr(Nil) { |x, xs| x + xs }
|
162
229
|
end
|
163
230
|
|
164
|
-
# An alias of <code>flatten</code>.
|
165
231
|
alias concat flatten
|
166
232
|
|
167
233
|
# Returns the list obtained by concatenating the results of the given
|
168
234
|
# block for each element in +self+.
|
235
|
+
#
|
236
|
+
# @return [List] the obtained list.
|
169
237
|
def flat_map
|
170
238
|
foldr(Nil) { |x, xs| yield(x) + xs }
|
171
239
|
end
|
172
240
|
|
173
|
-
# An alias of <code>flat_map</code>.
|
174
241
|
alias concat_map flat_map
|
175
242
|
|
176
|
-
# An alias of <code>flat_map</code>.
|
177
243
|
alias bind flat_map
|
178
244
|
|
179
|
-
# Computes the sum of the numbers in +self+.
|
180
|
-
def sum
|
181
|
-
foldl(0, &:+)
|
182
|
-
end
|
183
|
-
|
184
|
-
# Computes the product of the numbers in +self+.
|
185
|
-
def product
|
186
|
-
foldl(1, &:*)
|
187
|
-
end
|
188
|
-
|
189
245
|
# Builds a list from the seed value +e+ and the given block. The block
|
190
|
-
# takes a seed value and returns
|
191
|
-
# unfold to the empty list, or returns
|
192
|
-
#
|
246
|
+
# takes a seed value and returns +nil+ if the seed should
|
247
|
+
# unfold to the empty list, or returns +[a, b]+, where
|
248
|
+
# +a+ is the head of the list and +b+ is the next
|
193
249
|
# seed from which to unfold the tail. For example:
|
194
250
|
#
|
195
251
|
# xs = List.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
|
196
252
|
# p xs #=> List[3, 2, 1]
|
197
253
|
#
|
198
|
-
#
|
254
|
+
# +unfoldr+ is the dual of +foldr+.
|
255
|
+
#
|
256
|
+
# @param [Object] e the seed value.
|
257
|
+
# @return [List] the list built from the seed value and the block.
|
199
258
|
def self.unfoldr(e, &block)
|
200
259
|
x = yield(e)
|
201
260
|
if x.nil?
|
@@ -206,40 +265,68 @@ module Immutable
|
|
206
265
|
end
|
207
266
|
end
|
208
267
|
|
209
|
-
# Returns the
|
210
|
-
#
|
211
|
-
|
212
|
-
|
268
|
+
# Returns the +n+th element of +self+. If +n+ is out of range, +nil+ is
|
269
|
+
# returned.
|
270
|
+
#
|
271
|
+
# @return [Object] the +n+th element.
|
272
|
+
def [](n)
|
273
|
+
# this method should be overriden
|
213
274
|
end
|
214
275
|
|
276
|
+
# Returns the first +n+ elements of +self+, or all the elements of
|
277
|
+
# +self+ if +n > self.length+.
|
278
|
+
#
|
279
|
+
# @param [Integer] n the number of elements to take.
|
280
|
+
# @return [List] the first +n+ elements of +self+.
|
281
|
+
def take(n)
|
282
|
+
# this method should be overriden
|
283
|
+
end
|
215
284
|
|
216
285
|
# Returns the suffix of +self+ after the first +n+ elements, or
|
217
|
-
#
|
286
|
+
# +List[]+ if +n > self.length+.
|
287
|
+
#
|
288
|
+
# @param [Integer] n the number of elements to drop.
|
289
|
+
# @return [List] the suffix of +self+ after the first +n+ elements.
|
218
290
|
def drop(n)
|
219
|
-
|
291
|
+
# this method should be overriden
|
220
292
|
end
|
221
293
|
|
222
294
|
# Returns the longest prefix of the elements of +self+ for which +block+
|
223
295
|
# evaluates to true.
|
296
|
+
#
|
297
|
+
# @return [List] the prefix of the elements of +self+.
|
224
298
|
def take_while(&block)
|
225
|
-
|
299
|
+
# this method should be overriden
|
226
300
|
end
|
227
301
|
|
228
302
|
# Returns the suffix remaining after
|
229
|
-
#
|
303
|
+
# +self.take_while(&block)+.
|
304
|
+
#
|
305
|
+
# @return [List] the suffix of the elements of +self+.
|
230
306
|
def drop_while(&block)
|
231
|
-
|
307
|
+
# this method should be overriden
|
308
|
+
end
|
309
|
+
|
310
|
+
# Returns +self+.
|
311
|
+
#
|
312
|
+
# @return [List] +self+.
|
313
|
+
def to_list
|
314
|
+
self
|
232
315
|
end
|
233
316
|
|
234
317
|
# Returns the first element in +self+ for which the given block
|
235
318
|
# evaluates to true. If such an element is not found, it
|
236
|
-
# returns
|
319
|
+
# returns +nil+.
|
320
|
+
#
|
321
|
+
# @return [Object] the found element.
|
237
322
|
def find(&block)
|
238
|
-
|
323
|
+
# this method should be overriden
|
239
324
|
end
|
240
325
|
|
241
326
|
# Returns the elements in +self+ for which the given block evaluates to
|
242
327
|
# true.
|
328
|
+
#
|
329
|
+
# @return [List] the elements that satisfies the condition.
|
243
330
|
def filter
|
244
331
|
foldr(Nil) { |x, xs|
|
245
332
|
if yield(x)
|
@@ -249,12 +336,38 @@ module Immutable
|
|
249
336
|
end
|
250
337
|
}
|
251
338
|
end
|
339
|
+
|
340
|
+
# Takes zero or more lists and returns a new list in which each element
|
341
|
+
# is an array of the corresponding elements of +self+ and the input
|
342
|
+
# lists.
|
343
|
+
#
|
344
|
+
# @param [Array<List>] xss the input lists.
|
345
|
+
# @return [List] the new list.
|
346
|
+
def zip(*xss)
|
347
|
+
# this method should be overriden
|
348
|
+
end
|
349
|
+
|
350
|
+
# Takes zero or more lists and returns the list obtained by applying the
|
351
|
+
# given block to an array of the corresponding elements of +self+ and
|
352
|
+
# the input lists.
|
353
|
+
# +xs.zip_with(*yss, &block)+ is equivalent to
|
354
|
+
# +xs.zip(*yss).map(&block)+.
|
355
|
+
#
|
356
|
+
# @param [Array<List>] xss the input lists.
|
357
|
+
# @return [List] the new list.
|
358
|
+
def zip_with(*xss, &block)
|
359
|
+
# this method should be overriden
|
360
|
+
end
|
252
361
|
end
|
253
362
|
|
363
|
+
# +Immutable::Nil+ represents an empty list.
|
254
364
|
Nil = List.new
|
255
365
|
|
366
|
+
# +Immutable::Cons+ represents a cons cell.
|
256
367
|
class Cons < List
|
257
|
-
# Creates a list obtained by prepending +head+ to the list +tail+.
|
368
|
+
# Creates a new list obtained by prepending +head+ to the list +tail+.
|
369
|
+
#
|
370
|
+
# @return [Cons] the new list.
|
258
371
|
def self.[](head, tail = Nil)
|
259
372
|
self.new(head, tail)
|
260
373
|
end
|
@@ -329,6 +442,14 @@ module Immutable
|
|
329
442
|
end
|
330
443
|
end
|
331
444
|
|
445
|
+
def Nil.each
|
446
|
+
end
|
447
|
+
|
448
|
+
def each(&block)
|
449
|
+
yield(@head)
|
450
|
+
@tail.each(&block)
|
451
|
+
end
|
452
|
+
|
332
453
|
def Nil.foldl(e)
|
333
454
|
e
|
334
455
|
end
|
@@ -345,8 +466,12 @@ module Immutable
|
|
345
466
|
@tail.foldl(@head, &block)
|
346
467
|
end
|
347
468
|
|
469
|
+
def Nil.==(xs)
|
470
|
+
equal?(xs)
|
471
|
+
end
|
472
|
+
|
348
473
|
def ==(xs)
|
349
|
-
if xs.empty?
|
474
|
+
if !xs.is_a?(List) || xs.empty?
|
350
475
|
false
|
351
476
|
else
|
352
477
|
@head == xs.head && @tail == xs.tail
|
@@ -392,6 +517,31 @@ module Immutable
|
|
392
517
|
end
|
393
518
|
end
|
394
519
|
|
520
|
+
def Nil.nonempty_subsequences
|
521
|
+
List[]
|
522
|
+
end
|
523
|
+
|
524
|
+
def nonempty_subsequences
|
525
|
+
yss = @tail.nonempty_subsequences.foldr(List[]) { |xs, xss|
|
526
|
+
Cons[xs, Cons[Cons[@head, xs], xss]]
|
527
|
+
}
|
528
|
+
Cons[List[@head], yss]
|
529
|
+
end
|
530
|
+
|
531
|
+
def Nil.[](n)
|
532
|
+
nil
|
533
|
+
end
|
534
|
+
|
535
|
+
def [](n)
|
536
|
+
if n < 0
|
537
|
+
nil
|
538
|
+
elsif n == 0
|
539
|
+
head
|
540
|
+
else
|
541
|
+
tail[n - 1]
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
395
545
|
def Nil.take(n)
|
396
546
|
Nil
|
397
547
|
end
|
@@ -451,5 +601,25 @@ module Immutable
|
|
451
601
|
@tail.find(&block)
|
452
602
|
end
|
453
603
|
end
|
604
|
+
|
605
|
+
def Nil.zip(*xss)
|
606
|
+
Nil
|
607
|
+
end
|
608
|
+
|
609
|
+
def zip(*xss)
|
610
|
+
heads = xss.map { |xs| xs.null? ? nil : xs.head }
|
611
|
+
tails = xss.map { |xs| xs.null? ? Nil : xs.tail }
|
612
|
+
Cons[[head, *heads], tail.zip(*tails)]
|
613
|
+
end
|
614
|
+
|
615
|
+
def Nil.zip_with(*xss, &block)
|
616
|
+
Nil
|
617
|
+
end
|
618
|
+
|
619
|
+
def zip_with(*xss, &block)
|
620
|
+
heads = xss.map { |xs| xs.null? ? nil : xs.head }
|
621
|
+
tails = xss.map { |xs| xs.null? ? Nil : xs.tail }
|
622
|
+
Cons[yield(head, *heads), tail.zip_with(*tails, &block)]
|
623
|
+
end
|
454
624
|
end
|
455
625
|
end
|