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
@@ -0,0 +1,196 @@
|
|
1
|
+
require_relative "stream"
|
2
|
+
require_relative "consable"
|
3
|
+
|
4
|
+
module Immutable
|
5
|
+
# +Immutable::Deque+ is an implementation of real-time deques described in
|
6
|
+
# "Purely Functional Data Structures" by Chris Okasaki.
|
7
|
+
class Deque
|
8
|
+
include Consable
|
9
|
+
|
10
|
+
# +Deque.new+ is for internal use only. Use {Deque.empty} or {Deque.[]}
|
11
|
+
# instead.
|
12
|
+
def initialize(front, front_len, front_schedule,
|
13
|
+
rear, rear_len, rear_schedule)
|
14
|
+
@front = front
|
15
|
+
@front_len = front_len
|
16
|
+
@front_schedule = front_schedule
|
17
|
+
@rear = rear
|
18
|
+
@rear_len = rear_len
|
19
|
+
@rear_schedule = rear_schedule
|
20
|
+
@c = 3 # @c should be 2 or 3
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an empty deque.
|
24
|
+
#
|
25
|
+
# @return [Deque] the empty deque.
|
26
|
+
def self.empty
|
27
|
+
Deque.new(Stream.empty, 0, Stream.empty,
|
28
|
+
Stream.empty, 0, Stream.empty)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creates a new deque populated with the given objects.
|
32
|
+
#
|
33
|
+
# @param [Array<Object>] elements the elements of the deque.
|
34
|
+
# @return [Deque] the new deque.
|
35
|
+
def self.[](*elements)
|
36
|
+
elements.inject(empty, &:snoc)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns whether +self+ is empty.
|
40
|
+
#
|
41
|
+
# @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
|
42
|
+
def empty?
|
43
|
+
length == 0
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the number of elements in +self+. May be zero.
|
47
|
+
#
|
48
|
+
# @return [Integer] the number of elements in +self+.
|
49
|
+
def length
|
50
|
+
@front_len + @rear_len
|
51
|
+
end
|
52
|
+
|
53
|
+
# Adds a new element at the head of +self+.
|
54
|
+
#
|
55
|
+
# @param [Object] x the element to add.
|
56
|
+
# @return [Deque] a new deque.
|
57
|
+
def cons(x)
|
58
|
+
queue(Stream.cons(->{x}, ->{@front}), @front_len + 1,
|
59
|
+
exec1(@front_schedule),
|
60
|
+
@rear, @rear_len, exec1(@rear_schedule))
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the first element of +self+. If +self+ is empty,
|
64
|
+
# +Immutable::EmptyError+ is raised.
|
65
|
+
#
|
66
|
+
# @return [Object] the first element of +self+.
|
67
|
+
def head
|
68
|
+
if @front.empty?
|
69
|
+
if @rear.empty?
|
70
|
+
raise EmptyError
|
71
|
+
else
|
72
|
+
@rear.head
|
73
|
+
end
|
74
|
+
else
|
75
|
+
@front.head
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns the elements after the head of +self+. If +self+ is empty,
|
80
|
+
# +Immutable::EmptyError+ is raised.
|
81
|
+
#
|
82
|
+
# @return [Deque] the elements after the head of +self+.
|
83
|
+
def tail
|
84
|
+
if @front.empty?
|
85
|
+
if @rear.empty?
|
86
|
+
raise EmptyError
|
87
|
+
else
|
88
|
+
self.class.empty
|
89
|
+
end
|
90
|
+
else
|
91
|
+
queue(@front.tail, @front_len - 1, exec2(@front_schedule),
|
92
|
+
@rear, @rear_len, exec2(@rear_schedule))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Adds a new element at the end of +self+.
|
97
|
+
#
|
98
|
+
# @param [Object] x the element to add.
|
99
|
+
# @return [Deque] a new queue.
|
100
|
+
def snoc(x)
|
101
|
+
queue(@front, @front_len, exec1(@front_schedule),
|
102
|
+
Stream.cons(->{x}, ->{@rear}), @rear_len + 1,
|
103
|
+
exec1(@rear_schedule))
|
104
|
+
end
|
105
|
+
|
106
|
+
alias push snoc
|
107
|
+
|
108
|
+
# Returns the last element of +self+. If +self+ is empty,
|
109
|
+
# +Immutable::EmptyError+ is raised.
|
110
|
+
#
|
111
|
+
# @return [Object] the last element of +self+.
|
112
|
+
def last
|
113
|
+
if @rear.empty?
|
114
|
+
if @front.empty?
|
115
|
+
raise EmptyError
|
116
|
+
else
|
117
|
+
@front.head
|
118
|
+
end
|
119
|
+
else
|
120
|
+
@rear.head
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns all the elements of +self+ except the last one.
|
125
|
+
# If +self+ is empty, +Immutable::EmptyError+ is
|
126
|
+
# raised.
|
127
|
+
#
|
128
|
+
# @return [Deque] the elements of +self+ except the last one.
|
129
|
+
def init
|
130
|
+
if @rear.empty?
|
131
|
+
if @front.empty?
|
132
|
+
raise EmptyError
|
133
|
+
else
|
134
|
+
self.class.empty
|
135
|
+
end
|
136
|
+
else
|
137
|
+
queue(@front, @front_len, exec2(@front_schedule),
|
138
|
+
@rear.tail, @rear_len - 1, exec2(@rear_schedule))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
alias pop init
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def exec1(s)
|
147
|
+
if s.empty?
|
148
|
+
s
|
149
|
+
else
|
150
|
+
s.tail
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def exec2(s)
|
155
|
+
exec1(exec1(s))
|
156
|
+
end
|
157
|
+
|
158
|
+
def rotate_rev(r, f, a)
|
159
|
+
if r.empty?
|
160
|
+
f.reverse + a
|
161
|
+
else
|
162
|
+
Stream.cons(->{r.head},
|
163
|
+
->{rotate_rev(r.tail, f.drop(@c),
|
164
|
+
f.take(@c).reverse + a)})
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def rotate_drop(r, i, f)
|
169
|
+
if i < @c
|
170
|
+
rotate_rev(r, f.drop(i), Stream.empty)
|
171
|
+
else
|
172
|
+
x = r.head
|
173
|
+
r2 = r.tail
|
174
|
+
Stream.cons(->{x}, ->{rotate_drop(r2, i - @c, f.drop(@c))})
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def queue(f, f_len, f_schedule, r, r_len, r_schedule)
|
179
|
+
if f_len > @c * r_len + 1
|
180
|
+
i = (f_len + r_len) / 2
|
181
|
+
j = (f_len + r_len) - i
|
182
|
+
f2 = f.take(i)
|
183
|
+
r2 = rotate_drop(r, i, f)
|
184
|
+
self.class.new(f2, i, f2, r2, j, r2)
|
185
|
+
elsif r_len > @c * f_len + 1
|
186
|
+
j = (f_len + r_len) / 2
|
187
|
+
i = (f_len + r_len) - j
|
188
|
+
f2 = rotate_drop(f, j, r)
|
189
|
+
r2 = r.take(j)
|
190
|
+
self.class.new(f2, i, f2, r2, j, r2)
|
191
|
+
else
|
192
|
+
self.class.new(f, f_len, f_schedule, r, r_len, r_schedule)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
data/lib/immutable/foldable.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
module Immutable
|
2
2
|
module Foldable
|
3
|
+
# Reduces +self+ using +block+ from left to right. +e+ is used as the
|
4
|
+
# starting value. A class including +Immutable::Foldable+ must implement
|
5
|
+
# this method.
|
6
|
+
#
|
7
|
+
# @param [Object] e the start value.
|
8
|
+
# @return [Object] the reduced value.
|
9
|
+
def foldl(e, &block)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
3
13
|
# Returns the number of elements in +self+. May be zero.
|
4
14
|
#
|
5
15
|
# @return [Integer] the number of elements in +self+.
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require_relative "foldable"
|
2
|
+
|
3
|
+
module Immutable
|
4
|
+
class EmptyError < StandardError
|
5
|
+
def initialize(msg = "collection is empty")
|
6
|
+
super(msg)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Headable
|
11
|
+
include Enumerable
|
12
|
+
include Foldable
|
13
|
+
|
14
|
+
# Returns the first element of +self+. If +self+ is empty,
|
15
|
+
# +Immutable::EmptyError+ is raised. A class including
|
16
|
+
# +Immutable::Headable+ must implement this method.
|
17
|
+
#
|
18
|
+
# @return [Object] the first element of +self+.
|
19
|
+
def head
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
# Same as {#head}. This method just calls {#head}.
|
24
|
+
#
|
25
|
+
# @return [Object] the first element of +self+.
|
26
|
+
def first
|
27
|
+
head
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the elements after the head of +self+. If +self+ is empty,
|
31
|
+
# +Immutable::EmptyError+ is raised. A class including
|
32
|
+
# +Immutable::Headable+ must implement this method.
|
33
|
+
#
|
34
|
+
# @return [Headable] the elements after the head of +self+.
|
35
|
+
def tail
|
36
|
+
raise NotImplementedError
|
37
|
+
end
|
38
|
+
|
39
|
+
# Same as {#tail}. This method just calls {#tail}.
|
40
|
+
#
|
41
|
+
# @return [Headable] the elements after the head of +self+.
|
42
|
+
def shift
|
43
|
+
tail
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns whether +self+ is empty. A class including
|
47
|
+
# +Immutable::Headable+ must implement this method.
|
48
|
+
#
|
49
|
+
# @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
|
50
|
+
def empty?
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
# Same as {#empty?}. This method just calls {#empty?}.
|
55
|
+
#
|
56
|
+
# @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
|
57
|
+
def null?
|
58
|
+
empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns a string containing a human-readable representation of +self+.
|
62
|
+
#
|
63
|
+
# @return [String] a string representation of +self+.
|
64
|
+
def inspect
|
65
|
+
if empty?
|
66
|
+
class_name + "[]"
|
67
|
+
else
|
68
|
+
class_name + "[" + head.inspect +
|
69
|
+
tail.foldl("") {|x, y| x + ", " + y.inspect } + "]"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns whether +self+ equals to +x+.
|
74
|
+
#
|
75
|
+
# @param [Object] x the object to compare.
|
76
|
+
# @return [true, false] +true+ if +self+ equals to +x+; otherwise,
|
77
|
+
# +false+.
|
78
|
+
def ==(x)
|
79
|
+
if !x.is_a?(self.class)
|
80
|
+
false
|
81
|
+
else
|
82
|
+
if empty?
|
83
|
+
x.empty?
|
84
|
+
else
|
85
|
+
!x.empty? && head == x.head && tail == x.tail
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def eql?(x)
|
91
|
+
if !x.is_a?(self.class)
|
92
|
+
false
|
93
|
+
else
|
94
|
+
if empty?
|
95
|
+
x.empty?
|
96
|
+
else
|
97
|
+
!x.empty? && head.eql?(x.head) && tail.eql?(x.tail)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# @return [Integer]
|
103
|
+
def hash
|
104
|
+
to_a.hash
|
105
|
+
end
|
106
|
+
|
107
|
+
# Calls +block+ once for each element in +self+.
|
108
|
+
# @yield [element]
|
109
|
+
# @yieldreturn [self]
|
110
|
+
# @return [Enumerator]
|
111
|
+
def each(&block)
|
112
|
+
return to_enum unless block_given?
|
113
|
+
|
114
|
+
unless empty?
|
115
|
+
yield(head)
|
116
|
+
tail.each(&block)
|
117
|
+
end
|
118
|
+
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
# Reduces +self+ using +block+ from right to left. +e+ is used as the
|
123
|
+
# starting value. For example:
|
124
|
+
#
|
125
|
+
# List[1, 2, 3].foldr(9) { |x, y| x + y } #=> 1 - (2 - (3 - 9)) = -7
|
126
|
+
#
|
127
|
+
# @param [Object] e the start value.
|
128
|
+
# @return [Object] the reduced value.
|
129
|
+
def foldr(e, &block)
|
130
|
+
if empty?
|
131
|
+
e
|
132
|
+
else
|
133
|
+
yield(head, tail.foldr(e, &block))
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Reduces +self+ using +block+ from right to left. If +self+ is empty,
|
138
|
+
# +Immutable::EmptyError+ is raised.
|
139
|
+
#
|
140
|
+
# @return [Object] the reduced value.
|
141
|
+
def foldr1(&block)
|
142
|
+
if empty?
|
143
|
+
raise EmptyError
|
144
|
+
else
|
145
|
+
if tail.empty?
|
146
|
+
head
|
147
|
+
else
|
148
|
+
yield(head, tail.foldr1(&block))
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Reduces +self+ using +block+ from left to right. +e+ is used as the
|
154
|
+
# starting value. For example:
|
155
|
+
#
|
156
|
+
# List[1, 2, 3].foldl(9) { |x, y| x + y } #=> ((9 - 1) - 2) - 3 = 3
|
157
|
+
#
|
158
|
+
# @param [Object] e the start value.
|
159
|
+
# @return [Object] the reduced value.
|
160
|
+
def foldl(e, &block)
|
161
|
+
if empty?
|
162
|
+
e
|
163
|
+
else
|
164
|
+
tail.foldl(yield(e, head), &block)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Reduces +self+ using +block+ from left to right. If +self+ is empty,
|
169
|
+
# +Immutable::EmptyError+ is raised.
|
170
|
+
#
|
171
|
+
# @return [Object] the reduced value.
|
172
|
+
def foldl1(&block)
|
173
|
+
if empty?
|
174
|
+
raise EmptyError
|
175
|
+
else
|
176
|
+
tail.foldl(head, &block)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Returns the first element in +self+ for which the given block
|
181
|
+
# evaluates to true. If such an element is not found, it
|
182
|
+
# returns +nil+.
|
183
|
+
#
|
184
|
+
# @param [#call, nil] ifnone
|
185
|
+
# @yield [element]
|
186
|
+
# @yieldreturn [Object] the found element.
|
187
|
+
# @return [Enumerator]
|
188
|
+
def find(ifnone=nil, &block)
|
189
|
+
return to_enum(__callee__, ifnone) unless block_given?
|
190
|
+
|
191
|
+
if empty?
|
192
|
+
if ifnone.nil?
|
193
|
+
nil
|
194
|
+
else
|
195
|
+
ifnone.call
|
196
|
+
end
|
197
|
+
else
|
198
|
+
if yield(head)
|
199
|
+
head
|
200
|
+
else
|
201
|
+
tail.find(ifnone, &block)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
alias detect find
|
207
|
+
|
208
|
+
# Returns the +n+th element of +self+. If +n+ is out of range, +nil+ is
|
209
|
+
# returned.
|
210
|
+
#
|
211
|
+
# @return [Object] the +n+th element.
|
212
|
+
def [](n)
|
213
|
+
if n < 0 || empty?
|
214
|
+
nil
|
215
|
+
elsif n == 0
|
216
|
+
head
|
217
|
+
else
|
218
|
+
tail[n - 1]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Converts +self+ to a list.
|
223
|
+
#
|
224
|
+
# @return [List] a list.
|
225
|
+
def to_list
|
226
|
+
foldr(List[]) { |x, xs| Cons[x, xs] }
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
def class_name
|
232
|
+
self.class.name.slice(/[^:]*\z/)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
data/lib/immutable/list.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "consable"
|
2
2
|
|
3
3
|
module Immutable
|
4
4
|
# +Immutable::List+ represents an immutable list.
|
@@ -18,82 +18,33 @@ module Immutable
|
|
18
18
|
# p Nil #=> List[]
|
19
19
|
# p Cons[1, Cons[2, Nil]] #=> List[1, 2]
|
20
20
|
class List
|
21
|
-
include
|
22
|
-
include Foldable
|
21
|
+
include Consable
|
23
22
|
|
24
|
-
|
25
|
-
def initialize(msg = "list is empty")
|
26
|
-
super(msg)
|
27
|
-
end
|
28
|
-
end
|
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)
|
36
|
-
end
|
37
|
-
|
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+.
|
42
|
-
def self.from_array(ary)
|
43
|
-
ary.reverse_each.inject(Nil) { |x, y|
|
44
|
-
Cons.new(y, x)
|
45
|
-
}
|
46
|
-
end
|
47
|
-
|
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+.
|
52
|
-
def self.from_enum(enum)
|
53
|
-
enum.inject(Nil) { |x, y|
|
54
|
-
Cons.new(y, x)
|
55
|
-
}.reverse
|
56
|
-
end
|
57
|
-
|
58
|
-
# Calls +block+ once for each element in +self+.
|
59
|
-
def each(&block)
|
60
|
-
end
|
61
|
-
|
62
|
-
# Appends two lists +self+ and +xs+.
|
23
|
+
# Returns an empty list.
|
63
24
|
#
|
64
|
-
# @
|
65
|
-
|
66
|
-
|
67
|
-
foldr(xs) { |y, ys| Cons[y, ys] }
|
25
|
+
# @return [list] the empty list.
|
26
|
+
def self.empty
|
27
|
+
Nil
|
68
28
|
end
|
69
29
|
|
70
|
-
#
|
30
|
+
# Adds a new element at the head of +self+.
|
71
31
|
#
|
72
|
-
# @param [
|
73
|
-
# @return [
|
74
|
-
|
75
|
-
|
76
|
-
# this method should be overriden
|
32
|
+
# @param [Object] x the element to add.
|
33
|
+
# @return [List] a new list.
|
34
|
+
def cons(x)
|
35
|
+
Cons[x, self]
|
77
36
|
end
|
78
37
|
|
79
38
|
# Returns the first element of +self+. If +self+ is empty,
|
80
|
-
# +Immutable::
|
39
|
+
# +Immutable::EmptyError+ is raised.
|
81
40
|
#
|
82
41
|
# @return [Object] the first element of +self+.
|
83
42
|
def head
|
84
43
|
# this method should be overriden
|
85
44
|
end
|
86
45
|
|
87
|
-
# Returns the first element of +self+. If +self+ is empty,
|
88
|
-
# +Immutable::List::EmptyError+ is raised.
|
89
|
-
#
|
90
|
-
# @return [Object] the first element of +self+.
|
91
|
-
def first
|
92
|
-
head
|
93
|
-
end
|
94
|
-
|
95
46
|
# Returns the last element of +self+. If +self+ is empty,
|
96
|
-
# +Immutable::
|
47
|
+
# +Immutable::EmptyError+ is raised.
|
97
48
|
#
|
98
49
|
# @return [Object] the last element of +self+.
|
99
50
|
def last
|
@@ -101,7 +52,7 @@ module Immutable
|
|
101
52
|
end
|
102
53
|
|
103
54
|
# Returns the elements after the head of +self+. If +self+ is empty,
|
104
|
-
# +Immutable::
|
55
|
+
# +Immutable::EmptyError+ is raised.
|
105
56
|
#
|
106
57
|
# @return [List] the elements after the head of +self+.
|
107
58
|
def tail
|
@@ -109,7 +60,7 @@ module Immutable
|
|
109
60
|
end
|
110
61
|
|
111
62
|
# Returns all the elements of +self+ except the last one.
|
112
|
-
# If +self+ is empty, +Immutable::
|
63
|
+
# If +self+ is empty, +Immutable::EmptyError+ is
|
113
64
|
# raised.
|
114
65
|
#
|
115
66
|
# @return [List] the elements of +self+ except the last one.
|
@@ -117,193 +68,17 @@ module Immutable
|
|
117
68
|
# this method should be overriden
|
118
69
|
end
|
119
70
|
|
120
|
-
#
|
71
|
+
# Same as {#init}.
|
121
72
|
#
|
122
|
-
# @return [
|
123
|
-
def
|
124
|
-
|
73
|
+
# @return [List] the elements of +self+ except the last one.
|
74
|
+
def pop
|
75
|
+
init
|
125
76
|
end
|
126
77
|
|
127
78
|
# Returns whether +self+ is empty.
|
128
79
|
#
|
129
80
|
# @return [true, false] +true+ if +self+ is empty; otherwise, +false+.
|
130
|
-
def
|
131
|
-
empty?
|
132
|
-
end
|
133
|
-
|
134
|
-
# Returns the list obtained by applying the given block to each element
|
135
|
-
# in +self+.
|
136
|
-
#
|
137
|
-
# @return [List] the obtained list.
|
138
|
-
def map
|
139
|
-
foldr(Nil) { |x, xs| Cons[yield(x), xs] }
|
140
|
-
end
|
141
|
-
|
142
|
-
# Returns the elements of +self+ in reverse order.
|
143
|
-
#
|
144
|
-
# @return [List] the reversed list.
|
145
|
-
def reverse
|
146
|
-
foldl(Nil) { |x, y| Cons[y, x] }
|
147
|
-
end
|
148
|
-
|
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
|
156
|
-
end
|
157
|
-
|
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.
|
165
|
-
def intercalate(xs)
|
166
|
-
intersperse(xs).flatten
|
167
|
-
end
|
168
|
-
|
169
|
-
# Transposes the rows and columns of +self+. For example:
|
170
|
-
#
|
171
|
-
# p List[List[1, 2, 3], List[4, 5, 6]].transpose
|
172
|
-
# #=> List[List[1, 4], List[2, 5], List[3, 6]]
|
173
|
-
#
|
174
|
-
# @return [List] the transposed list.
|
175
|
-
def transpose
|
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]
|
184
|
-
end
|
185
|
-
|
186
|
-
# Reduces +self+ using +block+ from right to left. +e+ is used as the
|
187
|
-
# starting value. For example:
|
188
|
-
#
|
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.
|
193
|
-
def foldr(e, &block)
|
194
|
-
# this method should be overriden
|
195
|
-
end
|
196
|
-
|
197
|
-
# Reduces +self+ using +block+ from right to left. If +self+ is empty,
|
198
|
-
# +Immutable::List::EmptyError+ is raised.
|
199
|
-
#
|
200
|
-
# @return [Object] the reduced value.
|
201
|
-
def foldr1(&block)
|
202
|
-
# this method should be overriden
|
203
|
-
end
|
204
|
-
|
205
|
-
# Reduces +self+ using +block+ from left to right. +e+ is used as the
|
206
|
-
# starting value. For example:
|
207
|
-
#
|
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.
|
212
|
-
def foldl(e, &block)
|
213
|
-
# this method should be overriden
|
214
|
-
end
|
215
|
-
|
216
|
-
# Reduces +self+ using +block+ from left to right. If +self+ is empty,
|
217
|
-
# +Immutable::List::EmptyError+ is raised.
|
218
|
-
#
|
219
|
-
# @return [Object] the reduced value.
|
220
|
-
def foldl1(&block)
|
221
|
-
# this method should be overriden
|
222
|
-
end
|
223
|
-
|
224
|
-
# Concatenates a list of lists.
|
225
|
-
#
|
226
|
-
# @return [List] the concatenated list.
|
227
|
-
def flatten
|
228
|
-
foldr(Nil) { |x, xs| x + xs }
|
229
|
-
end
|
230
|
-
|
231
|
-
alias concat flatten
|
232
|
-
|
233
|
-
# Returns the list obtained by concatenating the results of the given
|
234
|
-
# block for each element in +self+.
|
235
|
-
#
|
236
|
-
# @return [List] the obtained list.
|
237
|
-
def flat_map
|
238
|
-
foldr(Nil) { |x, xs| yield(x) + xs }
|
239
|
-
end
|
240
|
-
|
241
|
-
alias concat_map flat_map
|
242
|
-
|
243
|
-
alias bind flat_map
|
244
|
-
|
245
|
-
# Builds a list from the seed value +e+ and the given block. The block
|
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
|
249
|
-
# seed from which to unfold the tail. For example:
|
250
|
-
#
|
251
|
-
# xs = List.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
|
252
|
-
# p xs #=> List[3, 2, 1]
|
253
|
-
#
|
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.
|
258
|
-
def self.unfoldr(e, &block)
|
259
|
-
x = yield(e)
|
260
|
-
if x.nil?
|
261
|
-
Nil
|
262
|
-
else
|
263
|
-
y, z = x
|
264
|
-
Cons[y, unfoldr(z, &block)]
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
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
|
274
|
-
end
|
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
|
284
|
-
|
285
|
-
# Returns the suffix of +self+ after the first +n+ elements, or
|
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.
|
290
|
-
def drop(n)
|
291
|
-
# this method should be overriden
|
292
|
-
end
|
293
|
-
|
294
|
-
# Returns the longest prefix of the elements of +self+ for which +block+
|
295
|
-
# evaluates to true.
|
296
|
-
#
|
297
|
-
# @return [List] the prefix of the elements of +self+.
|
298
|
-
def take_while(&block)
|
299
|
-
# this method should be overriden
|
300
|
-
end
|
301
|
-
|
302
|
-
# Returns the suffix remaining after
|
303
|
-
# +self.take_while(&block)+.
|
304
|
-
#
|
305
|
-
# @return [List] the suffix of the elements of +self+.
|
306
|
-
def drop_while(&block)
|
81
|
+
def empty?
|
307
82
|
# this method should be overriden
|
308
83
|
end
|
309
84
|
|
@@ -314,49 +89,10 @@ module Immutable
|
|
314
89
|
self
|
315
90
|
end
|
316
91
|
|
317
|
-
|
318
|
-
# evaluates to true. If such an element is not found, it
|
319
|
-
# returns +nil+.
|
320
|
-
#
|
321
|
-
# @return [Object] the found element.
|
322
|
-
def find(&block)
|
323
|
-
# this method should be overriden
|
324
|
-
end
|
325
|
-
|
326
|
-
# Returns the elements in +self+ for which the given block evaluates to
|
327
|
-
# true.
|
328
|
-
#
|
329
|
-
# @return [List] the elements that satisfies the condition.
|
330
|
-
def filter
|
331
|
-
foldr(Nil) { |x, xs|
|
332
|
-
if yield(x)
|
333
|
-
Cons[x, xs]
|
334
|
-
else
|
335
|
-
xs
|
336
|
-
end
|
337
|
-
}
|
338
|
-
end
|
92
|
+
private
|
339
93
|
|
340
|
-
|
341
|
-
|
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
|
94
|
+
def class_name
|
95
|
+
"List"
|
360
96
|
end
|
361
97
|
end
|
362
98
|
|
@@ -421,205 +157,5 @@ module Immutable
|
|
421
157
|
def empty?
|
422
158
|
false
|
423
159
|
end
|
424
|
-
|
425
|
-
def Nil.foldr(e)
|
426
|
-
e
|
427
|
-
end
|
428
|
-
|
429
|
-
def foldr(e, &block)
|
430
|
-
yield(@head, @tail.foldr(e, &block))
|
431
|
-
end
|
432
|
-
|
433
|
-
def Nil.foldr1
|
434
|
-
raise EmptyError
|
435
|
-
end
|
436
|
-
|
437
|
-
def foldr1(&block)
|
438
|
-
if @tail.empty?
|
439
|
-
@head
|
440
|
-
else
|
441
|
-
yield(@head, @tail.foldr1(&block))
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
def Nil.each
|
446
|
-
end
|
447
|
-
|
448
|
-
def each(&block)
|
449
|
-
yield(@head)
|
450
|
-
@tail.each(&block)
|
451
|
-
end
|
452
|
-
|
453
|
-
def Nil.foldl(e)
|
454
|
-
e
|
455
|
-
end
|
456
|
-
|
457
|
-
def foldl(e, &block)
|
458
|
-
@tail.foldl(yield(e, @head), &block)
|
459
|
-
end
|
460
|
-
|
461
|
-
def Nil.foldl1
|
462
|
-
raise EmptyError
|
463
|
-
end
|
464
|
-
|
465
|
-
def foldl1(&block)
|
466
|
-
@tail.foldl(@head, &block)
|
467
|
-
end
|
468
|
-
|
469
|
-
def Nil.==(xs)
|
470
|
-
equal?(xs)
|
471
|
-
end
|
472
|
-
|
473
|
-
def ==(xs)
|
474
|
-
if !xs.is_a?(List) || xs.empty?
|
475
|
-
false
|
476
|
-
else
|
477
|
-
@head == xs.head && @tail == xs.tail
|
478
|
-
end
|
479
|
-
end
|
480
|
-
|
481
|
-
def Nil.inspect
|
482
|
-
"List[]"
|
483
|
-
end
|
484
|
-
|
485
|
-
def inspect
|
486
|
-
"List[" + @head.inspect +
|
487
|
-
@tail.foldl("") {|x, y| x + ", " + y.inspect } + "]"
|
488
|
-
end
|
489
|
-
|
490
|
-
def Nil.intersperse(sep)
|
491
|
-
Nil
|
492
|
-
end
|
493
|
-
|
494
|
-
def intersperse(sep)
|
495
|
-
Cons[@head, @tail.prepend_to_all(sep)]
|
496
|
-
end
|
497
|
-
|
498
|
-
def Nil.prepend_to_all(sep)
|
499
|
-
Nil
|
500
|
-
end
|
501
|
-
|
502
|
-
def prepend_to_all(sep)
|
503
|
-
Cons[sep, Cons[@head, @tail.prepend_to_all(sep)]]
|
504
|
-
end
|
505
|
-
|
506
|
-
def Nil.transpose
|
507
|
-
Nil
|
508
|
-
end
|
509
|
-
|
510
|
-
def transpose
|
511
|
-
if @head == Nil
|
512
|
-
@tail.transpose
|
513
|
-
else
|
514
|
-
tail = @tail.filter { |x| !x.empty? }
|
515
|
-
Cons[Cons[@head.head, tail.map(&:head)],
|
516
|
-
Cons[@head.tail, tail.map(&:tail)].transpose]
|
517
|
-
end
|
518
|
-
end
|
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
|
-
|
545
|
-
def Nil.take(n)
|
546
|
-
Nil
|
547
|
-
end
|
548
|
-
|
549
|
-
def take(n)
|
550
|
-
if n <= 0
|
551
|
-
Nil
|
552
|
-
else
|
553
|
-
Cons[@head, @tail.take(n - 1)]
|
554
|
-
end
|
555
|
-
end
|
556
|
-
|
557
|
-
def Nil.take_while
|
558
|
-
Nil
|
559
|
-
end
|
560
|
-
|
561
|
-
def take_while(&block)
|
562
|
-
if yield(@head)
|
563
|
-
Cons[@head, @tail.take_while(&block)]
|
564
|
-
else
|
565
|
-
Nil
|
566
|
-
end
|
567
|
-
end
|
568
|
-
|
569
|
-
def Nil.drop(n)
|
570
|
-
Nil
|
571
|
-
end
|
572
|
-
|
573
|
-
def drop(n)
|
574
|
-
if n > 0
|
575
|
-
@tail.drop(n - 1)
|
576
|
-
else
|
577
|
-
self
|
578
|
-
end
|
579
|
-
end
|
580
|
-
|
581
|
-
def Nil.drop_while
|
582
|
-
Nil
|
583
|
-
end
|
584
|
-
|
585
|
-
def drop_while(&block)
|
586
|
-
if yield(@head)
|
587
|
-
@tail.drop_while(&block)
|
588
|
-
else
|
589
|
-
self
|
590
|
-
end
|
591
|
-
end
|
592
|
-
|
593
|
-
def Nil.find
|
594
|
-
nil
|
595
|
-
end
|
596
|
-
|
597
|
-
def find(&block)
|
598
|
-
if yield(@head)
|
599
|
-
@head
|
600
|
-
else
|
601
|
-
@tail.find(&block)
|
602
|
-
end
|
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
|
624
160
|
end
|
625
161
|
end
|