класс Set
Множество реализует коллекцию неупорядоченных значений без дубликатов. Это гибрид интуитивно понятных возможностей взаимодействия массива и быстрого поиска хэша.
Множество легко использовать с объектами Enumerable (реализующими each). Большинство методов инициализации и бинарных операторов принимают универсальные объекты Enumerable помимо множеств и массивов. Объект Enumerable может быть преобразован в Множество с помощью метода to_set.
Множество использует Хэш в качестве хранилища, поэтому следует учитывать следующие моменты:
-
Равенство элементов определяется в соответствии с Object#eql? и Object#hash.
-
Множество предполагает, что идентичность каждого элемента не изменяется во время хранения. Изменение элемента множества сделает состояние множества ненадежным.
-
Если строка должна быть сохранена, вместо неё сохраняется замороженная копия строки, если только исходная строка не заморожена.
Сравнение
Операторы сравнения <, >, <= и >= реализованы как сокращенная запись методов {proper_}, {subset?, superset?}. Однако оператор <=> намеренно опущен, так как не все пары множеств сравнимы. (Например, {x, y} и {x, z})
Пример
require 'set'
s1 = Set.new [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 <knu@iDaemons.org> (current maintainer)
Публичные методы класса
# File lib/set.rb, line 71 def self.[](*ary) new(ary) end
Создаёт новое множество, содержащее заданные объекты.
# File lib/set.rb, line 80
def initialize(enum = nil, &block) # :yields: o
@hash ||= Hash.new
enum.nil? and return
if block
do_with_enum(enum) { |o| add(block[o]) }
else
merge(enum)
end
end Создаёт новое множество, содержащее элементы заданного объекта перечисления.
Если задан блок, элементы enum предварительно обрабатываются данным блоком.
Общедоступные методы экземпляра
# File lib/set.rb, line 405
def &(enum)
n = self.class.new
do_with_enum(enum) { |o| n.add(o) if include?(o) }
n
end Возвращает новый набор, содержащий элементы, общие для набора и заданного перечисляемого объекта.
# File lib/set.rb, line 398 def -(enum) dup.subtract(enum) end
Возвращает новый набор, созданный путем дублирования набора, удаляя каждый элемент, который появляется в заданном перечисляемом объекте.
# File lib/set.rb, line 423
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, если два множества равны. Равенство каждой пары элементов определяется в соответствии с Object#eql?.
# File lib/set.rb, line 415
def ^(enum)
n = Set.new(enum)
each { |o| if n.include?(o) then n.delete(o) else n.add(o) end }
n
end Возвращает новый набор, содержащий элементы, уникальные для набора и заданного перечисляемого объекта. (set ^ enum) эквивалентно ((set | enum) - (set & enum)).
# File lib/set.rb, line 289 def add(o) @hash[o] = true self end
Добавляет заданный объект в набор и возвращает self. Используйте merge для добавления сразу нескольких элементов.
# File lib/set.rb, line 297
def add?(o)
if include?(o)
nil
else
add(o)
end
end Добавляет заданный объект в набор и возвращает self. Если объект уже содержится в наборе, возвращает nil.
# File lib/set.rb, line 457
def classify # :yields: o
block_given? or return enum_for(__method__)
h = {}
each { |i|
x = yield(i)
(h[x] ||= self.class.new).add(i)
}
h
end Классифицирует набор по возвращаемому значению заданного блока и возвращает хеш пар {значение => множество элементов}. Блок вызывается один раз для каждого элемента набора, передавая элемент в качестве параметра.
Например:
require 'set'
files = Set.new(Dir.glob("*.rb"))
hash = files.classify { |f| File.mtime(f).year }
p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>,
# 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
# 2002=>#<Set: {"f.rb"}>}
# File lib/set.rb, line 142 def clear @hash.clear self end
Удаляет все элементы и возвращает self.
# File lib/set.rb, line 343
def collect!
block_given? or return enum_for(__method__)
set = self.class.new
each { |o| set << yield(o) }
replace(set)
end Заменяет элементы теми, которые возвращаются collect().
# File lib/set.rb, line 307 def delete(o) @hash.delete(o) self end
Удаляет заданный объект из набора и возвращает self. Используйте subtract для удаления сразу нескольких элементов.
# File lib/set.rb, line 314
def delete?(o)
if include?(o)
delete(o)
else
nil
end
end Удаляет заданный объект из набора и возвращает self. Если объект не содержится в наборе, возвращает nil.
# File lib/set.rb, line 324
def delete_if
block_given? or return enum_for(__method__)
# @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, и возвращает self.
# File lib/set.rb, line 274 def disjoint?(set) !intersect?(set) end
Возвращает true, если в наборе и заданном наборе нет общих элементов. Этот метод является противоположностью intersect?.
Например:
require 'set' Set[1, 2, 3].disjoint? Set[3, 4] # => false Set[1, 2, 3].disjoint? Set[4, 5] # => true
# File lib/set.rb, line 486
def divide(&func)
func or return enum_for(__method__)
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 Делит набор на множество подмножеств в соответствии с общностью, определенной заданным блоком.
Если арность блока равна 2, элементы o1 и o2 являются общими, если block.call(o1, o2) истинно. В противном случае, элементы o1 и o2 являются общими, если block.call(o1) == block.call(o2).
Например:
require 'set'
numbers = Set[1, 3, 4, 6, 9, 10, 11]
set = numbers.divide { |i,j| (i - j).abs == 1 }
p set # => #<Set: {#<Set: {1}>,
# #<Set: {11, 9, 10}>,
# #<Set: {3, 4}>,
# #<Set: {6}>}>
# File lib/set.rb, line 281 def each(&block) block or return enum_for(__method__) @hash.each_key(&block) self end
Вызывает заданный блок один раз для каждого элемента в наборе, передавая элемент в качестве параметра. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 137 def empty? @hash.empty? end
Возвращает true, если набор не содержит элементов.
# File lib/set.rb, line 196 def flatten self.class.new.flatten_merge(self) end
Возвращает новый набор, который является копией набора, рекурсивно выравнивая каждый содержащийся набор.
# File lib/set.rb, line 202
def flatten!
if detect { |e| e.is_a?(Set) }
replace(flatten())
else
nil
end
end Эквивалентно #flatten, но заменяет приемник результатом на месте. Возвращает nil, если изменения не были внесены.
# File lib/set.rb, line 211 def include?(o) @hash.include?(o) end
Возвращает true, если набор содержит заданный объект.
# File lib/set.rb, line 110 def initialize_clone(orig) super @hash = orig.instance_variable_get(:@hash).clone end
Клонирует внутренний хеш.
# File lib/set.rb, line 104 def initialize_dup(orig) super @hash = orig.instance_variable_get(:@hash).dup end
Дублирует внутренний хеш.
# File lib/set.rb, line 520
def inspect
ids = (Thread.current[InspectKey] ||= [])
if ids.include?(object_id)
return sprintf('#<%s: {...}>', self.class.name)
end
begin
ids << object_id
return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2])
ensure
ids.pop
end
end Возвращает строку, содержащую удобочитаемое представление множества. («#<Множество: {элемент1, элемент2, …}>»)
# File lib/set.rb, line 256
def intersect?(set)
set.is_a?(Set) or raise ArgumentError, "value must be a set"
if size < set.size
any? { |o| set.include?(o) }
else
set.any? { |o| include?(o) }
end
end Возвращает true, если множество и заданное множество имеют хотя бы один общий элемент.
Например:
require 'set' Set[1, 2, 3].intersect? Set[4, 5] # => false Set[1, 2, 3].intersect? Set[3, 4] # => true
# File lib/set.rb, line 334
def keep_if
block_given? or return enum_for(__method__)
# @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 Удаляет каждый элемент множества, для которого блок возвращает false, и возвращает self.
# File lib/set.rb, line 371
def merge(enum)
if enum.instance_of?(self.class)
@hash.update(enum.instance_variable_get(:@hash))
else
do_with_enum(enum) { |o| add(o) }
end
self
end Объединяет элементы заданного перечислимого объекта в множество и возвращает self.
# File lib/set.rb, line 241
def proper_subset?(set)
set.is_a?(Set) or raise ArgumentError, "value must be a set"
return false if set.size <= size
all? { |o| set.include?(o) }
end Возвращает true, если множество является собственным подмножеством заданного множества.
# File lib/set.rb, line 225
def proper_superset?(set)
set.is_a?(Set) or raise ArgumentError, "value must be a set"
return false if size <= set.size
set.all? { |o| include?(o) }
end Возвращает true, если множество является собственным надмножеством заданного множества.
# File lib/set.rb, line 353 def reject!(&block) block or return enum_for(__method__) n = size delete_if(&block) size == n ? nil : self end
Эквивалентно #delete_if, но возвращает nil, если изменений не было.
# File lib/set.rb, line 149
def replace(enum)
if enum.instance_of?(self.class)
@hash.replace(enum.instance_variable_get(:@hash))
self
else
do_with_enum(enum)
clear
merge(enum)
end
end Заменяет содержимое множества содержимым заданного перечислимого объекта и возвращает self.
# File lib/set.rb, line 362 def select!(&block) block or return enum_for(__method__) n = size keep_if(&block) size == n ? nil : self end
Эквивалентно #keep_if, но возвращает nil, если изменений не было.
# File lib/set.rb, line 131 def size @hash.size end
Возвращает количество элементов.
# File lib/set.rb, line 233
def subset?(set)
set.is_a?(Set) or raise ArgumentError, "value must be a set"
return false if set.size < size
all? { |o| set.include?(o) }
end Возвращает true, если множество является подмножеством заданного множества.
# File lib/set.rb, line 383
def subtract(enum)
do_with_enum(enum) { |o| delete(o) }
self
end Удаляет каждый элемент, который появляется в заданном перечислимом объекте, и возвращает self.
# File lib/set.rb, line 217
def superset?(set)
set.is_a?(Set) or raise ArgumentError, "value must be a set"
return false if size < set.size
set.all? { |o| include?(o) }
end Возвращает true, если множество является надмножеством заданного множества.
# File lib/set.rb, line 161 def to_a @hash.keys end
Преобразует множество в массив. Порядок элементов не определен.
# File lib/set.rb, line 170 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
Возвращает self, если аргументы не указаны. В противном случае преобразует множество в другое с помощью klass.new(self, *args, &block).
В подклассах, возвращает klass.new(self, *args, &block), если не переопределено.
# File lib/set.rb, line 390 def |(enum) dup.merge(enum) end
Возвращает новое множество, созданное путём объединения множества и элементов заданного перечислимого объекта.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.