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

联系方式

这里有什么

首先,其他地方有什么。类 Set

  • 继承自类 Object。

  • 包含 模块 Enumerable,它提供了许多附加方法。

特别是,类 Set 本身没有很多用于获取或迭代的方法。相反,它依赖于 Enumerable 中的方法。

在这里,类 Set 提供了对以下方面有用的方法:

创建集合的方法

  • ::[]:返回一个包含给定对象的新集合。

  • ::new:返回一个包含给定对象(如果未给定块)或来自被调用块的返回值(如果给定了块)的新集合。

集合运算的方法

  • |(别名为 union+):返回一个包含 self 中的所有元素和给定可枚举中的所有元素(无重复)的新集合。

  • &(别名为 intersection):返回一个包含 self 和给定可枚举中所有公共元素的新集合。

  • -(别名为 difference):返回 self 的副本,其中删除了给定可枚举中的所有元素。

  • ^:返回一个包含 self 和给定可枚举中所有元素的新集合,但不包括两者共有的元素。

比较的方法

  • <=>:如果 self 小于、等于或大于给定对象,则返回 -1、0 或 1。

  • ==:返回 self 和给定的可枚举是否相等,由 Object#eql? 确定。

  • compare_by_identity?:返回集合在比较元素时是否仅考虑标识。

查询的方法

  • length(别名为 size):返回元素计数。

  • empty?:返回集合是否没有元素。

  • include?(别名为 member?===):返回给定对象是否是集合中的元素。

  • subset?(别名为 <=):返回给定对象是否是集合的子集。

  • proper_subset?(别名为 <):返回给定可枚举是否是集合的真子集。

  • superset?(别名为 >=):返回给定可枚举是否是集合的超集。

  • proper_superset?(别名为 >):返回给定可枚举是否是集合的真超集。

  • disjoint?:如果集合和给定的可枚举没有公共元素,则返回 true,否则返回 false

  • intersect?:如果集合和给定的可枚举有任何公共元素,则返回 true,否则返回 false

  • compare_by_identity?:返回集合在比较元素时是否仅考虑标识。

赋值的方法

  • add(别名为 <<):将给定对象添加到集合;返回 self

  • add?:如果给定对象不是集合中的元素,则添加它并返回 self;否则,返回 nil

  • merge:将每个给定可枚举对象的元素合并到集合中;返回 self

  • replace:用给定可枚举的内容替换集合的内容。

删除的方法

  • clear:删除集合中的所有元素;返回 self

  • delete:从集合中删除给定对象;返回 self

  • delete?:如果给定对象是集合中的元素,则删除它并返回 self;否则,返回 nil

  • subtract:从集合中删除每个给定对象;返回 self

  • delete_if - 删除由给定块指定的元素。

  • select!(别名为 filter!):删除由给定块指定的未指定的元素。

  • keep_if:删除由给定块指定的未指定的元素。

  • reject! 删除由给定块指定的元素。

转换的方法

  • classify:返回一个哈希,该哈希根据给定的块对元素进行分类。

  • collect!(别名为 map!):用块返回值替换每个元素。

  • divide:返回一个哈希,该哈希根据给定的块对元素进行分类;与 classify 的区别在于块可以接受一个或两个参数。

  • flatten:返回一个新的集合,该集合是 self 的递归展平。flatten!:用该集合中的元素替换 self 中的每个嵌套集合。

  • inspect(别名为 to_s):返回一个显示元素的字符串。

  • join:返回一个包含所有元素的字符串,根据需要转换为字符串,并用给定的记录分隔符连接。

  • to_a:返回一个包含所有集合元素的数组。

  • to_set:如果没有给定参数且没有给定块,则返回 self;如果给定块,则返回一个由块返回值组成的新集合。

迭代的方法

  • each:使用每个连续的元素调用该块;返回 self

其他方法

  • reset:重置内部状态;如果集合中的元素在对象被修改时非常有用。

常量

VERSION

公共类方法

[](*ary) 点击切换源代码

创建一个包含给定对象的新集合。

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
new(enum = nil) { |o| ... } 点击切换源代码

创建一个包含给定可枚举对象的元素的新集合。

如果给出了块,则枚举的元素会通过给定的块进行预处理。

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

公共实例方法

&(enum) 点击切换源代码

返回一个新的集合,其中包含集合和给定可枚举对象共有的元素。

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
也别名为:intersection
+(enum)
别名为:|
-(enum) 点击切换源代码

返回通过复制该集合构建的新集合,删除给定可枚举对象中出现的每个元素。

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
也别名为:difference
<(set)
别名为:proper_subset?
<<(o)
别名为:add
<=(set)
别名为:subset?
<=>(set) 点击切换源代码

如果两个集合相等,则返回 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
==(other) 点击切换源代码

如果两个集合相等,则返回 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
===(o)

如果给定对象是集合的成员,则返回 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
别名为:include?
>(set)
别名为:proper_superset?
>=(set)
别名为:superset?
^(enum) 点击切换源代码

返回一个新的集合,其中包含集合和给定可枚举对象之间的独有元素。(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
add(o) 点击切换源代码

将给定对象添加到集合并返回 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
也别名为:<<
add?(o) 点击切换源代码

将给定对象添加到集合并返回 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
classify() { |o| ... } 点击切换源代码

通过给定块的返回值对集合进行分类,并返回 {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
clear() 点击切换源代码

删除所有元素并返回 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!() { |o| ... } 点击切换源代码

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
也别名为:map!
compare_by_identity() 点击切换源代码

使集合通过其身份比较元素并返回 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
compare_by_identity?() 点击切换源代码

如果集合将通过其身份比较元素,则返回 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
delete(o) 点击切换源代码

从集合中删除给定对象并返回 self。使用 subtract 一次删除多个项目。

# File set.rb, line 532
def delete(o)
  @hash.delete(o)
  self
end
delete?(o) 点击切换源代码

从集合中删除给定对象并返回 self。如果该对象不在集合中,则返回 nil。

# File set.rb, line 539
def delete?(o)
  delete(o) if include?(o)
end
delete_if(&block) 点击切换源代码

删除块求值为 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
difference(enum)
别名为:-
disjoint?(set) 点击切换源代码

如果集合和给定可枚举对象没有共同的元素,则返回 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
divide(&func) 点击切换源代码

根据给定块定义的共性将集合划分为子集的集合。

如果块的元数为 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
each(&block) 点击切换源代码

为集合中的每个元素调用一次给定的块,并将该元素作为参数传递。如果未给出块,则返回一个枚举器。

# File set.rb, line 502
def each(&block)
  block_given? or return enum_for(__method__) { size }
  @hash.each_key(&block)
  self
end
empty?() 点击切换源代码

如果集合不包含任何元素,则返回 true。

# File set.rb, line 307
def empty?
  @hash.empty?
end
filter!(&block)

等效于 Set#select!

别名为:select!
flatten() 点击切换源代码

返回一个新集合,该集合是该集合的副本,递归地展平每个包含的集合。

# File set.rb, line 380
def flatten
  self.class.new.flatten_merge(self)
end
flatten!() 点击切换源代码

等效于 Set#flatten,但是用结果就地替换接收者。如果没有进行修改,则返回 nil。

# File set.rb, line 386
def flatten!
  replace(flatten()) if any?(Set)
end
include?(o) 点击切换源代码

如果集合包含给定对象,则返回 true。

请注意,include?member? 不会像其他 Enumerable 一样使用 == 测试成员相等性。

另请参见 Enumerable#include?

# File set.rb, line 396
def include?(o)
  @hash[o]
end
也别名为:member?===
initialize_clone(orig, **options) 点击切换源代码

克隆内部哈希。

调用超类方法
# File set.rb, line 290
def initialize_clone(orig, **options)
  super
  @hash = orig.instance_variable_get(:@hash).clone(**options)
end
initialize_dup(orig) 点击切换源代码

复制内部哈希。

调用超类方法
# File set.rb, line 284
def initialize_dup(orig)
  super
  @hash = orig.instance_variable_get(:@hash).dup
end
inspect() 点击切换源代码

返回一个包含该集合的人类可读表示形式的字符串(“#<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
也别名为:to_s
intersect?(set) 点击切换源代码

如果集合和给定可枚举对象至少有一个共同的元素,则返回 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
intersection(enum)
别名为:&
join(separator=nil) 点击切换源代码

返回通过将集合的每个元素转换为字符串而创建的字符串。另请参见:Array#join

# File set.rb, line 806
def join(separator=nil)
  to_a.join(separator)
end
keep_if(&block) 点击切换源代码

删除块求值为 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
length()
别名为:size
map!()
别名为:collect!
member?(o)
别名为:include?
merge(*enums, **nil) 点击切换源代码

将给定可枚举对象的元素合并到集合中,并返回 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
proper_subset?(set) 点击切换源代码

如果该集合是给定集合的真子集,则返回 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
也别名为:<
proper_superset?(set) 点击切换源代码

如果该集合是给定集合的真超集,则返回 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
也别名为:>
reject!(&block) 点击切换源代码

等效于 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
replace(enum) 点击切换源代码

将集合的内容替换为给定可枚举对象的内容,并返回 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
reset() 点击切换源代码

在修改现有元素后重置内部状态,并返回 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
select!(&block) 点击切换源代码

等效于 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
也别名为:filter!
size() 点击切换源代码

返回元素数。

# File set.rb, line 301
def size
  @hash.size
end
也别名为:length
subset?(set) 点击切换源代码

如果该集合是给定集合的子集,则返回 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
也别名为:<=
subtract(enum) 点击切换源代码

删除给定可枚举对象中出现的每个元素,并返回 self。

# File set.rb, line 612
def subtract(enum)
  do_with_enum(enum) { |o| delete(o) }
  self
end
superset?(set) 点击切换源代码

如果该集合是给定集合的超集,则返回 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
也别名为:>=
to_a() 点击切换源代码

返回一个包含集合中所有元素的数组。

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
to_s()
别名为:inspect
to_set(klass = Set, *args, &block) 点击切换源代码

如果没有提供任何参数,则返回自身。否则,使用 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
union(enum)
别名为:|
|(enum) 点击切换源代码

返回一个新集合,该集合是通过合并当前集合和给定可枚举对象的元素构建的。

Set[1, 2, 3] | Set[2, 4, 5]         #=> #<Set: {1, 2, 3, 4, 5}>
Set[1, 5, 'z'] | (1..6)             #=> #<Set: {1, 5, "z", 2, 3, 4, 6}>
# File set.rb, line 622
def |(enum)
  dup.merge(enum)
end
也别名为:+, union