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/map.rb
CHANGED
@@ -1,31 +1,945 @@
|
|
1
1
|
module Erlang
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
# A `Erlang::Map` maps a set of unique keys to corresponding values, much
|
3
|
+
# like a dictionary maps from words to definitions. Given a key, it can store
|
4
|
+
# and retrieve an associated value in constant time. If an existing key is
|
5
|
+
# stored again, the new value will replace the old. It behaves much like
|
6
|
+
# Ruby's built-in Hash, which we will call RubyHash for clarity. Like
|
7
|
+
# RubyHash, two keys that are `#eql?` to each other and have the same
|
8
|
+
# `#hash` are considered identical in a `Erlang::Map`.
|
9
|
+
#
|
10
|
+
# A `Erlang::Map` can be created in a couple of ways:
|
11
|
+
#
|
12
|
+
# Erlang::Map[first_name: 'John', last_name: 'Smith']
|
13
|
+
# Erlang::Map[:first_name, 'John', :last_name, 'Smith']
|
14
|
+
#
|
15
|
+
# Any `Enumerable` object with an even number of elements can be used to
|
16
|
+
# initialize a `Erlang::Map`:
|
17
|
+
#
|
18
|
+
# Erlang::Map[:first_name, 'John', :last_name, 'Smith']
|
19
|
+
#
|
20
|
+
# Any `Enumerable` object which yields two-element `[key, value]` arrays
|
21
|
+
# can be used to initialize a `Erlang::Map`:
|
22
|
+
#
|
23
|
+
# Erlang::Map.new([[:first_name, 'John'], [:last_name, 'Smith']])
|
24
|
+
#
|
25
|
+
# Key/value pairs can be added using {#put}. A new map is returned and the
|
26
|
+
# existing one is left unchanged:
|
27
|
+
#
|
28
|
+
# map = Erlang::Map[a: 100, b: 200]
|
29
|
+
# map.put(:c, 500) # => Erlang::Map[:a => 100, :b => 200, :c => 500]
|
30
|
+
# map # => Erlang::Map[:a => 100, :b => 200]
|
31
|
+
#
|
32
|
+
# {#put} can also take a block, which is used to calculate the value to be
|
33
|
+
# stored.
|
34
|
+
#
|
35
|
+
# map.put(:a) { |current| current + 200 } # => Erlang::Map[:a => 300, :b => 200]
|
36
|
+
#
|
37
|
+
# Since it is immutable, all methods which you might expect to "modify" a
|
38
|
+
# `Erlang::Map` actually return a new map and leave the existing one
|
39
|
+
# unchanged. This means that the `map[key] = value` syntax from RubyHash
|
40
|
+
# *cannot* be used with `Erlang::Map`.
|
41
|
+
#
|
42
|
+
# Nested data structures can easily be updated using {#update_in}:
|
43
|
+
#
|
44
|
+
# map = Erlang::Map["a" => Erlang::Tuple[Erlang::Map["c" => 42]]]
|
45
|
+
# map.update_in("a", 0, "c") { |value| value + 5 }
|
46
|
+
# # => Erlang::Map["a" => Erlang::Tuple[Erlang::Map["c" => 47]]]
|
47
|
+
#
|
48
|
+
# While a `Erlang::Map` can iterate over its keys or values, it does not
|
49
|
+
# guarantee any specific iteration order (unlike RubyHash). Methods like
|
50
|
+
# {#flatten} do not guarantee the order of returned key/value pairs.
|
51
|
+
#
|
52
|
+
# Like RubyHash, a `Erlang::Map` can have a default block which is used
|
53
|
+
# when looking up a key that does not exist. Unlike RubyHash, the default
|
54
|
+
# block will only be passed the missing key, without the hash itself:
|
55
|
+
#
|
56
|
+
# map = Erlang::Map.new { |missing_key| missing_key * 10 }
|
57
|
+
# map[5] # => 50
|
58
|
+
#
|
59
|
+
# Licensing
|
60
|
+
# =========
|
61
|
+
#
|
62
|
+
# Portions taken and modified from https://github.com/hamstergem/hamster
|
63
|
+
#
|
64
|
+
# Copyright (c) 2009-2014 Simon Harris
|
65
|
+
#
|
66
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
67
|
+
# a copy of this software and associated documentation files (the
|
68
|
+
# "Software"), to deal in the Software without restriction, including
|
69
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
70
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
71
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
72
|
+
# the following conditions:
|
73
|
+
#
|
74
|
+
# The above copyright notice and this permission notice shall be
|
75
|
+
# included in all copies or substantial portions of the Software.
|
76
|
+
#
|
77
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
78
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
79
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
80
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
81
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
82
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
83
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
84
|
+
#
|
85
|
+
class Map
|
86
|
+
include Erlang::Term
|
87
|
+
include Erlang::Immutable
|
88
|
+
include Erlang::Enumerable
|
89
|
+
include Erlang::Associable
|
90
|
+
|
91
|
+
# Return the number of pairs in this `Map`
|
92
|
+
# @return [Integer]
|
93
|
+
attr_reader :arity
|
94
|
+
alias :size :arity
|
95
|
+
|
96
|
+
class << self
|
97
|
+
# Create a new `Map` populated with the given key/value pairs.
|
98
|
+
#
|
99
|
+
# @example
|
100
|
+
# Erlang::Map["A" => 1, "B" => 2] # => Erlang::Map["A" => 1, "B" => 2]
|
101
|
+
# Erlang::Map["A", 1, "B", 2] # => Erlang::Map["A" => 1, "B" => 2]
|
102
|
+
#
|
103
|
+
# @param pairs [::Enumerable] initial content of hash. An empty hash is returned if not provided.
|
104
|
+
# @return [Map]
|
105
|
+
def [](*pairs)
|
106
|
+
return empty if pairs.nil? or pairs.empty?
|
107
|
+
if pairs.size == 1
|
108
|
+
return pairs[0] if pairs[0].is_a?(Erlang::Map)
|
109
|
+
return new(pairs[0]) if pairs[0].is_a?(::Hash)
|
110
|
+
end
|
111
|
+
raise ArgumentError, 'odd number of arguments for Erlang::Map' if pairs.size.odd?
|
112
|
+
pairs = pairs.each_slice(2).to_a
|
113
|
+
return new(pairs)
|
114
|
+
end
|
6
115
|
|
7
|
-
|
8
|
-
|
116
|
+
# Return an empty `Map`. If used on a subclass, returns an empty instance
|
117
|
+
# of that class.
|
118
|
+
#
|
119
|
+
# @return [Map]
|
120
|
+
def empty
|
121
|
+
return @empty ||= self.new
|
122
|
+
end
|
123
|
+
|
124
|
+
# "Raw" allocation of a new `Map`. Used internally to create a new
|
125
|
+
# instance quickly after obtaining a modified {Trie}.
|
126
|
+
#
|
127
|
+
# @return [Map]
|
128
|
+
# @private
|
129
|
+
def alloc(trie = EmptyTrie, block = nil)
|
130
|
+
obj = allocate
|
131
|
+
obj.instance_variable_set(:@trie, trie)
|
132
|
+
obj.instance_variable_set(:@default, block)
|
133
|
+
return obj
|
134
|
+
end
|
135
|
+
|
136
|
+
def compare(a, b)
|
137
|
+
raise ArgumentError, "'a' must be of Erlang::Map type" if not a.kind_of?(Erlang::Map)
|
138
|
+
raise ArgumentError, "'b' must be of Erlang::Map type" if not b.kind_of?(Erlang::Map)
|
139
|
+
c = a.size <=> b.size
|
140
|
+
return c if c != 0
|
141
|
+
return 0 if a.eql?(b)
|
142
|
+
return Erlang.compare(a.sort, b.sort)
|
143
|
+
end
|
9
144
|
end
|
10
145
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
146
|
+
# @param pairs [::Enumerable] initial content of map. An empty map is returned if not provided.
|
147
|
+
# @yield [key] Optional _default block_ to be stored and used to calculate the default value of a missing key. It will not be yielded during this method. It will not be preserved when marshalling.
|
148
|
+
# @yieldparam key Key that was not present in the map.
|
149
|
+
def initialize(pairs = nil, &block)
|
150
|
+
if pairs
|
151
|
+
obj = ::Array.new(pairs.size)
|
152
|
+
i = 0
|
153
|
+
pairs.each do |key, val|
|
154
|
+
obj[i] = [Erlang.from(key), Erlang.from(val)]
|
155
|
+
i += 1
|
156
|
+
end
|
157
|
+
pairs = obj
|
158
|
+
end
|
159
|
+
@trie = pairs ? Trie[pairs] : EmptyTrie
|
160
|
+
@default = block
|
161
|
+
if block_given?
|
162
|
+
@default = ->(key) {
|
163
|
+
return Erlang.from(block.call(key))
|
22
164
|
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Return the default block if there is one. Otherwise, return `nil`.
|
169
|
+
#
|
170
|
+
# @return [Proc]
|
171
|
+
def default_proc
|
172
|
+
return @default
|
173
|
+
end
|
174
|
+
|
175
|
+
# Return the number of key/value pairs in this `Map`.
|
176
|
+
#
|
177
|
+
# @example
|
178
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].size # => 3
|
179
|
+
#
|
180
|
+
# @return [Integer]
|
181
|
+
def size
|
182
|
+
return @trie.size
|
183
|
+
end
|
184
|
+
alias :arity :size
|
185
|
+
alias :length :size
|
186
|
+
|
187
|
+
# Return `true` if this `Map` contains no key/value pairs.
|
188
|
+
#
|
189
|
+
# @return [Boolean]
|
190
|
+
def empty?
|
191
|
+
return @trie.empty?
|
192
|
+
end
|
193
|
+
|
194
|
+
# Return `true` if the given key object is present in this `Map`. More precisely,
|
195
|
+
# return `true` if a key with the same `#hash` code, and which is also `#eql?`
|
196
|
+
# to the given key object is present.
|
197
|
+
#
|
198
|
+
# @example
|
199
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].key?("B") # => true
|
200
|
+
#
|
201
|
+
# @param key [Object] The key to check for
|
202
|
+
# @return [Boolean]
|
203
|
+
def key?(key)
|
204
|
+
key = Erlang.from(key)
|
205
|
+
return @trie.key?(key)
|
206
|
+
end
|
207
|
+
alias :has_key? :key?
|
208
|
+
alias :include? :key?
|
209
|
+
alias :member? :key?
|
210
|
+
|
211
|
+
# Return `true` if this `Map` has one or more keys which map to the provided value.
|
212
|
+
#
|
213
|
+
# @example
|
214
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].value?(2) # => true
|
215
|
+
#
|
216
|
+
# @param value [Object] The value to check for
|
217
|
+
# @return [Boolean]
|
218
|
+
def value?(value)
|
219
|
+
value = Erlang.from(value)
|
220
|
+
each { |k,v| return true if value == v }
|
221
|
+
return false
|
222
|
+
end
|
223
|
+
alias :has_value? :value?
|
224
|
+
|
225
|
+
# Retrieve the value corresponding to the provided key object. If not found, and
|
226
|
+
# this `Map` has a default block, the default block is called to provide the
|
227
|
+
# value. Otherwise, return `nil`.
|
228
|
+
#
|
229
|
+
# @example
|
230
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
231
|
+
# m["B"] # => 2
|
232
|
+
# m.get("B") # => 2
|
233
|
+
# m.get("Elephant") # => nil
|
234
|
+
#
|
235
|
+
# # Erlang Map with a default proc:
|
236
|
+
# m = Erlang::Map.new("A" => 1, "B" => 2, "C" => 3) { |key| key.size }
|
237
|
+
# m.get("B") # => 2
|
238
|
+
# m.get("Elephant") # => 8
|
239
|
+
#
|
240
|
+
# @param key [Object] The key to look up
|
241
|
+
# @return [Object]
|
242
|
+
def get(key)
|
243
|
+
key = Erlang.from(key)
|
244
|
+
entry = @trie.get(key)
|
245
|
+
if entry
|
246
|
+
return entry[1]
|
247
|
+
elsif @default
|
248
|
+
return @default.call(key)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
alias :[] :get
|
252
|
+
|
253
|
+
# Retrieve the value corresponding to the given key object, or use the provided
|
254
|
+
# default value or block, or otherwise raise a `KeyError`.
|
255
|
+
#
|
256
|
+
# @overload fetch(key)
|
257
|
+
# Retrieve the value corresponding to the given key, or raise a `KeyError`
|
258
|
+
# if it is not found.
|
259
|
+
# @param key [Object] The key to look up
|
260
|
+
# @overload fetch(key) { |key| ... }
|
261
|
+
# Retrieve the value corresponding to the given key, or call the optional
|
262
|
+
# code block (with the missing key) and get its return value.
|
263
|
+
# @yield [key] The key which was not found
|
264
|
+
# @yieldreturn [Object] Object to return since the key was not found
|
265
|
+
# @param key [Object] The key to look up
|
266
|
+
# @overload fetch(key, default)
|
267
|
+
# Retrieve the value corresponding to the given key, or else return
|
268
|
+
# the provided `default` value.
|
269
|
+
# @param key [Object] The key to look up
|
270
|
+
# @param default [Object] Object to return if the key is not found
|
271
|
+
#
|
272
|
+
# @example
|
273
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
274
|
+
# m.fetch("B") # => 2
|
275
|
+
# m.fetch("Elephant") # => KeyError: key not found: "Elephant"
|
276
|
+
#
|
277
|
+
# # with a default value:
|
278
|
+
# m.fetch("B", 99) # => 2
|
279
|
+
# m.fetch("Elephant", 99) # => 99
|
280
|
+
#
|
281
|
+
# # with a block:
|
282
|
+
# m.fetch("B") { |key| key.size } # => 2
|
283
|
+
# m.fetch("Elephant") { |key| key.size } # => 8
|
284
|
+
#
|
285
|
+
# @return [Object]
|
286
|
+
def fetch(key, default = Undefined)
|
287
|
+
key = Erlang.from(key)
|
288
|
+
entry = @trie.get(key)
|
289
|
+
if entry
|
290
|
+
return entry[1]
|
291
|
+
elsif block_given?
|
292
|
+
return yield(key)
|
293
|
+
elsif not Undefined.equal?(default)
|
294
|
+
return Erlang.from(default)
|
295
|
+
else
|
296
|
+
raise KeyError, "key not found: #{key.inspect}"
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# Return a new `Map` with the existing key/value associations, plus an association
|
301
|
+
# between the provided key and value. If an equivalent key is already present, its
|
302
|
+
# associated value will be replaced with the provided one.
|
303
|
+
#
|
304
|
+
# If the `value` argument is missing, but an optional code block is provided,
|
305
|
+
# it will be passed the existing value (or `nil` if there is none) and what it
|
306
|
+
# returns will replace the existing value. This is useful for "transforming"
|
307
|
+
# the value associated with a certain key.
|
308
|
+
#
|
309
|
+
# Avoid mutating objects which are used as keys. `String`s are an exception:
|
310
|
+
# unfrozen `String`s which are used as keys are internally duplicated and
|
311
|
+
# frozen. This matches RubyHash's behaviour.
|
312
|
+
#
|
313
|
+
# @example
|
314
|
+
# m = Erlang::Map["A" => 1, "B" => 2]
|
315
|
+
# m.put("C", 3)
|
316
|
+
# # => Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
317
|
+
# m.put("B") { |value| value * 10 }
|
318
|
+
# # => Erlang::Map["A" => 1, "B" => 20]
|
319
|
+
#
|
320
|
+
# @param key [Object] The key to store
|
321
|
+
# @param value [Object] The value to associate it with
|
322
|
+
# @yield [value] The previously stored value, or `nil` if none.
|
323
|
+
# @yieldreturn [Object] The new value to store
|
324
|
+
# @return [Map]
|
325
|
+
def put(key, value = yield(get(key)))
|
326
|
+
new_trie = @trie.put(Erlang.from(key), Erlang.from(value))
|
327
|
+
if new_trie.equal?(@trie)
|
328
|
+
return self
|
329
|
+
else
|
330
|
+
return self.class.alloc(new_trie, @default)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# @!method update_in(*key_path, &block)
|
335
|
+
# Return a new `Map` with a deeply nested value modified to the result of
|
336
|
+
# the given code block. When traversing the nested `Map`es and `Tuple`s,
|
337
|
+
# non-existing keys are created with empty `Map` values.
|
338
|
+
#
|
339
|
+
# The code block receives the existing value of the deeply nested key (or
|
340
|
+
# `nil` if it doesn't exist). This is useful for "transforming" the value
|
341
|
+
# associated with a certain key.
|
342
|
+
#
|
343
|
+
# Note that the original `Map` and sub-`Map`es and sub-`Tuple`s are left
|
344
|
+
# unmodified; new data structure copies are created along the path wherever
|
345
|
+
# needed.
|
346
|
+
#
|
347
|
+
# @example
|
348
|
+
# map = Erlang::Map["a" => Erlang::Map["b" => Erlang::Map["c" => 42]]]
|
349
|
+
# map.update_in("a", "b", "c") { |value| value + 5 }
|
350
|
+
# # => Erlang::Map["a" => Erlang::Map["b" => Erlang::Map["c" => 47]]]
|
351
|
+
#
|
352
|
+
# @param key_path [::Array<Object>] List of keys which form the path to the key to be modified
|
353
|
+
# @yield [value] The previously stored value
|
354
|
+
# @yieldreturn [Object] The new value to store
|
355
|
+
# @return [Map]
|
356
|
+
# @see Associable#update_in
|
357
|
+
|
358
|
+
# An alias for {#put} to match RubyHash's API. Does not support {#put}'s
|
359
|
+
# block form.
|
360
|
+
#
|
361
|
+
# @see #put
|
362
|
+
# @param key [Object] The key to store
|
363
|
+
# @param value [Object] The value to associate it with
|
364
|
+
# @return [Map]
|
365
|
+
def store(key, value)
|
366
|
+
return put(key, value)
|
367
|
+
end
|
368
|
+
|
369
|
+
# Return a new `Map` with `key` removed. If `key` is not present, return
|
370
|
+
# `self`.
|
371
|
+
#
|
372
|
+
# @example
|
373
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].delete("B")
|
374
|
+
# # => Erlang::Map["A" => 1, "C" => 3]
|
375
|
+
#
|
376
|
+
# @param key [Object] The key to remove
|
377
|
+
# @return [Map]
|
378
|
+
def delete(key)
|
379
|
+
return derive_new_map(@trie.delete(key))
|
380
|
+
end
|
381
|
+
|
382
|
+
# Call the block once for each key/value pair in this `Map`, passing the key/value
|
383
|
+
# pair as parameters. No specific iteration order is guaranteed, though the order will
|
384
|
+
# be stable for any particular `Map`.
|
385
|
+
#
|
386
|
+
# @example
|
387
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].each { |k, v| puts "k=#{k} v=#{v}" }
|
388
|
+
#
|
389
|
+
# k=A v=1
|
390
|
+
# k=C v=3
|
391
|
+
# k=B v=2
|
392
|
+
# # => Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
393
|
+
#
|
394
|
+
# @yield [key, value] Once for each key/value pair.
|
395
|
+
# @return [self]
|
396
|
+
def each(&block)
|
397
|
+
return to_enum if not block_given?
|
398
|
+
@trie.each(&block)
|
399
|
+
return self
|
400
|
+
end
|
401
|
+
alias :each_pair :each
|
402
|
+
|
403
|
+
# Call the block once for each key/value pair in this `Map`, passing the key/value
|
404
|
+
# pair as parameters. Iteration order will be the opposite of {#each}.
|
405
|
+
#
|
406
|
+
# @example
|
407
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].reverse_each { |k, v| puts "k=#{k} v=#{v}" }
|
408
|
+
#
|
409
|
+
# k=B v=2
|
410
|
+
# k=C v=3
|
411
|
+
# k=A v=1
|
412
|
+
# # => Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
413
|
+
#
|
414
|
+
# @yield [key, value] Once for each key/value pair.
|
415
|
+
# @return [self]
|
416
|
+
def reverse_each(&block)
|
417
|
+
return enum_for(:reverse_each) if not block_given?
|
418
|
+
@trie.reverse_each(&block)
|
419
|
+
return self
|
420
|
+
end
|
421
|
+
|
422
|
+
# Call the block once for each key/value pair in this `Map`, passing the key as a
|
423
|
+
# parameter. Ordering guarantees are the same as {#each}.
|
424
|
+
#
|
425
|
+
# @example
|
426
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].each_key { |k| puts "k=#{k}" }
|
427
|
+
#
|
428
|
+
# k=A
|
429
|
+
# k=C
|
430
|
+
# k=B
|
431
|
+
# # => Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
432
|
+
#
|
433
|
+
# @yield [key] Once for each key/value pair.
|
434
|
+
# @return [self]
|
435
|
+
def each_key
|
436
|
+
return enum_for(:each_key) if not block_given?
|
437
|
+
@trie.each { |k,v| yield k }
|
438
|
+
return self
|
439
|
+
end
|
440
|
+
|
441
|
+
# Call the block once for each key/value pair in this `Map`, passing the value as a
|
442
|
+
# parameter. Ordering guarantees are the same as {#each}.
|
443
|
+
#
|
444
|
+
# @example
|
445
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].each_value { |v| puts "v=#{v}" }
|
446
|
+
#
|
447
|
+
# v=1
|
448
|
+
# v=3
|
449
|
+
# v=2
|
450
|
+
# # => Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
451
|
+
#
|
452
|
+
# @yield [value] Once for each key/value pair.
|
453
|
+
# @return [self]
|
454
|
+
def each_value
|
455
|
+
return enum_for(:each_value) if not block_given?
|
456
|
+
@trie.each { |k,v| yield v }
|
457
|
+
return self
|
458
|
+
end
|
459
|
+
|
460
|
+
# Call the block once for each key/value pair in this `Map`, passing the key/value
|
461
|
+
# pair as parameters. The block should return a `[key, value]` array each time.
|
462
|
+
# All the returned `[key, value]` arrays will be gathered into a new `Map`.
|
463
|
+
#
|
464
|
+
# @example
|
465
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
466
|
+
# m.map { |k, v| ["new-#{k}", v * v] }
|
467
|
+
# # => Erlang::Map["new-C" => 9, "new-B" => 4, "new-A" => 1]
|
468
|
+
#
|
469
|
+
# @yield [key, value] Once for each key/value pair.
|
470
|
+
# @return [Map]
|
471
|
+
def map
|
472
|
+
return enum_for(:map) unless block_given?
|
473
|
+
return self if empty?
|
474
|
+
return self.class.new(super, &@default)
|
475
|
+
end
|
476
|
+
alias :collect :map
|
477
|
+
|
478
|
+
# Return a new `Map` with all the key/value pairs for which the block returns true.
|
479
|
+
#
|
480
|
+
# @example
|
481
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
482
|
+
# m.select { |k, v| v >= 2 }
|
483
|
+
# # => Erlang::Map["B" => 2, "C" => 3]
|
484
|
+
#
|
485
|
+
# @yield [key, value] Once for each key/value pair.
|
486
|
+
# @yieldreturn Truthy if this pair should be present in the new `Map`.
|
487
|
+
# @return [Map]
|
488
|
+
def select(&block)
|
489
|
+
return enum_for(:select) unless block_given?
|
490
|
+
return derive_new_map(@trie.select(&block))
|
491
|
+
end
|
492
|
+
alias :find_all :select
|
493
|
+
alias :keep_if :select
|
494
|
+
|
495
|
+
# Yield `[key, value]` pairs until one is found for which the block returns true.
|
496
|
+
# Return that `[key, value]` pair. If the block never returns true, return `nil`.
|
497
|
+
#
|
498
|
+
# @example
|
499
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
500
|
+
# m.find { |k, v| v.even? }
|
501
|
+
# # => ["B", 2]
|
502
|
+
#
|
503
|
+
# @return [Array]
|
504
|
+
# @yield [key, value] At most once for each key/value pair, until the block returns `true`.
|
505
|
+
# @yieldreturn Truthy to halt iteration and return the yielded key/value pair.
|
506
|
+
def find
|
507
|
+
return enum_for(:find) unless block_given?
|
508
|
+
each { |entry| return entry if yield entry }
|
509
|
+
return nil
|
510
|
+
end
|
511
|
+
alias :detect :find
|
512
|
+
|
513
|
+
# Return a new `Map` containing all the key/value pairs from this `Map` and
|
514
|
+
# `other`. If no block is provided, the value for entries with colliding keys
|
515
|
+
# will be that from `other`. Otherwise, the value for each duplicate key is
|
516
|
+
# determined by calling the block.
|
517
|
+
#
|
518
|
+
# `other` can be a `Erlang::Map`, a built-in Ruby `Map`, or any `Enumerable`
|
519
|
+
# object which yields `[key, value]` pairs.
|
520
|
+
#
|
521
|
+
# @example
|
522
|
+
# m1 = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
523
|
+
# m2 = Erlang::Map["C" => 70, "D" => 80]
|
524
|
+
# m1.merge(m2)
|
525
|
+
# # => Erlang::Map["C" => 70, "A" => 1, "D" => 80, "B" => 2]
|
526
|
+
# m1.merge(m2) { |key, v1, v2| v1 + v2 }
|
527
|
+
# # => Erlang::Map["C" => 73, "A" => 1, "D" => 80, "B" => 2]
|
528
|
+
#
|
529
|
+
# @param other [::Enumerable] The collection to merge with
|
530
|
+
# @yieldparam key [Object] The key which was present in both collections
|
531
|
+
# @yieldparam my_value [Object] The associated value from this `Map`
|
532
|
+
# @yieldparam other_value [Object] The associated value from the other collection
|
533
|
+
# @yieldreturn [Object] The value to associate this key with in the new `Map`
|
534
|
+
# @return [Map]
|
535
|
+
def merge(other)
|
536
|
+
other = Erlang.from(other)
|
537
|
+
trie = if block_given?
|
538
|
+
other.reduce(@trie) do |acc, (key, value)|
|
539
|
+
if entry = acc.get(key)
|
540
|
+
acc.put(key, yield(key, entry[1], value))
|
541
|
+
else
|
542
|
+
acc.put(key, value)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
else
|
546
|
+
@trie.bulk_put(other)
|
547
|
+
end
|
548
|
+
|
549
|
+
return derive_new_map(trie)
|
550
|
+
end
|
551
|
+
|
552
|
+
# Return a sorted {List} which contains all the `[key, value]` pairs in
|
553
|
+
# this `Map` as two-element `Tuple`s.
|
554
|
+
#
|
555
|
+
# @overload sort
|
556
|
+
# Uses `#<=>` to determine sorted order.
|
557
|
+
# @overload sort { |(k1, v1), (k2, v2)| ... }
|
558
|
+
# Uses the block as a comparator to determine sorted order.
|
559
|
+
#
|
560
|
+
# @example
|
561
|
+
# m = Erlang::Map["Dog" => 1, "Elephant" => 2, "Lion" => 3]
|
562
|
+
# m.sort { |(k1, v1), (k2, v2)| k1.size <=> k2.size }
|
563
|
+
# # => Erlang::List[Erlang::Tuple["Dog", 1], Erlang::Tuple["Lion", 3], Erlang::Tuple["Elephant", 2]]
|
564
|
+
# @yield [(k1, v1), (k2, v2)] Any number of times with different pairs of key/value associations.
|
565
|
+
# @yieldreturn [Integer] Negative if the first pair should be sorted
|
566
|
+
# lower, positive if the latter pair, or 0 if equal.
|
567
|
+
#
|
568
|
+
# @see ::Enumerable#sort
|
569
|
+
#
|
570
|
+
# @return [List]
|
571
|
+
def sort(&comparator)
|
572
|
+
comparator = Erlang.method(:compare) unless block_given?
|
573
|
+
array = super(&comparator)
|
574
|
+
array.map! { |k, v| next Erlang::Tuple[k, v] }
|
575
|
+
return List.from_enum(array)
|
576
|
+
end
|
577
|
+
|
578
|
+
# Return a {List} which contains all the `[key, value]` pairs in this `Hash`
|
579
|
+
# as two-element `Tuple`s. The order which the pairs will appear in is determined by
|
580
|
+
# passing each pair to the code block to obtain a sort key object, and comparing
|
581
|
+
# the sort keys using `#<=>`.
|
582
|
+
#
|
583
|
+
# @see ::Enumerable#sort_by
|
584
|
+
#
|
585
|
+
# @example
|
586
|
+
# m = Erlang::Map["Dog" => 1, "Elephant" => 2, "Lion" => 3]
|
587
|
+
# m.sort_by { |key, value| key.size }
|
588
|
+
# # => Erlang::List[Erlang::Tuple["Dog", 1], Erlang::Tuple["Lion", 3], Erlang::Tuple["Elephant", 2]]
|
589
|
+
#
|
590
|
+
# @yield [key, value] Once for each key/value pair.
|
591
|
+
# @yieldreturn a sort key object for the yielded pair.
|
592
|
+
# @return [List]
|
593
|
+
def sort_by(&transformer)
|
594
|
+
return sort unless block_given?
|
595
|
+
block = ->(x) { Erlang.from(transformer.call(x)) }
|
596
|
+
array = super(&block)
|
597
|
+
array.map! { |k, v| next Erlang::Tuple[k, v] }
|
598
|
+
return List.from_enum(array)
|
599
|
+
end
|
600
|
+
|
601
|
+
# Return a new `Map` with the associations for all of the given `keys` removed.
|
602
|
+
#
|
603
|
+
# @example
|
604
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
605
|
+
# m.except("A", "C") # => Erlang::Map["B" => 2]
|
606
|
+
#
|
607
|
+
# @param keys [Array] The keys to remove
|
608
|
+
# @return [Map]
|
609
|
+
def except(*keys)
|
610
|
+
return keys.reduce(self) { |map, key| map.delete(key) }
|
611
|
+
end
|
612
|
+
|
613
|
+
# Return a new `Map` with only the associations for the `wanted` keys retained.
|
614
|
+
#
|
615
|
+
# @example
|
616
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
617
|
+
# m.slice("B", "C") # => Erlang::Map["B" => 2, "C" => 3]
|
618
|
+
#
|
619
|
+
# @param wanted [::Enumerable] The keys to retain
|
620
|
+
# @return [Map]
|
621
|
+
def slice(*wanted)
|
622
|
+
trie = Trie.new(0)
|
623
|
+
wanted.each { |key|
|
624
|
+
key = Erlang.from(key)
|
625
|
+
trie.put!(key, get(key)) if key?(key)
|
23
626
|
}
|
627
|
+
return self.class.alloc(trie, @default)
|
628
|
+
end
|
629
|
+
|
630
|
+
# Return a {List} of the values which correspond to the `wanted` keys.
|
631
|
+
# If any of the `wanted` keys are not present in this `Map`, `nil` will be
|
632
|
+
# placed instead, or the result of the default proc (if one is defined),
|
633
|
+
# similar to the behavior of {#get}.
|
634
|
+
#
|
635
|
+
# @example
|
636
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
637
|
+
# m.values_at("B", "A", "D") # => Erlang::List[2, 1, nil]
|
638
|
+
#
|
639
|
+
# @param wanted [Array] The keys to retrieve
|
640
|
+
# @return [List]
|
641
|
+
def values_at(*wanted)
|
642
|
+
array = wanted.map { |key|
|
643
|
+
key = Erlang.from(key)
|
644
|
+
get(key)
|
645
|
+
}
|
646
|
+
return List.from_enum(array.freeze)
|
647
|
+
end
|
648
|
+
|
649
|
+
# Return a {List} of the values which correspond to the `wanted` keys.
|
650
|
+
# If any of the `wanted` keys are not present in this `Map`, raise `KeyError`
|
651
|
+
# exception.
|
652
|
+
#
|
653
|
+
# @example
|
654
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
655
|
+
# m.fetch_values("C", "A") # => Erlang::List[3, 1]
|
656
|
+
# m.fetch_values("C", "Z") # => KeyError: key not found: "Z"
|
657
|
+
#
|
658
|
+
# @param wanted [Array] The keys to retrieve
|
659
|
+
# @return [Tuple]
|
660
|
+
def fetch_values(*wanted)
|
661
|
+
array = wanted.map { |key|
|
662
|
+
key = Erlang.from(key)
|
663
|
+
fetch(key)
|
664
|
+
}
|
665
|
+
return List.from_enum(array.freeze)
|
666
|
+
end
|
667
|
+
|
668
|
+
# Return a new {List} containing the keys from this `Map`.
|
669
|
+
#
|
670
|
+
# @example
|
671
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3, "D" => 2].keys
|
672
|
+
# # => Erlang::List["D", "C", "B", "A"]
|
673
|
+
#
|
674
|
+
# @return [Set]
|
675
|
+
def keys
|
676
|
+
return Erlang::List.from_enum(each_key.to_a.freeze)
|
677
|
+
end
|
678
|
+
|
679
|
+
# Return a new {List} populated with the values from this `Map`.
|
680
|
+
#
|
681
|
+
# @example
|
682
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3, "D" => 2].values
|
683
|
+
# # => Erlang::List[2, 3, 2, 1]
|
684
|
+
#
|
685
|
+
# @return [List]
|
686
|
+
def values
|
687
|
+
return Erlang::List.from_enum(each_value.to_a.freeze)
|
688
|
+
end
|
689
|
+
|
690
|
+
# Return a new `Map` created by using keys as values and values as keys.
|
691
|
+
# If there are multiple values which are equivalent (as determined by `#hash` and
|
692
|
+
# `#eql?`), only one out of each group of equivalent values will be
|
693
|
+
# retained. Which one specifically is undefined.
|
694
|
+
#
|
695
|
+
# @example
|
696
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3, "D" => 2].invert
|
697
|
+
# # => Erlang::Map[1 => "A", 3 => "C", 2 => "B"]
|
698
|
+
#
|
699
|
+
# @return [Map]
|
700
|
+
def invert
|
701
|
+
pairs = []
|
702
|
+
each { |k,v| pairs << [v, k] }
|
703
|
+
return self.class.new(pairs, &@default)
|
704
|
+
end
|
705
|
+
|
706
|
+
# Return a new {List} which is a one-dimensional flattening of this `Map`.
|
707
|
+
# If `level` is 1, all the `[key, value]` pairs in the hash will be concatenated
|
708
|
+
# into one {List}. If `level` is greater than 1, keys or values which are
|
709
|
+
# themselves `Array`s or {List}s will be recursively flattened into the output
|
710
|
+
# {List}. The depth to which that flattening will be recursively applied is
|
711
|
+
# determined by `level`.
|
712
|
+
#
|
713
|
+
# As a special case, if `level` is 0, each `[key, value]` pair will be a
|
714
|
+
# separate element in the returned {List}.
|
715
|
+
#
|
716
|
+
# @example
|
717
|
+
# m = Erlang::Map["A" => 1, "B" => [2, 3, 4]]
|
718
|
+
# m.flatten
|
719
|
+
# # => Erlang::List["A", 1, "B", [2, 3, 4]]
|
720
|
+
# h.flatten(2)
|
721
|
+
# # => Erlang::List["A", 1, "B", 2, 3, 4]
|
722
|
+
#
|
723
|
+
# @param level [Integer] The number of times to recursively flatten the `[key, value]` pairs in this `Map`.
|
724
|
+
# @return [List]
|
725
|
+
def flatten(level = 1)
|
726
|
+
return List.from_enum(self) if level == 0
|
727
|
+
array = []
|
728
|
+
each { |k,v| array << k; array << v }
|
729
|
+
array.flatten!(level-1) if level > 1
|
730
|
+
return List.from_enum(array.freeze)
|
731
|
+
end
|
732
|
+
|
733
|
+
# Searches through the `Map`, comparing `obj` with each key (using `#==`).
|
734
|
+
# When a matching key is found, return the `[key, value]` pair as a `Tuple`.
|
735
|
+
# Return `nil` if no match is found.
|
736
|
+
#
|
737
|
+
# @example
|
738
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].assoc("B") # => Erlang::Tuple["B", 2]
|
739
|
+
#
|
740
|
+
# @param obj [Object] The key to search for (using #==)
|
741
|
+
# @return [Tuple]
|
742
|
+
def assoc(obj)
|
743
|
+
obj = Erlang.from(obj)
|
744
|
+
each { |entry| return Erlang::Tuple[*entry] if obj == entry[0] }
|
745
|
+
return nil
|
746
|
+
end
|
747
|
+
|
748
|
+
# Searches through the `Map`, comparing `obj` with each value (using `#==`).
|
749
|
+
# When a matching value is found, return the `[key, value]` pair as a `Tuple`.
|
750
|
+
# Return `nil` if no match is found.
|
751
|
+
#
|
752
|
+
# @example
|
753
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].rassoc(2) # => Erlang::Tuple["B", 2]
|
754
|
+
#
|
755
|
+
# @param obj [Object] The value to search for (using #==)
|
756
|
+
# @return [Tuple]
|
757
|
+
def rassoc(obj)
|
758
|
+
obj = Erlang.from(obj)
|
759
|
+
each { |entry| return Erlang::Tuple[*entry] if obj == entry[1] }
|
760
|
+
return nil
|
761
|
+
end
|
762
|
+
|
763
|
+
# Searches through the `Map`, comparing `value` with each value (using `#==`).
|
764
|
+
# When a matching value is found, return its associated key object.
|
765
|
+
# Return `nil` if no match is found.
|
766
|
+
#
|
767
|
+
# @example
|
768
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].key(2) # => "B"
|
769
|
+
#
|
770
|
+
# @param value [Object] The value to search for (using #==)
|
771
|
+
# @return [Object]
|
772
|
+
def key(value)
|
773
|
+
value = Erlang.from(value)
|
774
|
+
each { |entry| return entry[0] if value == entry[1] }
|
775
|
+
return nil
|
776
|
+
end
|
777
|
+
|
778
|
+
# Return a randomly chosen `[key, value]` pair from this `Map`. If the hash is empty,
|
779
|
+
# return `nil`.
|
780
|
+
#
|
781
|
+
# @example
|
782
|
+
# Erlang::Map["A" => 1, "B" => 2, "C" => 3].sample
|
783
|
+
# # => Erlang::Tuple["C", 3]
|
784
|
+
#
|
785
|
+
# @return [Tuple]
|
786
|
+
def sample
|
787
|
+
return Erlang::Tuple[*@trie.at(rand(size))]
|
24
788
|
end
|
25
789
|
|
26
|
-
|
27
|
-
|
28
|
-
|
790
|
+
# Return an empty `Map` instance, of the same class as this one. Useful if you
|
791
|
+
# have multiple subclasses of `Map` and want to treat them polymorphically.
|
792
|
+
# Maintains the default block, if there is one.
|
793
|
+
#
|
794
|
+
# @return [Map]
|
795
|
+
def clear
|
796
|
+
if @default
|
797
|
+
return self.class.alloc(EmptyTrie, @default)
|
798
|
+
else
|
799
|
+
return self.class.empty
|
800
|
+
end
|
29
801
|
end
|
802
|
+
|
803
|
+
# Return true if `other` has the same type and contents as this `Map`.
|
804
|
+
#
|
805
|
+
# @param other [Object] The collection to compare with
|
806
|
+
# @return [Boolean]
|
807
|
+
def eql?(other)
|
808
|
+
return true if other.equal?(self)
|
809
|
+
return @trie.eql?(other.instance_variable_get(:@trie)) if instance_of?(other.class)
|
810
|
+
return !!(Erlang.compare(other, self) == 0)
|
811
|
+
end
|
812
|
+
alias :== :eql?
|
813
|
+
|
814
|
+
# See `Object#hash`.
|
815
|
+
# @return [Integer]
|
816
|
+
def hash
|
817
|
+
return keys.sort.reduce(Erlang::Map.hash) do |acc, key|
|
818
|
+
(acc << 32) - acc + key.hash + get(key).hash
|
819
|
+
end
|
820
|
+
end
|
821
|
+
|
822
|
+
# Return the contents of this `Map` as a programmer-readable `String`. If all the
|
823
|
+
# keys and values are serializable as Ruby literal strings, the returned string can
|
824
|
+
# be passed to `eval` to reconstitute an equivalent `Map`. The default
|
825
|
+
# block (if there is one) will be lost when doing this, however.
|
826
|
+
#
|
827
|
+
# @return [String]
|
828
|
+
def inspect
|
829
|
+
# result = "#{self.class}["
|
830
|
+
result = "{"
|
831
|
+
i = 0
|
832
|
+
each do |key, val|
|
833
|
+
result << ', ' if i > 0
|
834
|
+
result << key.inspect << ' => ' << val.inspect
|
835
|
+
i += 1
|
836
|
+
end
|
837
|
+
return result << "}"
|
838
|
+
end
|
839
|
+
|
840
|
+
# Allows this `Map` to be printed using `Erlang.inspect()`.
|
841
|
+
#
|
842
|
+
# @return [String]
|
843
|
+
def erlang_inspect(raw = false)
|
844
|
+
result = '#{'
|
845
|
+
each_with_index do |(key, val), i|
|
846
|
+
result << ',' if i > 0
|
847
|
+
result << Erlang.inspect(key, raw: raw)
|
848
|
+
result << ' => '
|
849
|
+
result << Erlang.inspect(val, raw: raw)
|
850
|
+
end
|
851
|
+
return result << '}'
|
852
|
+
end
|
853
|
+
|
854
|
+
# Allows this `Map` to be printed at the `pry` console, or using `pp` (from the
|
855
|
+
# Ruby standard library), in a way which takes the amount of horizontal space on
|
856
|
+
# the screen into account, and which indents nested structures to make them easier
|
857
|
+
# to read.
|
858
|
+
#
|
859
|
+
# @private
|
860
|
+
def pretty_print(pp)
|
861
|
+
# return pp.group(1, "#{self.class}[", "]") do
|
862
|
+
return pp.group(1, "{", "}") do
|
863
|
+
pp.breakable ''
|
864
|
+
pp.seplist(self, nil) do |key, val|
|
865
|
+
pp.group do
|
866
|
+
key.pretty_print(pp)
|
867
|
+
pp.text ' => '
|
868
|
+
pp.group(1) do
|
869
|
+
pp.breakable ''
|
870
|
+
val.pretty_print(pp)
|
871
|
+
end
|
872
|
+
end
|
873
|
+
end
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
877
|
+
# Convert this `Erlang::Map` to an instance of Ruby's built-in `Hash`.
|
878
|
+
#
|
879
|
+
# @return [::Hash]
|
880
|
+
def to_hash
|
881
|
+
output = {}
|
882
|
+
each do |key, value|
|
883
|
+
output[key] = value
|
884
|
+
end
|
885
|
+
return output
|
886
|
+
end
|
887
|
+
alias :to_h :to_hash
|
888
|
+
|
889
|
+
# Return a Proc which accepts a key as an argument and returns the value.
|
890
|
+
# The Proc behaves like {#get} (when the key is missing, it returns nil or
|
891
|
+
# result of the default proc).
|
892
|
+
#
|
893
|
+
# @example
|
894
|
+
# m = Erlang::Map["A" => 1, "B" => 2, "C" => 3]
|
895
|
+
# m.to_proc.call("B")
|
896
|
+
# # => 2
|
897
|
+
# ["A", "C", "X"].map(&h) # The & is short for .to_proc in Ruby
|
898
|
+
# # => [1, 3, nil]
|
899
|
+
#
|
900
|
+
# @return [Proc]
|
901
|
+
def to_proc
|
902
|
+
return lambda { |key| get(key) }
|
903
|
+
end
|
904
|
+
|
905
|
+
# @return [::Hash]
|
906
|
+
# @private
|
907
|
+
def marshal_dump
|
908
|
+
return to_hash
|
909
|
+
end
|
910
|
+
|
911
|
+
# @private
|
912
|
+
def marshal_load(dictionary)
|
913
|
+
@trie = Trie[dictionary]
|
914
|
+
__send__(:immutable!)
|
915
|
+
return self
|
916
|
+
end
|
917
|
+
|
918
|
+
private
|
919
|
+
|
920
|
+
# Return a new `Map` which is derived from this one, using a modified {Trie}.
|
921
|
+
# The new `Map` will retain the existing default block, if there is one.
|
922
|
+
#
|
923
|
+
def derive_new_map(trie)
|
924
|
+
if trie.equal?(@trie)
|
925
|
+
return self
|
926
|
+
elsif trie.empty?
|
927
|
+
if @default
|
928
|
+
return self.class.alloc(EmptyTrie, @default)
|
929
|
+
else
|
930
|
+
return self.class.empty
|
931
|
+
end
|
932
|
+
else
|
933
|
+
return self.class.alloc(trie, @default)
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
30
937
|
end
|
31
|
-
|
938
|
+
|
939
|
+
# The canonical empty `Map`. Returned by `Map[]` when
|
940
|
+
# invoked with no arguments; also returned by `Map.empty`. Prefer using this
|
941
|
+
# one rather than creating many empty hashes using `Map.new`.
|
942
|
+
#
|
943
|
+
# @private
|
944
|
+
EmptyMap = Erlang::Map.empty
|
945
|
+
end
|