class Set
这个库提供了 Set
类,它实现了一个不包含重复值的无序值的集合。它是数组直观的互操作性和哈希快速查找的混合体。
为了方便起见,方法 to_set
被添加到 Enumerable
中。
Set
很容易与 Enumerable
对象(实现 each
)一起使用。大多数初始化方法和二元运算符除了接受集合和数组外,还接受通用的 Enumerable
对象。可以使用 to_set
方法将 Enumerable
对象转换为 Set
。
Set
使用哈希作为存储,因此您必须注意以下几点
-
元素的相等性是根据 Object#eql? 和 Object#hash 确定的。使用
Set#compare_by_identity
使集合通过元素的标识来比较它们。 -
Set
假设每个元素的标识在存储时不会更改。修改集合中的元素会使集合处于不可靠状态。 -
当要存储字符串时,除非原始字符串已经冻结,否则将存储该字符串的冻结副本。
比较¶ ↑
比较运算符 <
、>
、<=
和 >=
被实现为 {proper_,}{subset?,superset?} 方法的简写。<=>
运算符反映了此顺序,或者对于具有不同元素的集合(例如,{x, y}
与 {x, z}
)返回 nil
。
示例¶ ↑
require 'set' s1 = Set[1, 2] #=> #<Set: {1, 2}> s2 = [1, 2].to_set #=> #<Set: {1, 2}> s1 == s2 #=> true s1.add("foo") #=> #<Set: {1, 2, "foo"}> s1.merge([2, 6]) #=> #<Set: {1, 2, "foo", 6}> s1.subset?(s2) #=> false s2.subset?(s1) #=> true
联系方式¶ ↑
-
Akinori MUSHA <[email protected]>(当前维护者)
这里有什么¶ ↑
首先,其他地方有什么。类 Set
-
继承自类 Object。
-
包含 模块 Enumerable,它提供了许多附加方法。
特别是,类 Set 本身没有很多用于获取或迭代的方法。相反,它依赖于 Enumerable 中的方法。
在这里,类 Set 提供了对以下方面有用的方法:
创建集合的方法¶ ↑
集合运算的方法¶ ↑
-
&(别名为
intersection
):返回一个包含self
和给定可枚举中所有公共元素的新集合。 -
-(别名为
difference
):返回self
的副本,其中删除了给定可枚举中的所有元素。 -
^:返回一个包含
self
和给定可枚举中所有元素的新集合,但不包括两者共有的元素。
比较的方法¶ ↑
-
<=>:如果
self
小于、等于或大于给定对象,则返回 -1、0 或 1。 -
==:返回
self
和给定的可枚举是否相等,由 Object#eql? 确定。 -
compare_by_identity?
:返回集合在比较元素时是否仅考虑标识。
查询的方法¶ ↑
-
empty?
:返回集合是否没有元素。 -
proper_subset?
(别名为 <):返回给定可枚举是否是集合的真子集。 -
proper_superset?
(别名为 >):返回给定可枚举是否是集合的真超集。 -
disjoint?
:如果集合和给定的可枚举没有公共元素,则返回true
,否则返回false
。 -
intersect?
:如果集合和给定的可枚举有任何公共元素,则返回true
,否则返回false
。 -
compare_by_identity?
:返回集合在比较元素时是否仅考虑标识。
赋值的方法¶ ↑
-
add?
:如果给定对象不是集合中的元素,则添加它并返回self
;否则,返回nil
。 -
merge
:将每个给定可枚举对象的元素合并到集合中;返回self
。 -
replace
:用给定可枚举的内容替换集合的内容。
删除的方法¶ ↑
-
clear
:删除集合中的所有元素;返回self
。 -
delete
:从集合中删除给定对象;返回self
。 -
delete?
:如果给定对象是集合中的元素,则删除它并返回self
;否则,返回nil
。 -
subtract
:从集合中删除每个给定对象;返回self
。 -
delete_if
- 删除由给定块指定的元素。 -
keep_if
:删除由给定块指定的未指定的元素。 -
reject!
删除由给定块指定的元素。
转换的方法¶ ↑
-
classify
:返回一个哈希,该哈希根据给定的块对元素进行分类。 -
divide
:返回一个哈希,该哈希根据给定的块对元素进行分类;与classify
的区别在于块可以接受一个或两个参数。 -
flatten
:返回一个新的集合,该集合是self
的递归展平。flatten!
:用该集合中的元素替换self
中的每个嵌套集合。 -
join
:返回一个包含所有元素的字符串,根据需要转换为字符串,并用给定的记录分隔符连接。 -
to_a
:返回一个包含所有集合元素的数组。 -
to_set
:如果没有给定参数且没有给定块,则返回self
;如果给定块,则返回一个由块返回值组成的新集合。
迭代的方法¶ ↑
-
each
:使用每个连续的元素调用该块;返回self
。
其他方法¶ ↑
-
reset
:重置内部状态;如果集合中的元素在对象被修改时非常有用。
常量
- VERSION
公共类方法
创建一个包含给定对象的新集合。
Set[1, 2] # => #<Set: {1, 2}> Set[1, 2, 1] # => #<Set: {1, 2}> Set[1, 'c', :s] # => #<Set: {1, "c", :s}>
# File set.rb, line 228 def self.[](*ary) new(ary) end
创建一个包含给定可枚举对象的元素的新集合。
如果给出了块,则枚举的元素会通过给定的块进行预处理。
Set.new([1, 2]) #=> #<Set: {1, 2}> Set.new([1, 2, 1]) #=> #<Set: {1, 2}> Set.new([1, 'c', :s]) #=> #<Set: {1, "c", :s}> Set.new(1..5) #=> #<Set: {1, 2, 3, 4, 5}> Set.new([1, 2, 3]) { |x| x * x } #=> #<Set: {1, 4, 9}>
# File set.rb, line 243 def initialize(enum = nil, &block) # :yields: o @hash ||= Hash.new(false) enum.nil? and return if block do_with_enum(enum) { |o| add(block[o]) } else merge(enum) end end
公共实例方法
返回一个新的集合,其中包含集合和给定可枚举对象共有的元素。
Set[1, 3, 5] & Set[3, 2, 1] #=> #<Set: {3, 1}> Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> #<Set: {"a", "b"}>
# File set.rb, line 643 def &(enum) n = self.class.new if enum.is_a?(Set) if enum.size > size each { |o| n.add(o) if enum.include?(o) } else enum.each { |o| n.add(o) if include?(o) } end else do_with_enum(enum) { |o| n.add(o) if include?(o) } end n end
返回通过复制该集合构建的新集合,删除给定可枚举对象中出现的每个元素。
Set[1, 3, 5] - Set[1, 5] #=> #<Set: {3}> Set['a', 'b', 'z'] - ['a', 'c'] #=> #<Set: {"b", "z"}>
# File set.rb, line 633 def -(enum) dup.subtract(enum) end
如果两个集合相等,则返回 0;如果该集合是给定集合的真子集/超集,则返回 -1 / +1;如果它们都具有唯一的元素,则返回 nil。
# File set.rb, line 456 def <=>(set) return unless set.is_a?(Set) case size <=> set.size when -1 then -1 if proper_subset?(set) when +1 then +1 if proper_superset?(set) else 0 if self.==(set) end end
如果两个集合相等,则返回 true。根据 Object#eql? 定义每对元素的相等性。
Set[1, 2] == Set[2, 1] #=> true Set[1, 3, 5] == Set[1, 5] #=> false Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false
# File set.rb, line 677 def ==(other) if self.equal?(other) true elsif other.instance_of?(self.class) @hash == other.instance_variable_get(:@hash) elsif other.is_a?(Set) && self.size == other.size other.all? { |o| @hash.include?(o) } else false end end
如果给定对象是集合的成员,则返回 true;否则返回 false。
用于 case 语句中
require 'set' case :apple when Set[:potato, :carrot] "vegetable" when Set[:apple, :banana] "fruit" end # => "fruit"
或者自身使用
Set[1, 2, 3] === 2 #=> true Set[1, 2, 3] === 4 #=> false
返回一个新的集合,其中包含集合和给定可枚举对象之间的独有元素。(set ^ enum)
等效于 ((set | enum) - (set & enum))
。
Set[1, 2] ^ Set[2, 3] #=> #<Set: {3, 1}> Set[1, 'b', 'c'] ^ ['b', 'd'] #=> #<Set: {"d", 1, "c"}>
# File set.rb, line 664 def ^(enum) n = self.class.new(enum) each { |o| n.add(o) unless n.delete?(o) } n end
将给定对象添加到集合并返回 self。使用 merge
一次添加多个元素。
Set[1, 2].add(3) #=> #<Set: {1, 2, 3}> Set[1, 2].add([3, 4]) #=> #<Set: {1, 2, [3, 4]}> Set[1, 2].add(2) #=> #<Set: {1, 2}>
# File set.rb, line 514 def add(o) @hash[o] = true self end
将给定对象添加到集合并返回 self。如果该对象已在集合中,则返回 nil。
Set[1, 2].add?(3) #=> #<Set: {1, 2, 3}> Set[1, 2].add?([3, 4]) #=> #<Set: {1, 2, [3, 4]}> Set[1, 2].add?(2) #=> nil
# File set.rb, line 526 def add?(o) add(o) unless include?(o) end
通过给定块的返回值对集合进行分类,并返回 {value => 元素集合} 对的哈希。为集合的每个元素调用一次块,并将该元素作为参数传递。
require 'set' files = Set.new(Dir.glob("*.rb")) hash = files.classify { |f| File.mtime(f).year } hash #=> {2000=>#<Set: {"a.rb", "b.rb"}>, # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>, # 2002=>#<Set: {"f.rb"}>}
如果未给出块,则返回一个枚举器。
# File set.rb, line 746 def classify # :yields: o block_given? or return enum_for(__method__) { size } h = {} each { |i| (h[yield(i)] ||= self.class.new).add(i) } h end
删除所有元素并返回 self。
set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}> set.clear #=> #<Set: {}> set #=> #<Set: {}>
# File set.rb, line 316 def clear @hash.clear self end
用 collect()
返回的元素替换元素。如果未给出块,则返回一个枚举器。
# File set.rb, line 567 def collect! block_given? or return enum_for(__method__) { size } set = self.class.new each { |o| set << yield(o) } replace(set) end
使集合通过其身份比较元素并返回 self。并非所有 Set
的子类都支持此方法。
# File set.rb, line 257 def compare_by_identity if @hash.respond_to?(:compare_by_identity) @hash.compare_by_identity self else raise NotImplementedError, "#{self.class.name}\##{__method__} is not implemented" end end
如果集合将通过其身份比较元素,则返回 true。另请参见 Set#compare_by_identity
。
# File set.rb, line 268 def compare_by_identity? @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? end
从集合中删除给定对象并返回 self。使用 subtract
一次删除多个项目。
# File set.rb, line 532 def delete(o) @hash.delete(o) self end
从集合中删除给定对象并返回 self。如果该对象不在集合中,则返回 nil。
# File set.rb, line 539 def delete?(o) delete(o) if include?(o) end
删除块求值为 true 的集合的每个元素,并返回 self。如果未给出块,则返回一个枚举器。
# File set.rb, line 546 def delete_if(&block) block_given? or return enum_for(__method__) { size } # Instead of directly using @hash.delete_if, perform enumeration # using self.each that subclasses may override. select(&block).each { |o| @hash.delete(o) } self end
如果集合和给定可枚举对象没有共同的元素,则返回 true。此方法与 intersect?
相反。
Set[1, 2, 3].disjoint? Set[3, 4] #=> false Set[1, 2, 3].disjoint? Set[4, 5] #=> true Set[1, 2, 3].disjoint? [3, 4] #=> false Set[1, 2, 3].disjoint? 4..5 #=> true
# File set.rb, line 495 def disjoint?(set) !intersect?(set) end
根据给定块定义的共性将集合划分为子集的集合。
如果块的元数为 2,则如果 block.call(o1, o2) 为 true,则元素 o1 和 o2 是共有的。否则,如果 block.call(o1) == block.call(o2),则元素 o1 和 o2 是共有的。
require 'set' numbers = Set[1, 3, 4, 6, 9, 10, 11] set = numbers.divide { |i,j| (i - j).abs == 1 } set #=> #<Set: {#<Set: {1}>, # #<Set: {11, 9, 10}>, # #<Set: {3, 4}>, # #<Set: {6}>}>
如果未给出块,则返回一个枚举器。
# File set.rb, line 774 def divide(&func) func or return enum_for(__method__) { size } if func.arity == 2 require 'tsort' class << dig = {} # :nodoc: include TSort alias tsort_each_node each_key def tsort_each_child(node, &block) fetch(node).each(&block) end end each { |u| dig[u] = a = [] each{ |v| func.call(u, v) and a << v } } set = Set.new() dig.each_strongly_connected_component { |css| set.add(self.class.new(css)) } set else Set.new(classify(&func).values) end end
为集合中的每个元素调用一次给定的块,并将该元素作为参数传递。如果未给出块,则返回一个枚举器。
# File set.rb, line 502 def each(&block) block_given? or return enum_for(__method__) { size } @hash.each_key(&block) self end
如果集合不包含任何元素,则返回 true。
# File set.rb, line 307 def empty? @hash.empty? end
返回一个新集合,该集合是该集合的副本,递归地展平每个包含的集合。
# File set.rb, line 380 def flatten self.class.new.flatten_merge(self) end
等效于 Set#flatten
,但是用结果就地替换接收者。如果没有进行修改,则返回 nil。
# File set.rb, line 386 def flatten! replace(flatten()) if any?(Set) end
如果集合包含给定对象,则返回 true。
请注意,include?
和 member?
不会像其他 Enumerable 一样使用 ==
测试成员相等性。
另请参见 Enumerable#include?
# File set.rb, line 396 def include?(o) @hash[o] end
克隆内部哈希。
# File set.rb, line 290 def initialize_clone(orig, **options) super @hash = orig.instance_variable_get(:@hash).clone(**options) end
复制内部哈希。
# File set.rb, line 284 def initialize_dup(orig) super @hash = orig.instance_variable_get(:@hash).dup end
返回一个包含该集合的人类可读表示形式的字符串(“#<Set: {element1, element2, …}>”)。
# File set.rb, line 814 def inspect ids = (Thread.current[InspectKey] ||= []) if ids.include?(object_id) return sprintf('#<%s: {...}>', self.class.name) end ids << object_id begin return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2]) ensure ids.pop end end
如果集合和给定可枚举对象至少有一个共同的元素,则返回 true。
Set[1, 2, 3].intersect? Set[4, 5] #=> false Set[1, 2, 3].intersect? Set[3, 4] #=> true Set[1, 2, 3].intersect? 4..5 #=> false Set[1, 2, 3].intersect? [3, 4] #=> true
# File set.rb, line 473 def intersect?(set) case set when Set if size < set.size any?(set) else set.any?(self) end when Enumerable set.any?(self) else raise ArgumentError, "value must be enumerable" end end
返回通过将集合的每个元素转换为字符串而创建的字符串。另请参见:Array#join
# File set.rb, line 806 def join(separator=nil) to_a.join(separator) end
删除块求值为 false 的集合的每个元素,并返回 self。如果未给出块,则返回一个枚举器。
# File set.rb, line 557 def keep_if(&block) block_given? or return enum_for(__method__) { size } # Instead of directly using @hash.keep_if, perform enumeration # using self.each that subclasses may override. reject(&block).each { |o| @hash.delete(o) } self end
将给定可枚举对象的元素合并到集合中,并返回 self。
# File set.rb, line 598 def merge(*enums, **nil) enums.each do |enum| if enum.instance_of?(self.class) @hash.update(enum.instance_variable_get(:@hash)) else do_with_enum(enum) { |o| add(o) } end end self end
如果该集合是给定集合的真子集,则返回 true。
# File set.rb, line 441 def proper_subset?(set) case when set.instance_of?(self.class) && @hash.respond_to?(:<) @hash < set.instance_variable_get(:@hash) when set.is_a?(Set) size < set.size && all?(set) else raise ArgumentError, "value must be a set" end end
如果该集合是给定集合的真超集,则返回 true。
# File set.rb, line 415 def proper_superset?(set) case when set.instance_of?(self.class) && @hash.respond_to?(:>) @hash > set.instance_variable_get(:@hash) when set.is_a?(Set) size > set.size && set.all?(self) else raise ArgumentError, "value must be a set" end end
等效于 Set#delete_if
,但如果没有进行任何更改,则返回 nil。如果未给出块,则返回一个枚举器。
# File set.rb, line 577 def reject!(&block) block_given? or return enum_for(__method__) { size } n = size delete_if(&block) self if size != n end
将集合的内容替换为给定可枚举对象的内容,并返回 self。
set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}> set.replace([1, 2]) #=> #<Set: {1, 2}> set #=> #<Set: {1, 2}>
# File set.rb, line 327 def replace(enum) if enum.instance_of?(self.class) @hash.replace(enum.instance_variable_get(:@hash)) self else do_with_enum(enum) # make sure enum is enumerable before calling clear clear merge(enum) end end
在修改现有元素后重置内部状态,并返回 self。
元素将被重新索引和去重。
# File set.rb, line 702 def reset if @hash.respond_to?(:rehash) @hash.rehash # This should perform frozenness check. else raise FrozenError, "can't modify frozen #{self.class.name}" if frozen? end self end
等效于 Set#keep_if
,但如果没有进行任何更改,则返回 nil。如果未给出块,则返回一个枚举器。
# File set.rb, line 586 def select!(&block) block_given? or return enum_for(__method__) { size } n = size keep_if(&block) self if size != n end
如果该集合是给定集合的子集,则返回 true。
# File set.rb, line 428 def subset?(set) case when set.instance_of?(self.class) && @hash.respond_to?(:<=) @hash <= set.instance_variable_get(:@hash) when set.is_a?(Set) size <= set.size && all?(set) else raise ArgumentError, "value must be a set" end end
删除给定可枚举对象中出现的每个元素,并返回 self。
# File set.rb, line 612 def subtract(enum) do_with_enum(enum) { |o| delete(o) } self end
如果该集合是给定集合的超集,则返回 true。
# File set.rb, line 402 def superset?(set) case when set.instance_of?(self.class) && @hash.respond_to?(:>=) @hash >= set.instance_variable_get(:@hash) when set.is_a?(Set) size >= set.size && set.all?(self) else raise ArgumentError, "value must be a set" end end
返回一个包含集合中所有元素的数组。
Set[1, 2].to_a #=> [1, 2] Set[1, 'c', :s].to_a #=> [1, "c", :s]
# File set.rb, line 342 def to_a @hash.keys end
如果没有提供任何参数,则返回自身。否则,使用 klass.new(self, *args, &block)
将该集合转换为另一个集合。
在子类中,除非被覆盖,否则返回 klass.new(self, *args, &block)
。
# File set.rb, line 351 def to_set(klass = Set, *args, &block) return self if instance_of?(Set) && klass == Set && block.nil? && args.empty? klass.new(self, *args, &block) end