immutable-ruby 0.0.3 → 0.0.4
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.
- checksums.yaml +5 -5
- data/lib/immutable/deque.rb +36 -1
- data/lib/immutable/enumerable.rb +1 -1
- data/lib/immutable/hash.rb +1 -1
- data/lib/immutable/trie.rb +19 -27
- data/lib/immutable/version.rb +1 -1
- data/spec/lib/immutable/deque/dequeue_spec.rb +1 -1
- data/spec/lib/immutable/deque/rotate_spec.rb +68 -0
- data/spec/lib/immutable/hash/eql_spec.rb +6 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d4dfc5d75d70e963c56837a552ac1902889f05f2fd73a3995c69959815780594
|
4
|
+
data.tar.gz: b9daefb94ad6bd5e3bf3a0bc7b74265a9d97188856ab78508f2cdf813b342636
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 306f0ef37c0ba06cf729b230f8eb6ec850cf6ff6130fea45eaf125d307078617f8688bc02b2dd6a75aae11134a45172f7ca11a12e6693409c36d5f00b7f18fc0
|
7
|
+
data.tar.gz: '0039ef718ca215f12d6826ea6803c7e404fd211536095c5a4995ca04e30b0effefa11951a640bca03b37ee9834ff94bac7acd9d1ccaf2d08ba449204850e42cb'
|
data/lib/immutable/deque.rb
CHANGED
@@ -110,10 +110,38 @@ module Immutable
|
|
110
110
|
@front.last # memoize?
|
111
111
|
end
|
112
112
|
|
113
|
+
# Return a new `Deque` with elements rotated by `n` positions.
|
114
|
+
# A positive rotation moves elements to the right, negative to the left, and 0 is a no-op.
|
115
|
+
#
|
116
|
+
# @example
|
117
|
+
# Immutable::Deque["A", "B", "C"].rotate(1)
|
118
|
+
# # => Immutable::Deque["C", "A", "B"]
|
119
|
+
# Immutable::Deque["A", "B", "C"].rotate(-1)
|
120
|
+
# # => Immutable::Deque["B", "C", "A"]
|
121
|
+
#
|
122
|
+
# @param n [Integer] number of positions to move elements by
|
123
|
+
# @return [Deque]
|
124
|
+
def rotate(n)
|
125
|
+
return self.class.empty if self.empty?
|
126
|
+
|
127
|
+
n %= self.size
|
128
|
+
return self if n == 0
|
129
|
+
|
130
|
+
a, b = @front, @rear
|
131
|
+
|
132
|
+
if b.size >= n
|
133
|
+
n.times { a = a.cons(b.head); b = b.tail }
|
134
|
+
else
|
135
|
+
(self.size - n).times { b = b.cons(a.head); a = a.tail }
|
136
|
+
end
|
137
|
+
|
138
|
+
self.class.alloc(a, b)
|
139
|
+
end
|
140
|
+
|
113
141
|
# Return a new `Deque` with `item` added at the end.
|
114
142
|
#
|
115
143
|
# @example
|
116
|
-
# Immutable::Deque["A", "B", "C"].
|
144
|
+
# Immutable::Deque["A", "B", "C"].push("Z")
|
117
145
|
# # => Immutable::Deque["A", "B", "C", "Z"]
|
118
146
|
#
|
119
147
|
# @param item [Object] The item to add
|
@@ -180,6 +208,13 @@ module Immutable
|
|
180
208
|
self.class.empty
|
181
209
|
end
|
182
210
|
|
211
|
+
# Return a new `Deque` with the same items, but in reverse order.
|
212
|
+
#
|
213
|
+
# @return [Deque]
|
214
|
+
def reverse
|
215
|
+
self.class.alloc(@rear, @front)
|
216
|
+
end
|
217
|
+
|
183
218
|
# Return true if `other` has the same type and contents as this `Deque`.
|
184
219
|
#
|
185
220
|
# @param other [Object] The collection to compare with
|
data/lib/immutable/enumerable.rb
CHANGED
@@ -107,7 +107,7 @@ module Immutable
|
|
107
107
|
# Return true if `other` contains the same elements, in the same order.
|
108
108
|
# @return [Boolean]
|
109
109
|
def ==(other)
|
110
|
-
self.eql?(other) || other.respond_to?(:to_ary) && to_ary
|
110
|
+
self.eql?(other) || (other.respond_to?(:to_ary) && to_ary == other.to_ary)
|
111
111
|
end
|
112
112
|
|
113
113
|
# Convert all the elements into strings and join them together, separated by
|
data/lib/immutable/hash.rb
CHANGED
@@ -771,7 +771,7 @@ module Immutable
|
|
771
771
|
# @param other [Object] The object to compare with
|
772
772
|
# @return [Boolean]
|
773
773
|
def ==(other)
|
774
|
-
self.eql?(other) || (other.respond_to?(:to_hash) && to_hash
|
774
|
+
self.eql?(other) || (other.respond_to?(:to_hash) && to_hash == other.to_hash)
|
775
775
|
end
|
776
776
|
|
777
777
|
# Return true if this `Hash` is a proper superset of `other`, which means
|
data/lib/immutable/trie.rb
CHANGED
@@ -10,8 +10,8 @@ module Immutable
|
|
10
10
|
# Returns the number of key-value pairs in the trie.
|
11
11
|
attr_reader :size
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
13
|
+
def initialize(bitshift, size = 0, entries = [], children = [])
|
14
|
+
@bitshift = bitshift
|
15
15
|
@entries = entries
|
16
16
|
@children = children
|
17
17
|
@size = size
|
@@ -19,7 +19,7 @@ module Immutable
|
|
19
19
|
|
20
20
|
# Returns <tt>true</tt> if the trie contains no key-value pairs.
|
21
21
|
def empty?
|
22
|
-
size == 0
|
22
|
+
@size == 0
|
23
23
|
end
|
24
24
|
|
25
25
|
# Returns <tt>true</tt> if the given key is present in the trie.
|
@@ -29,15 +29,7 @@ module Immutable
|
|
29
29
|
|
30
30
|
# Calls <tt>block</tt> once for each entry in the trie, passing the key-value pair as parameters.
|
31
31
|
def each(&block)
|
32
|
-
|
33
|
-
# the latter segfaults on ruby 2.2 and above. Once that is fixed and
|
34
|
-
# broken versions are sufficiently old, we should revert back to yield
|
35
|
-
# with a warning that the broken versions are unsupported.
|
36
|
-
#
|
37
|
-
# For more context:
|
38
|
-
# * https://bugs.ruby-lang.org/issues/11451
|
39
|
-
# * https://github.com/hamstergem/hamster/issues/189
|
40
|
-
@entries.each { |entry| block.call(entry) if entry }
|
32
|
+
@entries.each { |entry| yield entry if entry }
|
41
33
|
@children.each do |child|
|
42
34
|
child.each(&block) if child
|
43
35
|
end
|
@@ -65,7 +57,7 @@ module Immutable
|
|
65
57
|
|
66
58
|
# @return [Trie] A copy of `self` with the given value associated with the
|
67
59
|
# key (or `self` if no modification was needed because an identical
|
68
|
-
# key-value pair
|
60
|
+
# key-value pair was already stored
|
69
61
|
def put(key, value)
|
70
62
|
index = index_for(key)
|
71
63
|
entry = @entries[index]
|
@@ -74,7 +66,7 @@ module Immutable
|
|
74
66
|
entries = @entries.dup
|
75
67
|
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
76
68
|
entries[index] = [key, value].freeze
|
77
|
-
Trie.new(@
|
69
|
+
Trie.new(@bitshift, @size + 1, entries, @children)
|
78
70
|
elsif entry[0].eql?(key)
|
79
71
|
if entry[1].equal?(value)
|
80
72
|
self
|
@@ -82,7 +74,7 @@ module Immutable
|
|
82
74
|
entries = @entries.dup
|
83
75
|
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
84
76
|
entries[index] = [key, value].freeze
|
85
|
-
Trie.new(@
|
77
|
+
Trie.new(@bitshift, @size, entries, @children)
|
86
78
|
end
|
87
79
|
else
|
88
80
|
child = @children[index]
|
@@ -94,12 +86,12 @@ module Immutable
|
|
94
86
|
children = @children.dup
|
95
87
|
children[index] = new_child
|
96
88
|
new_self_size = @size + (new_child.size - child.size)
|
97
|
-
Trie.new(@
|
89
|
+
Trie.new(@bitshift, new_self_size, @entries, children)
|
98
90
|
end
|
99
91
|
else
|
100
92
|
children = @children.dup
|
101
|
-
children[index] = Trie.new(@
|
102
|
-
Trie.new(@
|
93
|
+
children[index] = Trie.new(@bitshift + 5).put!(key, value)
|
94
|
+
Trie.new(@bitshift, @size + 1, @entries, children)
|
103
95
|
end
|
104
96
|
end
|
105
97
|
end
|
@@ -141,14 +133,14 @@ module Immutable
|
|
141
133
|
end
|
142
134
|
else
|
143
135
|
new_children ||= @children.dup
|
144
|
-
new_children[index] = Trie.new(@
|
136
|
+
new_children[index] = Trie.new(@bitshift + 5).put!(key, value)
|
145
137
|
new_size += 1
|
146
138
|
end
|
147
139
|
end
|
148
140
|
end
|
149
141
|
|
150
142
|
if new_entries || new_children
|
151
|
-
Trie.new(@
|
143
|
+
Trie.new(@bitshift, new_size, new_entries || @entries, new_children || @children)
|
152
144
|
else
|
153
145
|
self
|
154
146
|
end
|
@@ -172,7 +164,7 @@ module Immutable
|
|
172
164
|
@children[index] = child.put!(key, value)
|
173
165
|
@size += child.size - old_child_size
|
174
166
|
else
|
175
|
-
@children[index] = Trie.new(@
|
167
|
+
@children[index] = Trie.new(@bitshift + 5).put!(key, value)
|
176
168
|
@size += 1
|
177
169
|
end
|
178
170
|
end
|
@@ -193,7 +185,7 @@ module Immutable
|
|
193
185
|
|
194
186
|
# Returns a copy of <tt>self</tt> with the given key (and associated value) deleted. If not found, returns <tt>self</tt>.
|
195
187
|
def delete(key)
|
196
|
-
find_and_delete(key) || Trie.new(@
|
188
|
+
find_and_delete(key) || Trie.new(@bitshift)
|
197
189
|
end
|
198
190
|
|
199
191
|
# Delete multiple elements from a Trie. This is more efficient than
|
@@ -238,7 +230,7 @@ module Immutable
|
|
238
230
|
end
|
239
231
|
|
240
232
|
if new_entries || new_children
|
241
|
-
Trie.new(@
|
233
|
+
Trie.new(@bitshift, new_size, new_entries || @entries, new_children || @children)
|
242
234
|
else
|
243
235
|
self
|
244
236
|
end
|
@@ -297,7 +289,7 @@ module Immutable
|
|
297
289
|
children = @children.dup
|
298
290
|
children[index] = copy
|
299
291
|
new_size = @size - (child.size - copy_size(copy))
|
300
|
-
return Trie.new(@
|
292
|
+
return Trie.new(@bitshift, new_size, @entries, children)
|
301
293
|
end
|
302
294
|
end
|
303
295
|
end
|
@@ -318,14 +310,14 @@ module Immutable
|
|
318
310
|
else
|
319
311
|
entries[index] = nil
|
320
312
|
end
|
321
|
-
Trie.new(@
|
313
|
+
Trie.new(@bitshift, @size - 1, entries, children || @children)
|
322
314
|
end
|
323
315
|
end
|
324
316
|
|
325
317
|
private
|
326
318
|
|
327
319
|
def index_for(key)
|
328
|
-
(key.hash.abs >> @
|
320
|
+
(key.hash.abs >> @bitshift) & 31
|
329
321
|
end
|
330
322
|
|
331
323
|
def copy_size(copy)
|
@@ -334,5 +326,5 @@ module Immutable
|
|
334
326
|
end
|
335
327
|
|
336
328
|
# @private
|
337
|
-
EmptyTrie = Trie.new(0)
|
329
|
+
EmptyTrie = Trie.new(0).freeze
|
338
330
|
end
|
data/lib/immutable/version.rb
CHANGED
@@ -26,7 +26,7 @@ describe Immutable::Deque do
|
|
26
26
|
context "on empty subclass" do
|
27
27
|
let(:subclass) { Class.new(Immutable::Deque) }
|
28
28
|
let(:empty_instance) { subclass.new }
|
29
|
-
it "returns
|
29
|
+
it "returns empty object of same class" do
|
30
30
|
empty_instance.send(method).class.should be subclass
|
31
31
|
end
|
32
32
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Immutable::Deque do
|
4
|
+
|
5
|
+
# Deques can have items distributed differently between the 'front' and 'rear' lists
|
6
|
+
# and still be equivalent
|
7
|
+
# Since the implementation of #rotate depends on how items are distributed between the
|
8
|
+
# two lists, we need to test both the case where most items are on the 'front' and
|
9
|
+
# where most are on the 'rear'
|
10
|
+
big_front = D.alloc(L.from_enum([1, 2, 3]), L.from_enum([5, 4]))
|
11
|
+
big_rear = D.alloc(L.from_enum([1, 2]), L.from_enum([5, 4, 3]))
|
12
|
+
|
13
|
+
describe "#rotate" do
|
14
|
+
[
|
15
|
+
[[], 9999, []],
|
16
|
+
[['A'], -1, ['A']],
|
17
|
+
[['A', 'B', 'C'], -1, ['B', 'C', 'A']],
|
18
|
+
[['A', 'B', 'C', 'D'], 0, ['A', 'B', 'C', 'D']],
|
19
|
+
[%w[A B C D], 2, %w[C D A B]],
|
20
|
+
].each do |values, rotation, expected|
|
21
|
+
context "on #{values.inspect}" do
|
22
|
+
let(:deque) { D[*values] }
|
23
|
+
|
24
|
+
it "preserves the original" do
|
25
|
+
deque.rotate(rotation)
|
26
|
+
deque.should eql(D[*values])
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns #{expected.inspect}" do
|
30
|
+
deque.rotate(rotation).should eql(D[*expected])
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns a frozen instance" do
|
34
|
+
deque.rotate(rotation).should be_frozen
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "on a Deque with most items on 'front' list" do
|
40
|
+
it "works with a small rotation" do
|
41
|
+
big_front.rotate(2).should eql(D[4, 5, 1, 2, 3])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "works with a larger rotation" do
|
45
|
+
big_front.rotate(4).should eql(D[2, 3, 4, 5, 1])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "on a Deque with most items on 'rear' list" do
|
50
|
+
it "works with a small rotation" do
|
51
|
+
big_rear.rotate(2).should eql(D[4, 5, 1, 2, 3])
|
52
|
+
end
|
53
|
+
|
54
|
+
it "works with a larger rotation" do
|
55
|
+
big_rear.rotate(4).should eql(D[2, 3, 4, 5, 1])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "on empty subclass" do
|
60
|
+
let(:subclass) { Class.new(Immutable::Deque) }
|
61
|
+
let(:empty_instance) { subclass.new }
|
62
|
+
it "returns an empty object of the same class" do
|
63
|
+
empty_instance.rotate(1).class.should be subclass
|
64
|
+
empty_instance.rotate(-1).class.should be subclass
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "spec_helper"
|
2
|
+
require "bigdecimal"
|
2
3
|
|
3
4
|
describe Immutable::Hash do
|
4
5
|
let(:hash) { H["A" => "aye", "B" => "bee", "C" => "see"] }
|
@@ -33,6 +34,11 @@ describe Immutable::Hash do
|
|
33
34
|
instance = subclass.new("A" => "aye", "B" => "bee", "C" => "see")
|
34
35
|
(hash == instance).should == true
|
35
36
|
end
|
37
|
+
|
38
|
+
it "performs numeric conversions between floats and BigDecimals" do
|
39
|
+
expect(H[a: 0.0] == H[a: BigDecimal('0.0')]).to be true
|
40
|
+
expect(H[a: BigDecimal('0.0')] == H[a: 0.0]).to be true
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
44
|
[:eql?, :==].each do |method|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: immutable-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Dowad
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2019-05-16 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: concurrent-ruby
|
@@ -167,6 +167,7 @@ files:
|
|
167
167
|
- spec/lib/immutable/deque/pretty_print_spec.rb
|
168
168
|
- spec/lib/immutable/deque/push_spec.rb
|
169
169
|
- spec/lib/immutable/deque/random_modification_spec.rb
|
170
|
+
- spec/lib/immutable/deque/rotate_spec.rb
|
170
171
|
- spec/lib/immutable/deque/shift_spec.rb
|
171
172
|
- spec/lib/immutable/deque/size_spec.rb
|
172
173
|
- spec/lib/immutable/deque/to_a_spec.rb
|
@@ -504,8 +505,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
504
505
|
- !ruby/object:Gem::Version
|
505
506
|
version: '0'
|
506
507
|
requirements: []
|
507
|
-
|
508
|
-
rubygems_version: 2.6.13
|
508
|
+
rubygems_version: 3.0.1
|
509
509
|
signing_key:
|
510
510
|
specification_version: 4
|
511
511
|
summary: Efficient, immutable, thread-safe collection classes for Ruby
|
@@ -606,6 +606,7 @@ test_files:
|
|
606
606
|
- spec/lib/immutable/deque/copying_spec.rb
|
607
607
|
- spec/lib/immutable/deque/pop_spec.rb
|
608
608
|
- spec/lib/immutable/deque/to_a_spec.rb
|
609
|
+
- spec/lib/immutable/deque/rotate_spec.rb
|
609
610
|
- spec/lib/immutable/deque/marshal_spec.rb
|
610
611
|
- spec/lib/immutable/set/difference_spec.rb
|
611
612
|
- spec/lib/immutable/set/disjoint_spec.rb
|