immutable-ruby 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/immutable/_core.rb +3067 -0
- data/lib/immutable/hash.rb +4 -941
- data/lib/immutable/set.rb +3 -583
- data/lib/immutable/sorted_set.rb +0 -2
- data/lib/immutable/vector.rb +3 -1554
- data/lib/immutable/version.rb +1 -1
- metadata +7 -6
data/lib/immutable/set.rb
CHANGED
@@ -1,583 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'immutable/
|
4
|
-
require 'immutable/trie'
|
5
|
-
require 'immutable/sorted_set'
|
6
|
-
require 'set'
|
7
|
-
|
8
|
-
module Immutable
|
9
|
-
|
10
|
-
# `Immutable::Set` is a collection of unordered values with no duplicates. Testing whether
|
11
|
-
# an object is present in the `Set` can be done in constant time. `Set` is also `Enumerable`, so you can
|
12
|
-
# iterate over the members of the set with {#each}, transform them with {#map}, filter
|
13
|
-
# them with {#select}, and so on. Some of the `Enumerable` methods are overridden to
|
14
|
-
# return `immutable-ruby` collections.
|
15
|
-
#
|
16
|
-
# Like the `Set` class in Ruby's standard library, which we will call RubySet,
|
17
|
-
# `Immutable::Set` defines equivalency of objects using `#hash` and `#eql?`. No two
|
18
|
-
# objects with the same `#hash` code, and which are also `#eql?`, can coexist in the
|
19
|
-
# same `Set`. If one is already in the `Set`, attempts to add another one will have
|
20
|
-
# no effect.
|
21
|
-
#
|
22
|
-
# `Set`s have no natural ordering and cannot be compared using `#<=>`. However, they
|
23
|
-
# define {#<}, {#>}, {#<=}, and {#>=} as shorthand for {#proper_subset?},
|
24
|
-
# {#proper_superset?}, {#subset?}, and {#superset?} respectively.
|
25
|
-
#
|
26
|
-
# The basic set-theoretic operations {#union}, {#intersection}, {#difference}, and
|
27
|
-
# {#exclusion} work with any `Enumerable` object.
|
28
|
-
#
|
29
|
-
# A `Set` can be created in either of the following ways:
|
30
|
-
#
|
31
|
-
# Immutable::Set.new([1, 2, 3]) # any Enumerable can be used to initialize
|
32
|
-
# Immutable::Set['A', 'B', 'C', 'D']
|
33
|
-
#
|
34
|
-
# The latter 2 forms of initialization can be used with your own, custom subclasses
|
35
|
-
# of `Immutable::Set`.
|
36
|
-
#
|
37
|
-
# Unlike RubySet, all methods which you might expect to "modify" an `Immutable::Set`
|
38
|
-
# actually return a new set and leave the existing one unchanged.
|
39
|
-
#
|
40
|
-
# @example
|
41
|
-
# set1 = Immutable::Set[1, 2] # => Immutable::Set[1, 2]
|
42
|
-
# set2 = Immutable::Set[1, 2] # => Immutable::Set[1, 2]
|
43
|
-
# set1 == set2 # => true
|
44
|
-
# set3 = set1.add("foo") # => Immutable::Set[1, 2, "foo"]
|
45
|
-
# set3 - set2 # => Immutable::Set["foo"]
|
46
|
-
# set3.subset?(set1) # => false
|
47
|
-
# set1.subset?(set3) # => true
|
48
|
-
#
|
49
|
-
class Set
|
50
|
-
include Immutable::Enumerable
|
51
|
-
|
52
|
-
class << self
|
53
|
-
# Create a new `Set` populated with the given items.
|
54
|
-
# @return [Set]
|
55
|
-
def [](*items)
|
56
|
-
items.empty? ? empty : new(items)
|
57
|
-
end
|
58
|
-
|
59
|
-
# Return an empty `Set`. If used on a subclass, returns an empty instance
|
60
|
-
# of that class.
|
61
|
-
#
|
62
|
-
# @return [Set]
|
63
|
-
def empty
|
64
|
-
@empty ||= new
|
65
|
-
end
|
66
|
-
|
67
|
-
# "Raw" allocation of a new `Set`. Used internally to create a new
|
68
|
-
# instance quickly after obtaining a modified {Trie}.
|
69
|
-
#
|
70
|
-
# @return [Set]
|
71
|
-
# @private
|
72
|
-
def alloc(trie = EmptyTrie)
|
73
|
-
allocate.tap { |s| s.instance_variable_set(:@trie, trie) }.freeze
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def initialize(items=[])
|
78
|
-
@trie = Trie.new(0)
|
79
|
-
items.each { |item| @trie.put!(item, nil) }
|
80
|
-
freeze
|
81
|
-
end
|
82
|
-
|
83
|
-
# Return `true` if this `Set` contains no items.
|
84
|
-
# @return [Boolean]
|
85
|
-
def empty?
|
86
|
-
@trie.empty?
|
87
|
-
end
|
88
|
-
|
89
|
-
# Return the number of items in this `Set`.
|
90
|
-
# @return [Integer]
|
91
|
-
def size
|
92
|
-
@trie.size
|
93
|
-
end
|
94
|
-
alias length size
|
95
|
-
|
96
|
-
# Return a new `Set` with `item` added. If `item` is already in the set,
|
97
|
-
# return `self`.
|
98
|
-
#
|
99
|
-
# @example
|
100
|
-
# Immutable::Set[1, 2, 3].add(4) # => Immutable::Set[1, 2, 4, 3]
|
101
|
-
# Immutable::Set[1, 2, 3].add(2) # => Immutable::Set[1, 2, 3]
|
102
|
-
#
|
103
|
-
# @param item [Object] The object to add
|
104
|
-
# @return [Set]
|
105
|
-
def add(item)
|
106
|
-
include?(item) ? self : self.class.alloc(@trie.put(item, nil))
|
107
|
-
end
|
108
|
-
alias << add
|
109
|
-
|
110
|
-
# If `item` is not a member of this `Set`, return a new `Set` with `item` added.
|
111
|
-
# Otherwise, return `false`.
|
112
|
-
#
|
113
|
-
# @example
|
114
|
-
# Immutable::Set[1, 2, 3].add?(4) # => Immutable::Set[1, 2, 4, 3]
|
115
|
-
# Immutable::Set[1, 2, 3].add?(2) # => false
|
116
|
-
#
|
117
|
-
# @param item [Object] The object to add
|
118
|
-
# @return [Set, false]
|
119
|
-
def add?(item)
|
120
|
-
!include?(item) && add(item)
|
121
|
-
end
|
122
|
-
|
123
|
-
# Return a new `Set` with `item` removed. If `item` is not a member of the set,
|
124
|
-
# return `self`.
|
125
|
-
#
|
126
|
-
# @example
|
127
|
-
# Immutable::Set[1, 2, 3].delete(1) # => Immutable::Set[2, 3]
|
128
|
-
# Immutable::Set[1, 2, 3].delete(99) # => Immutable::Set[1, 2, 3]
|
129
|
-
#
|
130
|
-
# @param item [Object] The object to remove
|
131
|
-
# @return [Set]
|
132
|
-
def delete(item)
|
133
|
-
trie = @trie.delete(item)
|
134
|
-
new_trie(trie)
|
135
|
-
end
|
136
|
-
|
137
|
-
# If `item` is a member of this `Set`, return a new `Set` with `item` removed.
|
138
|
-
# Otherwise, return `false`.
|
139
|
-
#
|
140
|
-
# @example
|
141
|
-
# Immutable::Set[1, 2, 3].delete?(1) # => Immutable::Set[2, 3]
|
142
|
-
# Immutable::Set[1, 2, 3].delete?(99) # => false
|
143
|
-
#
|
144
|
-
# @param item [Object] The object to remove
|
145
|
-
# @return [Set, false]
|
146
|
-
def delete?(item)
|
147
|
-
include?(item) && delete(item)
|
148
|
-
end
|
149
|
-
|
150
|
-
# Call the block once for each item in this `Set`. No specific iteration order
|
151
|
-
# is guaranteed, but the order will be stable for any particular `Set`. If
|
152
|
-
# no block is given, an `Enumerator` is returned instead.
|
153
|
-
#
|
154
|
-
# @example
|
155
|
-
# Immutable::Set["Dog", "Elephant", "Lion"].each { |e| puts e }
|
156
|
-
# Elephant
|
157
|
-
# Dog
|
158
|
-
# Lion
|
159
|
-
# # => Immutable::Set["Dog", "Elephant", "Lion"]
|
160
|
-
#
|
161
|
-
# @yield [item] Once for each item.
|
162
|
-
# @return [self, Enumerator]
|
163
|
-
def each
|
164
|
-
return to_enum if not block_given?
|
165
|
-
@trie.each { |key, _| yield(key) }
|
166
|
-
self
|
167
|
-
end
|
168
|
-
|
169
|
-
# Call the block once for each item in this `Set`. Iteration order will be
|
170
|
-
# the opposite of {#each}. If no block is given, an `Enumerator` is
|
171
|
-
# returned instead.
|
172
|
-
#
|
173
|
-
# @example
|
174
|
-
# Immutable::Set["Dog", "Elephant", "Lion"].reverse_each { |e| puts e }
|
175
|
-
# Lion
|
176
|
-
# Dog
|
177
|
-
# Elephant
|
178
|
-
# # => Immutable::Set["Dog", "Elephant", "Lion"]
|
179
|
-
#
|
180
|
-
# @yield [item] Once for each item.
|
181
|
-
# @return [self]
|
182
|
-
def reverse_each
|
183
|
-
return enum_for(:reverse_each) if not block_given?
|
184
|
-
@trie.reverse_each { |key, _| yield(key) }
|
185
|
-
self
|
186
|
-
end
|
187
|
-
|
188
|
-
# Return a new `Set` with all the items for which the block returns true.
|
189
|
-
#
|
190
|
-
# @example
|
191
|
-
# Immutable::Set["Elephant", "Dog", "Lion"].select { |e| e.size >= 4 }
|
192
|
-
# # => Immutable::Set["Elephant", "Lion"]
|
193
|
-
# @yield [item] Once for each item.
|
194
|
-
# @return [Set]
|
195
|
-
def select
|
196
|
-
return enum_for(:select) unless block_given?
|
197
|
-
trie = @trie.select { |key, _| yield(key) }
|
198
|
-
new_trie(trie)
|
199
|
-
end
|
200
|
-
alias find_all select
|
201
|
-
alias keep_if select
|
202
|
-
|
203
|
-
# Call the block once for each item in this `Set`. All the values returned
|
204
|
-
# from the block will be gathered into a new `Set`. If no block is given,
|
205
|
-
# an `Enumerator` is returned instead.
|
206
|
-
#
|
207
|
-
# @example
|
208
|
-
# Immutable::Set["Cat", "Elephant", "Dog", "Lion"].map { |e| e.size }
|
209
|
-
# # => Immutable::Set[8, 4, 3]
|
210
|
-
#
|
211
|
-
# @yield [item] Once for each item.
|
212
|
-
# @return [Set]
|
213
|
-
def map
|
214
|
-
return enum_for(:map) if not block_given?
|
215
|
-
return self if empty?
|
216
|
-
self.class.new(super)
|
217
|
-
end
|
218
|
-
alias collect map
|
219
|
-
|
220
|
-
# Return `true` if the given item is present in this `Set`. More precisely,
|
221
|
-
# return `true` if an object with the same `#hash` code, and which is also `#eql?`
|
222
|
-
# to the given object is present.
|
223
|
-
#
|
224
|
-
# @example
|
225
|
-
# Immutable::Set["A", "B", "C"].include?("B") # => true
|
226
|
-
# Immutable::Set["A", "B", "C"].include?("Z") # => false
|
227
|
-
#
|
228
|
-
# @param object [Object] The object to check for
|
229
|
-
# @return [Boolean]
|
230
|
-
def include?(object)
|
231
|
-
@trie.key?(object)
|
232
|
-
end
|
233
|
-
alias member? include?
|
234
|
-
|
235
|
-
# Return a member of this `Set`. The member chosen will be the first one which
|
236
|
-
# would be yielded by {#each}. If the set is empty, return `nil`.
|
237
|
-
#
|
238
|
-
# @example
|
239
|
-
# Immutable::Set["A", "B", "C"].first # => "C"
|
240
|
-
#
|
241
|
-
# @return [Object]
|
242
|
-
def first
|
243
|
-
(entry = @trie.at(0)) && entry[0]
|
244
|
-
end
|
245
|
-
|
246
|
-
# Return a {SortedSet} which contains the same items as this `Set`, ordered by
|
247
|
-
# the given comparator block.
|
248
|
-
#
|
249
|
-
# @example
|
250
|
-
# Immutable::Set["Elephant", "Dog", "Lion"].sort
|
251
|
-
# # => Immutable::SortedSet["Dog", "Elephant", "Lion"]
|
252
|
-
# Immutable::Set["Elephant", "Dog", "Lion"].sort { |a,b| a.size <=> b.size }
|
253
|
-
# # => Immutable::SortedSet["Dog", "Lion", "Elephant"]
|
254
|
-
#
|
255
|
-
# @yield [a, b] Any number of times with different pairs of elements.
|
256
|
-
# @yieldreturn [Integer] Negative if the first element should be sorted
|
257
|
-
# lower, positive if the latter element, or 0 if
|
258
|
-
# equal.
|
259
|
-
# @return [SortedSet]
|
260
|
-
def sort(&comparator)
|
261
|
-
SortedSet.new(to_a, &comparator)
|
262
|
-
end
|
263
|
-
|
264
|
-
# Return a {SortedSet} which contains the same items as this `Set`, ordered
|
265
|
-
# by mapping each item through the provided block to obtain sort keys, and
|
266
|
-
# then sorting the keys.
|
267
|
-
#
|
268
|
-
# @example
|
269
|
-
# Immutable::Set["Elephant", "Dog", "Lion"].sort_by { |e| e.size }
|
270
|
-
# # => Immutable::SortedSet["Dog", "Lion", "Elephant"]
|
271
|
-
#
|
272
|
-
# @yield [item] Once for each item to create the set, and then potentially
|
273
|
-
# again depending on what operations are performed on the
|
274
|
-
# returned {SortedSet}. As such, it is recommended that the
|
275
|
-
# block be a pure function.
|
276
|
-
# @yieldreturn [Object] sort key for the item
|
277
|
-
# @return [SortedSet]
|
278
|
-
def sort_by(&mapper)
|
279
|
-
SortedSet.new(to_a, &mapper)
|
280
|
-
end
|
281
|
-
|
282
|
-
# Return a new `Set` which contains all the members of both this `Set` and `other`.
|
283
|
-
# `other` can be any `Enumerable` object.
|
284
|
-
#
|
285
|
-
# @example
|
286
|
-
# Immutable::Set[1, 2] | Immutable::Set[2, 3] # => Immutable::Set[1, 2, 3]
|
287
|
-
#
|
288
|
-
# @param other [Enumerable] The collection to merge with
|
289
|
-
# @return [Set]
|
290
|
-
def union(other)
|
291
|
-
if other.is_a?(Immutable::Set)
|
292
|
-
if other.size > size
|
293
|
-
small_set_pairs = @trie
|
294
|
-
large_set_trie = other.instance_variable_get(:@trie)
|
295
|
-
else
|
296
|
-
small_set_pairs = other.instance_variable_get(:@trie)
|
297
|
-
large_set_trie = @trie
|
298
|
-
end
|
299
|
-
else
|
300
|
-
if other.respond_to?(:lazy)
|
301
|
-
small_set_pairs = other.lazy.map { |e| [e, nil] }
|
302
|
-
else
|
303
|
-
small_set_pairs = other.map { |e| [e, nil] }
|
304
|
-
end
|
305
|
-
large_set_trie = @trie
|
306
|
-
end
|
307
|
-
|
308
|
-
trie = large_set_trie.bulk_put(small_set_pairs)
|
309
|
-
new_trie(trie)
|
310
|
-
end
|
311
|
-
alias | union
|
312
|
-
alias + union
|
313
|
-
alias merge union
|
314
|
-
|
315
|
-
# Return a new `Set` which contains all the items which are members of both
|
316
|
-
# this `Set` and `other`. `other` can be any `Enumerable` object.
|
317
|
-
#
|
318
|
-
# @example
|
319
|
-
# Immutable::Set[1, 2] & Immutable::Set[2, 3] # => Immutable::Set[2]
|
320
|
-
#
|
321
|
-
# @param other [Enumerable] The collection to intersect with
|
322
|
-
# @return [Set]
|
323
|
-
def intersection(other)
|
324
|
-
if other.size < @trie.size
|
325
|
-
if other.is_a?(Immutable::Set)
|
326
|
-
trie = other.instance_variable_get(:@trie).select { |key, _| include?(key) }
|
327
|
-
else
|
328
|
-
trie = Trie.new(0)
|
329
|
-
other.each { |obj| trie.put!(obj, nil) if include?(obj) }
|
330
|
-
end
|
331
|
-
else
|
332
|
-
trie = @trie.select { |key, _| other.include?(key) }
|
333
|
-
end
|
334
|
-
new_trie(trie)
|
335
|
-
end
|
336
|
-
alias & intersection
|
337
|
-
|
338
|
-
# Return a new `Set` with all the items in `other` removed. `other` can be
|
339
|
-
# any `Enumerable` object.
|
340
|
-
#
|
341
|
-
# @example
|
342
|
-
# Immutable::Set[1, 2] - Immutable::Set[2, 3] # => Immutable::Set[1]
|
343
|
-
#
|
344
|
-
# @param other [Enumerable] The collection to subtract from this set
|
345
|
-
# @return [Set]
|
346
|
-
def difference(other)
|
347
|
-
trie = if (@trie.size <= other.size) && (other.is_a?(Immutable::Set) || (defined?(::Set) && other.is_a?(::Set)))
|
348
|
-
@trie.select { |key, _| !other.include?(key) }
|
349
|
-
else
|
350
|
-
@trie.bulk_delete(other)
|
351
|
-
end
|
352
|
-
new_trie(trie)
|
353
|
-
end
|
354
|
-
alias subtract difference
|
355
|
-
alias - difference
|
356
|
-
|
357
|
-
# Return a new `Set` which contains all the items which are members of this
|
358
|
-
# `Set` or of `other`, but not both. `other` can be any `Enumerable` object.
|
359
|
-
#
|
360
|
-
# @example
|
361
|
-
# Immutable::Set[1, 2] ^ Immutable::Set[2, 3] # => Immutable::Set[1, 3]
|
362
|
-
#
|
363
|
-
# @param other [Enumerable] The collection to take the exclusive disjunction of
|
364
|
-
# @return [Set]
|
365
|
-
def exclusion(other)
|
366
|
-
((self | other) - (self & other))
|
367
|
-
end
|
368
|
-
alias ^ exclusion
|
369
|
-
|
370
|
-
# Return `true` if all items in this `Set` are also in `other`.
|
371
|
-
#
|
372
|
-
# @example
|
373
|
-
# Immutable::Set[2, 3].subset?(Immutable::Set[1, 2, 3]) # => true
|
374
|
-
#
|
375
|
-
# @param other [Set]
|
376
|
-
# @return [Boolean]
|
377
|
-
def subset?(other)
|
378
|
-
return false if other.size < size
|
379
|
-
|
380
|
-
# This method has the potential to be very slow if 'other' is a large Array, so to avoid that,
|
381
|
-
# we convert those Arrays to Sets before checking presence of items
|
382
|
-
# Time to convert Array -> Set is linear in array.size
|
383
|
-
# Time to check for presence of all items in an Array is proportional to set.size * array.size
|
384
|
-
# Note that both sides of that equation have array.size -- hence those terms cancel out,
|
385
|
-
# and the break-even point is solely dependent on the size of this collection
|
386
|
-
# After doing some benchmarking to estimate the constants, it appears break-even is at ~190 items
|
387
|
-
# We also check other.size, to avoid the more expensive #is_a? checks in cases where it doesn't matter
|
388
|
-
#
|
389
|
-
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(Immutable::Set) || other.is_a?(::Set))
|
390
|
-
other = ::Set.new(other)
|
391
|
-
end
|
392
|
-
all? { |item| other.include?(item) }
|
393
|
-
end
|
394
|
-
alias <= subset?
|
395
|
-
|
396
|
-
# Return `true` if all items in `other` are also in this `Set`.
|
397
|
-
#
|
398
|
-
# @example
|
399
|
-
# Immutable::Set[1, 2, 3].superset?(Immutable::Set[2, 3]) # => true
|
400
|
-
#
|
401
|
-
# @param other [Set]
|
402
|
-
# @return [Boolean]
|
403
|
-
def superset?(other)
|
404
|
-
other.subset?(self)
|
405
|
-
end
|
406
|
-
alias >= superset?
|
407
|
-
|
408
|
-
# Returns `true` if `other` contains all the items in this `Set`, plus at least
|
409
|
-
# one item which is not in this set.
|
410
|
-
#
|
411
|
-
# @example
|
412
|
-
# Immutable::Set[2, 3].proper_subset?(Immutable::Set[1, 2, 3]) # => true
|
413
|
-
# Immutable::Set[1, 2, 3].proper_subset?(Immutable::Set[1, 2, 3]) # => false
|
414
|
-
#
|
415
|
-
# @param other [Set]
|
416
|
-
# @return [Boolean]
|
417
|
-
def proper_subset?(other)
|
418
|
-
return false if other.size <= size
|
419
|
-
# See comments above
|
420
|
-
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(Immutable::Set) || other.is_a?(::Set))
|
421
|
-
other = ::Set.new(other)
|
422
|
-
end
|
423
|
-
all? { |item| other.include?(item) }
|
424
|
-
end
|
425
|
-
alias < proper_subset?
|
426
|
-
|
427
|
-
# Returns `true` if this `Set` contains all the items in `other`, plus at least
|
428
|
-
# one item which is not in `other`.
|
429
|
-
#
|
430
|
-
# @example
|
431
|
-
# Immutable::Set[1, 2, 3].proper_superset?(Immutable::Set[2, 3]) # => true
|
432
|
-
# Immutable::Set[1, 2, 3].proper_superset?(Immutable::Set[1, 2, 3]) # => false
|
433
|
-
#
|
434
|
-
# @param other [Set]
|
435
|
-
# @return [Boolean]
|
436
|
-
def proper_superset?(other)
|
437
|
-
other.proper_subset?(self)
|
438
|
-
end
|
439
|
-
alias > proper_superset?
|
440
|
-
|
441
|
-
# Return `true` if this `Set` and `other` do not share any items.
|
442
|
-
#
|
443
|
-
# @example
|
444
|
-
# Immutable::Set[1, 2].disjoint?(Immutable::Set[8, 9]) # => true
|
445
|
-
#
|
446
|
-
# @param other [Set]
|
447
|
-
# @return [Boolean]
|
448
|
-
def disjoint?(other)
|
449
|
-
if other.size <= size
|
450
|
-
other.each { |item| return false if include?(item) }
|
451
|
-
else
|
452
|
-
# See comment on #subset?
|
453
|
-
if other.size >= 150 && @trie.size >= 190 && !(other.is_a?(Immutable::Set) || other.is_a?(::Set))
|
454
|
-
other = ::Set.new(other)
|
455
|
-
end
|
456
|
-
each { |item| return false if other.include?(item) }
|
457
|
-
end
|
458
|
-
true
|
459
|
-
end
|
460
|
-
|
461
|
-
# Return `true` if this `Set` and `other` have at least one item in common.
|
462
|
-
#
|
463
|
-
# @example
|
464
|
-
# Immutable::Set[1, 2].intersect?(Immutable::Set[2, 3]) # => true
|
465
|
-
#
|
466
|
-
# @param other [Set]
|
467
|
-
# @return [Boolean]
|
468
|
-
def intersect?(other)
|
469
|
-
!disjoint?(other)
|
470
|
-
end
|
471
|
-
|
472
|
-
# Recursively insert the contents of any nested `Set`s into this `Set`, and
|
473
|
-
# remove them.
|
474
|
-
#
|
475
|
-
# @example
|
476
|
-
# Immutable::Set[Immutable::Set[1, 2], Immutable::Set[3, 4]].flatten
|
477
|
-
# # => Immutable::Set[1, 2, 3, 4]
|
478
|
-
#
|
479
|
-
# @return [Set]
|
480
|
-
def flatten
|
481
|
-
reduce(self.class.empty) do |set, item|
|
482
|
-
next set.union(item.flatten) if item.is_a?(Set)
|
483
|
-
set.add(item)
|
484
|
-
end
|
485
|
-
end
|
486
|
-
|
487
|
-
alias group group_by
|
488
|
-
alias classify group_by
|
489
|
-
|
490
|
-
# Return a randomly chosen item from this `Set`. If the set is empty, return `nil`.
|
491
|
-
#
|
492
|
-
# @example
|
493
|
-
# Immutable::Set[1, 2, 3, 4, 5].sample # => 3
|
494
|
-
#
|
495
|
-
# @return [Object]
|
496
|
-
def sample
|
497
|
-
empty? ? nil : @trie.at(rand(size))[0]
|
498
|
-
end
|
499
|
-
|
500
|
-
# Return an empty `Set` instance, of the same class as this one. Useful if you
|
501
|
-
# have multiple subclasses of `Set` and want to treat them polymorphically.
|
502
|
-
#
|
503
|
-
# @return [Set]
|
504
|
-
def clear
|
505
|
-
self.class.empty
|
506
|
-
end
|
507
|
-
|
508
|
-
# Return true if `other` has the same type and contents as this `Set`.
|
509
|
-
#
|
510
|
-
# @param other [Object] The object to compare with
|
511
|
-
# @return [Boolean]
|
512
|
-
def eql?(other)
|
513
|
-
return true if other.equal?(self)
|
514
|
-
return false if not instance_of?(other.class)
|
515
|
-
other_trie = other.instance_variable_get(:@trie)
|
516
|
-
return false if @trie.size != other_trie.size
|
517
|
-
@trie.each do |key, _|
|
518
|
-
return false if !other_trie.key?(key)
|
519
|
-
end
|
520
|
-
true
|
521
|
-
end
|
522
|
-
alias == eql?
|
523
|
-
|
524
|
-
# See `Object#hash`.
|
525
|
-
# @return [Integer]
|
526
|
-
def hash
|
527
|
-
reduce(0) { |hash, item| (hash << 5) - hash + item.hash }
|
528
|
-
end
|
529
|
-
|
530
|
-
# Return `self`. Since this is an immutable object duplicates are
|
531
|
-
# equivalent.
|
532
|
-
# @return [Set]
|
533
|
-
def dup
|
534
|
-
self
|
535
|
-
end
|
536
|
-
alias clone dup
|
537
|
-
|
538
|
-
undef :"<=>" # Sets are not ordered, so Enumerable#<=> will give a meaningless result
|
539
|
-
undef :each_index # Set members cannot be accessed by 'index', so #each_index is not meaningful
|
540
|
-
|
541
|
-
# Return `self`.
|
542
|
-
#
|
543
|
-
# @return [self]
|
544
|
-
def to_set
|
545
|
-
self
|
546
|
-
end
|
547
|
-
|
548
|
-
# @private
|
549
|
-
def marshal_dump
|
550
|
-
output = {}
|
551
|
-
each do |key|
|
552
|
-
output[key] = nil
|
553
|
-
end
|
554
|
-
output
|
555
|
-
end
|
556
|
-
|
557
|
-
# @private
|
558
|
-
def marshal_load(dictionary)
|
559
|
-
@trie = dictionary.reduce(EmptyTrie) do |trie, key_value|
|
560
|
-
trie.put(key_value.first, nil)
|
561
|
-
end
|
562
|
-
end
|
563
|
-
|
564
|
-
private
|
565
|
-
|
566
|
-
def new_trie(trie)
|
567
|
-
if trie.empty?
|
568
|
-
self.class.empty
|
569
|
-
elsif trie.equal?(@trie)
|
570
|
-
self
|
571
|
-
else
|
572
|
-
self.class.alloc(trie)
|
573
|
-
end
|
574
|
-
end
|
575
|
-
end
|
576
|
-
|
577
|
-
# The canonical empty `Set`. Returned by `Set[]` when
|
578
|
-
# invoked with no arguments; also returned by `Set.empty`. Prefer using this
|
579
|
-
# one rather than creating many empty sets using `Set.new`.
|
580
|
-
#
|
581
|
-
# @private
|
582
|
-
EmptySet = Immutable::Set.empty
|
583
|
-
end
|
1
|
+
# Definition of Immutable::Vector is in a separate file to avoid
|
2
|
+
# circular dependency warnings
|
3
|
+
require 'immutable/_core'
|