класс Set
Множество реализует коллекцию неупорядоченных значений без дубликатов. Это гибрид интуитивных средств взаимодействия массива и быстрого поиска хэша.
Множество легко использовать с объектами Enumerable (реализующими each). Большинство методов инициализации и бинарных операторов принимают общие объекты Enumerable, помимо множеств и массивов. Объект Enumerable можно преобразовать в Множество с помощью метода to_set.
Множество использует Хэш в качестве хранилища, поэтому необходимо учитывать следующие моменты:
-
Равенство элементов определяется в соответствии с Object#eql? и Object#hash. Используйте #compare_by_identity, чтобы множество сравнивало элементы по их идентичности.
-
Множество предполагает, что идентичность каждого элемента не изменяется во время хранения. Изменение элемента множества сделает множество ненадежным.
-
Если строка должна быть сохранена, вместо неё хранится замороженная копия строки, если только исходная строка не заморожена.
Сравнение
Операторы сравнения <, >, <= и >= реализованы как сокращенная запись для методов {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 74 def self.[](*ary) new(ary) end
Создаёт новое множество, содержащее заданные объекты.
# File lib/set.rb, line 83
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 Создаёт новое множество, содержащее элементы заданного объекта Enumerable.
Если задан блок, элементы enum предварительно обрабатываются с помощью данного блока.
Методы публичного экземпляра
# File lib/set.rb, line 439
def &(enum)
n = self.class.new
do_with_enum(enum) { |o| n.add(o) if include?(o) }
n
end Возвращает новый набор, содержащий элементы, общие для набора и заданного перечисляемого объекта.
# File lib/set.rb, line 432 def -(enum) dup.subtract(enum) end
Возвращает новый набор, созданный путём дублирования набора, удаляя каждый элемент, который появляется в заданном перечисляемом объекте.
# File lib/set.rb, line 457
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 449
def ^(enum)
n = Set.new(enum)
each { |o| n.add(o) unless n.delete?(o) }
n
end Возвращает новый набор, содержащий элементы, исключительные между набором и заданным перечисляемым объектом. (set ^ enum) эквивалентно ((set | enum) - (set & enum)).
# File lib/set.rb, line 330 def add(o) @hash[o] = true self end
Добавляет заданный объект в набор и возвращает self. Используйте merge для добавления сразу нескольких элементов.
# File lib/set.rb, line 338 def add?(o) add(o) unless include?(o) end
Добавляет заданный объект в набор и возвращает self. Если объект уже находится в наборе, возвращает nil.
# File lib/set.rb, line 493
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 Классифицирует набор по возвращаемому значению заданного блока и возвращает хэш пар {значение => набор элементов}. Блок вызывается один раз для каждого элемента набора, передавая элемент в качестве параметра.
Например:
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 162 def clear @hash.clear self end
Удаляет все элементы и возвращает self.
# File lib/set.rb, line 379
def collect!
block_given? or return enum_for(__method__) { size }
replace(self.class.new(self) { |o| yield(o) })
end Заменяет элементы элементами, возвращёнными collect(). Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 97
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 Заставляет набор сравнивать свои элементы по их идентичности и возвращает self. Этот метод может не поддерживаться всеми подклассами Set.
# File lib/set.rb, line 108 def compare_by_identity? @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? end
Возвращает true, если набор будет сравнивать свои элементы по их идентичности. Также см. #сравнить_по_идентичности.
# File lib/set.rb, line 344 def delete(o) @hash.delete(o) self end
Удаляет заданный объект из набора и возвращает self. Используйте subtract для удаления сразу нескольких элементов.
# File lib/set.rb, line 351 def delete?(o) delete(o) if include?(o) end
Удаляет заданный объект из набора и возвращает self. Если объект не находится в наборе, возвращает nil.
# File lib/set.rb, line 358
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, и возвращает self. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 315 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 523
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 Разделяет набор на набор подмножеств в соответствии с общностью, определённой заданным блоком.
Если арность блока равна 2, элементы o1 и o2 являются общими, если блок.call(o1, o2) равен true. В противном случае, элементы o1 и o2 являются общими, если блок.call(o1) == блок.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 322
def each(&block)
block or return enum_for(__method__) { size }
@hash.each_key(&block)
self
end Вызывает заданный блок один раз для каждого элемента в наборе, передавая элемент в качестве параметра. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 157 def empty? @hash.empty? end
Возвращает true, если набор не содержит элементов.
# File lib/set.rb, line 216 def flatten self.class.new.flatten_merge(self) end
Возвращает новый набор, являющийся копией набора, рекурсивно сглаживая каждый содержащий набор.
# File lib/set.rb, line 222
def flatten!
replace(flatten()) if any? { |e| e.is_a?(Set) }
end Эквивалентно #flatten, но заменяет получатель на результат на месте. Возвращает nil, если изменений не было.
# File lib/set.rb, line 232 def include?(o) @hash[o] end
Возвращает true, если множество содержит заданный объект.
Обратите внимание, что include? и member? не проверяют равенство элементов с использованием ==, как и другие Enumerables.
См. также Enumerable#include?
# File lib/set.rb, line 130 def initialize_clone(orig) super @hash = orig.instance_variable_get(:@hash).clone end
Клонирование внутренней хеш-таблицы.
# File lib/set.rb, line 124 def initialize_dup(orig) super @hash = orig.instance_variable_get(:@hash).dup end
Дублирование внутренней хеш-таблицы.
# File lib/set.rb, line 557
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 Возвращает строку, содержащую удобочитаемое представление множества. (“#<Set: {элемент1, элемент2, …}>”)
# File lib/set.rb, line 297
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 369
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 Удаляет каждый элемент множества, для которого блок возвращает false, и возвращает self. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 405
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 277
def proper_subset?(set)
case
when set.instance_of?(self.class)
@hash < set.instance_variable_get(:@hash)
when set.is_a?(Set)
size < set.size && all? { |o| set.include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является собственным подмножеством заданного множества.
# File lib/set.rb, line 251
def proper_superset?(set)
case
when set.instance_of?(self.class)
@hash > set.instance_variable_get(:@hash)
when set.is_a?(Set)
size > set.size && set.all? { |o| include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является собственным надмножеством заданного множества.
# File lib/set.rb, line 387
def reject!(&block)
block or return enum_for(__method__) { size }
n = size
delete_if(&block)
self if size != n
end Эквивалентно #delete_if, но возвращает nil, если изменений не было. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 169
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 lib/set.rb, line 396
def select!(&block)
block or return enum_for(__method__) { size }
n = size
keep_if(&block)
self if size != n
end Эквивалентно #keep_if, но возвращает nil, если изменений не было. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 151 def size @hash.size end
Возвращает количество элементов.
# File lib/set.rb, line 264
def subset?(set)
case
when set.instance_of?(self.class)
@hash <= set.instance_variable_get(:@hash)
when set.is_a?(Set)
size <= set.size && all? { |o| set.include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является подмножеством заданного множества.
# File lib/set.rb, line 417
def subtract(enum)
do_with_enum(enum) { |o| delete(o) }
self
end Удаляет каждый элемент, который присутствует в заданном перечислимом объекте, и возвращает self.
# File lib/set.rb, line 238
def superset?(set)
case
when set.instance_of?(self.class)
@hash >= set.instance_variable_get(:@hash)
when set.is_a?(Set)
size >= set.size && set.all? { |o| include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является надмножеством заданного множества.
# File lib/set.rb, line 181 def to_a @hash.keys end
Преобразует множество в массив. Порядок элементов неопределён.
# File lib/set.rb, line 190 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, █).
В подклассах, возвращает klass.new(self, *args, █), если не переопределено.
# File lib/set.rb, line 424 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.