immutable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,39 @@
1
+ immutable - immutable data structures for Ruby
2
+ ==============================================
3
+
4
+ This project aims to provide immutable data structures for Ruby.
5
+
6
+ Currently, immutable provides the following classes:
7
+
8
+ * Immutable::List
9
+ * Immutable::Map
10
+
11
+ Install
12
+ ========================
13
+
14
+ $ gem install immutable
15
+
16
+ License
17
+ ========================
18
+ (The MIT License)
19
+
20
+ Copyright (c) 2012 Shugo Maeda
21
+
22
+ Permission is hereby granted, free of charge, to any person obtaining
23
+ a copy of this software and associated documentation files (the
24
+ 'Software'), to deal in the Software without restriction, including
25
+ without limitation the rights to use, copy, modify, merge, publish,
26
+ distribute, sublicense, and/or sell copies of the Software, and to
27
+ permit persons to whom the Software is furnished to do so, subject to
28
+ the following conditions:
29
+
30
+ The above copyright notice and this permission notice shall be
31
+ included in all copies or substantial portions of the Software.
32
+
33
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
34
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
35
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
36
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
37
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
38
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
39
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,78 @@
1
+ require 'benchmark'
2
+ require 'avl_tree'
3
+ require 'red_black_tree'
4
+ require 'immutable/map'
5
+ require 'openssl'
6
+
7
+ #random = Random.new(0)
8
+
9
+ TIMES = 100000
10
+ key_size = 10
11
+
12
+ def aset(h, keys)
13
+ keys.each do |k|
14
+ h[k] = 1
15
+ end
16
+ end
17
+
18
+ def aref(h, keys)
19
+ keys.each do |k|
20
+ h[k]
21
+ end
22
+ end
23
+
24
+ def delete(h, keys)
25
+ keys.each do |k|
26
+ h.delete(k)
27
+ end
28
+ end
29
+
30
+ def map_insert(h, keys)
31
+ keys.inject(h) { |m, k|
32
+ m.insert(k, 1)
33
+ }
34
+ end
35
+
36
+ def map_delete(h, keys)
37
+ keys.inject(h) { |m, k|
38
+ m.delete(k)
39
+ }
40
+ end
41
+
42
+ def run(bm, h, keys)
43
+ name = h.class.name
44
+ bm.report("#{name} aset") do
45
+ aset(h, keys)
46
+ end
47
+ bm.report("#{name} aref") do
48
+ aref(h, keys)
49
+ end
50
+ bm.report("#{name} delete") do
51
+ delete(h, keys)
52
+ end
53
+ end
54
+
55
+ def map_run(bm, h, keys)
56
+ name = h.class.name
57
+ bm.report("#{name} insert") do
58
+ h = map_insert(h, keys)
59
+ end
60
+ bm.report("#{name} aref") do
61
+ aref(h, keys)
62
+ end
63
+ bm.report("#{name} delete") do
64
+ h = map_delete(h, keys)
65
+ end
66
+ end
67
+
68
+ keys = []
69
+ TIMES.times do
70
+ keys << OpenSSL::Random.random_bytes(key_size)
71
+ end
72
+
73
+ Benchmark.bmbm do |bm|
74
+ run(bm, Hash.new, keys)
75
+ run(bm, AVLTree.new, keys)
76
+ run(bm, RedBlackTree.new, keys)
77
+ map_run(bm, Immutable::Map.empty, keys)
78
+ end
@@ -0,0 +1,455 @@
1
+ # -*- tailcall-optimization: true; trace-instruction: false -*-
2
+
3
+ module Immutable
4
+ # <code>Immutable::List</code> represents an immutable list.
5
+ #
6
+ # <code>Immutable::List</code> is an abstract class and
7
+ # <code>Immutable::List.[]</code> should be used instead of
8
+ # <code>Immutable::List.new</code>. For example:
9
+ #
10
+ # include Immutable
11
+ # p List[] #=> List[]
12
+ # p List[1, 2] #=> List[1, 2]
13
+ #
14
+ # <code>Immutable::Nil</code> represents an empty list, and
15
+ # <code>Immutable::Cons</code> represents a cons cell, which is a node in
16
+ # a list. For example:
17
+ #
18
+ # p Nil #=> List[]
19
+ # p Cons[1, Cons[2, Nil]] #=> List[1, 2]
20
+ class List
21
+ class EmptyError < StandardError
22
+ def initialize(msg = "list is empty")
23
+ super(msg)
24
+ end
25
+ end
26
+
27
+ # Returns a new list populated with the given objects.
28
+ def self.[](*args)
29
+ from_array(args)
30
+ end
31
+
32
+ # Converts the given array +ary+ to a list.
33
+ def self.from_array(ary)
34
+ ary.reverse_each.inject(Nil) { |x, y|
35
+ Cons.new(y, x)
36
+ }
37
+ end
38
+
39
+ # Converts +enum+ to a list. +enum+ should respond to <code>each</code>.
40
+ def self.from_enum(enum)
41
+ enum.inject(Nil) { |x, y|
42
+ Cons.new(y, x)
43
+ }.reverse
44
+ end
45
+
46
+ # Appends two lists +self+ and +xs+.
47
+ def +(xs)
48
+ foldr(xs) { |y, ys| Cons[y, ys] }
49
+ end
50
+
51
+ # Returns the first element of +self+. If +self+ is empty,
52
+ # <code>Immutable::List::EmptyError</code> is raised.
53
+ def head
54
+ raise ScriptError, "this method should be overriden"
55
+ end
56
+
57
+ # Returns the first element of +self+. If +self+ is empty,
58
+ # <code>Immutable::List::EmptyError</code> is raised.
59
+ def first
60
+ head
61
+ end
62
+
63
+ # Returns the last element of +self+. If +self+ is empty,
64
+ # <code>Immutable::List::EmptyError</code> is raised.
65
+ def last
66
+ raise ScriptError, "this method should be overriden"
67
+ end
68
+
69
+ # Returns the element after the head of +self+. If +self+ is empty,
70
+ # <code>Immutable::List::EmptyError</code> is raised.
71
+ def tail
72
+ raise ScriptError, "this method should be overriden"
73
+ end
74
+
75
+ # Returns all the element of +self+ except the last one.
76
+ # If +self+ is empty, <code>Immutable::List::EmptyError</code> is
77
+ # raised.
78
+ def init
79
+ raise ScriptError, "this method should be overriden"
80
+ end
81
+
82
+ # Returns <code>true</code> if +self+ is empty.
83
+ def empty?
84
+ raise ScriptError, "this method should be overriden"
85
+ end
86
+
87
+ # Returns <code>true</code> if +self+ is empty.
88
+ def null?
89
+ empty?
90
+ end
91
+
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
+ # Returns the list obtained by applying the given block to each element
100
+ # in +self+.
101
+ def map
102
+ foldr(Nil) { |x, xs| Cons[yield(x), xs] }
103
+ end
104
+
105
+ # Returns the elements of +self+ in reverse order.
106
+ def reverse
107
+ foldl(Nil) { |x, y| Cons[y, x] }
108
+ end
109
+
110
+ # Inserts +xs+ in between the lists in +self+.
111
+ def intersperse(xs)
112
+ raise ScriptError, "this method should be overriden"
113
+ end
114
+
115
+ # Inserts +xs+ in between the lists in +self+ and concatenates the
116
+ # result.
117
+ # <code>xss.intercalate(xs)</code> is equivalent to
118
+ # <code>xss.intersperse(xs).flatten</code>.
119
+ def intercalate(xs)
120
+ intersperse(xs).flatten
121
+ end
122
+
123
+ # Transposes the rows and columns of +self+. For example:
124
+ #
125
+ # p List[List[1, 2, 3], List[4, 5, 6]].transpose
126
+ # #=> List[List[1, 4], List[2, 5], List[3, 6]]
127
+ def transpose
128
+ raise ScriptError, "this method should be overriden"
129
+ end
130
+
131
+ # Reduces +self+ using +block+ from right to left. +e+ is used as the
132
+ # starting value. For example:
133
+ #
134
+ # List[1, 2, 3].foldr(9) { |x, y| x + y } #=> 1 - (2 - (3 - 9)) = -7
135
+ def foldr(e, &block)
136
+ raise ScriptError, "this method should be overriden"
137
+ end
138
+
139
+ # Reduces +self+ using +block+ from right to left. If +self+ is empty,
140
+ # <code>Immutable::List::EmptyError</code> is raised.
141
+ def foldr1(&block)
142
+ raise ScriptError, "this method should be overriden"
143
+ end
144
+
145
+ # Reduces +self+ using +block+ from left to right. +e+ is used as the
146
+ # starting value. For example:
147
+ #
148
+ # List[1, 2, 3].foldl(9) { |x, y| x + y } #=> ((9 - 1) - 2) - 3 = 3
149
+ def foldl(e, &block)
150
+ raise ScriptError, "this method should be overriden"
151
+ end
152
+
153
+ # Reduces +self+ using +block+ from left to right. If +self+ is empty,
154
+ # <code>Immutable::List::EmptyError</code> is raised.
155
+ def foldl1(&block)
156
+ raise ScriptError, "this method should be overriden"
157
+ end
158
+
159
+ # Concatenates a list of lists.
160
+ def flatten
161
+ foldr(Nil) { |x, xs| x + xs }
162
+ end
163
+
164
+ # An alias of <code>flatten</code>.
165
+ alias concat flatten
166
+
167
+ # Returns the list obtained by concatenating the results of the given
168
+ # block for each element in +self+.
169
+ def flat_map
170
+ foldr(Nil) { |x, xs| yield(x) + xs }
171
+ end
172
+
173
+ # An alias of <code>flat_map</code>.
174
+ alias concat_map flat_map
175
+
176
+ # An alias of <code>flat_map</code>.
177
+ alias bind flat_map
178
+
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
+ # Builds a list from the seed value +e+ and the given block. The block
190
+ # takes a seed value and returns <code>nil</code> if the seed should
191
+ # unfold to the empty list, or returns <code>[a, b]</code>, where
192
+ # <code>a</code> is the head of the list and <code>b</code> is the next
193
+ # seed from which to unfold the tail. For example:
194
+ #
195
+ # xs = List.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
196
+ # p xs #=> List[3, 2, 1]
197
+ #
198
+ # <code>unfoldr</code> is the dual of <code>foldr</code>.
199
+ def self.unfoldr(e, &block)
200
+ x = yield(e)
201
+ if x.nil?
202
+ Nil
203
+ else
204
+ y, z = x
205
+ Cons[y, unfoldr(z, &block)]
206
+ end
207
+ end
208
+
209
+ # Returns the first +n+ elements of +self+, or +self+ itself if
210
+ # <code>n > self.length</code>.
211
+ def take(n)
212
+ raise ScriptError, "this method should be overriden"
213
+ end
214
+
215
+
216
+ # Returns the suffix of +self+ after the first +n+ elements, or
217
+ # <code>List[]</code> if <code>n > self.length</code>.
218
+ def drop(n)
219
+ raise ScriptError, "this method should be overriden"
220
+ end
221
+
222
+ # Returns the longest prefix of the elements of +self+ for which +block+
223
+ # evaluates to true.
224
+ def take_while(&block)
225
+ raise ScriptError, "this method should be overriden"
226
+ end
227
+
228
+ # Returns the suffix remaining after
229
+ # <code>self.take_while(&block)</code>.
230
+ def drop_while(&block)
231
+ raise ScriptError, "this method should be overriden"
232
+ end
233
+
234
+ # Returns the first element in +self+ for which the given block
235
+ # evaluates to true. If such an element is not found, it
236
+ # returns <code>nil</code>.
237
+ def find(&block)
238
+ raise ScriptError, "this method should be overriden"
239
+ end
240
+
241
+ # Returns the elements in +self+ for which the given block evaluates to
242
+ # true.
243
+ def filter
244
+ foldr(Nil) { |x, xs|
245
+ if yield(x)
246
+ Cons[x, xs]
247
+ else
248
+ xs
249
+ end
250
+ }
251
+ end
252
+ end
253
+
254
+ Nil = List.new
255
+
256
+ class Cons < List
257
+ # Creates a list obtained by prepending +head+ to the list +tail+.
258
+ def self.[](head, tail = Nil)
259
+ self.new(head, tail)
260
+ end
261
+
262
+ # Creates a list obtained by prepending +head+ to the list +tail+.
263
+ def initialize(head, tail = Nil)
264
+ @head = head
265
+ @tail = tail
266
+ end
267
+
268
+ def Nil.head
269
+ raise EmptyError
270
+ end
271
+
272
+ attr_reader :head
273
+
274
+ def Nil.last
275
+ raise EmptyError
276
+ end
277
+
278
+ def last
279
+ if @tail.empty?
280
+ @head
281
+ else
282
+ @tail.last
283
+ end
284
+ end
285
+
286
+ def Nil.tail
287
+ raise EmptyError
288
+ end
289
+
290
+ attr_reader :tail
291
+
292
+ def Nil.init
293
+ raise EmptyError
294
+ end
295
+
296
+ def init
297
+ if @tail.empty?
298
+ Nil
299
+ else
300
+ Cons[@head, @tail.init]
301
+ end
302
+ end
303
+
304
+ def Nil.empty?
305
+ true
306
+ end
307
+
308
+ def empty?
309
+ false
310
+ end
311
+
312
+ def Nil.foldr(e)
313
+ e
314
+ end
315
+
316
+ def foldr(e, &block)
317
+ yield(@head, @tail.foldr(e, &block))
318
+ end
319
+
320
+ def Nil.foldr1
321
+ raise EmptyError
322
+ end
323
+
324
+ def foldr1(&block)
325
+ if @tail.empty?
326
+ @head
327
+ else
328
+ yield(@head, @tail.foldr1(&block))
329
+ end
330
+ end
331
+
332
+ def Nil.foldl(e)
333
+ e
334
+ end
335
+
336
+ def foldl(e, &block)
337
+ @tail.foldl(yield(e, @head), &block)
338
+ end
339
+
340
+ def Nil.foldl1
341
+ raise EmptyError
342
+ end
343
+
344
+ def foldl1(&block)
345
+ @tail.foldl(@head, &block)
346
+ end
347
+
348
+ def ==(xs)
349
+ if xs.empty?
350
+ false
351
+ else
352
+ @head == xs.head && @tail == xs.tail
353
+ end
354
+ end
355
+
356
+ def Nil.inspect
357
+ "List[]"
358
+ end
359
+
360
+ def inspect
361
+ "List[" + @head.inspect +
362
+ @tail.foldl("") {|x, y| x + ", " + y.inspect } + "]"
363
+ end
364
+
365
+ def Nil.intersperse(sep)
366
+ Nil
367
+ end
368
+
369
+ def intersperse(sep)
370
+ Cons[@head, @tail.prepend_to_all(sep)]
371
+ end
372
+
373
+ def Nil.prepend_to_all(sep)
374
+ Nil
375
+ end
376
+
377
+ def prepend_to_all(sep)
378
+ Cons[sep, Cons[@head, @tail.prepend_to_all(sep)]]
379
+ end
380
+
381
+ def Nil.transpose
382
+ Nil
383
+ end
384
+
385
+ def transpose
386
+ if @head == Nil
387
+ @tail.transpose
388
+ else
389
+ tail = @tail.filter { |x| !x.empty? }
390
+ Cons[Cons[@head.head, tail.map(&:head)],
391
+ Cons[@head.tail, tail.map(&:tail)].transpose]
392
+ end
393
+ end
394
+
395
+ def Nil.take(n)
396
+ Nil
397
+ end
398
+
399
+ def take(n)
400
+ if n <= 0
401
+ Nil
402
+ else
403
+ Cons[@head, @tail.take(n - 1)]
404
+ end
405
+ end
406
+
407
+ def Nil.take_while
408
+ Nil
409
+ end
410
+
411
+ def take_while(&block)
412
+ if yield(@head)
413
+ Cons[@head, @tail.take_while(&block)]
414
+ else
415
+ Nil
416
+ end
417
+ end
418
+
419
+ def Nil.drop(n)
420
+ Nil
421
+ end
422
+
423
+ def drop(n)
424
+ if n > 0
425
+ @tail.drop(n - 1)
426
+ else
427
+ self
428
+ end
429
+ end
430
+
431
+ def Nil.drop_while
432
+ Nil
433
+ end
434
+
435
+ def drop_while(&block)
436
+ if yield(@head)
437
+ @tail.drop_while(&block)
438
+ else
439
+ self
440
+ end
441
+ end
442
+
443
+ def Nil.find
444
+ nil
445
+ end
446
+
447
+ def find(&block)
448
+ if yield(@head)
449
+ @head
450
+ else
451
+ @tail.find(&block)
452
+ end
453
+ end
454
+ end
455
+ end
@@ -0,0 +1,385 @@
1
+ # -*- tailcall-optimization: true; trace-instruction: false -*-
2
+ # ported from http://www.cs.kent.ac.uk/people/staff/smk/redblack/Untyped.hs
3
+
4
+ module Immutable
5
+ # <code>Immutable::Map</code> represents an immutable map from keys to
6
+ # values.
7
+ #
8
+ # <code>Immutable::Map</code> is an abstract class and
9
+ # <code>Immutable::Map.[]</code> should be used instead of
10
+ # <code>Immutable::Map.new</code>. For example:
11
+ #
12
+ # include Immutable
13
+ # p Map[] #=> Map[]
14
+ # p Map[a: 1, b: 2] #=> Map[:a => 1, :b => 2]
15
+ #
16
+ # <code>Immutable::Map#insert</code> inserts a key/value pair and
17
+ # returns a new <code>Immutable::Map</code>. The original map never be
18
+ # changed by <code>Immutable::Map#insert</code>. For example:
19
+ #
20
+ # m = Map[a: 1]
21
+ # p m #=> Map[:a => 1]
22
+ # m2 = m.insert(:b, 2)
23
+ # p m2 #=> Map[:a => 1, :b => 2]
24
+ # p m #=> Map[:a => 1]
25
+ class Map
26
+ class InvarianceViolationError < StandardError
27
+ end
28
+
29
+ # Returns an empty map.
30
+ def self.empty
31
+ Leaf
32
+ end
33
+
34
+ # Returns a map that has only one pair whose key is +key+ and whose
35
+ # value is +value+.
36
+ def self.singleton(key, value)
37
+ Leaf.insert(key, value)
38
+ end
39
+
40
+ # Returns a map that has the same key/value pairs as the
41
+ # <code>Hash</code> object +h+.
42
+ def self.[](h = {})
43
+ h.inject(Leaf) { |m, (k, v)| m.insert(k, v) }
44
+ end
45
+
46
+ # Inserts +key+/+value+ in +self+.
47
+ def insert(key, value)
48
+ ins(key, value).make_black
49
+ end
50
+
51
+ # Returns the value at +key+ in +self+, or <code>nil</code> if +key+
52
+ # isn't in +self+.
53
+ def [](key)
54
+ raise ScriptError, "this method should be overriden"
55
+ end
56
+
57
+ # Deletes +key+ and its value from +self+.
58
+ def delete(key)
59
+ m = del(key)
60
+ if m.empty?
61
+ m
62
+ else
63
+ m.make_black
64
+ end
65
+ end
66
+
67
+ def inspect
68
+ "Map[" + foldr_with_key("") { |k, v, s|
69
+ x = k.inspect + " => " + v.inspect
70
+ if s.empty?
71
+ x
72
+ else
73
+ x + ", " + s
74
+ end
75
+ } + "]"
76
+ end
77
+
78
+ # Folds the values in +self+ from right to left.
79
+ def foldr(e)
80
+ foldr_with_key(e) { |k, v, x| yield(v, x) }
81
+ end
82
+
83
+ # Folds the values in +self+ from left to right.
84
+ def foldl(e)
85
+ foldl_with_key(e) { |x, k, v| yield(x, v) }
86
+ end
87
+
88
+ # Maps the given block over all values in +self+.
89
+ def map
90
+ map_with_key { |k, v| yield(v) }
91
+ end
92
+
93
+ # Maps the given block over all keys and values in +self+.
94
+ def map_with_key
95
+ foldr_with_key(List[]) { |k, v, xs| Cons[yield(k, v), xs] }
96
+ end
97
+
98
+ # :nodoc:
99
+ Leaf = Map.new
100
+
101
+ def Leaf.empty?
102
+ true
103
+ end
104
+
105
+ def Leaf.red?
106
+ false
107
+ end
108
+
109
+ def Leaf.black?
110
+ true
111
+ end
112
+
113
+ def Leaf.[](key)
114
+ nil
115
+ end
116
+
117
+ def Leaf.ins(key, value)
118
+ RedFork[Leaf, key, value, Leaf]
119
+ end
120
+
121
+ def Leaf.del(key)
122
+ Leaf
123
+ end
124
+
125
+ def Leaf.each
126
+ end
127
+
128
+ class Fork < Map #:nodoc:
129
+ attr_reader :left, :key, :value, :right
130
+
131
+ def initialize(left, key, value, right)
132
+ @left, @key, @value, @right = left, key, value, right
133
+ end
134
+
135
+ def self.extract(val)
136
+ accept_self_instance_only(val)
137
+ [val.left, val.key, val.value, val.right]
138
+ end
139
+
140
+ class << self
141
+ alias [] new
142
+ end
143
+
144
+ def empty?
145
+ false
146
+ end
147
+
148
+ def [](key)
149
+ if key < self.key
150
+ left[key]
151
+ elsif key > self.key
152
+ right[key]
153
+ else
154
+ value
155
+ end
156
+ end
157
+
158
+ def del(key)
159
+ if key < self.key
160
+ del_left(left, self.key, self.value, right, key)
161
+ elsif key > self.key
162
+ del_right(left, self.key, self.value, right, key)
163
+ else
164
+ app(left, right)
165
+ end
166
+ end
167
+
168
+ def each(&block)
169
+ left.each(&block)
170
+ yield key, value
171
+ right.each(&block)
172
+ end
173
+
174
+ def Leaf.foldr_with_key(e)
175
+ e
176
+ end
177
+
178
+ def foldr_with_key(e, &block)
179
+ r = @right.foldr_with_key(e, &block)
180
+ @left.foldr_with_key(yield(@key, @value, r), &block)
181
+ end
182
+
183
+ def Leaf.foldl_with_key(e)
184
+ e
185
+ end
186
+
187
+ def foldl_with_key(e, &block)
188
+ l = @left.foldl_with_key(e, &block)
189
+ @right.foldl_with_key(yield(l, @key, @value), &block)
190
+ end
191
+
192
+ private
193
+
194
+ def balance(left, key, value, right)
195
+ # balance (T R a x b) y (T R c z d) = T R (T B a x b) y (T B c z d)
196
+ if left.red? && right.red?
197
+ RedFork[left.make_black, key, value, right.make_black]
198
+ # balance (T R (T R a x b) y c) z d = T R (T B a x b) y (T B c z d)
199
+ elsif left.red? && left.left.red?
200
+ RedFork[
201
+ left.left.make_black, left.key, left.value,
202
+ BlackFork[left.right, key, value, right]
203
+ ]
204
+ # balance (T R a x (T R b y c)) z d = T R (T B a x b) y (T B c z d)
205
+ elsif left.red? && left.right.red?
206
+ RedFork[
207
+ BlackFork[left.left, left.key, left.value, left.right.left],
208
+ left.right.key, left.right.value,
209
+ BlackFork[left.right.right, key, value, right]
210
+ ]
211
+ # balance a x (T R b y (T R c z d)) = T R (T B a x b) y (T B c z d)
212
+ elsif right.red? && right.right.red?
213
+ RedFork[
214
+ BlackFork[left, key, value, right.left],
215
+ right.key, right.value, right.right.make_black
216
+ ]
217
+ # balance a x (T R (T R b y c) z d) = T R (T B a x b) y (T B c z d)
218
+ elsif right.red? && right.left.red?
219
+ RedFork[
220
+ BlackFork[left, key, value, right.left.left],
221
+ right.left.key, right.left.value,
222
+ BlackFork[right.left.right, right.key, right.value, right.right]
223
+ ]
224
+ # balance a x b = T B a x b
225
+ else
226
+ BlackFork[left, key, value, right]
227
+ end
228
+ end
229
+
230
+ def del_left(left, key, value, right, del_key)
231
+ if left.black?
232
+ bal_left(left.del(del_key), key, value, right)
233
+ else
234
+ RedFork[left.del(del_key), key, value, right]
235
+ end
236
+ end
237
+
238
+ def del_right(left, key, value, right, del_key)
239
+ if right.black?
240
+ bal_right(left, key, value, right.del(del_key))
241
+ else
242
+ RedFork[left, key, value, right.del(del_key)]
243
+ end
244
+ end
245
+
246
+ def bal_left(left, key, value, right)
247
+ if left.red?
248
+ RedFork[left.make_black, key, value, right]
249
+ elsif right.black?
250
+ balance(left, key, value, right.make_red)
251
+ elsif right.red? && right.left.black?
252
+ RedFork[
253
+ BlackFork[left, key, value, right.left.left],
254
+ right.left.key, right.left.value,
255
+ balance(right.left.right, right.key, right.value,
256
+ sub1(right.right))
257
+ ]
258
+ else
259
+ raise ScriptError, "should not reach here"
260
+ end
261
+ end
262
+
263
+ def bal_right(left, key, value, right)
264
+ if right.red?
265
+ RedFork[left, key, value, right.make_black]
266
+ elsif left.black?
267
+ balance(left.make_red, key, value, right)
268
+ elsif left.red? && left.right.black?
269
+ RedFork[
270
+ balance(sub1(left.left), left.key, left.value, left.right.left),
271
+ left.right.key, left.right.value,
272
+ BlackFork[left.right.right, key, value, right]
273
+ ]
274
+ else
275
+ raise ScriptError, "should not reach here"
276
+ end
277
+ end
278
+
279
+ def sub1(node)
280
+ if node.black?
281
+ node.make_red
282
+ else
283
+ raise InvarianceViolationError, "invariance violation"
284
+ end
285
+ end
286
+
287
+ def app(left, right)
288
+ if left.empty?
289
+ right
290
+ elsif right.empty?
291
+ left
292
+ elsif left.red? && right.red?
293
+ m = app(left.right, right.left)
294
+ if m.red?
295
+ RedFork[
296
+ RedFork[left.left, left.key, left.value, m.left],
297
+ m.key, m.value,
298
+ RedFork[m.right, right.key, right.value, right.right]
299
+ ]
300
+ else
301
+ RedFork[
302
+ left.left, left.key, left.value,
303
+ RedFork[m, right.key, right.value, right.right]
304
+ ]
305
+ end
306
+ elsif left.black? && right.black?
307
+ m = app(left.right, right.left)
308
+ if m.red?
309
+ RedFork[
310
+ BlackFork[left.left, left.key, left.value, m.left],
311
+ m.key, m.value,
312
+ BlackFork[m.right, right.key, right.value, right.right]
313
+ ]
314
+ else
315
+ bal_left(left.left, left.key, left.value,
316
+ BlackFork[m, right.key, right.value, right.right])
317
+ end
318
+ elsif right.red?
319
+ RedFork[app(left, right.left), right.key, right.value,
320
+ right.right]
321
+ elsif left.red?
322
+ RedFork[left.left, left.key, left.value, app(left.right, right)]
323
+ else
324
+ raise ScriptError, "should not reach here"
325
+ end
326
+ end
327
+ end
328
+
329
+ class RedFork < Fork #:nodoc:
330
+ def red?
331
+ true
332
+ end
333
+
334
+ def black?
335
+ false
336
+ end
337
+
338
+ def make_red
339
+ self
340
+ end
341
+
342
+ def make_black
343
+ BlackFork[left, key, value, right]
344
+ end
345
+
346
+ def ins(key, value)
347
+ if key < self.key
348
+ RedFork[left.ins(key, value), self.key, self.value, right]
349
+ elsif key > self.key
350
+ RedFork[left, self.key, self.value, right.ins(key, value)]
351
+ else
352
+ RedFork[left, key, value, right]
353
+ end
354
+ end
355
+ end
356
+
357
+ class BlackFork < Fork #:nodoc:
358
+ def red?
359
+ false
360
+ end
361
+
362
+ def black?
363
+ true
364
+ end
365
+
366
+ def make_red
367
+ RedFork[left, key, value, right]
368
+ end
369
+
370
+ def make_black
371
+ self
372
+ end
373
+
374
+ def ins(key, value)
375
+ if key < self.key
376
+ balance(left.ins(key, value), self.key, self.value, right)
377
+ elsif key > self.key
378
+ balance(left, self.key, self.value, right.ins(key, value))
379
+ else
380
+ BlackFork[left, key, value, right]
381
+ end
382
+ end
383
+ end
384
+ end
385
+ end
@@ -0,0 +1,255 @@
1
+ require "test/unit"
2
+ require "immutable/list"
3
+
4
+ module Immutable
5
+ class TestList < Test::Unit::TestCase
6
+ def test_s_from_array
7
+ assert_equal(List[], List.from_array([]))
8
+ assert_equal(List[1, 2, 3], List.from_array([1, 2, 3]))
9
+ end
10
+
11
+ def test_s_from_enum
12
+ assert_equal(List[], List.from_enum([]))
13
+ assert_equal(List[1, 2, 3], List.from_enum(1..3))
14
+ assert_equal(List["a", "b", "c"], List.from_enum("abc".chars))
15
+ end
16
+
17
+ def test_head
18
+ assert_raise(List::EmptyError) do
19
+ List[].head
20
+ end
21
+ assert_equal(1, List[1].head)
22
+ assert_equal(1, List[1, 2, 3].head)
23
+ end
24
+
25
+ def test_last
26
+ assert_raise(List::EmptyError) do
27
+ List[].last
28
+ end
29
+ assert_equal(1, List[1].last)
30
+ assert_equal(3, List[1, 2, 3].last)
31
+ end
32
+
33
+ def test_tail
34
+ assert_raise(List::EmptyError) do
35
+ List[].tail
36
+ end
37
+ assert_equal(List[], List[1].tail)
38
+ assert_equal(List[2, 3], List[1, 2, 3].tail)
39
+ end
40
+
41
+ def test_init
42
+ assert_raise(List::EmptyError) do
43
+ List[].init
44
+ end
45
+ assert_equal(List[], List[1].init)
46
+ assert_equal(List[1, 2], List[1, 2, 3].init)
47
+ end
48
+
49
+ def test_empty?
50
+ assert(List[].empty?)
51
+ assert(!List[1].empty?)
52
+ assert(!List[1, 2, 3].empty?)
53
+ end
54
+
55
+ def test_foldr
56
+ assert_equal(0, List[].foldr(0, &:+))
57
+ assert_equal(123, List[].foldr(123, &:+))
58
+
59
+ assert_equal(6, List[1, 2, 3].foldr(0, &:+))
60
+ # 1 - (2 - (3 - 10))
61
+ assert_equal(-8, List[1, 2, 3].foldr(10, &:-))
62
+ end
63
+
64
+ def test_foldr1
65
+ assert_raise(List::EmptyError) do
66
+ List[].foldr1(&:+)
67
+ end
68
+ assert_equal(1, List[1].foldr1(&:+))
69
+ assert_equal(3, List[1, 2].foldr1(&:+))
70
+ assert_equal(6, List[1, 2, 3].foldr1(&:+))
71
+ assert_equal(2, List[1, 2, 3].foldr1(&:-))
72
+ end
73
+
74
+ def test_foldl
75
+ assert_equal(0, List[].foldl(0, &:+))
76
+ assert_equal(123, List[].foldl(123, &:+))
77
+
78
+ assert_equal(6, List[1, 2, 3].foldl(0, &:+))
79
+ # ((10 - 1) - 2) - 3
80
+ assert_equal(4, List[1, 2, 3].foldl(10, &:-))
81
+ end
82
+
83
+ def test_foldl1
84
+ assert_raise(List::EmptyError) do
85
+ List[].foldl1(&:+)
86
+ end
87
+ assert_equal(1, List[1].foldl1(&:+))
88
+ assert_equal(3, List[1, 2].foldl1(&:+))
89
+ assert_equal(6, List[1, 2, 3].foldl1(&:+))
90
+ assert_equal(-4, List[1, 2, 3].foldl1(&:-))
91
+ end
92
+
93
+ def test_eq
94
+ assert(List[] == List[])
95
+ assert(List[] != List[1])
96
+ assert(List[1] != List[])
97
+ assert(List[1] == List[1])
98
+ assert(List[1] != List[2])
99
+ assert(List["foo"] == List["foo"])
100
+ assert(List["foo"] != List["bar"])
101
+ assert(List[1, 2, 3] == List[1, 2, 3])
102
+ assert(List[1, 2, 3] != List[1, 2])
103
+ assert(List[1, 2, 3] != List[1, 2, 3, 4])
104
+ assert(List[List[1, 2], List[3, 4]] == List[List[1, 2], List[3, 4]])
105
+ assert(List[List[1, 2], List[3, 4]] != List[List[1, 2], List[3]])
106
+ end
107
+
108
+ def test_inspect
109
+ assert_equal('List[]', List[].inspect)
110
+ assert_equal('List[1]', List[1].inspect)
111
+ assert_equal('List["foo"]', List["foo"].inspect)
112
+ assert_equal('List[1, 2, 3]', List[1, 2, 3].inspect)
113
+ assert_equal('List[List[1, 2], List[3, 4]]',
114
+ List[List[1, 2], List[3, 4]].inspect)
115
+ end
116
+
117
+ def test_length
118
+ assert_equal(0, List[].length)
119
+ assert_equal(1, List[1].length)
120
+ assert_equal(3, List[1, 2, 3].length)
121
+ assert_equal(100, List[*(1..100)].length)
122
+ end
123
+
124
+ def test_plus
125
+ assert_equal(List[], List[] + List[])
126
+ assert_equal(List[1, 2, 3], List[] + List[1, 2, 3])
127
+ assert_equal(List[1, 2, 3], List[1, 2, 3] + List[])
128
+ assert_equal(List[1, 2, 3], List[1] + List[2, 3])
129
+ assert_equal(List[1, 2, 3], List[1, 2] + List[3])
130
+ end
131
+
132
+ def test_flatten
133
+ assert_equal(List[], List[].flatten)
134
+ assert_equal(List[1], List[List[1]].flatten)
135
+ assert_equal(List[List[1]], List[List[List[1]]].flatten)
136
+ assert_equal(List[1, 2, 3], List[List[1, 2], List[3]].flatten)
137
+ assert_equal(List[1, 2, 3], List[List[1], List[2], List[3]].flatten)
138
+ end
139
+
140
+ def test_map
141
+ assert_equal(List[], List[].map(&:to_s))
142
+ assert_equal(List["1", "2", "3"], List[1, 2, 3].map(&:to_s))
143
+ end
144
+
145
+ def test_flat_map
146
+ assert_equal(List[], List[].flat_map {})
147
+ assert_equal(List["1", "2", "3"], List[1, 2, 3].map(&:to_s))
148
+ end
149
+
150
+ def test_find
151
+ assert_equal(nil, List[].find(&:odd?))
152
+ assert_equal(1, List[1, 2, 3, 4, 5].find(&:odd?))
153
+ assert_equal(2, List[1, 2, 3, 4, 5].find(&:even?))
154
+ end
155
+
156
+ def test_filter
157
+ assert_equal(List[], List[].filter(&:odd?))
158
+ assert_equal(List[1, 3, 5], List[1, 2, 3, 4, 5].filter(&:odd?))
159
+ assert_equal(List[2, 4], List[1, 2, 3, 4, 5].filter(&:even?))
160
+ end
161
+
162
+ def test_take
163
+ assert_equal(List[], List[].take(1))
164
+ assert_equal(List[], List[1, 2, 3].take(0))
165
+ assert_equal(List[], List[1, 2, 3].take(-1))
166
+ assert_equal(List[1], List[1, 2, 3].take(1))
167
+ assert_equal(List[1, 2], List[1, 2, 3].take(2))
168
+ assert_equal(List[1, 2, 3], List[1, 2, 3].take(3))
169
+ assert_equal(List[1, 2, 3], List[1, 2, 3].take(4))
170
+ end
171
+
172
+ def test_take_while
173
+ assert_equal(List[], List[].take_while { true })
174
+ assert_equal(List[], List[1, 2, 3].take_while { |x| x < 1 })
175
+ assert_equal(List[1], List[1, 2, 3].take_while { |x| x < 2 })
176
+ assert_equal(List[1, 2], List[1, 2, 3].take_while { |x| x < 3 })
177
+ assert_equal(List[1, 2, 3], List[1, 2, 3].take_while { |x| x < 4 })
178
+ end
179
+
180
+ def test_drop
181
+ assert_equal(List[], List[].drop(1))
182
+ assert_equal(List[1, 2, 3], List[1, 2, 3].drop(0))
183
+ assert_equal(List[1, 2, 3], List[1, 2, 3].drop(-1))
184
+ assert_equal(List[2, 3], List[1, 2, 3].drop(1))
185
+ assert_equal(List[3], List[1, 2, 3].drop(2))
186
+ assert_equal(List[], List[1, 2, 3].drop(3))
187
+ assert_equal(List[], List[1, 2, 3].drop(4))
188
+ end
189
+
190
+ def test_drop_while
191
+ assert_equal(List[], List[].drop_while { false })
192
+ assert_equal(List[1, 2, 3], List[1, 2, 3].drop_while { |x| x < 1 })
193
+ assert_equal(List[2, 3], List[1, 2, 3].drop_while { |x| x < 2 })
194
+ assert_equal(List[3], List[1, 2, 3].drop_while { |x| x < 3 })
195
+ assert_equal(List[], List[1, 2, 3].drop_while { |x| x < 4 })
196
+ end
197
+
198
+ def test_reverse
199
+ assert_equal(List[], List[].reverse)
200
+ assert_equal(List[1], List[1].reverse)
201
+ assert_equal(List[2, 1], List[1, 2].reverse)
202
+ assert_equal(List[3, 2, 1], List[1, 2, 3].reverse)
203
+ end
204
+
205
+ def test_intersperse
206
+ assert_equal(List[], List[].intersperse(0))
207
+ assert_equal(List[1], List[1].intersperse(0))
208
+ assert_equal(List[1, 0, 2], List[1, 2].intersperse(0))
209
+ assert_equal(List[1, 0, 2, 0, 3], List[1, 2, 3].intersperse(0))
210
+ end
211
+
212
+ def test_intercalate
213
+ assert_equal(List[], List[].intercalate(List[0]))
214
+ assert_equal(List[1], List[List[1]].intercalate(List[0]))
215
+ xs = List[List[1, 2], List[3, 4], List[5, 6]].intercalate(List[0])
216
+ assert_equal(List[1, 2, 0, 3, 4, 0, 5, 6], xs)
217
+ end
218
+
219
+ def test_transpose
220
+ assert_equal(List[], List[].transpose)
221
+ assert_equal(List[], List[List[]].transpose)
222
+ assert_equal(List[List[1], List[2]], List[List[1, 2]].transpose)
223
+ assert_equal(List[List[1, 4], List[2, 5], List[3, 6]],
224
+ List[List[1, 2, 3], List[4, 5, 6]].transpose)
225
+ assert_equal(List[List[1, 4], List[2, 5], List[3]],
226
+ List[List[1, 2, 3], List[4, 5]].transpose)
227
+ end
228
+
229
+ def test_sum
230
+ assert_equal(0, List[].sum)
231
+ assert_equal(1, List[1].sum)
232
+ assert_equal(10, List[1, 2, 3, 4].sum)
233
+ end
234
+
235
+ def test_product
236
+ assert_equal(1, List[].product)
237
+ assert_equal(1, List[1].product)
238
+ assert_equal(24, List[1, 2, 3, 4].product)
239
+ end
240
+
241
+ def test_s_unfoldr
242
+ xs = List.unfoldr(3) { |x| x == 0 ? nil : [x, x - 1] }
243
+ assert_equal(List[3, 2, 1], xs)
244
+ xs = List.unfoldr("foo,bar,baz") { |x|
245
+ if x.empty?
246
+ nil
247
+ else
248
+ y = x.slice(/([^,]*),?/, 1)
249
+ [y, $']
250
+ end
251
+ }
252
+ assert_equal(List["foo", "bar", "baz"], xs)
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,113 @@
1
+ require "test/unit"
2
+ require "immutable/map"
3
+
4
+ module Immutable
5
+ class TestMap < Test::Unit::TestCase
6
+ def test_empty?
7
+ assert(Map[].empty?)
8
+ assert(!Map[a: 1].empty?)
9
+ assert(!Map[a: 1, b: 2].empty?)
10
+ end
11
+
12
+ def test_inspect
13
+ assert_equal("Map[]", Map.empty.inspect)
14
+ assert_equal("Map[:a => 1, :b => 2]", Map[a: 1, b: 2].inspect)
15
+ end
16
+
17
+ def test_insert
18
+ 5.times do
19
+ map = (1..100).to_a.shuffle.inject(Map.empty) { |m, k|
20
+ m.insert(k, k.to_s)
21
+ }
22
+ for i in 1..100
23
+ assert_equal(i.to_s, map[i])
24
+ end
25
+ end
26
+ end
27
+
28
+ def test_delete
29
+ 5.times do
30
+ keys = (1..100).to_a.shuffle
31
+ map = keys.inject(Map.empty) { |m, k|
32
+ m.insert(k, k.to_s)
33
+ }
34
+ keys.shuffle!
35
+ deleted_keys = keys.shift(20)
36
+ map2 = deleted_keys.inject(map) { |m, k|
37
+ m.delete(k)
38
+ }
39
+ for i in 1..100
40
+ assert_equal(i.to_s, map[i])
41
+ end
42
+ keys.each do |k|
43
+ assert_equal(k.to_s, map2[k])
44
+ end
45
+ deleted_keys.each do |k|
46
+ assert_equal(nil, map2[k])
47
+ end
48
+ end
49
+ end
50
+
51
+ def test_foldr
52
+ xs = Map[].foldr(List[]) { |v, ys| Cons[v, ys] }
53
+ assert_equal(List[], xs)
54
+
55
+ xs = Map[a: 1, c: 3, b: 2].foldr(List[]) { |v, ys| Cons[v, ys] }
56
+ assert_equal(List[1, 2, 3], xs)
57
+ end
58
+
59
+ def test_foldl
60
+ xs = Map[].foldl(List[]) { |ys, v| Cons[v, ys] }
61
+ assert_equal(List[], xs)
62
+
63
+ xs = Map[a: 1, c: 3, b: 2].foldl(List[]) { |ys, v| Cons[v, ys] }
64
+ assert_equal(List[3, 2, 1], xs)
65
+ end
66
+
67
+ def test_foldl_with_key
68
+ xs = Map[].foldl_with_key(List[]) { |ys, k, v| Cons[[k, v], ys] }
69
+ assert_equal(List[], xs)
70
+
71
+ xs = Map[a: 1, c: 3, b: 2].foldl_with_key(List[]) { |ys, k, v|
72
+ Cons[[k, v], ys]
73
+ }
74
+ assert_equal(List[[:c, 3], [:b, 2], [:a, 1]], xs)
75
+ end
76
+
77
+ def test_foldr_with_key
78
+ xs = Map[].foldr_with_key(List[]) { |k, v, ys| Cons[[k, v], ys] }
79
+ assert_equal(List[], xs)
80
+
81
+ xs = Map[a: 1, c: 3, b: 2].foldr_with_key(List[]) { |k, v, ys|
82
+ Cons[[k, v], ys]
83
+ }
84
+ assert_equal(List[[:a, 1], [:b, 2], [:c, 3]], xs)
85
+ end
86
+
87
+ def test_foldl_with_key
88
+ xs = Map[].foldl_with_key(List[]) { |ys, k, v| Cons[[k, v], ys] }
89
+ assert_equal(List[], xs)
90
+
91
+ xs = Map[a: 1, c: 3, b: 2].foldl_with_key(List[]) { |ys, k, v|
92
+ Cons[[k, v], ys]
93
+ }
94
+ assert_equal(List[[:c, 3], [:b, 2], [:a, 1]], xs)
95
+ end
96
+
97
+ def test_map
98
+ xs = Map[].map { |v| v.to_s }
99
+ assert_equal(List[], xs)
100
+
101
+ xs = Map[a: 1, c: 3, b: 2].map { |v| v.to_s }
102
+ assert_equal(List["1", "2", "3"], xs)
103
+ end
104
+
105
+ def test_map_with_key
106
+ xs = Map[].map_with_key { |k, v| [k, v].join(":") }
107
+ assert_equal(List[], xs)
108
+
109
+ xs = Map[a: 1, c: 3, b: 2].map_with_key { |k, v| [k, v].join(":") }
110
+ assert_equal(List["a:1", "b:2", "c:3"], xs)
111
+ end
112
+ end
113
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: immutable
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - Shugo Maeda
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-04-09 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description:
17
+ email: shugo@ruby-lang.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/immutable/map.rb
26
+ - lib/immutable/list.rb
27
+ - benchmark/benchmark_map.rb
28
+ - test/immutable/test_list.rb
29
+ - test/immutable/test_map.rb
30
+ - README.md
31
+ homepage: http://github.com/shugo/immutable
32
+ licenses: []
33
+
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.15
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Immutable data structures for Ruby
58
+ test_files: []
59
+