erlang-terms 1.1.0 → 2.0.1
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 +4 -4
- data/.coveralls.yml +1 -0
- data/.editorconfig +20 -0
- data/.gitignore +10 -18
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -3
- data/.yardopts +6 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +21 -1
- data/LICENSE.txt +1 -1
- data/README.md +95 -17
- data/Rakefile +8 -3
- data/erlang-terms.gemspec +14 -11
- data/lib/erlang-terms.rb +1 -0
- data/lib/erlang/associable.rb +98 -0
- data/lib/erlang/atom.rb +257 -0
- data/lib/erlang/binary.rb +425 -0
- data/lib/erlang/bitstring.rb +464 -0
- data/lib/erlang/cons.rb +122 -0
- data/lib/erlang/enumerable.rb +160 -0
- data/lib/erlang/error.rb +4 -0
- data/lib/erlang/export.rb +110 -12
- data/lib/erlang/float.rb +201 -0
- data/lib/erlang/function.rb +259 -0
- data/lib/erlang/immutable.rb +101 -0
- data/lib/erlang/list.rb +1685 -24
- data/lib/erlang/map.rb +935 -21
- data/lib/erlang/nil.rb +73 -10
- data/lib/erlang/pid.rb +120 -18
- data/lib/erlang/port.rb +123 -0
- data/lib/erlang/reference.rb +161 -0
- data/lib/erlang/string.rb +175 -3
- data/lib/erlang/term.rb +24 -0
- data/lib/erlang/terms.rb +324 -8
- data/lib/erlang/terms/version.rb +1 -1
- data/lib/erlang/trie.rb +364 -0
- data/lib/erlang/tuple.rb +1582 -14
- data/lib/erlang/undefined.rb +32 -0
- metadata +49 -71
- data/spec/erlang/export_spec.rb +0 -17
- data/spec/erlang/list_spec.rb +0 -39
- data/spec/erlang/map_spec.rb +0 -24
- data/spec/erlang/nil_spec.rb +0 -18
- data/spec/erlang/pid_spec.rb +0 -21
- data/spec/erlang/string_spec.rb +0 -11
- data/spec/erlang/terms_spec.rb +0 -7
- data/spec/erlang/tuple_spec.rb +0 -20
- data/spec/spec_helper.rb +0 -7
data/lib/erlang/terms/version.rb
CHANGED
data/lib/erlang/trie.rb
ADDED
@@ -0,0 +1,364 @@
|
|
1
|
+
module Erlang
|
2
|
+
# Licensing
|
3
|
+
# =========
|
4
|
+
#
|
5
|
+
# Portions taken and modified from https://github.com/hamstergem/hamster
|
6
|
+
#
|
7
|
+
# Copyright (c) 2009-2014 Simon Harris
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
#
|
28
|
+
# @private
|
29
|
+
class Trie
|
30
|
+
def self.[](pairs)
|
31
|
+
result = self.new(0)
|
32
|
+
pairs.each { |key, val| result.put!(key, val) }
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the number of key-value pairs in the trie.
|
37
|
+
attr_reader :size
|
38
|
+
|
39
|
+
def initialize(significant_bits, size = 0, entries = [], children = [])
|
40
|
+
@significant_bits = significant_bits
|
41
|
+
@entries = entries
|
42
|
+
@children = children
|
43
|
+
@size = size
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns <tt>true</tt> if the trie contains no key-value pairs.
|
47
|
+
def empty?
|
48
|
+
size == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns <tt>true</tt> if the given key is present in the trie.
|
52
|
+
def key?(key)
|
53
|
+
!!get(key)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Calls <tt>block</tt> once for each entry in the trie, passing the key-value pair as parameters.
|
57
|
+
def each(&block)
|
58
|
+
# TODO: Using block.call here is slower than using yield by 5-10%, but
|
59
|
+
# the latter segfaults on ruby 2.2 and above. Once that is fixed and
|
60
|
+
# broken versions are sufficiently old, we should revert back to yield
|
61
|
+
# with a warning that the broken versions are unsupported.
|
62
|
+
#
|
63
|
+
# For more context:
|
64
|
+
# * https://bugs.ruby-lang.org/issues/11451
|
65
|
+
# * https://github.com/hamstergem/hamster/issues/189
|
66
|
+
@entries.each { |entry| block.call(entry) if entry }
|
67
|
+
@children.each do |child|
|
68
|
+
child.each(&block) if child
|
69
|
+
end
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def reverse_each(&block)
|
74
|
+
@children.reverse_each do |child|
|
75
|
+
child.reverse_each(&block) if child
|
76
|
+
end
|
77
|
+
@entries.reverse_each { |entry| yield(entry) if entry }
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
|
81
|
+
def reduce(memo)
|
82
|
+
each { |entry| memo = yield(memo, entry) }
|
83
|
+
memo
|
84
|
+
end
|
85
|
+
|
86
|
+
def select
|
87
|
+
keys_to_delete = []
|
88
|
+
each { |entry| keys_to_delete << entry[0] unless yield(entry) }
|
89
|
+
bulk_delete(keys_to_delete)
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Trie] A copy of `self` with the given value associated with the
|
93
|
+
# key (or `self` if no modification was needed because an identical
|
94
|
+
# key-value pair wes already stored
|
95
|
+
def put(key, value)
|
96
|
+
index = index_for(key)
|
97
|
+
entry = @entries[index]
|
98
|
+
|
99
|
+
if !entry
|
100
|
+
entries = @entries.dup
|
101
|
+
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
102
|
+
entries[index] = [key, value].freeze
|
103
|
+
Trie.new(@significant_bits, @size + 1, entries, @children)
|
104
|
+
elsif entry[0].eql?(key)
|
105
|
+
if entry[1].equal?(value)
|
106
|
+
self
|
107
|
+
else
|
108
|
+
entries = @entries.dup
|
109
|
+
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
110
|
+
entries[index] = [key, value].freeze
|
111
|
+
Trie.new(@significant_bits, @size, entries, @children)
|
112
|
+
end
|
113
|
+
else
|
114
|
+
child = @children[index]
|
115
|
+
if child
|
116
|
+
new_child = child.put(key, value)
|
117
|
+
if new_child.equal?(child)
|
118
|
+
self
|
119
|
+
else
|
120
|
+
children = @children.dup
|
121
|
+
children[index] = new_child
|
122
|
+
new_self_size = @size + (new_child.size - child.size)
|
123
|
+
Trie.new(@significant_bits, new_self_size, @entries, children)
|
124
|
+
end
|
125
|
+
else
|
126
|
+
children = @children.dup
|
127
|
+
children[index] = Trie.new(@significant_bits + 5).put!(key, value)
|
128
|
+
Trie.new(@significant_bits, @size + 1, @entries, children)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Put multiple elements into a Trie. This is more efficient than several
|
134
|
+
# calls to `#put`.
|
135
|
+
#
|
136
|
+
# @param key_value_pairs Enumerable of pairs (`[key, value]`)
|
137
|
+
# @return [Trie] A copy of `self` after associated the given keys and
|
138
|
+
# values (or `self` if no modifications where needed).
|
139
|
+
def bulk_put(key_value_pairs)
|
140
|
+
new_entries = nil
|
141
|
+
new_children = nil
|
142
|
+
new_size = @size
|
143
|
+
|
144
|
+
key_value_pairs.each do |key, value|
|
145
|
+
index = index_for(key)
|
146
|
+
entry = (new_entries || @entries)[index]
|
147
|
+
|
148
|
+
if !entry
|
149
|
+
new_entries ||= @entries.dup
|
150
|
+
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
151
|
+
new_entries[index] = [key, value].freeze
|
152
|
+
new_size += 1
|
153
|
+
elsif entry[0].eql?(key)
|
154
|
+
if !entry[1].equal?(value)
|
155
|
+
new_entries ||= @entries.dup
|
156
|
+
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
157
|
+
new_entries[index] = [key, value].freeze
|
158
|
+
end
|
159
|
+
else
|
160
|
+
child = (new_children || @children)[index]
|
161
|
+
if child
|
162
|
+
new_child = child.put(key, value)
|
163
|
+
if !new_child.equal?(child)
|
164
|
+
new_children ||= @children.dup
|
165
|
+
new_children[index] = new_child
|
166
|
+
new_size += new_child.size - child.size
|
167
|
+
end
|
168
|
+
else
|
169
|
+
new_children ||= @children.dup
|
170
|
+
new_children[index] = Trie.new(@significant_bits + 5).put!(key, value)
|
171
|
+
new_size += 1
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
if new_entries || new_children
|
177
|
+
Trie.new(@significant_bits, new_size, new_entries || @entries, new_children || @children)
|
178
|
+
else
|
179
|
+
self
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns <tt>self</tt> after overwriting the element associated with the specified key.
|
184
|
+
def put!(key, value)
|
185
|
+
index = index_for(key)
|
186
|
+
entry = @entries[index]
|
187
|
+
if !entry
|
188
|
+
@size += 1
|
189
|
+
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
190
|
+
@entries[index] = [key, value].freeze
|
191
|
+
elsif entry[0].eql?(key)
|
192
|
+
key = key.dup.freeze if key.is_a?(String) && !key.frozen?
|
193
|
+
@entries[index] = [key, value].freeze
|
194
|
+
else
|
195
|
+
child = @children[index]
|
196
|
+
if child
|
197
|
+
old_child_size = child.size
|
198
|
+
@children[index] = child.put!(key, value)
|
199
|
+
@size += child.size - old_child_size
|
200
|
+
else
|
201
|
+
@children[index] = Trie.new(@significant_bits + 5).put!(key, value)
|
202
|
+
@size += 1
|
203
|
+
end
|
204
|
+
end
|
205
|
+
self
|
206
|
+
end
|
207
|
+
|
208
|
+
# Retrieves the entry corresponding to the given key. If not found, returns <tt>nil</tt>.
|
209
|
+
def get(key)
|
210
|
+
index = index_for(key)
|
211
|
+
entry = @entries[index]
|
212
|
+
if entry && entry[0].eql?(key)
|
213
|
+
entry
|
214
|
+
else
|
215
|
+
child = @children[index]
|
216
|
+
child.get(key) if child
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Returns a copy of <tt>self</tt> with the given key (and associated value) deleted. If not found, returns <tt>self</tt>.
|
221
|
+
def delete(key)
|
222
|
+
find_and_delete(key) || Trie.new(@significant_bits)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Delete multiple elements from a Trie. This is more efficient than
|
226
|
+
# several calls to `#delete`.
|
227
|
+
#
|
228
|
+
# @param keys [Enumerable] The keys to delete
|
229
|
+
# @return [Trie]
|
230
|
+
def bulk_delete(keys)
|
231
|
+
new_entries = nil
|
232
|
+
new_children = nil
|
233
|
+
new_size = @size
|
234
|
+
|
235
|
+
keys.each do |key|
|
236
|
+
index = index_for(key)
|
237
|
+
entry = (new_entries || @entries)[index]
|
238
|
+
if !entry
|
239
|
+
next
|
240
|
+
elsif entry[0].eql?(key)
|
241
|
+
new_entries ||= @entries.dup
|
242
|
+
child = (new_children || @children)[index]
|
243
|
+
if child
|
244
|
+
# Bring up the first entry from the child into entries
|
245
|
+
new_children ||= @children.dup
|
246
|
+
new_children[index] = child.delete_at do |child_entry|
|
247
|
+
new_entries[index] = child_entry
|
248
|
+
end
|
249
|
+
else
|
250
|
+
new_entries[index] = nil
|
251
|
+
end
|
252
|
+
new_size -= 1
|
253
|
+
else
|
254
|
+
child = (new_children || @children)[index]
|
255
|
+
if child
|
256
|
+
copy = child.find_and_delete(key)
|
257
|
+
unless copy.equal?(child)
|
258
|
+
new_children ||= @children.dup
|
259
|
+
new_children[index] = copy
|
260
|
+
new_size -= (child.size - copy_size(copy))
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
if new_entries || new_children
|
267
|
+
Trie.new(@significant_bits, new_size, new_entries || @entries, new_children || @children)
|
268
|
+
else
|
269
|
+
self
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def include?(key, value)
|
274
|
+
entry = get(key)
|
275
|
+
entry && value.eql?(entry[1])
|
276
|
+
end
|
277
|
+
|
278
|
+
def at(index)
|
279
|
+
@entries.each do |entry|
|
280
|
+
if entry
|
281
|
+
return entry if index == 0
|
282
|
+
index -= 1
|
283
|
+
end
|
284
|
+
end
|
285
|
+
@children.each do |child|
|
286
|
+
if child
|
287
|
+
if child.size >= index+1
|
288
|
+
return child.at(index)
|
289
|
+
else
|
290
|
+
index -= child.size
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
nil
|
295
|
+
end
|
296
|
+
|
297
|
+
# Returns <tt>true</tt> if . <tt>eql?</tt> is synonymous with <tt>==</tt>
|
298
|
+
def eql?(other)
|
299
|
+
return true if equal?(other)
|
300
|
+
return false unless instance_of?(other.class) && size == other.size
|
301
|
+
each do |entry|
|
302
|
+
return false unless other.include?(entry[0], entry[1])
|
303
|
+
end
|
304
|
+
true
|
305
|
+
end
|
306
|
+
alias :== :eql?
|
307
|
+
|
308
|
+
protected
|
309
|
+
|
310
|
+
# Returns a replacement instance after removing the specified key.
|
311
|
+
# If not found, returns <tt>self</tt>.
|
312
|
+
# If empty, returns <tt>nil</tt>.
|
313
|
+
def find_and_delete(key)
|
314
|
+
index = index_for(key)
|
315
|
+
entry = @entries[index]
|
316
|
+
if entry && entry[0].eql?(key)
|
317
|
+
return delete_at(index)
|
318
|
+
else
|
319
|
+
child = @children[index]
|
320
|
+
if child
|
321
|
+
copy = child.find_and_delete(key)
|
322
|
+
unless copy.equal?(child)
|
323
|
+
children = @children.dup
|
324
|
+
children[index] = copy
|
325
|
+
new_size = @size - (child.size - copy_size(copy))
|
326
|
+
return Trie.new(@significant_bits, new_size, @entries, children)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
self
|
331
|
+
end
|
332
|
+
|
333
|
+
# Returns a replacement instance after removing the specified entry. If empty, returns <tt>nil</tt>
|
334
|
+
def delete_at(index = @entries.index { |e| e })
|
335
|
+
yield(@entries[index]) if block_given?
|
336
|
+
if size > 1
|
337
|
+
entries = @entries.dup
|
338
|
+
child = @children[index]
|
339
|
+
if child
|
340
|
+
children = @children.dup
|
341
|
+
children[index] = child.delete_at do |entry|
|
342
|
+
entries[index] = entry
|
343
|
+
end
|
344
|
+
else
|
345
|
+
entries[index] = nil
|
346
|
+
end
|
347
|
+
Trie.new(@significant_bits, @size - 1, entries, children || @children)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
private
|
352
|
+
|
353
|
+
def index_for(key)
|
354
|
+
(key.hash.abs >> @significant_bits) & 31
|
355
|
+
end
|
356
|
+
|
357
|
+
def copy_size(copy)
|
358
|
+
copy ? copy.size : 0
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# @private
|
363
|
+
EmptyTrie = Erlang::Trie.new(0)
|
364
|
+
end
|
data/lib/erlang/tuple.rb
CHANGED
@@ -1,23 +1,1591 @@
|
|
1
1
|
module Erlang
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
# A `Tuple` is an ordered, integer-indexed collection of objects. Like
|
3
|
+
# Ruby's `Array`, `Tuple` indexing starts at zero and negative indexes count
|
4
|
+
# back from the end.
|
5
|
+
#
|
6
|
+
# `Tuple` has a similar interface to `Array`. The main difference is methods
|
7
|
+
# that would destructively update an `Array` (such as {#insert} or
|
8
|
+
# {#delete_at}) instead return new `Tuple`s and leave the existing one
|
9
|
+
# unchanged.
|
10
|
+
#
|
11
|
+
# ### Creating New Tuples
|
12
|
+
#
|
13
|
+
# Erlang::Tuple.new([:first, :second, :third])
|
14
|
+
# Erlang::Tuple[1, 2, 3, 4, 5]
|
15
|
+
#
|
16
|
+
# ### Retrieving Elements from Tuples
|
17
|
+
#
|
18
|
+
# tuple = Erlang::Tuple[1, 2, 3, 4, 5]
|
19
|
+
#
|
20
|
+
# tuple[0] # => 1
|
21
|
+
# tuple[-1] # => 5
|
22
|
+
# tuple[0,3] # => Erlang::Tuple[1, 2, 3]
|
23
|
+
# tuple[1..-1] # => Erlang::Tuple[2, 3, 4, 5]
|
24
|
+
# tuple.first # => 1
|
25
|
+
# tuple.last # => 5
|
26
|
+
#
|
27
|
+
# ### Creating Modified Tuples
|
28
|
+
#
|
29
|
+
# tuple.add(6) # => Erlang::Tuple[1, 2, 3, 4, 5, 6]
|
30
|
+
# tuple.insert(1, :a, :b) # => Erlang::Tuple[1, :a, :b, 2, 3, 4, 5]
|
31
|
+
# tuple.delete_at(2) # => Erlang::Tuple[1, 2, 4, 5]
|
32
|
+
# tuple + [6, 7] # => Erlang::Tuple[1, 2, 3, 4, 5, 6, 7]
|
33
|
+
#
|
34
|
+
# Licensing
|
35
|
+
# =========
|
36
|
+
#
|
37
|
+
# Portions taken and modified from https://github.com/hamstergem/hamster
|
38
|
+
#
|
39
|
+
# Copyright (c) 2009-2014 Simon Harris
|
40
|
+
#
|
41
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
42
|
+
# a copy of this software and associated documentation files (the
|
43
|
+
# "Software"), to deal in the Software without restriction, including
|
44
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
45
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
46
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
47
|
+
# the following conditions:
|
48
|
+
#
|
49
|
+
# The above copyright notice and this permission notice shall be
|
50
|
+
# included in all copies or substantial portions of the Software.
|
51
|
+
#
|
52
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
53
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
54
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
55
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
56
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
57
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
58
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
59
|
+
#
|
60
|
+
class Tuple
|
61
|
+
include Erlang::Term
|
62
|
+
include Erlang::Immutable
|
63
|
+
include Erlang::Enumerable
|
64
|
+
include Erlang::Associable
|
65
|
+
|
66
|
+
# @private
|
67
|
+
BLOCK_SIZE = 32
|
68
|
+
# @private
|
69
|
+
INDEX_MASK = BLOCK_SIZE - 1
|
70
|
+
# @private
|
71
|
+
BITS_PER_LEVEL = 5
|
72
|
+
|
73
|
+
# Return the number of elements in this `Tuple`
|
74
|
+
# @return [Integer]
|
75
|
+
attr_reader :size
|
76
|
+
alias :arity :size
|
77
|
+
alias :length :size
|
78
|
+
|
79
|
+
class << self
|
80
|
+
# Create a new `Tuple` populated with the given elements.
|
81
|
+
# @return [Tuple]
|
82
|
+
def [](*elements)
|
83
|
+
return new(elements.freeze)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return an empty `Tuple`. If used on a subclass, returns an empty instance
|
87
|
+
# of that class.
|
88
|
+
#
|
89
|
+
# @return [Tuple]
|
90
|
+
def empty
|
91
|
+
return @empty ||= self.new
|
92
|
+
end
|
93
|
+
|
94
|
+
# "Raw" allocation of a new `Tuple`. Used internally to create a new
|
95
|
+
# instance quickly after building a modified trie.
|
96
|
+
#
|
97
|
+
# @return [Tuple]
|
98
|
+
# @private
|
99
|
+
def alloc(root, size, levels)
|
100
|
+
obj = allocate
|
101
|
+
obj.instance_variable_set(:@root, root)
|
102
|
+
obj.instance_variable_set(:@size, size)
|
103
|
+
obj.instance_variable_set(:@levels, levels)
|
104
|
+
return obj
|
105
|
+
end
|
106
|
+
|
107
|
+
def compare(a, b)
|
108
|
+
raise ArgumentError, "'a' must be of Erlang::Tuple type" if not a.kind_of?(Erlang::Tuple)
|
109
|
+
raise ArgumentError, "'b' must be of Erlang::Tuple type" if not b.kind_of?(Erlang::Tuple)
|
110
|
+
c = a.size <=> b.size
|
111
|
+
i = 0
|
112
|
+
while c == 0 and i < a.size and i < b.size
|
113
|
+
c = Erlang.compare(a[i], b[i])
|
114
|
+
i += 1
|
115
|
+
end
|
116
|
+
return c
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def initialize(elements=[].freeze)
|
121
|
+
elements = elements.to_a.map { |element| Erlang.from(element) }
|
122
|
+
if elements.size <= 32
|
123
|
+
elements = elements.dup.freeze if !elements.frozen?
|
124
|
+
@root, @size, @levels = elements, elements.size, 0
|
125
|
+
else
|
126
|
+
root, size, levels = elements, elements.size, 0
|
127
|
+
while root.size > 32
|
128
|
+
root = root.each_slice(32).to_a
|
129
|
+
levels += 1
|
130
|
+
end
|
131
|
+
@root, @size, @levels = root.freeze, size, levels
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return `true` if this `Tuple` contains no elements.
|
136
|
+
#
|
137
|
+
# @return [Boolean]
|
138
|
+
def empty?
|
139
|
+
return @size == 0
|
140
|
+
end
|
141
|
+
|
142
|
+
# Return the first element in the `Tuple`. If the tuple is empty, return `nil`.
|
143
|
+
#
|
144
|
+
# @example
|
145
|
+
# Erlang::Tuple["A", "B", "C"].first # => "A"
|
146
|
+
#
|
147
|
+
# @return [Object]
|
148
|
+
def first
|
149
|
+
return get(0)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Return the last element in the `Tuple`. If the tuple is empty, return `nil`.
|
153
|
+
#
|
154
|
+
# @example
|
155
|
+
# Erlang::Tuple["A", "B", "C"].last # => "C"
|
156
|
+
#
|
157
|
+
# @return [Object]
|
158
|
+
def last
|
159
|
+
return get(-1)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Return a new `Tuple` with `element` added after the last occupied position.
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# Erlang::Tuple[1, 2].add(99) # => Erlang::Tuple[1, 2, 99]
|
166
|
+
#
|
167
|
+
# @param element [Object] The object to insert at the end of the tuple
|
168
|
+
# @return [Tuple]
|
169
|
+
def add(element)
|
170
|
+
return update_root(@size, Erlang.from(element))
|
171
|
+
end
|
172
|
+
alias :<< :add
|
173
|
+
alias :push :add
|
174
|
+
|
175
|
+
# Return a new `Tuple` with a new value at the given `index`. If `index`
|
176
|
+
# is greater than the length of the tuple, the returned tuple will be
|
177
|
+
# padded with `nil`s to the correct size.
|
178
|
+
#
|
179
|
+
# @overload put(index, element)
|
180
|
+
# Return a new `Tuple` with the element at `index` replaced by `element`.
|
181
|
+
#
|
182
|
+
# @param element [Object] The object to insert into that position
|
183
|
+
# @example
|
184
|
+
# Erlang::Tuple[1, 2, 3, 4].put(2, 99)
|
185
|
+
# # => Erlang::Tuple[1, 2, 99, 4]
|
186
|
+
# Erlang::Tuple[1, 2, 3, 4].put(-1, 99)
|
187
|
+
# # => Erlang::Tuple[1, 2, 3, 99]
|
188
|
+
# Erlang::Tuple[].put(2, 99)
|
189
|
+
# # => Erlang::Tuple[nil, nil, 99]
|
190
|
+
#
|
191
|
+
# @overload put(index)
|
192
|
+
# Return a new `Tuple` with the element at `index` replaced by the return
|
193
|
+
# value of the block.
|
194
|
+
#
|
195
|
+
# @yield (existing) Once with the existing value at the given `index`.
|
196
|
+
# @example
|
197
|
+
# Erlang::Tuple[1, 2, 3, 4].put(2) { |v| v * 10 }
|
198
|
+
# # => Erlang::Tuple[1, 2, 30, 4]
|
199
|
+
#
|
200
|
+
# @param index [Integer] The index to update. May be negative.
|
201
|
+
# @return [Tuple]
|
202
|
+
def put(index, element = yield(get(index)))
|
203
|
+
raise IndexError, "index #{index} outside of tuple bounds" if index < -@size
|
204
|
+
element = Erlang.from(element)
|
205
|
+
index += @size if index < 0
|
206
|
+
if index > @size
|
207
|
+
suffix = Array.new(index - @size, nil)
|
208
|
+
suffix << element
|
209
|
+
return replace_suffix(@size, suffix)
|
210
|
+
else
|
211
|
+
return update_root(index, element)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
alias :set :put
|
215
|
+
|
216
|
+
# @!method update_in(*key_path, &block)
|
217
|
+
# Return a new `Tuple` with a deeply nested value modified to the result
|
218
|
+
# of the given code block. When traversing the nested `Tuple`s and
|
219
|
+
# `Hash`es, non-existing keys are created with empty `Hash` values.
|
220
|
+
#
|
221
|
+
# The code block receives the existing value of the deeply nested key (or
|
222
|
+
# `nil` if it doesn't exist). This is useful for "transforming" the value
|
223
|
+
# associated with a certain key.
|
224
|
+
#
|
225
|
+
# Note that the original `Tuple` and sub-`Tuple`s and sub-`Hash`es are
|
226
|
+
# left unmodified; new data structure copies are created along the path
|
227
|
+
# wherever needed.
|
228
|
+
#
|
229
|
+
# @example
|
230
|
+
# t = Erlang::Tuple[123, 456, 789, Erlang::Map["a" => Erlang::Tuple[5, 6, 7]]]
|
231
|
+
# t.update_in(3, "a", 1) { |value| value + 9 }
|
232
|
+
# # => Erlang::Tuple[123, 456, 789, Erlang::Map["a" => Erlang::Tuple[5, 15, 7]]]
|
233
|
+
#
|
234
|
+
# @param key_path [Object(s)] List of keys which form the path to the key to be modified
|
235
|
+
# @yield [value] The previously stored value
|
236
|
+
# @yieldreturn [Object] The new value to store
|
237
|
+
# @return [Tuple]
|
238
|
+
# @see Associable#update_in
|
239
|
+
|
240
|
+
# Retrieve the element at `index`. If there is none (either the provided index
|
241
|
+
# is too high or too low), return `nil`.
|
242
|
+
#
|
243
|
+
# @example
|
244
|
+
# t = Erlang::Tuple["A", "B", "C", "D"]
|
245
|
+
# t.get(2) # => "C"
|
246
|
+
# t.get(-1) # => "D"
|
247
|
+
# t.get(4) # => nil
|
248
|
+
#
|
249
|
+
# @param index [Integer] The index to retrieve
|
250
|
+
# @return [Object]
|
251
|
+
def get(index)
|
252
|
+
return nil if @size == 0
|
253
|
+
index += @size if index < 0
|
254
|
+
return nil if index >= @size || index < 0
|
255
|
+
return leaf_node_for(@root, @levels * BITS_PER_LEVEL, index)[index & INDEX_MASK]
|
256
|
+
end
|
257
|
+
alias :at :get
|
258
|
+
|
259
|
+
# Retrieve the value at `index` with optional default.
|
260
|
+
#
|
261
|
+
# @overload fetch(index)
|
262
|
+
# Retrieve the value at the given index, or raise an `IndexError` if not
|
263
|
+
# found.
|
264
|
+
#
|
265
|
+
# @param index [Integer] The index to look up
|
266
|
+
# @raise [IndexError] if index does not exist
|
267
|
+
# @example
|
268
|
+
# t = Erlang::Tuple["A", "B", "C", "D"]
|
269
|
+
# t.fetch(2) # => "C"
|
270
|
+
# t.fetch(-1) # => "D"
|
271
|
+
# t.fetch(4) # => IndexError: index 4 outside of tuple bounds
|
272
|
+
#
|
273
|
+
# @overload fetch(index) { |index| ... }
|
274
|
+
# Retrieve the value at the given index, or return the result of yielding
|
275
|
+
# the block if not found.
|
276
|
+
#
|
277
|
+
# @yield Once if the index is not found.
|
278
|
+
# @yieldparam [Integer] index The index which does not exist
|
279
|
+
# @yieldreturn [Object] Default value to return
|
280
|
+
# @param index [Integer] The index to look up
|
281
|
+
# @example
|
282
|
+
# t = Erlang::Tuple["A", "B", "C", "D"]
|
283
|
+
# t.fetch(2) { |i| i * i } # => "C"
|
284
|
+
# t.fetch(4) { |i| i * i } # => 16
|
285
|
+
#
|
286
|
+
# @overload fetch(index, default)
|
287
|
+
# Retrieve the value at the given index, or return the provided `default`
|
288
|
+
# value if not found.
|
289
|
+
#
|
290
|
+
# @param index [Integer] The index to look up
|
291
|
+
# @param default [Object] Object to return if the key is not found
|
292
|
+
# @example
|
293
|
+
# t = Erlang::Tuple["A", "B", "C", "D"]
|
294
|
+
# t.fetch(2, "Z") # => "C"
|
295
|
+
# t.fetch(4, "Z") # => "Z"
|
296
|
+
#
|
297
|
+
# @return [Object]
|
298
|
+
def fetch(index, default = (missing_default = true))
|
299
|
+
if index >= -@size && index < @size
|
300
|
+
return get(index)
|
301
|
+
elsif block_given?
|
302
|
+
return Erlang.from(yield(index))
|
303
|
+
elsif !missing_default
|
304
|
+
return Erlang.from(default)
|
305
|
+
else
|
306
|
+
raise IndexError, "index #{index} outside of tuple bounds"
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
# Return specific objects from the `Tuple`. All overloads return `nil` if
|
311
|
+
# the starting index is out of range.
|
312
|
+
#
|
313
|
+
# @overload tuple.slice(index)
|
314
|
+
# Returns a single object at the given `index`. If `index` is negative,
|
315
|
+
# count backwards from the end.
|
316
|
+
#
|
317
|
+
# @param index [Integer] The index to retrieve. May be negative.
|
318
|
+
# @return [Object]
|
319
|
+
# @example
|
320
|
+
# t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
|
321
|
+
# t[2] # => "C"
|
322
|
+
# t[-1] # => "F"
|
323
|
+
# t[6] # => nil
|
324
|
+
#
|
325
|
+
# @overload tuple.slice(index, length)
|
326
|
+
# Return a subtuple starting at `index` and continuing for `length`
|
327
|
+
# elements or until the end of the `Tuple`, whichever occurs first.
|
328
|
+
#
|
329
|
+
# @param start [Integer] The index to start retrieving elements from. May be
|
330
|
+
# negative.
|
331
|
+
# @param length [Integer] The number of elements to retrieve.
|
332
|
+
# @return [Tuple]
|
333
|
+
# @example
|
334
|
+
# t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
|
335
|
+
# t[2, 3] # => Erlang::Tuple["C", "D", "E"]
|
336
|
+
# t[-2, 3] # => Erlang::Tuple["E", "F"]
|
337
|
+
# t[20, 1] # => nil
|
338
|
+
#
|
339
|
+
# @overload tuple.slice(index..end)
|
340
|
+
# Return a subtuple starting at `index` and continuing to index
|
341
|
+
# `end` or the end of the `Tuple`, whichever occurs first.
|
342
|
+
#
|
343
|
+
# @param range [Range] The range of indices to retrieve.
|
344
|
+
# @return [Tuple]
|
345
|
+
# @example
|
346
|
+
# t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
|
347
|
+
# t[2..3] # => Erlang::Tuple["C", "D"]
|
348
|
+
# t[-2..100] # => Erlang::Tuple["E", "F"]
|
349
|
+
# t[20..21] # => nil
|
350
|
+
def slice(arg, length = (missing_length = true))
|
351
|
+
if missing_length
|
352
|
+
if arg.is_a?(Range)
|
353
|
+
from, to = arg.begin, arg.end
|
354
|
+
from += @size if from < 0
|
355
|
+
to += @size if to < 0
|
356
|
+
to += 1 if !arg.exclude_end?
|
357
|
+
length = to - from
|
358
|
+
length = 0 if length < 0
|
359
|
+
return subsequence(from, length)
|
360
|
+
else
|
361
|
+
return get(arg)
|
362
|
+
end
|
363
|
+
else
|
364
|
+
arg += @size if arg < 0
|
365
|
+
return subsequence(arg, length)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
alias :[] :slice
|
369
|
+
|
370
|
+
# Return a new `Tuple` with the given values inserted before the element
|
371
|
+
# at `index`. If `index` is greater than the current length, `nil` values
|
372
|
+
# are added to pad the `Tuple` to the required size.
|
373
|
+
#
|
374
|
+
# @example
|
375
|
+
# Erlang::Tuple["A", "B", "C", "D"].insert(2, "X", "Y", "Z")
|
376
|
+
# # => Erlang::Tuple["A", "B", "X", "Y", "Z", "C", "D"]
|
377
|
+
# Erlang::Tuple[].insert(2, "X", "Y", "Z")
|
378
|
+
# # => Erlang::Tuple[nil, nil, "X", "Y", "Z"]
|
379
|
+
#
|
380
|
+
# @param index [Integer] The index where the new elements should go
|
381
|
+
# @param elements [Array] The elements to add
|
382
|
+
# @return [Tuple]
|
383
|
+
# @raise [IndexError] if index exceeds negative range.
|
384
|
+
def insert(index, *elements)
|
385
|
+
raise IndexError if index < -@size
|
386
|
+
index += @size if index < 0
|
387
|
+
|
388
|
+
elements = elements.map { |element| Erlang.from(element) }
|
389
|
+
|
390
|
+
if index < @size
|
391
|
+
suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
|
392
|
+
suffix.unshift(*elements)
|
393
|
+
elsif index == @size
|
394
|
+
suffix = elements
|
395
|
+
else
|
396
|
+
suffix = Array.new(index - @size, nil).concat(elements)
|
397
|
+
index = @size
|
398
|
+
end
|
399
|
+
|
400
|
+
return replace_suffix(index, suffix)
|
401
|
+
end
|
402
|
+
|
403
|
+
# Return a new `Tuple` with the element at `index` removed. If the given `index`
|
404
|
+
# does not exist, return `self`.
|
405
|
+
#
|
406
|
+
# @example
|
407
|
+
# Erlang::Tuple["A", "B", "C", "D"].delete_at(2)
|
408
|
+
# # => Erlang::Tuple["A", "B", "D"]
|
409
|
+
#
|
410
|
+
# @param index [Integer] The index to remove
|
411
|
+
# @return [Tuple]
|
412
|
+
def delete_at(index)
|
413
|
+
return self if index >= @size || index < -@size
|
414
|
+
index += @size if index < 0
|
415
|
+
|
416
|
+
suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
|
417
|
+
return replace_suffix(index, suffix.tap { |a| a.shift })
|
418
|
+
end
|
419
|
+
|
420
|
+
# Return a new `Tuple` with the last element removed. Return `self` if
|
421
|
+
# empty.
|
422
|
+
#
|
423
|
+
# @example
|
424
|
+
# Erlang::Tuple["A", "B", "C"].pop # => Erlang::Tuple["A", "B"]
|
425
|
+
#
|
426
|
+
# @return [Tuple]
|
427
|
+
def pop
|
428
|
+
return self if @size == 0
|
429
|
+
return replace_suffix(@size-1, [])
|
430
|
+
end
|
431
|
+
|
432
|
+
# Return a new `Tuple` with `object` inserted before the first element,
|
433
|
+
# moving the other elements upwards.
|
434
|
+
#
|
435
|
+
# @example
|
436
|
+
# Erlang::Tuple["A", "B"].unshift("Z")
|
437
|
+
# # => Erlang::Tuple["Z", "A", "B"]
|
438
|
+
#
|
439
|
+
# @param object [Object] The value to prepend
|
440
|
+
# @return [Tuple]
|
441
|
+
def unshift(object)
|
442
|
+
return insert(0, Erlang.from(object))
|
443
|
+
end
|
444
|
+
|
445
|
+
# Return a new `Tuple` with the first element removed. If empty, return
|
446
|
+
# `self`.
|
447
|
+
#
|
448
|
+
# @example
|
449
|
+
# Erlang::Tuple["A", "B", "C"].shift # => Erlang::Tuple["B", "C"]
|
450
|
+
#
|
451
|
+
# @return [Tuple]
|
452
|
+
def shift
|
453
|
+
return delete_at(0)
|
454
|
+
end
|
455
|
+
|
456
|
+
# Call the given block once for each element in the tuple, passing each
|
457
|
+
# element from first to last successively to the block. If no block is given,
|
458
|
+
# an `Enumerator` is returned instead.
|
459
|
+
#
|
460
|
+
# @example
|
461
|
+
# Erlang::Tuple["A", "B", "C"].each { |e| puts "Element: #{e}" }
|
462
|
+
#
|
463
|
+
# Element: A
|
464
|
+
# Element: B
|
465
|
+
# Element: C
|
466
|
+
# # => Erlang::Tuple["A", "B", "C"]
|
467
|
+
#
|
468
|
+
# @return [self, Enumerator]
|
469
|
+
def each(&block)
|
470
|
+
return to_enum unless block_given?
|
471
|
+
traverse_depth_first(@root, @levels, &block)
|
472
|
+
return self
|
473
|
+
end
|
474
|
+
|
475
|
+
# Call the given block once for each element in the tuple, from last to
|
476
|
+
# first.
|
477
|
+
#
|
478
|
+
# @example
|
479
|
+
# Erlang::Tuple["A", "B", "C"].reverse_each { |e| puts "Element: #{e}" }
|
480
|
+
#
|
481
|
+
# Element: C
|
482
|
+
# Element: B
|
483
|
+
# Element: A
|
484
|
+
#
|
485
|
+
# @return [self]
|
486
|
+
def reverse_each(&block)
|
487
|
+
return enum_for(:reverse_each) unless block_given?
|
488
|
+
reverse_traverse_depth_first(@root, @levels, &block)
|
489
|
+
return self
|
490
|
+
end
|
491
|
+
|
492
|
+
# Return a new `Tuple` containing all elements for which the given block returns
|
493
|
+
# true.
|
494
|
+
#
|
495
|
+
# @example
|
496
|
+
# Erlang::Tuple["Bird", "Cow", "Elephant"].select { |e| e.size >= 4 }
|
497
|
+
# # => Erlang::Tuple["Bird", "Elephant"]
|
498
|
+
#
|
499
|
+
# @return [Tuple]
|
500
|
+
# @yield [element] Once for each element.
|
501
|
+
def select
|
502
|
+
return enum_for(:select) unless block_given?
|
503
|
+
return reduce(self.class.empty) { |tuple, element| yield(element) ? tuple.add(element) : tuple }
|
504
|
+
end
|
505
|
+
alias :find_all :select
|
506
|
+
alias :keep_if :select
|
507
|
+
|
508
|
+
# Return a new `Tuple` with all elements which are equal to `obj` removed.
|
509
|
+
# `#==` is used for checking equality.
|
510
|
+
#
|
511
|
+
# @example
|
512
|
+
# Erlang::Tuple["C", "B", "A", "B"].delete("B") # => Erlang::Tuple["C", "A"]
|
513
|
+
#
|
514
|
+
# @param obj [Object] The object to remove (every occurrence)
|
515
|
+
# @return [Tuple]
|
516
|
+
def delete(obj)
|
517
|
+
obj = Erlang.from(obj)
|
518
|
+
return select { |element| element != obj }
|
519
|
+
end
|
520
|
+
|
521
|
+
# Invoke the given block once for each element in the tuple, and return a new
|
522
|
+
# `Tuple` containing the values returned by the block. If no block is
|
523
|
+
# provided, return an enumerator.
|
524
|
+
#
|
525
|
+
# @example
|
526
|
+
# Erlang::Tuple[3, 2, 1].map { |e| e * e } # => Erlang::Tuple[9, 4, 1]
|
527
|
+
#
|
528
|
+
# @return [Tuple, Enumerator]
|
529
|
+
def map
|
530
|
+
return enum_for(:map) if not block_given?
|
531
|
+
return self if empty?
|
532
|
+
return self.class.new(super)
|
533
|
+
end
|
534
|
+
alias :collect :map
|
535
|
+
|
536
|
+
# Return a new `Tuple` with the concatenated results of running the block once
|
537
|
+
# for every element in this `Tuple`.
|
538
|
+
#
|
539
|
+
# @example
|
540
|
+
# Erlang::Tuple[1, 2, 3].flat_map { |x| [x, -x] }
|
541
|
+
# # => Erlang::Tuple[1, -1, 2, -2, 3, -3]
|
542
|
+
#
|
543
|
+
# @return [Tuple]
|
544
|
+
def flat_map
|
545
|
+
return enum_for(:flat_map) if not block_given?
|
546
|
+
return self if empty?
|
547
|
+
return self.class.new(super)
|
548
|
+
end
|
549
|
+
|
550
|
+
# Return a new `Tuple` with the same elements as this one, but randomly permuted.
|
551
|
+
#
|
552
|
+
# @example
|
553
|
+
# Erlang::Tuple[1, 2, 3, 4].shuffle # => Erlang::Tuple[4, 1, 3, 2]
|
554
|
+
#
|
555
|
+
# @return [Tuple]
|
556
|
+
def shuffle
|
557
|
+
return self.class.new(((array = to_a).frozen? ? array.shuffle : array.shuffle!).freeze)
|
558
|
+
end
|
559
|
+
|
560
|
+
# Return a new `Tuple` with no duplicate elements, as determined by `#hash` and
|
561
|
+
# `#eql?`. For each group of equivalent elements, only the first will be retained.
|
562
|
+
#
|
563
|
+
# @example
|
564
|
+
# Erlang::Tuple["A", "B", "C", "B"].uniq # => Erlang::Tuple["A", "B", "C"]
|
565
|
+
# Erlang::Tuple["a", "A", "b"].uniq(&:upcase) # => Erlang::Tuple["a", "b"]
|
566
|
+
#
|
567
|
+
# @return [Tuple]
|
568
|
+
def uniq(&block)
|
569
|
+
array = self.to_a
|
570
|
+
if block_given?
|
571
|
+
if array.frozen?
|
572
|
+
return self.class.new(array.uniq(&block).freeze)
|
573
|
+
elsif array.uniq!(&block) # returns nil if no changes were made
|
574
|
+
return self.class.new(array.freeze)
|
575
|
+
else
|
576
|
+
return self
|
577
|
+
end
|
578
|
+
elsif array.frozen?
|
579
|
+
return self.class.new(array.uniq.freeze)
|
580
|
+
elsif array.uniq! # returns nil if no changes were made
|
581
|
+
return self.class.new(array.freeze)
|
582
|
+
else
|
583
|
+
return self
|
584
|
+
end
|
585
|
+
end
|
586
|
+
|
587
|
+
# Return a new `Tuple` with the same elements as this one, but in reverse order.
|
588
|
+
#
|
589
|
+
# @example
|
590
|
+
# Erlang::Tuple["A", "B", "C"].reverse # => Erlang::Tuple["C", "B", "A"]
|
591
|
+
#
|
592
|
+
# @return [Tuple]
|
593
|
+
def reverse
|
594
|
+
return self.class.new(((array = to_a).frozen? ? array.reverse : array.reverse!).freeze)
|
595
|
+
end
|
596
|
+
|
597
|
+
# Return a new `Tuple` with the same elements, but rotated so that the one at
|
598
|
+
# index `count` is the first element of the new tuple. If `count` is positive,
|
599
|
+
# the elements will be shifted left, and those shifted past the lowest position
|
600
|
+
# will be moved to the end. If `count` is negative, the elements will be shifted
|
601
|
+
# right, and those shifted past the last position will be moved to the beginning.
|
602
|
+
#
|
603
|
+
# @example
|
604
|
+
# t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
|
605
|
+
# t.rotate(2) # => Erlang::Tuple["C", "D", "E", "F", "A", "B"]
|
606
|
+
# t.rotate(-1) # => Erlang::Tuple["F", "A", "B", "C", "D", "E"]
|
607
|
+
#
|
608
|
+
# @param count [Integer] The number of positions to shift elements by
|
609
|
+
# @return [Tuple]
|
610
|
+
def rotate(count = 1)
|
611
|
+
return self if (count % @size) == 0
|
612
|
+
return self.class.new(((array = to_a).frozen? ? array.rotate(count) : array.rotate!(count)).freeze)
|
613
|
+
end
|
614
|
+
|
615
|
+
# Return a new `Tuple` with all nested tuples and arrays recursively "flattened
|
616
|
+
# out". That is, their elements inserted into the new `Tuple` in the place where
|
617
|
+
# the nested array/tuple originally was. If an optional `level` argument is
|
618
|
+
# provided, the flattening will only be done recursively that number of times.
|
619
|
+
# A `level` of 0 means not to flatten at all, 1 means to only flatten nested
|
620
|
+
# arrays/tuples which are directly contained within this `Tuple`.
|
621
|
+
#
|
622
|
+
# @example
|
623
|
+
# t = Erlang::Tuple["A", Erlang::Tuple["B", "C", Erlang::Tuple["D"]]]
|
624
|
+
# t.flatten(1)
|
625
|
+
# # => Erlang::Tuple["A", "B", "C", Erlang::Tuple["D"]]
|
626
|
+
# t.flatten
|
627
|
+
# # => Erlang::Tuple["A", "B", "C", "D"]
|
628
|
+
#
|
629
|
+
# @param level [Integer] The depth to which flattening should be applied
|
630
|
+
# @return [Tuple]
|
631
|
+
def flatten(level = -1)
|
632
|
+
return self if level == 0
|
633
|
+
array = self.to_a
|
634
|
+
if array.frozen?
|
635
|
+
return self.class.new(array.flatten(level).freeze)
|
636
|
+
elsif array.flatten!(level) # returns nil if no changes were made
|
637
|
+
return self.class.new(array.freeze)
|
638
|
+
else
|
639
|
+
return self
|
640
|
+
end
|
641
|
+
end
|
642
|
+
|
643
|
+
# Return a new `Tuple` built by concatenating this one with `other`. `other`
|
644
|
+
# can be any object which is convertible to an `Array` using `#to_a`.
|
645
|
+
#
|
646
|
+
# @example
|
647
|
+
# Erlang::Tuple["A", "B", "C"] + ["D", "E"]
|
648
|
+
# # => Erlang::Tuple["A", "B", "C", "D", "E"]
|
649
|
+
#
|
650
|
+
# @param other [Enumerable] The collection to concatenate onto this tuple
|
651
|
+
# @return [Tuple]
|
652
|
+
def +(other)
|
653
|
+
other = Erlang.from(other)
|
654
|
+
other = other.to_a
|
655
|
+
other = other.dup if other.frozen?
|
656
|
+
return replace_suffix(@size, other)
|
657
|
+
end
|
658
|
+
alias :concat :+
|
659
|
+
|
660
|
+
# Combine two tuples by "zipping" them together. `others` should be arrays
|
661
|
+
# and/or tuples. The corresponding elements from this `Tuple` and each of
|
662
|
+
# `others` (that is, the elements with the same indices) will be gathered
|
663
|
+
# into arrays.
|
664
|
+
#
|
665
|
+
# If `others` contains fewer elements than this tuple, `nil` will be used
|
666
|
+
# for padding.
|
667
|
+
#
|
668
|
+
# @overload zip(*others)
|
669
|
+
# Return a new tuple containing the new arrays.
|
670
|
+
#
|
671
|
+
# @return [Tuple]
|
672
|
+
#
|
673
|
+
# @overload zip(*others)
|
674
|
+
# @yield [pair] once for each array
|
675
|
+
# @return [nil]
|
676
|
+
#
|
677
|
+
# @example
|
678
|
+
# t1 = Erlang::Tuple["A", "B", "C"]
|
679
|
+
# t2 = Erlang::Tuple[1, 2]
|
680
|
+
# t1.zip(t2)
|
681
|
+
# # => Erlang::Tuple[["A", 1], ["B", 2], ["C", nil]]
|
682
|
+
#
|
683
|
+
# @param others [Array] The arrays/tuples to zip together with this one
|
684
|
+
# @return [Tuple]
|
685
|
+
def zip(*others)
|
686
|
+
others = others.map { |other| Erlang.from(other) }
|
687
|
+
if block_given?
|
688
|
+
return super(*others)
|
689
|
+
else
|
690
|
+
return self.class.new(super(*others))
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
# Return a new `Tuple` with the same elements, but sorted.
|
695
|
+
#
|
696
|
+
# @overload sort
|
697
|
+
# Compare elements with their natural sort key (`#<=>`).
|
698
|
+
#
|
699
|
+
# @example
|
700
|
+
# Erlang::Tuple["Elephant", "Dog", "Lion"].sort
|
701
|
+
# # => Erlang::Tuple["Dog", "Elephant", "Lion"]
|
702
|
+
#
|
703
|
+
# @overload sort
|
704
|
+
# Uses the block as a comparator to determine sorted order.
|
705
|
+
#
|
706
|
+
# @yield [a, b] Any number of times with different pairs of elements.
|
707
|
+
# @yieldreturn [Integer] Negative if the first element should be sorted
|
708
|
+
# lower, positive if the latter element, or 0 if
|
709
|
+
# equal.
|
710
|
+
# @example
|
711
|
+
# Erlang::Tuple["Elephant", "Dog", "Lion"].sort { |a,b| a.size <=> b.size }
|
712
|
+
# # => Erlang::Tuple["Dog", "Lion", "Elephant"]
|
713
|
+
#
|
714
|
+
# @return [Tuple]
|
715
|
+
def sort(&comparator)
|
716
|
+
comparator = Erlang.method(:compare) unless block_given?
|
717
|
+
array = super(&comparator)
|
718
|
+
return self.class.new(array)
|
719
|
+
end
|
720
|
+
|
721
|
+
# Return a new `Tuple` with the same elements, but sorted. The sort order is
|
722
|
+
# determined by mapping the elements through the given block to obtain sort
|
723
|
+
# keys, and then sorting the keys according to their natural sort order
|
724
|
+
# (`#<=>`).
|
725
|
+
#
|
726
|
+
# @yield [element] Once for each element.
|
727
|
+
# @yieldreturn a sort key object for the yielded element.
|
728
|
+
# @example
|
729
|
+
# Erlang::Tuple["Elephant", "Dog", "Lion"].sort_by { |e| e.size }
|
730
|
+
# # => Erlang::Tuple["Dog", "Lion", "Elephant"]
|
731
|
+
#
|
732
|
+
# @return [Tuple]
|
733
|
+
def sort_by
|
734
|
+
return sort unless block_given?
|
735
|
+
block = ->(x) { Erlang.from(transformer.call(x)) }
|
736
|
+
array = super(&block)
|
737
|
+
return self.class.new(array)
|
738
|
+
end
|
739
|
+
|
740
|
+
# Drop the first `n` elements and return the rest in a new `Tuple`.
|
741
|
+
#
|
742
|
+
# @example
|
743
|
+
# Erlang::Tuple["A", "B", "C", "D", "E", "F"].drop(2)
|
744
|
+
# # => Erlang::Tuple["C", "D", "E", "F"]
|
745
|
+
#
|
746
|
+
# @param n [Integer] The number of elements to remove
|
747
|
+
# @return [Tuple]
|
748
|
+
# @raise ArgumentError if `n` is negative.
|
749
|
+
def drop(n)
|
750
|
+
return self if n == 0
|
751
|
+
return self.class.empty if n >= @size
|
752
|
+
raise ArgumentError, "attempt to drop negative size" if n < 0
|
753
|
+
return self.class.new(flatten_suffix(@root, @levels * BITS_PER_LEVEL, n, []))
|
754
|
+
end
|
755
|
+
|
756
|
+
# Return only the first `n` elements in a new `Tuple`.
|
757
|
+
#
|
758
|
+
# @example
|
759
|
+
# Erlang::Tuple["A", "B", "C", "D", "E", "F"].take(4)
|
760
|
+
# # => Erlang::Tuple["A", "B", "C", "D"]
|
761
|
+
#
|
762
|
+
# @param n [Integer] The number of elements to retain
|
763
|
+
# @return [Tuple]
|
764
|
+
def take(n)
|
765
|
+
return self if n >= @size
|
766
|
+
return self.class.new(super)
|
767
|
+
end
|
768
|
+
|
769
|
+
# Drop elements up to, but not including, the first element for which the
|
770
|
+
# block returns `nil` or `false`. Gather the remaining elements into a new
|
771
|
+
# `Tuple`. If no block is given, an `Enumerator` is returned instead.
|
772
|
+
#
|
773
|
+
# @example
|
774
|
+
# Erlang::Tuple[1, 3, 5, 7, 6, 4, 2].drop_while { |e| e < 5 }
|
775
|
+
# # => Erlang::Tuple[5, 7, 6, 4, 2]
|
776
|
+
#
|
777
|
+
# @return [Tuple, Enumerator]
|
778
|
+
def drop_while
|
779
|
+
return enum_for(:drop_while) if not block_given?
|
780
|
+
return self.class.new(super)
|
781
|
+
end
|
782
|
+
|
783
|
+
# Gather elements up to, but not including, the first element for which the
|
784
|
+
# block returns `nil` or `false`, and return them in a new `Tuple`. If no block
|
785
|
+
# is given, an `Enumerator` is returned instead.
|
786
|
+
#
|
787
|
+
# @example
|
788
|
+
# Erlang::Tuple[1, 3, 5, 7, 6, 4, 2].take_while { |e| e < 5 }
|
789
|
+
# # => Erlang::Tuple[1, 3]
|
790
|
+
#
|
791
|
+
# @return [Tuple, Enumerator]
|
792
|
+
def take_while
|
793
|
+
return enum_for(:take_while) if not block_given?
|
794
|
+
return self.class.new(super)
|
795
|
+
end
|
796
|
+
|
797
|
+
# Repetition. Return a new `Tuple` built by concatenating `times` copies
|
798
|
+
# of this one together.
|
799
|
+
#
|
800
|
+
# @example
|
801
|
+
# Erlang::Tuple["A", "B"] * 3
|
802
|
+
# # => Erlang::Tuple["A", "B", "A", "B", "A", "B"]
|
803
|
+
#
|
804
|
+
# @param times [Integer] The number of times to repeat the elements in this tuple
|
805
|
+
# @return [Tuple]
|
806
|
+
def *(times)
|
807
|
+
return self.class.empty if times == 0
|
808
|
+
return self if times == 1
|
809
|
+
result = (to_a * times)
|
810
|
+
return result.is_a?(Array) ? self.class.new(result) : result
|
5
811
|
end
|
6
812
|
|
7
|
-
|
8
|
-
|
813
|
+
# Replace a range of indexes with the given object.
|
814
|
+
#
|
815
|
+
# @overload fill(object)
|
816
|
+
# Return a new `Tuple` of the same size, with every index set to
|
817
|
+
# `object`.
|
818
|
+
#
|
819
|
+
# @param [Object] object Fill value.
|
820
|
+
# @example
|
821
|
+
# Erlang::Tuple["A", "B", "C", "D", "E", "F"].fill("Z")
|
822
|
+
# # => Erlang::Tuple["Z", "Z", "Z", "Z", "Z", "Z"]
|
823
|
+
#
|
824
|
+
# @overload fill(object, index)
|
825
|
+
# Return a new `Tuple` with all indexes from `index` to the end of the
|
826
|
+
# tuple set to `object`.
|
827
|
+
#
|
828
|
+
# @param [Object] object Fill value.
|
829
|
+
# @param [Integer] index Starting index. May be negative.
|
830
|
+
# @example
|
831
|
+
# Erlang::Tuple["A", "B", "C", "D", "E", "F"].fill("Z", 3)
|
832
|
+
# # => Erlang::Tuple["A", "B", "C", "Z", "Z", "Z"]
|
833
|
+
#
|
834
|
+
# @overload fill(object, index, length)
|
835
|
+
# Return a new `Tuple` with `length` indexes, beginning from `index`,
|
836
|
+
# set to `object`. Expands the `Tuple` if `length` would extend beyond
|
837
|
+
# the current length.
|
838
|
+
#
|
839
|
+
# @param [Object] object Fill value.
|
840
|
+
# @param [Integer] index Starting index. May be negative.
|
841
|
+
# @param [Integer] length
|
842
|
+
# @example
|
843
|
+
# Erlang::Tuple["A", "B", "C", "D", "E", "F"].fill("Z", 3, 2)
|
844
|
+
# # => Erlang::Tuple["A", "B", "C", "Z", "Z", "F"]
|
845
|
+
# Erlang::Tuple["A", "B"].fill("Z", 1, 5)
|
846
|
+
# # => Erlang::Tuple["A", "Z", "Z", "Z", "Z", "Z"]
|
847
|
+
#
|
848
|
+
# @return [Tuple]
|
849
|
+
# @raise [IndexError] if index is out of negative range.
|
850
|
+
def fill(object, index = 0, length = nil)
|
851
|
+
raise IndexError if index < -@size
|
852
|
+
object = Erlang.from(object)
|
853
|
+
index += @size if index < 0
|
854
|
+
length ||= @size - index # to the end of the array, if no length given
|
855
|
+
|
856
|
+
if index < @size
|
857
|
+
suffix = flatten_suffix(@root, @levels * BITS_PER_LEVEL, index, [])
|
858
|
+
suffix.fill(object, 0, length)
|
859
|
+
elsif index == @size
|
860
|
+
suffix = Array.new(length, object)
|
861
|
+
else
|
862
|
+
suffix = Array.new(index - @size, nil).concat(Array.new(length, object))
|
863
|
+
index = @size
|
864
|
+
end
|
865
|
+
|
866
|
+
return replace_suffix(index, suffix)
|
867
|
+
end
|
868
|
+
|
869
|
+
# When invoked with a block, yields all combinations of length `n` of elements
|
870
|
+
# from the `Tuple`, and then returns `self`. There is no guarantee about
|
871
|
+
# which order the combinations will be yielded.
|
872
|
+
#
|
873
|
+
# If no block is given, an `Enumerator` is returned instead.
|
874
|
+
#
|
875
|
+
# @example
|
876
|
+
# t = Erlang::Tuple[5, 6, 7, 8]
|
877
|
+
# t.combination(3) { |c| puts "Combination: #{c}" }
|
878
|
+
#
|
879
|
+
# Combination: [5, 6, 7]
|
880
|
+
# Combination: [5, 6, 8]
|
881
|
+
# Combination: [5, 7, 8]
|
882
|
+
# Combination: [6, 7, 8]
|
883
|
+
# #=> Erlang::Tuple[5, 6, 7, 8]
|
884
|
+
#
|
885
|
+
# @return [self, Enumerator]
|
886
|
+
def combination(n)
|
887
|
+
return enum_for(:combination, n) if not block_given?
|
888
|
+
return self if n < 0 || @size < n
|
889
|
+
if n == 0
|
890
|
+
yield []
|
891
|
+
elsif n == 1
|
892
|
+
each { |element| yield [element] }
|
893
|
+
elsif n == @size
|
894
|
+
yield self.to_a
|
895
|
+
else
|
896
|
+
combos = lambda do |result,index,remaining|
|
897
|
+
while @size - index > remaining
|
898
|
+
if remaining == 1
|
899
|
+
yield result.dup << get(index)
|
900
|
+
else
|
901
|
+
combos[result.dup << get(index), index+1, remaining-1]
|
902
|
+
end
|
903
|
+
index += 1
|
904
|
+
end
|
905
|
+
index.upto(@size-1) { |i| result << get(i) }
|
906
|
+
yield result
|
907
|
+
end
|
908
|
+
combos[[], 0, n]
|
909
|
+
end
|
910
|
+
return self
|
911
|
+
end
|
912
|
+
|
913
|
+
# When invoked with a block, yields all repeated combinations of length `n` of
|
914
|
+
# tuples from the `Tuple`, and then returns `self`. A "repeated combination" is
|
915
|
+
# one in which any tuple from the `Tuple` can appear consecutively any number of
|
916
|
+
# times.
|
917
|
+
#
|
918
|
+
# There is no guarantee about which order the combinations will be yielded in.
|
919
|
+
#
|
920
|
+
# If no block is given, an `Enumerator` is returned instead.
|
921
|
+
#
|
922
|
+
# @example
|
923
|
+
# t = Erlang::Tuple[5, 6, 7, 8]
|
924
|
+
# t.repeated_combination(2) { |c| puts "Combination: #{c}" }
|
925
|
+
#
|
926
|
+
# Combination: [5, 5]
|
927
|
+
# Combination: [5, 6]
|
928
|
+
# Combination: [5, 7]
|
929
|
+
# Combination: [5, 8]
|
930
|
+
# Combination: [6, 6]
|
931
|
+
# Combination: [6, 7]
|
932
|
+
# Combination: [6, 8]
|
933
|
+
# Combination: [7, 7]
|
934
|
+
# Combination: [7, 8]
|
935
|
+
# Combination: [8, 8]
|
936
|
+
# # => Erlang::Tuple[5, 6, 7, 8]
|
937
|
+
#
|
938
|
+
# @return [self, Enumerator]
|
939
|
+
def repeated_combination(n)
|
940
|
+
return enum_for(:repeated_combination, n) if not block_given?
|
941
|
+
if n < 0
|
942
|
+
# yield nothing
|
943
|
+
elsif n == 0
|
944
|
+
yield []
|
945
|
+
elsif n == 1
|
946
|
+
each { |element| yield [element] }
|
947
|
+
elsif @size == 0
|
948
|
+
# yield nothing
|
949
|
+
else
|
950
|
+
combos = lambda do |result,index,remaining|
|
951
|
+
while index < @size-1
|
952
|
+
if remaining == 1
|
953
|
+
yield result.dup << get(index)
|
954
|
+
else
|
955
|
+
combos[result.dup << get(index), index, remaining-1]
|
956
|
+
end
|
957
|
+
index += 1
|
958
|
+
end
|
959
|
+
element = get(index)
|
960
|
+
remaining.times { result << element }
|
961
|
+
yield result
|
962
|
+
end
|
963
|
+
combos[[], 0, n]
|
964
|
+
end
|
965
|
+
return self
|
966
|
+
end
|
967
|
+
|
968
|
+
# Yields all permutations of length `n` of elements from the `Tuple`, and then
|
969
|
+
# returns `self`. If no length `n` is specified, permutations of all elements
|
970
|
+
# will be yielded.
|
971
|
+
#
|
972
|
+
# There is no guarantee about which order the permutations will be yielded in.
|
973
|
+
#
|
974
|
+
# If no block is given, an `Enumerator` is returned instead.
|
975
|
+
#
|
976
|
+
# @example
|
977
|
+
# t = Erlang::Tuple[5, 6, 7]
|
978
|
+
# t.permutation(2) { |p| puts "Permutation: #{p}" }
|
979
|
+
#
|
980
|
+
# Permutation: [5, 6]
|
981
|
+
# Permutation: [5, 7]
|
982
|
+
# Permutation: [6, 5]
|
983
|
+
# Permutation: [6, 7]
|
984
|
+
# Permutation: [7, 5]
|
985
|
+
# Permutation: [7, 6]
|
986
|
+
# # => Erlang::Tuple[5, 6, 7]
|
987
|
+
#
|
988
|
+
# @return [self, Enumerator]
|
989
|
+
def permutation(n = @size)
|
990
|
+
return enum_for(:permutation, n) if not block_given?
|
991
|
+
if n < 0 || @size < n
|
992
|
+
# yield nothing
|
993
|
+
elsif n == 0
|
994
|
+
yield []
|
995
|
+
elsif n == 1
|
996
|
+
each { |element| yield [element] }
|
997
|
+
else
|
998
|
+
used, result = [], []
|
999
|
+
perms = lambda do |index|
|
1000
|
+
0.upto(@size-1) do |i|
|
1001
|
+
if !used[i]
|
1002
|
+
result[index] = get(i)
|
1003
|
+
if index < n-1
|
1004
|
+
used[i] = true
|
1005
|
+
perms[index+1]
|
1006
|
+
used[i] = false
|
1007
|
+
else
|
1008
|
+
yield result.dup
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
perms[0]
|
1014
|
+
end
|
1015
|
+
return self
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
# When invoked with a block, yields all repeated permutations of length `n` of
|
1019
|
+
# elements from the `Tuple`, and then returns `self`. A "repeated permutation" is
|
1020
|
+
# one where any element from the `Tuple` can appear any number of times, and in
|
1021
|
+
# any position (not just consecutively)
|
1022
|
+
#
|
1023
|
+
# If no length `n` is specified, permutations of all elements will be yielded.
|
1024
|
+
# There is no guarantee about which order the permutations will be yielded in.
|
1025
|
+
#
|
1026
|
+
# If no block is given, an `Enumerator` is returned instead.
|
1027
|
+
#
|
1028
|
+
# @example
|
1029
|
+
# t = Erlang::Tuple[5, 6, 7]
|
1030
|
+
# t.repeated_permutation(2) { |p| puts "Permutation: #{p}" }
|
1031
|
+
#
|
1032
|
+
# Permutation: [5, 5]
|
1033
|
+
# Permutation: [5, 6]
|
1034
|
+
# Permutation: [5, 7]
|
1035
|
+
# Permutation: [6, 5]
|
1036
|
+
# Permutation: [6, 6]
|
1037
|
+
# Permutation: [6, 7]
|
1038
|
+
# Permutation: [7, 5]
|
1039
|
+
# Permutation: [7, 6]
|
1040
|
+
# Permutation: [7, 7]
|
1041
|
+
# # => Erlang::Tuple[5, 6, 7]
|
1042
|
+
#
|
1043
|
+
# @return [self, Enumerator]
|
1044
|
+
def repeated_permutation(n = @size)
|
1045
|
+
return enum_for(:repeated_permutation, n) if not block_given?
|
1046
|
+
if n < 0
|
1047
|
+
# yield nothing
|
1048
|
+
elsif n == 0
|
1049
|
+
yield []
|
1050
|
+
elsif n == 1
|
1051
|
+
each { |element| yield [element] }
|
1052
|
+
else
|
1053
|
+
result = []
|
1054
|
+
perms = lambda do |index|
|
1055
|
+
0.upto(@size-1) do |i|
|
1056
|
+
result[index] = get(i)
|
1057
|
+
if index < n-1
|
1058
|
+
perms[index+1]
|
1059
|
+
else
|
1060
|
+
yield result.dup
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
end
|
1064
|
+
perms[0]
|
1065
|
+
end
|
1066
|
+
return self
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
# Cartesian product or multiplication.
|
1070
|
+
#
|
1071
|
+
# @overload product(*tuples)
|
1072
|
+
# Return a `Tuple` of all combinations of elements from this `Tuple` and each
|
1073
|
+
# of the given tuples or arrays. The length of the returned `Tuple` is the product
|
1074
|
+
# of `self.size` and the size of each argument tuple or array.
|
1075
|
+
# @example
|
1076
|
+
# t1 = Erlang::Tuple[1, 2, 3]
|
1077
|
+
# t2 = Erlang::Tuple["A", "B"]
|
1078
|
+
# t1.product(t2)
|
1079
|
+
# # => [[1, "A"], [1, "B"], [2, "A"], [2, "B"], [3, "A"], [3, "B"]]
|
1080
|
+
# @overload product
|
1081
|
+
# Return the result of multiplying all the elements in this `Tuple` together.
|
1082
|
+
#
|
1083
|
+
# @example
|
1084
|
+
# Erlang::Tuple[1, 2, 3, 4, 5].product # => 120
|
1085
|
+
#
|
1086
|
+
# @return [Tuple]
|
1087
|
+
def product(*tuples)
|
1088
|
+
tuples = tuples.map { |tuple| Erlang.from(tuple) }
|
1089
|
+
# if no tuples passed, return "product" as in result of multiplying all elements
|
1090
|
+
return super if tuples.empty?
|
1091
|
+
|
1092
|
+
tuples.unshift(self)
|
1093
|
+
|
1094
|
+
if tuples.any?(&:empty?)
|
1095
|
+
return block_given? ? self : []
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
counters = Array.new(tuples.size, 0)
|
1099
|
+
|
1100
|
+
bump_counters = lambda do
|
1101
|
+
i = tuples.size-1
|
1102
|
+
counters[i] += 1
|
1103
|
+
while counters[i] == tuples[i].size
|
1104
|
+
counters[i] = 0
|
1105
|
+
i -= 1
|
1106
|
+
return true if i == -1 # we are done
|
1107
|
+
counters[i] += 1
|
1108
|
+
end
|
1109
|
+
false # not done yet
|
1110
|
+
end
|
1111
|
+
build_array = lambda do
|
1112
|
+
array = []
|
1113
|
+
counters.each_with_index { |index,i| array << tuples[i][index] }
|
1114
|
+
array
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
if block_given?
|
1118
|
+
while true
|
1119
|
+
yield build_array[]
|
1120
|
+
return self if bump_counters[]
|
1121
|
+
end
|
1122
|
+
else
|
1123
|
+
result = []
|
1124
|
+
while true
|
1125
|
+
result << build_array[]
|
1126
|
+
return result if bump_counters[]
|
1127
|
+
end
|
1128
|
+
end
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
# Assume all elements are tuples or arrays and transpose the rows and columns.
|
1132
|
+
# In other words, take the first element of each nested tuple/array and gather
|
1133
|
+
# them together into a new `Tuple`. Do likewise for the second, third, and so on
|
1134
|
+
# down to the end of each nested Tuple/array. Gather all the resulting `Tuple`s
|
1135
|
+
# into a new `Tuple` and return it.
|
1136
|
+
#
|
1137
|
+
# This operation is closely related to {#zip}. The result is almost the same as
|
1138
|
+
# calling {#zip} on the first nested Tuple/array with the others supplied as
|
1139
|
+
# arguments.
|
1140
|
+
#
|
1141
|
+
# @example
|
1142
|
+
# Erlang::Tuple[["A", 10], ["B", 20], ["C", 30]].transpose
|
1143
|
+
# # => Erlang::Tuple[Erlang::Tuple["A", "B", "C"], Erlang::Tuple[10, 20, 30]]
|
1144
|
+
#
|
1145
|
+
# @return [Tuple]
|
1146
|
+
# @raise [IndexError] if elements are not of the same size.
|
1147
|
+
# @raise [TypeError] if an element can not be implicitly converted to an array (using `#to_ary`)
|
1148
|
+
def transpose
|
1149
|
+
return self.class.empty if empty?
|
1150
|
+
result = Array.new(first.size) { [] }
|
1151
|
+
|
1152
|
+
0.upto(@size-1) do |i|
|
1153
|
+
source = get(i)
|
1154
|
+
if source.size != result.size
|
1155
|
+
raise IndexError, "element size differs (#{source.size} should be #{result.size})"
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
0.upto(result.size-1) do |j|
|
1159
|
+
result[j].push(source[j])
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
result.map! { |a| self.class.new(a) }
|
1164
|
+
return self.class.new(result)
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
# Finds a value from this `Tuple` which meets the condition defined by the
|
1168
|
+
# provided block, using a binary search. The tuple must already be sorted
|
1169
|
+
# with respect to the block. See Ruby's `Array#bsearch` for details,
|
1170
|
+
# behaviour is equivalent.
|
1171
|
+
#
|
1172
|
+
# @example
|
1173
|
+
# t = Erlang::Tuple[1, 3, 5, 7, 9, 11, 13]
|
1174
|
+
# # Block returns true/false for exact element match:
|
1175
|
+
# t.bsearch { |e| e > 4 } # => 5
|
1176
|
+
# # Block returns number to match an element in 4 <= e <= 7:
|
1177
|
+
# t.bsearch { |e| 1 - e / 4 } # => 7
|
1178
|
+
#
|
1179
|
+
# @yield Once for at most `log n` elements, where `n` is the size of the
|
1180
|
+
# tuple. The exact elements and ordering are undefined.
|
1181
|
+
# @yieldreturn [Boolean] `true` if this element matches the criteria, `false` otherwise.
|
1182
|
+
# @yieldreturn [Integer] See `Array#bsearch` for details.
|
1183
|
+
# @yieldparam [Object] element element to be evaluated
|
1184
|
+
# @return [Object] The matched element, or `nil` if none found.
|
1185
|
+
# @raise TypeError if the block returns a non-numeric, non-boolean, non-nil
|
1186
|
+
# value.
|
1187
|
+
def bsearch
|
1188
|
+
return enum_for(:bsearch) if not block_given?
|
1189
|
+
low, high, result = 0, @size, nil
|
1190
|
+
while low < high
|
1191
|
+
mid = (low + ((high - low) >> 1))
|
1192
|
+
val = get(mid)
|
1193
|
+
v = yield val
|
1194
|
+
if v.is_a? Numeric
|
1195
|
+
if v == 0
|
1196
|
+
return val
|
1197
|
+
elsif v > 0
|
1198
|
+
high = mid
|
1199
|
+
else
|
1200
|
+
low = mid + 1
|
1201
|
+
end
|
1202
|
+
elsif v == true
|
1203
|
+
result = val
|
1204
|
+
high = mid
|
1205
|
+
elsif !v
|
1206
|
+
low = mid + 1
|
1207
|
+
else
|
1208
|
+
raise TypeError, "wrong argument type #{v.class} (must be numeric, true, false, or nil)"
|
1209
|
+
end
|
1210
|
+
end
|
1211
|
+
return result
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
# Return an empty `Tuple` instance, of the same class as this one. Useful if you
|
1215
|
+
# have multiple subclasses of `Tuple` and want to treat them polymorphically.
|
1216
|
+
#
|
1217
|
+
# @return [Tuple]
|
1218
|
+
def clear
|
1219
|
+
return self.class.empty
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
# Return a randomly chosen element from this `Tuple`. If the tuple is empty, return `nil`.
|
1223
|
+
#
|
1224
|
+
# @example
|
1225
|
+
# Erlang::Tuple[1, 2, 3, 4, 5].sample # => 2
|
1226
|
+
#
|
1227
|
+
# @return [Object]
|
1228
|
+
def sample
|
1229
|
+
return get(rand(@size))
|
1230
|
+
end
|
1231
|
+
|
1232
|
+
# Return a new `Tuple` with only the elements at the given `indices`, in the
|
1233
|
+
# order specified by `indices`. If any of the `indices` do not exist, `nil`s will
|
1234
|
+
# appear in their places.
|
1235
|
+
#
|
1236
|
+
# @example
|
1237
|
+
# t = Erlang::Tuple["A", "B", "C", "D", "E", "F"]
|
1238
|
+
# t.values_at(2, 4, 5) # => Erlang::Tuple["C", "E", "F"]
|
1239
|
+
#
|
1240
|
+
# @param indices [Array] The indices to retrieve and gather into a new `Tuple`
|
1241
|
+
# @return [Tuple]
|
1242
|
+
def values_at(*indices)
|
1243
|
+
return self.class.new(indices.map { |i| get(i) }.freeze)
|
1244
|
+
end
|
1245
|
+
|
1246
|
+
# Find the index of an element, starting from the end of the tuple.
|
1247
|
+
# Returns `nil` if no element is found.
|
1248
|
+
#
|
1249
|
+
# @overload rindex(obj)
|
1250
|
+
# Return the index of the last element which is `#==` to `obj`.
|
1251
|
+
#
|
1252
|
+
# @example
|
1253
|
+
# t = Erlang::Tuple[7, 8, 9, 7, 8, 9]
|
1254
|
+
# t.rindex(8) # => 4
|
1255
|
+
#
|
1256
|
+
# @overload rindex
|
1257
|
+
# Return the index of the last element for which the block returns true.
|
1258
|
+
#
|
1259
|
+
# @yield [element] Once for each element, last to first, until the block
|
1260
|
+
# returns true.
|
1261
|
+
# @example
|
1262
|
+
# t = Erlang::Tuple[7, 8, 9, 7, 8, 9]
|
1263
|
+
# t.rindex { |e| e.even? } # => 4
|
1264
|
+
#
|
1265
|
+
# @return [Integer]
|
1266
|
+
def rindex(obj = (missing_arg = true))
|
1267
|
+
obj = Erlang.from(obj)
|
1268
|
+
i = @size - 1
|
1269
|
+
if missing_arg
|
1270
|
+
if block_given?
|
1271
|
+
reverse_each { |element| return i if yield element; i -= 1 }
|
1272
|
+
return nil
|
1273
|
+
else
|
1274
|
+
return enum_for(:rindex)
|
1275
|
+
end
|
1276
|
+
else
|
1277
|
+
reverse_each { |element| return i if element == obj; i -= 1 }
|
1278
|
+
return nil
|
1279
|
+
end
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
# Assumes all elements are nested, indexable collections, and searches through them,
|
1283
|
+
# comparing `obj` with the first element of each nested collection. Return the
|
1284
|
+
# first nested collection which matches, or `nil` if none is found.
|
1285
|
+
# Behaviour is undefined when elements do not meet assumptions (i.e. are
|
1286
|
+
# not indexable collections).
|
1287
|
+
#
|
1288
|
+
# @example
|
1289
|
+
# t = Erlang::Tuple[Erlang::Tuple["A", 10], Erlang::Tuple["B", 20], Erlang::Tuple["C", 30]]
|
1290
|
+
# t.assoc("B") # => Erlang::Tuple["B", 20]
|
1291
|
+
#
|
1292
|
+
# @param obj [Object] The object to search for
|
1293
|
+
# @return [Object]
|
1294
|
+
def assoc(obj)
|
1295
|
+
obj = Erlang.from(obj)
|
1296
|
+
each do |array|
|
1297
|
+
next if !array.respond_to?(:[])
|
1298
|
+
return array if obj == array[0]
|
1299
|
+
end
|
1300
|
+
return nil
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
# Assumes all elements are nested, indexable collections, and searches through them,
|
1304
|
+
# comparing `obj` with the second element of each nested collection. Return
|
1305
|
+
# the first nested collection which matches, or `nil` if none is found.
|
1306
|
+
# Behaviour is undefined when elements do not meet assumptions (i.e. are
|
1307
|
+
# not indexable collections).
|
1308
|
+
#
|
1309
|
+
# @example
|
1310
|
+
# t = Erlang::Tuple[Erlang::Tuple["A", 10], Erlang::Tuple["B", 20], Erlang::Tuple["C", 30]]
|
1311
|
+
# t.rassoc(20) # => Erlang::Tuple["B", 20]
|
1312
|
+
#
|
1313
|
+
# @param obj [Object] The object to search for
|
1314
|
+
# @return [Object]
|
1315
|
+
def rassoc(obj)
|
1316
|
+
obj = Erlang.from(obj)
|
1317
|
+
each do |array|
|
1318
|
+
next if !array.respond_to?(:[])
|
1319
|
+
return array if obj == array[1]
|
1320
|
+
end
|
1321
|
+
return nil
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
# Return an `Array` with the same elements, in the same order. The returned
|
1325
|
+
# `Array` may or may not be frozen.
|
1326
|
+
#
|
1327
|
+
# @return [Array]
|
1328
|
+
def to_a
|
1329
|
+
if @levels == 0
|
1330
|
+
# When initializing a Tuple with 32 or less elements, we always make
|
1331
|
+
# sure @root is frozen, so we can return it directly here
|
1332
|
+
return @root
|
1333
|
+
else
|
1334
|
+
return flatten_node(@root, @levels * BITS_PER_LEVEL, [])
|
1335
|
+
end
|
1336
|
+
end
|
1337
|
+
alias :to_ary :to_a
|
1338
|
+
|
1339
|
+
# Return true if `other` has the same type and contents as this `Tuple`.
|
1340
|
+
#
|
1341
|
+
# @param other [Object] The collection to compare with
|
1342
|
+
# @return [Boolean]
|
1343
|
+
def eql?(other)
|
1344
|
+
return true if other.equal?(self)
|
1345
|
+
if instance_of?(other.class)
|
1346
|
+
return false if @size != other.size
|
1347
|
+
return @root.eql?(other.instance_variable_get(:@root))
|
1348
|
+
else
|
1349
|
+
return !!(Erlang.compare(other, self) == 0)
|
1350
|
+
end
|
9
1351
|
end
|
1352
|
+
alias :== :eql?
|
10
1353
|
|
11
|
-
|
12
|
-
|
1354
|
+
# See `Object#hash`.
|
1355
|
+
# @return [Integer]
|
1356
|
+
def hash
|
1357
|
+
return reduce(Erlang::Tuple.hash) { |hash, item| (hash << 5) - hash + item.hash }
|
13
1358
|
end
|
14
1359
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1360
|
+
# @return [::Array]
|
1361
|
+
# @private
|
1362
|
+
def marshal_dump
|
1363
|
+
return to_a
|
1364
|
+
end
|
1365
|
+
|
1366
|
+
# @private
|
1367
|
+
def marshal_load(array)
|
1368
|
+
initialize(array.freeze)
|
1369
|
+
__send__(:immutable!)
|
1370
|
+
return self
|
1371
|
+
end
|
1372
|
+
|
1373
|
+
# Allows this `Tuple` to be printed using `Erlang.inspect()`.
|
1374
|
+
#
|
1375
|
+
# @return [String]
|
1376
|
+
def erlang_inspect(raw = false)
|
1377
|
+
result = '{'
|
1378
|
+
each_with_index { |obj, i| result << ',' if i > 0; result << Erlang.inspect(obj, raw: raw) }
|
1379
|
+
result << '}'
|
1380
|
+
return result
|
1381
|
+
end
|
1382
|
+
|
1383
|
+
private
|
1384
|
+
|
1385
|
+
def traverse_depth_first(node, level, &block)
|
1386
|
+
return node.each(&block) if level == 0
|
1387
|
+
return node.each { |child| traverse_depth_first(child, level - 1, &block) }
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def reverse_traverse_depth_first(node, level, &block)
|
1391
|
+
return node.reverse_each(&block) if level == 0
|
1392
|
+
return node.reverse_each { |child| reverse_traverse_depth_first(child, level - 1, &block) }
|
1393
|
+
end
|
1394
|
+
|
1395
|
+
def leaf_node_for(node, bitshift, index)
|
1396
|
+
while bitshift > 0
|
1397
|
+
node = node[(index >> bitshift) & INDEX_MASK]
|
1398
|
+
bitshift -= BITS_PER_LEVEL
|
1399
|
+
end
|
1400
|
+
return node
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
def update_root(index, item)
|
1404
|
+
root, levels = @root, @levels
|
1405
|
+
while index >= (1 << (BITS_PER_LEVEL * (levels + 1)))
|
1406
|
+
root = [root].freeze
|
1407
|
+
levels += 1
|
1408
|
+
end
|
1409
|
+
new_root = update_leaf_node(root, levels * BITS_PER_LEVEL, index, item)
|
1410
|
+
if new_root.equal?(root)
|
1411
|
+
return self
|
1412
|
+
else
|
1413
|
+
return self.class.alloc(new_root, @size > index ? @size : index + 1, levels)
|
1414
|
+
end
|
1415
|
+
end
|
1416
|
+
|
1417
|
+
def update_leaf_node(node, bitshift, index, item)
|
1418
|
+
slot_index = (index >> bitshift) & INDEX_MASK
|
1419
|
+
if bitshift > 0
|
1420
|
+
old_child = node[slot_index] || []
|
1421
|
+
item = update_leaf_node(old_child, bitshift - BITS_PER_LEVEL, index, item)
|
1422
|
+
end
|
1423
|
+
existing_item = node[slot_index]
|
1424
|
+
if existing_item.equal?(item)
|
1425
|
+
return node
|
1426
|
+
else
|
1427
|
+
return node.dup.tap { |n| n[slot_index] = item }.freeze
|
1428
|
+
end
|
1429
|
+
end
|
1430
|
+
|
1431
|
+
def flatten_range(node, bitshift, from, to)
|
1432
|
+
from_slot = (from >> bitshift) & INDEX_MASK
|
1433
|
+
to_slot = (to >> bitshift) & INDEX_MASK
|
1434
|
+
|
1435
|
+
if bitshift == 0 # are we at the bottom?
|
1436
|
+
return node.slice(from_slot, to_slot-from_slot+1)
|
1437
|
+
elsif from_slot == to_slot
|
1438
|
+
return flatten_range(node[from_slot], bitshift - BITS_PER_LEVEL, from, to)
|
1439
|
+
else
|
1440
|
+
# the following bitmask can be used to pick out the part of the from/to indices
|
1441
|
+
# which will be used to direct path BELOW this node
|
1442
|
+
mask = ((1 << bitshift) - 1)
|
1443
|
+
result = []
|
1444
|
+
|
1445
|
+
if from & mask == 0
|
1446
|
+
flatten_node(node[from_slot], bitshift - BITS_PER_LEVEL, result)
|
1447
|
+
else
|
1448
|
+
result.concat(flatten_range(node[from_slot], bitshift - BITS_PER_LEVEL, from, from | mask))
|
1449
|
+
end
|
1450
|
+
|
1451
|
+
(from_slot+1).upto(to_slot-1) do |slot_index|
|
1452
|
+
flatten_node(node[slot_index], bitshift - BITS_PER_LEVEL, result)
|
1453
|
+
end
|
1454
|
+
|
1455
|
+
if to & mask == mask
|
1456
|
+
flatten_node(node[to_slot], bitshift - BITS_PER_LEVEL, result)
|
1457
|
+
else
|
1458
|
+
result.concat(flatten_range(node[to_slot], bitshift - BITS_PER_LEVEL, to & ~mask, to))
|
1459
|
+
end
|
1460
|
+
|
1461
|
+
return result
|
1462
|
+
end
|
1463
|
+
end
|
1464
|
+
|
1465
|
+
def flatten_node(node, bitshift, result)
|
1466
|
+
if bitshift == 0
|
1467
|
+
result.concat(node)
|
1468
|
+
elsif bitshift == BITS_PER_LEVEL
|
1469
|
+
node.each { |a| result.concat(a) }
|
1470
|
+
else
|
1471
|
+
bitshift -= BITS_PER_LEVEL
|
1472
|
+
node.each { |a| flatten_node(a, bitshift, result) }
|
1473
|
+
end
|
1474
|
+
return result
|
1475
|
+
end
|
1476
|
+
|
1477
|
+
def subsequence(from, length)
|
1478
|
+
return nil if from > @size || from < 0 || length < 0
|
1479
|
+
length = @size - from if @size < from + length
|
1480
|
+
return self.class.empty if length == 0
|
1481
|
+
return self.class.new(flatten_range(@root, @levels * BITS_PER_LEVEL, from, from + length - 1))
|
1482
|
+
end
|
1483
|
+
|
1484
|
+
def flatten_suffix(node, bitshift, from, result)
|
1485
|
+
from_slot = (from >> bitshift) & INDEX_MASK
|
1486
|
+
|
1487
|
+
if bitshift == 0
|
1488
|
+
if from_slot == 0
|
1489
|
+
return result.concat(node)
|
1490
|
+
else
|
1491
|
+
return result.concat(node.slice(from_slot, 32)) # entire suffix of node. excess length is ignored by #slice
|
1492
|
+
end
|
1493
|
+
else
|
1494
|
+
mask = ((1 << bitshift) - 1)
|
1495
|
+
if from & mask == 0
|
1496
|
+
from_slot.upto(node.size-1) do |i|
|
1497
|
+
flatten_node(node[i], bitshift - BITS_PER_LEVEL, result)
|
1498
|
+
end
|
1499
|
+
elsif child = node[from_slot]
|
1500
|
+
flatten_suffix(child, bitshift - BITS_PER_LEVEL, from, result)
|
1501
|
+
(from_slot+1).upto(node.size-1) do |i|
|
1502
|
+
flatten_node(node[i], bitshift - BITS_PER_LEVEL, result)
|
1503
|
+
end
|
1504
|
+
end
|
1505
|
+
return result
|
1506
|
+
end
|
1507
|
+
end
|
1508
|
+
|
1509
|
+
def replace_suffix(from, suffix)
|
1510
|
+
# new suffix can go directly after existing elements
|
1511
|
+
raise IndexError if from > @size
|
1512
|
+
root, levels = @root, @levels
|
1513
|
+
|
1514
|
+
if (from >> (BITS_PER_LEVEL * (@levels + 1))) != 0
|
1515
|
+
# index where new suffix goes doesn't fall within current tree
|
1516
|
+
# we will need to deepen tree
|
1517
|
+
root = [root].freeze
|
1518
|
+
levels += 1
|
1519
|
+
end
|
1520
|
+
|
1521
|
+
new_size = from + suffix.size
|
1522
|
+
root = replace_node_suffix(root, levels * BITS_PER_LEVEL, from, suffix)
|
1523
|
+
|
1524
|
+
if !suffix.empty?
|
1525
|
+
levels.times { suffix = suffix.each_slice(32).to_a }
|
1526
|
+
root.concat(suffix)
|
1527
|
+
while root.size > 32
|
1528
|
+
root = root.each_slice(32).to_a
|
1529
|
+
levels += 1
|
1530
|
+
end
|
1531
|
+
else
|
1532
|
+
while root.size == 1 && levels > 0
|
1533
|
+
root = root[0]
|
1534
|
+
levels -= 1
|
1535
|
+
end
|
1536
|
+
end
|
1537
|
+
|
1538
|
+
return self.class.alloc(root.freeze, new_size, levels)
|
1539
|
+
end
|
1540
|
+
|
1541
|
+
def replace_node_suffix(node, bitshift, from, suffix)
|
1542
|
+
from_slot = (from >> bitshift) & INDEX_MASK
|
1543
|
+
|
1544
|
+
if bitshift == 0
|
1545
|
+
if from_slot == 0
|
1546
|
+
return suffix.shift(32)
|
1547
|
+
else
|
1548
|
+
return node.take(from_slot).concat(suffix.shift(32 - from_slot))
|
1549
|
+
end
|
1550
|
+
else
|
1551
|
+
mask = ((1 << bitshift) - 1)
|
1552
|
+
if from & mask == 0
|
1553
|
+
if from_slot == 0
|
1554
|
+
new_node = suffix.shift(32 * (1 << bitshift))
|
1555
|
+
while bitshift != 0
|
1556
|
+
new_node = new_node.each_slice(32).to_a
|
1557
|
+
bitshift -= BITS_PER_LEVEL
|
1558
|
+
end
|
1559
|
+
return new_node
|
1560
|
+
else
|
1561
|
+
result = node.take(from_slot)
|
1562
|
+
remainder = suffix.shift((32 - from_slot) * (1 << bitshift))
|
1563
|
+
while bitshift != 0
|
1564
|
+
remainder = remainder.each_slice(32).to_a
|
1565
|
+
bitshift -= BITS_PER_LEVEL
|
1566
|
+
end
|
1567
|
+
return result.concat(remainder)
|
1568
|
+
end
|
1569
|
+
elsif child = node[from_slot]
|
1570
|
+
result = node.take(from_slot)
|
1571
|
+
result.push(replace_node_suffix(child, bitshift - BITS_PER_LEVEL, from, suffix))
|
1572
|
+
remainder = suffix.shift((31 - from_slot) * (1 << bitshift))
|
1573
|
+
while bitshift != 0
|
1574
|
+
remainder = remainder.each_slice(32).to_a
|
1575
|
+
bitshift -= BITS_PER_LEVEL
|
1576
|
+
end
|
1577
|
+
return result.concat(remainder)
|
1578
|
+
else
|
1579
|
+
raise "Shouldn't happen"
|
1580
|
+
end
|
1581
|
+
end
|
21
1582
|
end
|
22
1583
|
end
|
23
|
-
|
1584
|
+
|
1585
|
+
# The canonical empty `Tuple`. Returned by `Tuple[]` when
|
1586
|
+
# invoked with no arguments; also returned by `Tuple.empty`. Prefer using this
|
1587
|
+
# one rather than creating many empty tuples using `Tuple.new`.
|
1588
|
+
#
|
1589
|
+
# @private
|
1590
|
+
EmptyTuple = Erlang::Tuple.empty
|
1591
|
+
end
|