класс 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 73 def self.[](*ary) new(ary) end
Создаёт новое множество, содержащее заданные объекты.
# File lib/set.rb, line 82
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 предварительно обрабатываются данным блоком.
Общедоступные методы экземпляров
# File lib/set.rb, line 418
def &(enum)
n = self.class.new
do_with_enum(enum) { |o| n.add(o) if include?(o) }
n
end Возвращает новый набор, содержащий элементы, общие для набора и заданного перечислимого объекта.
# File lib/set.rb, line 411 def -(enum) dup.subtract(enum) end
Возвращает новый набор, созданный путем дублирования набора, удаляя каждый элемент, который появляется в заданном перечислимом объекте.
# File lib/set.rb, line 436
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 428
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 312 def add(o) @hash[o] = true self end
Добавляет заданный объект в набор и возвращает self. Используйте merge для добавления сразу нескольких элементов.
# File lib/set.rb, line 320 def add?(o) add(o) unless include?(o) end
Добавляет заданный объект в набор и возвращает self. Если объект уже находится в наборе, возвращает nil.
# File lib/set.rb, line 470
def classify # :yields: o
block_given? or return enum_for(__method__)
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 144 def clear @hash.clear self end
Удаляет все элементы и возвращает self.
# File lib/set.rb, line 358
def collect!
block_given? or return enum_for(__method__)
replace(self.class.new(self) { |o| yield(o) })
end Заменяет элементы элементами, возвращёнными collect().
# File lib/set.rb, line 326 def delete(o) @hash.delete(o) self end
Удаляет заданный объект из набора и возвращает self. Используйте subtract для удаления сразу нескольких элементов.
# File lib/set.rb, line 333 def delete?(o) delete(o) if include?(o) end
Удаляет заданный объект из набора и возвращает self. Если объект не находится в наборе, возвращает nil.
# File lib/set.rb, line 339
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 297 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 498
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) равно true. В противном случае, элементы 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 304
def each(&block)
block or return enum_for(__method__) { size }
@hash.each_key(&block)
self
end Вызывает заданный блок один раз для каждого элемента в наборе, передавая элемент в качестве параметра. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 139 def empty? @hash.empty? end
Возвращает true, если набор не содержит элементов.
# File lib/set.rb, line 198 def flatten self.class.new.flatten_merge(self) end
Возвращает новый набор, являющийся копией набора, рекурсивно сглаживая каждый содержащийся набор.
# File lib/set.rb, line 204
def flatten!
replace(flatten()) if any? { |e| e.is_a?(Set) }
end Эквивалентно #сгладить, но заменяет получатель результатом на месте. Возвращает nil, если никаких изменений не было сделано.
# File lib/set.rb, line 214 def include?(o) @hash[o] end
Возвращает true, если набор содержит заданный объект.
Обратите внимание, что include? и member? не проверяют равенство элементов с помощью ==, как это делают другие Enumerables.
См. также Enumerable#include?
# File lib/set.rb, line 112 def initialize_clone(orig) super @hash = orig.instance_variable_get(:@hash).clone end
Клонирует внутренний хэш.
# File lib/set.rb, line 106 def initialize_dup(orig) super @hash = orig.instance_variable_get(:@hash).dup end
Дублирует внутренний хэш.
# File lib/set.rb, line 532
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 Возвращает строку, содержащую удобочитаемое представление множества. («#<Множество: {элемент1, элемент2, …}>»)
# File lib/set.rb, line 279
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 349
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 384
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 259
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 233
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 366 def reject!(&block) block or return enum_for(__method__) n = size delete_if(&block) self if size != n end
Эквивалентно #delete_if, но возвращает nil, если изменений не произошло.
# File lib/set.rb, line 151
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 375 def select!(&block) block or return enum_for(__method__) n = size keep_if(&block) self if size != n end
Эквивалентно #keep_if, но возвращает nil, если изменений не произошло.
# File lib/set.rb, line 133 def size @hash.size end
Возвращает количество элементов.
# File lib/set.rb, line 246
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 396
def subtract(enum)
do_with_enum(enum) { |o| delete(o) }
self
end Удаляет каждый элемент, присутствующий в заданном перечислимом объекте, и возвращает self.
# File lib/set.rb, line 220
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 163 def to_a @hash.keys end
Преобразует множество в массив. Порядок элементов не определён.
# File lib/set.rb, line 172 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 403 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.