类 Set
该库提供了 Set
类,它实现了一个无序且不包含重复值的集合。它结合了 Array 的直观交互操作功能和 Hash 的快速查找功能。
为了方便起见,to_set
方法被添加到 Enumerable
中。
Set
易于与 Enumerable
对象(实现 each
)一起使用。大多数初始化方法和二元运算符除了集合和数组之外,还接受通用的 Enumerable
对象。可以使用 to_set
方法将 Enumerable
对象转换为 Set
。
-
元素的相等性由
Object#eql?
和Object#hash
决定。使用Set#compare_by_identity
使集合通过其标识来比较其元素。 -
Set
假设每个元素在存储期间的标识不会改变。修改集合中的元素将导致集合处于不可靠状态。 -
当要存储字符串时,将存储字符串的冻结副本,除非原始字符串已经冻结。
比较¶ ↑
比较运算符 <
、>
、<=
和 >=
被实现为 {proper_,}{subset?,superset?} 方法的简写。<=>
运算符反映了这种顺序,或者对于具有不同元素的集合返回 nil
(例如 {x, y}
与 {x, z}
)。
示例¶ ↑
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
联系¶ ↑
-
MUSHA Akinori <[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
: 返回一个哈希表,根据给定块对元素进行分类。 -
flatten
: 返回一个新的集合,它是self
的递归扁平化。flatten!
: 用来自该集合的元素替换self
中的每个嵌套集合。 -
join
: 返回一个字符串,包含所有元素,根据需要转换为字符串,并用给定的记录分隔符连接。 -
to_a
: 返回包含所有集合元素的数组。 -
to_set
: 如果没有给出参数和块,则返回self
;如果给出块,则返回一个新的集合,其中包含块的返回值。
迭代方法¶ ↑
-
each
: 使用每个连续元素调用块;返回self
。
其他方法¶ ↑
-
reset
: 重置内部状态;如果集合中的元素已被修改,则很有用。
常量
- 版本
公共类方法
创建一个包含给定对象的新集合。
Set[1, 2] # => #<Set: {1, 2}> Set[1, 2, 1] # => #<Set: {1, 2}> Set[1, 'c', :s] # => #<Set: {1, "c", :s}>
# File lib/set.rb, line 228 def self.[](*ary) new(ary) end
参见 as_json
.
# File ext/json/lib/json/add/set.rb, line 9 def self.json_create(object) new object['a'] 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 lib/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 lib/set.rb, line 640 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 lib/set.rb, line 630 def -(enum) dup.subtract(enum) end
如果两个集合相等,则返回 0;如果集合是给定集合的真子集/真超集,则返回 -1/+1;如果两个集合都有唯一的元素,则返回 nil。
# File lib/set.rb, line 453 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 lib/set.rb, line 674 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 lib/set.rb, line 661 def ^(enum) n = Set.new(enum) each { |o| n.add(o) unless n.delete?(o) } n end
将给定对象添加到集合中并返回自身。使用 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 lib/set.rb, line 511 def add(o) @hash[o] = true self end
将给定对象添加到集合中并返回自身。如果对象已在集合中,则返回 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 lib/set.rb, line 523 def add?(o) add(o) unless include?(o) end
方法 Set#as_json
和 Set.json_create
可用于序列化和反序列化 Set 对象;请参阅 Marshal
。
方法 Set#as_json
序列化 self
,返回一个表示 self
的 2 元素哈希。
require 'json/add/set' x = Set.new(%w/foo bar baz/).as_json # => {"json_class"=>"Set", "a"=>["foo", "bar", "baz"]}
方法 JSON.create
反序列化此类哈希,返回一个 Set 对象。
Set.json_create(x) # => #<Set: {"foo", "bar", "baz"}>
# File ext/json/lib/json/add/set.rb, line 28 def as_json(*) { JSON.create_id => self.class.name, 'a' => to_a, } 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 lib/set.rb, line 743 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
删除所有元素并返回自身。
set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}> set.clear #=> #<Set: {}> set #=> #<Set: {}>
# File lib/set.rb, line 316 def clear @hash.clear self end
用 collect()
返回的元素替换元素。如果没有给出块,则返回一个枚举器。
# File lib/set.rb, line 564 def collect! block_given? or return enum_for(__method__) { size } set = self.class.new each { |o| set << yield(o) } replace(set) end
使集合按其标识比较其元素并返回自身。此方法可能不被 Set
的所有子类支持。
# File lib/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 lib/set.rb, line 268 def compare_by_identity? @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? end
从集合中删除给定对象并返回自身。使用 subtract
一次删除多个项目。
# File lib/set.rb, line 529 def delete(o) @hash.delete(o) self end
从集合中删除给定对象并返回自身。如果对象不在集合中,则返回 nil。
# File lib/set.rb, line 536 def delete?(o) delete(o) if include?(o) end
删除集合中每个块评估为 true 的元素,并返回自身。如果未给出块,则返回枚举器。
# File lib/set.rb, line 543 def delete_if block_given? or return enum_for(__method__) { size } # @hash.delete_if should be faster, but using it breaks the order # of enumeration in subclasses. select { |o| yield o }.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 lib/set.rb, line 492 def disjoint?(set) !intersect?(set) end
根据给定块定义的共性将集合划分为一组子集。
如果块的元数为 2,则元素 o1 和 o2 具有共同点,如果 block.call(o1, o2) 为 true。否则,如果 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 lib/set.rb, line 771 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 lib/set.rb, line 499 def each(&block) block_given? or return enum_for(__method__) { size } @hash.each_key(&block) self end
如果集合不包含任何元素,则返回 true。
# File lib/set.rb, line 307 def empty? @hash.empty? end
返回一个新的集合,它是该集合的副本,递归地展平每个包含的集合。
# File lib/set.rb, line 377 def flatten self.class.new.flatten_merge(self) end
等效于 Set#flatten
,但将接收者替换为结果。如果未进行任何修改,则返回 nil。
# File lib/set.rb, line 383 def flatten! replace(flatten()) if any?(Set) end
如果集合包含给定对象,则返回 true。
请注意,include?
和 member?
不会使用 ==
测试成员相等性,就像其他可枚举对象一样。
另请参见 Enumerable#include?
# File lib/set.rb, line 393 def include?(o) @hash[o] end
克隆内部哈希表。
# File lib/set.rb, line 290 def initialize_clone(orig, **options) super @hash = orig.instance_variable_get(:@hash).clone(**options) end
复制内部哈希表。
# File lib/set.rb, line 284 def initialize_dup(orig) super @hash = orig.instance_variable_get(:@hash).dup end
返回一个字符串,其中包含集合的人类可读表示形式(“#<Set: {element1, element2, …}>”)。
# File lib/set.rb, line 811 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 lib/set.rb, line 470 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 lib/set.rb, line 803 def join(separator=nil) to_a.join(separator) end
删除集合中每个块计算结果为 false 的元素,并返回 self。如果未给出块,则返回一个枚举器。
# File lib/set.rb, line 554 def keep_if block_given? or return enum_for(__method__) { size } # @hash.keep_if should be faster, but using it breaks the order of # enumeration in subclasses. reject { |o| yield o }.each { |o| @hash.delete(o) } self end
将给定可枚举对象的元素合并到集合中,并返回自身。
# File lib/set.rb, line 595 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 lib/set.rb, line 438 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 lib/set.rb, line 412 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 lib/set.rb, line 574 def reject!(&block) block_given? or return enum_for(__method__) { size } n = size delete_if(&block) self if size != n end
用给定可枚举对象的內容替换集合的內容,并返回自身。
set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}> set.replace([1, 2]) #=> #<Set: {1, 2}> set #=> #<Set: {1, 2}>
# File lib/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
在修改现有元素后重置内部状态,并返回自身。
元素将被重新索引和去重。
# File lib/set.rb, line 699 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 lib/set.rb, line 583 def select!(&block) block_given? or return enum_for(__method__) { size } n = size keep_if(&block) self if size != n end
如果集合是给定集合的子集,则返回 true。
# File lib/set.rb, line 425 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
删除给定可枚举对象中出现的每个元素,并返回自身。
# File lib/set.rb, line 609 def subtract(enum) do_with_enum(enum) { |o| delete(o) } self end
如果该集合是给定集合的超集,则返回 true。
# File lib/set.rb, line 399 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 lib/set.rb, line 342 def to_a @hash.keys end
返回一个表示 self
的 JSON
字符串。
require 'json/add/set' puts Set.new(%w/foo bar baz/).to_json
输出
{"json_class":"Set","a":["foo","bar","baz"]}
# File ext/json/lib/json/add/set.rb, line 44 def to_json(*args) as_json.to_json(*args) end
如果没有给出参数,则返回自身。否则,使用 klass.new(self, *args, &block)
将集合转换为另一个集合。
在子类中,返回 klass.new(self, *args, &block)
,除非被重写。
# File lib/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