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,116 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
with_tailcall_optimization {
|
4
|
+
require_relative "../../lib/immutable/deque"
|
5
|
+
}
|
6
|
+
|
7
|
+
module Immutable
|
8
|
+
class TestDeque < Test::Unit::TestCase
|
9
|
+
def test_head
|
10
|
+
assert_raise(EmptyError) do
|
11
|
+
Deque[].head
|
12
|
+
end
|
13
|
+
assert_equal(1, Deque[1].head)
|
14
|
+
assert_equal(1, Deque[1, 2, 3].head)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_last
|
18
|
+
assert_raise(EmptyError) do
|
19
|
+
Deque[].last
|
20
|
+
end
|
21
|
+
assert_equal(1, Deque[1].last)
|
22
|
+
assert_equal(3, Deque[1, 2, 3].last)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_tail
|
26
|
+
assert_raise(EmptyError) do
|
27
|
+
Deque[].tail
|
28
|
+
end
|
29
|
+
assert(Deque[1].tail.empty?)
|
30
|
+
assert_equal(2, Deque[1, 2].tail.head)
|
31
|
+
assert_equal(2, Deque[1, 2, 3].tail.head)
|
32
|
+
assert_equal(3, Deque[1, 2, 3].tail.tail.head)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_init
|
36
|
+
assert_raise(EmptyError) do
|
37
|
+
Deque[].init
|
38
|
+
end
|
39
|
+
assert(Deque[1].init.empty?)
|
40
|
+
assert_equal(1, Deque[1, 2].init.last)
|
41
|
+
assert_equal(2, Deque[1, 2, 3].init.last)
|
42
|
+
assert_equal(1, Deque[1, 2, 3].init.init.last)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_cons
|
46
|
+
q1 = Deque.empty.cons(1)
|
47
|
+
assert_equal(1, q1.head)
|
48
|
+
assert(q1.tail.empty?)
|
49
|
+
q2 = q1.cons(2)
|
50
|
+
assert_equal(2, q2.head)
|
51
|
+
assert_equal(1, q2.tail.head)
|
52
|
+
assert(q2.tail.tail.empty?)
|
53
|
+
assert_equal(1, q1.head)
|
54
|
+
assert(q1.tail.empty?)
|
55
|
+
|
56
|
+
a = (1..1000).to_a.shuffle
|
57
|
+
q = a.inject(Deque.empty, :cons)
|
58
|
+
assert_equal(a.reverse, q.to_a)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_snoc
|
62
|
+
q1 = Deque.empty.snoc(1)
|
63
|
+
assert_equal(1, q1.head)
|
64
|
+
assert(q1.tail.empty?)
|
65
|
+
q2 = q1.snoc(2)
|
66
|
+
assert_equal(1, q2.head)
|
67
|
+
assert_equal(2, q2.tail.head)
|
68
|
+
assert(q2.tail.tail.empty?)
|
69
|
+
assert_equal(1, q1.head)
|
70
|
+
assert(q1.tail.empty?)
|
71
|
+
|
72
|
+
a = (1..1000).to_a.shuffle
|
73
|
+
q = a.inject(Deque.empty, :snoc)
|
74
|
+
assert_equal(a, q.to_a)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_invariants
|
78
|
+
a = (1..1000).to_a.shuffle
|
79
|
+
deque = a.inject(Deque.empty) { |d, i|
|
80
|
+
assert_deque_invariants(d)
|
81
|
+
if rand(2) == 0
|
82
|
+
d2 = d.snoc(i)
|
83
|
+
else
|
84
|
+
d2 = d.cons(i)
|
85
|
+
end
|
86
|
+
case rand(4)
|
87
|
+
when 0
|
88
|
+
d2.tail
|
89
|
+
when 1
|
90
|
+
d2.init
|
91
|
+
else
|
92
|
+
d2
|
93
|
+
end
|
94
|
+
}
|
95
|
+
assert_deque_invariants(deque)
|
96
|
+
until deque.empty?
|
97
|
+
if rand(2) == 0
|
98
|
+
deque = deque.tail
|
99
|
+
else
|
100
|
+
deque = deque.init
|
101
|
+
end
|
102
|
+
assert_deque_invariants(deque)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def assert_deque_invariants(d)
|
109
|
+
c = d.instance_variable_get(:@c)
|
110
|
+
front_len = d.instance_variable_get(:@front_len)
|
111
|
+
rear_len = d.instance_variable_get(:@rear_len)
|
112
|
+
assert(front_len <= c * rear_len + 1)
|
113
|
+
assert(rear_len <= c * front_len + 1)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/test/immutable/test_list.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require_relative "../test_helper"
|
2
2
|
|
3
3
|
with_tailcall_optimization {
|
4
|
-
|
4
|
+
require_relative "../../lib/immutable/list"
|
5
5
|
}
|
6
6
|
|
7
7
|
module Immutable
|
@@ -18,7 +18,7 @@ module Immutable
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_head
|
21
|
-
assert_raise(
|
21
|
+
assert_raise(EmptyError) do
|
22
22
|
List[].head
|
23
23
|
end
|
24
24
|
assert_equal(1, List[1].head)
|
@@ -26,7 +26,7 @@ module Immutable
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_last
|
29
|
-
assert_raise(
|
29
|
+
assert_raise(EmptyError) do
|
30
30
|
List[].last
|
31
31
|
end
|
32
32
|
assert_equal(1, List[1].last)
|
@@ -34,7 +34,7 @@ module Immutable
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def test_tail
|
37
|
-
assert_raise(
|
37
|
+
assert_raise(EmptyError) do
|
38
38
|
List[].tail
|
39
39
|
end
|
40
40
|
assert_equal(List[], List[1].tail)
|
@@ -42,7 +42,7 @@ module Immutable
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def test_init
|
45
|
-
assert_raise(
|
45
|
+
assert_raise(EmptyError) do
|
46
46
|
List[].init
|
47
47
|
end
|
48
48
|
assert_equal(List[], List[1].init)
|
@@ -56,13 +56,24 @@ module Immutable
|
|
56
56
|
end
|
57
57
|
|
58
58
|
def test_each
|
59
|
+
list = List[]
|
59
60
|
a = []
|
60
|
-
|
61
|
+
assert_same(list, list.each { |x| a << x })
|
61
62
|
assert_equal([], a)
|
62
|
-
|
63
|
+
|
64
|
+
list = List[1, 2, 3]
|
63
65
|
a = []
|
64
|
-
|
66
|
+
assert_same(list, list.each { |x| a << x })
|
65
67
|
assert_equal([1, 2, 3], a)
|
68
|
+
|
69
|
+
enum = List[1, 2, 3].each
|
70
|
+
assert_instance_of(Enumerator, enum)
|
71
|
+
assert_equal(1, enum.next)
|
72
|
+
assert_equal(2, enum.next)
|
73
|
+
assert_equal(3, enum.next)
|
74
|
+
assert_raise(StopIteration) do
|
75
|
+
enum.next
|
76
|
+
end
|
66
77
|
end
|
67
78
|
|
68
79
|
def test_foldr
|
@@ -75,7 +86,7 @@ module Immutable
|
|
75
86
|
end
|
76
87
|
|
77
88
|
def test_foldr1
|
78
|
-
assert_raise(
|
89
|
+
assert_raise(EmptyError) do
|
79
90
|
List[].foldr1(&:+)
|
80
91
|
end
|
81
92
|
assert_equal(1, List[1].foldr1(&:+))
|
@@ -94,7 +105,7 @@ module Immutable
|
|
94
105
|
end
|
95
106
|
|
96
107
|
def test_foldl1
|
97
|
-
assert_raise(
|
108
|
+
assert_raise(EmptyError) do
|
98
109
|
List[].foldl1(&:+)
|
99
110
|
end
|
100
111
|
assert_equal(1, List[1].foldl1(&:+))
|
@@ -110,6 +121,7 @@ module Immutable
|
|
110
121
|
assert(List[1] == List[1])
|
111
122
|
assert(List[1] != List[2])
|
112
123
|
assert(List[1] != [1])
|
124
|
+
assert(List[1] == List[1.0])
|
113
125
|
assert(List["foo"] == List["foo"])
|
114
126
|
assert(List["foo"] != List["bar"])
|
115
127
|
assert(List[1, 2, 3] == List[1, 2, 3])
|
@@ -119,6 +131,20 @@ module Immutable
|
|
119
131
|
assert(List[List[1, 2], List[3, 4]] != List[List[1, 2], List[3]])
|
120
132
|
end
|
121
133
|
|
134
|
+
def test_eql
|
135
|
+
assert(List[].eql? List[])
|
136
|
+
assert(List[1].eql? List[1])
|
137
|
+
assert_same(List[1].eql?(List[1.0]), false)
|
138
|
+
assert(List["foo"].eql? List["foo"])
|
139
|
+
assert(List[1, 2, 3].eql? List[1, 2, 3])
|
140
|
+
assert(List[List[1, 2], List[3, 4]].eql? List[List[1, 2], List[3, 4]])
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_hash_key
|
144
|
+
assert_same({List[1] => true}[List[1.0]], nil)
|
145
|
+
assert_same({List[1] => true}[List[1]], true)
|
146
|
+
end
|
147
|
+
|
122
148
|
def test_inspect
|
123
149
|
assert_equal('List[]', List[].inspect)
|
124
150
|
assert_equal('List[1]', List[1].inspect)
|
@@ -135,6 +161,15 @@ module Immutable
|
|
135
161
|
assert_equal(100, List[*(1..100)].length)
|
136
162
|
end
|
137
163
|
|
164
|
+
def test_cons
|
165
|
+
assert_equal(List[1], List[].cons(1))
|
166
|
+
assert_equal(List[1, 2, 3], List[2, 3].cons(1))
|
167
|
+
xs = List[1, 2, 3]
|
168
|
+
ys = xs.cons(0)
|
169
|
+
assert_equal(List[1, 2, 3], xs)
|
170
|
+
assert_equal(List[0, 1, 2, 3], ys)
|
171
|
+
end
|
172
|
+
|
138
173
|
def test_plus
|
139
174
|
assert_equal(List[], List[] + List[])
|
140
175
|
assert_equal(List[1, 2, 3], List[] + List[1, 2, 3])
|
@@ -156,6 +191,11 @@ module Immutable
|
|
156
191
|
assert_equal(List["1", "2", "3"], List[1, 2, 3].map(&:to_s))
|
157
192
|
end
|
158
193
|
|
194
|
+
def test_rev_map
|
195
|
+
assert_equal(List[], List[].rev_map(&:to_s))
|
196
|
+
assert_equal(List["3", "2", "1"], List[1, 2, 3].rev_map(&:to_s))
|
197
|
+
end
|
198
|
+
|
159
199
|
def test_flat_map
|
160
200
|
assert_equal(List[], List[].flat_map {})
|
161
201
|
assert_equal(List["1", "2", "3"], List[1, 2, 3].map(&:to_s))
|
@@ -165,6 +205,14 @@ module Immutable
|
|
165
205
|
assert_equal(nil, List[].find(&:odd?))
|
166
206
|
assert_equal(1, List[1, 2, 3, 4, 5].find(&:odd?))
|
167
207
|
assert_equal(2, List[1, 2, 3, 4, 5].find(&:even?))
|
208
|
+
assert_equal(nil, List[1, 2, 3, 4, 5].find{|elm|elm < 0})
|
209
|
+
assert_raise(ArgumentError) do
|
210
|
+
List[1, 2, 3, 4, 5].find(->{raise ArgumentError}){|elm|elm < 0}
|
211
|
+
end
|
212
|
+
|
213
|
+
enum = List[1, 2, 3, 4, 5].find
|
214
|
+
assert_instance_of(Enumerator, enum)
|
215
|
+
assert_equal(2, enum.each(&:even?))
|
168
216
|
end
|
169
217
|
|
170
218
|
def test_filter
|
@@ -268,9 +316,12 @@ module Immutable
|
|
268
316
|
assert_equal(24, List[1, 2, 3, 4].product)
|
269
317
|
end
|
270
318
|
|
271
|
-
def
|
319
|
+
def test_unfoldr
|
272
320
|
xs = List.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
|
273
321
|
assert_equal(List[3, 2, 1], xs)
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_s_unfoldr
|
274
325
|
xs = List.unfoldr("foo,bar,baz") { |x|
|
275
326
|
if x.empty?
|
276
327
|
nil
|
data/test/immutable/test_map.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require_relative "../test_helper"
|
2
2
|
|
3
3
|
with_tailcall_optimization {
|
4
|
-
|
4
|
+
require_relative "../../lib/immutable/list"
|
5
|
+
require_relative "../../lib/immutable/map"
|
5
6
|
}
|
6
7
|
|
7
8
|
module Immutable
|
@@ -97,16 +98,6 @@ module Immutable
|
|
97
98
|
assert_equal(List[[:a, 1], [:b, 2], [:c, 3]], xs)
|
98
99
|
end
|
99
100
|
|
100
|
-
def test_foldl_with_key
|
101
|
-
xs = Map[].foldl_with_key(List[]) { |ys, k, v| Cons[[k, v], ys] }
|
102
|
-
assert_equal(List[], xs)
|
103
|
-
|
104
|
-
xs = Map[a: 1, c: 3, b: 2].foldl_with_key(List[]) { |ys, k, v|
|
105
|
-
Cons[[k, v], ys]
|
106
|
-
}
|
107
|
-
assert_equal(List[[:c, 3], [:b, 2], [:a, 1]], xs)
|
108
|
-
end
|
109
|
-
|
110
101
|
def test_map
|
111
102
|
xs = Map[].map { |v| v.to_s }
|
112
103
|
assert_equal(List[], xs)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
require_relative "../test_helper"
|
2
2
|
|
3
3
|
with_tailcall_optimization {
|
4
|
-
|
4
|
+
require_relative "../../lib/immutable/queue"
|
5
5
|
}
|
6
6
|
|
7
7
|
module Immutable
|
8
|
-
class
|
8
|
+
class TestQueue < Test::Unit::TestCase
|
9
9
|
def test_head
|
10
|
-
assert_raise(
|
10
|
+
assert_raise(EmptyError) do
|
11
11
|
Queue[].head
|
12
12
|
end
|
13
13
|
assert_equal(1, Queue[1].head)
|
@@ -15,7 +15,7 @@ module Immutable
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_tail
|
18
|
-
assert_raise(
|
18
|
+
assert_raise(EmptyError) do
|
19
19
|
Queue[].tail
|
20
20
|
end
|
21
21
|
assert(Queue[1].tail.empty?)
|
@@ -39,5 +39,32 @@ module Immutable
|
|
39
39
|
q = a.inject(Queue.empty, :snoc)
|
40
40
|
assert_equal(a, q.to_a)
|
41
41
|
end
|
42
|
+
|
43
|
+
def test_invariant
|
44
|
+
a = (1..100).to_a.shuffle
|
45
|
+
queue = a.inject(Queue.empty) { |q, i|
|
46
|
+
assert_queue_invariant(q)
|
47
|
+
q2 = q.snoc(i)
|
48
|
+
if rand(3) == 0
|
49
|
+
q2.tail
|
50
|
+
else
|
51
|
+
q2
|
52
|
+
end
|
53
|
+
}
|
54
|
+
assert_queue_invariant(queue)
|
55
|
+
until queue.empty?
|
56
|
+
queue = queue.tail
|
57
|
+
assert_queue_invariant(queue)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def assert_queue_invariant(d)
|
64
|
+
front = d.instance_variable_get(:@front)
|
65
|
+
rear = d.instance_variable_get(:@rear)
|
66
|
+
schedule = d.instance_variable_get(:@schedule)
|
67
|
+
assert_equal(schedule.length, front.length - rear.length)
|
68
|
+
end
|
42
69
|
end
|
43
70
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require_relative "../test_helper"
|
2
2
|
|
3
3
|
with_tailcall_optimization {
|
4
|
-
|
4
|
+
require_relative "../../lib/immutable/stream"
|
5
5
|
}
|
6
6
|
|
7
7
|
module Immutable
|
@@ -33,38 +33,40 @@ module Immutable
|
|
33
33
|
assert_equal(Stream[1, 3, 5], Stream.from(1, 2).take(3))
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
37
|
-
assert_equal(Stream[1], Stream.
|
38
|
-
assert_equal(Stream[1, 2], Stream.
|
36
|
+
def test_cons
|
37
|
+
assert_equal(Stream[1], Stream.empty.cons(1))
|
38
|
+
assert_equal(Stream[1, 2], Stream.empty.cons(2).cons(1))
|
39
|
+
assert_equal(Stream[1], Stream.empty.cons { 1 })
|
40
|
+
assert_equal(Stream[1, 2], Stream.empty.cons { 2 }.cons { 1 })
|
39
41
|
end
|
40
42
|
|
41
43
|
def test_head
|
42
|
-
assert_raise(
|
43
|
-
Stream.
|
44
|
+
assert_raise(EmptyError) do
|
45
|
+
Stream.empty.head
|
44
46
|
end
|
45
47
|
assert_equal(1, Stream[1].head)
|
46
48
|
assert_equal(1, Stream[1, 2, 3].head)
|
47
49
|
end
|
48
50
|
|
49
51
|
def test_tail
|
50
|
-
assert_raise(
|
51
|
-
Stream.
|
52
|
+
assert_raise(EmptyError) do
|
53
|
+
Stream.empty.tail
|
52
54
|
end
|
53
|
-
assert(Stream[1].tail.
|
55
|
+
assert(Stream[1].tail.empty?)
|
54
56
|
assert_equal([2, 3], Stream[1, 2, 3].tail.to_a)
|
55
57
|
end
|
56
58
|
|
57
59
|
def test_last
|
58
|
-
assert_raise(
|
59
|
-
Stream.
|
60
|
+
assert_raise(EmptyError) do
|
61
|
+
Stream.empty.last
|
60
62
|
end
|
61
63
|
assert_equal(1, Stream[1].last)
|
62
64
|
assert_equal(3, Stream[1, 2, 3].last)
|
63
65
|
end
|
64
66
|
|
65
67
|
def test_init
|
66
|
-
assert_raise(
|
67
|
-
Stream.
|
68
|
+
assert_raise(EmptyError) do
|
69
|
+
Stream.empty.init.force
|
68
70
|
end
|
69
71
|
assert_equal(Stream[], Stream[1].init)
|
70
72
|
assert_equal(Stream[1], Stream[1, 2].init)
|
@@ -108,7 +110,7 @@ module Immutable
|
|
108
110
|
end
|
109
111
|
|
110
112
|
def test_foldr1
|
111
|
-
assert_raise(
|
113
|
+
assert_raise(EmptyError) do
|
112
114
|
Stream[].foldr1(&:+)
|
113
115
|
end
|
114
116
|
assert_equal(1, Stream[1].foldr1(&:+))
|
@@ -127,7 +129,7 @@ module Immutable
|
|
127
129
|
end
|
128
130
|
|
129
131
|
def test_foldl1
|
130
|
-
assert_raise(
|
132
|
+
assert_raise(EmptyError) do
|
131
133
|
Stream[].foldl1(&:+)
|
132
134
|
end
|
133
135
|
assert_equal(1, Stream[1].foldl1(&:+))
|
@@ -307,9 +309,12 @@ module Immutable
|
|
307
309
|
assert_equal(Stream[], Stream[1, 2, 3].drop_while { |x| x < 4 })
|
308
310
|
end
|
309
311
|
|
310
|
-
def
|
312
|
+
def test_unfoldr
|
311
313
|
xs = Stream.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
|
312
314
|
assert_equal(Stream[3, 2, 1], xs)
|
315
|
+
end
|
316
|
+
|
317
|
+
def test_s_unfoldr
|
313
318
|
xs = Stream.unfoldr("foo,bar,baz") { |x|
|
314
319
|
if x.empty?
|
315
320
|
nil
|
@@ -355,5 +360,10 @@ module Immutable
|
|
355
360
|
}
|
356
361
|
assert_equal(Stream[0, 6, 12, 18], s)
|
357
362
|
end
|
363
|
+
|
364
|
+
def test_to_list
|
365
|
+
assert_equal(List[], Stream[].to_list)
|
366
|
+
assert_equal(List[1, 2, 3], Stream.from(1).take(3).to_list)
|
367
|
+
end
|
358
368
|
end
|
359
369
|
end
|