класс Set
Эта библиотека предоставляет класс Set, который обрабатывает коллекцию неупорядоченных значений без дублирования. Это гибрид интуитивных средств взаимодействия массива и быстрого поиска хеша.
Метод to_set добавлен в Enumerable для удобства.
Set реализует коллекцию неупорядоченных значений без дублирования. Это гибрид интуитивных средств взаимодействия массива и быстрого поиска хеша.
Set легко использовать с объектами Enumerable (реализующими each). Большинство методов инициализации и бинарных операторов принимают универсальные объекты Enumerable помимо множеств и массивов. Объект Enumerable может быть преобразован в Set с помощью метода to_set.
Set использует Hash в качестве хранилища, поэтому необходимо учитывать следующие моменты:
-
Равенство элементов определяется в соответствии с
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
Контакты
-
Акинори Муша <knu@iDaemons.org> (текущий основной разработчик)
Публичные методы класса
# File lib/set.rb, line 73 def self.[](*ary) new(ary) end
Создаёт новое множество, содержащее заданные объекты.
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 88
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.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 481
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[3, 2, 1] #=> #<Set: {3, 1}>
Set['a', 'b', 'z'] & ['a', 'b', 'c'] #=> #<Set: {"a", "b"}>
# File lib/set.rb, line 471 def -(enum) dup.subtract(enum) end
Возвращает новый набор, созданный путём дублирования набора, удаляя каждый элемент, который появляется в заданном перечислимом объекте.
Set[1, 3, 5] - Set[1, 5] #=> #<Set: {3}>
Set['a', 'b', 'z'] - ['a', 'c'] #=> #<Set: {"b", "z"}>
# File lib/set.rb, line 306 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
Возвращает 0, если наборы равны, -1 / +1, если набор является надлежащим подмножеством/надмножеством заданного набора, или nil, если у них оба уникальные элементы.
# File lib/set.rb, line 515
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?.
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
Возвращает 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
# File lib/set.rb, line 502
def ^(enum)
n = Set.new(enum)
each { |o| n.add(o) unless n.delete?(o) }
n
end Возвращает новый набор, содержащий элементы, исключительные между набором и заданным перечислимым объектом. (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 354 def add(o) @hash[o] = true self 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 366 def add?(o) add(o) unless include?(o) 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 584
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 }
hash #=> {2000=>#<Set: {"a.rb", "b.rb"}>,
# 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
# 2002=>#<Set: {"f.rb"}>}
Возвращает итератор, если блок не указан.
# File lib/set.rb, line 169 def clear @hash.clear self end
Удаляет все элементы и возвращает себя.
set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}>
set.clear #=> #<Set: {}>
set #=> #<Set: {}>
# File lib/set.rb, line 407
def collect!
block_given? or return enum_for(__method__) { size }
set = self.class.new
each { |o| set << yield(o) }
replace(set)
end Заменяет элементы на те, которые возвращаются collect(). Возвращает итератор, если блок не указан.
# File lib/set.rb, line 102
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 Заставляет набор сравнивать свои элементы по их идентичности и возвращает себя. Этот метод может не поддерживаться всеми подклассами Set.
# File lib/set.rb, line 113 def compare_by_identity? @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? end
Возвращает true, если набор будет сравнивать свои элементы по их идентичности. Также см. Set#compare_by_identity.
# File lib/set.rb, line 372 def delete(o) @hash.delete(o) self end
Удаляет заданный объект из набора и возвращает себя. Используйте subtract для удаления сразу многих элементов.
# File lib/set.rb, line 379 def delete?(o) delete(o) if include?(o) end
Удаляет заданный объект из набора и возвращает себя. Если объекта нет в наборе, возвращает nil.
# File lib/set.rb, line 386
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, и возвращает себя. Возвращает итератор, если блок не указан.
# File lib/set.rb, line 335 def disjoint?(set) !intersect?(set) end
Возвращает true, если в наборе и заданном наборе нет общих элементов. Этот метод является противоположностью intersect?.
Set[1, 2, 3].disjoint? Set[3, 4] #=> false Set[1, 2, 3].disjoint? Set[4, 5] #=> true
# File lib/set.rb, line 612
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 являются общими, если 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 }
set #=> #<Set: {#<Set: {1}>,
# #<Set: {11, 9, 10}>,
# #<Set: {3, 4}>,
# #<Set: {6}>}>
Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 342
def each(&block)
block or return enum_for(__method__) { size }
@hash.each_key(&block)
self
end Вызывает заданный блок один раз для каждого элемента в множестве, передавая элемент в качестве параметра. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 160 def empty? @hash.empty? end
Возвращает true, если множество не содержит элементов.
# File lib/set.rb, line 230 def flatten self.class.new.flatten_merge(self) end
Возвращает новое множество, являющееся копией множества, рекурсивно разворачивающего каждое вложенное множество.
# File lib/set.rb, line 236
def flatten!
replace(flatten()) if any? { |e| e.is_a?(Set) }
end Эквивалентно Set#flatten, но заменяет получатель результатом на месте. Возвращает nil, если изменений не было.
# File lib/set.rb, line 246 def include?(o) @hash[o] end
Возвращает true, если множество содержит указанный объект.
Обратите внимание, что include? и member? не проверяют равенство членов с использованием ==, как и другие Enumerables.
См. также Enumerable#include?
# File lib/set.rb, line 136 def initialize_clone(orig, **options) super @hash = orig.instance_variable_get(:@hash).clone(**options) end
Клонирование внутренней хеш-таблицы.
# File lib/set.rb, line 129 def initialize_dup(orig) super @hash = orig.instance_variable_get(:@hash).dup end
Дублирование внутренней хеш-таблицы.
# File lib/set.rb, line 652
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 321
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, если у множества и заданного множества есть хотя бы один общий элемент.
Set[1, 2, 3].intersect? Set[4, 5] #=> false Set[1, 2, 3].intersect? Set[3, 4] #=> true
# File lib/set.rb, line 644 def join(separator=nil) to_a.join(separator) end
Возвращает строку, созданную путём преобразования каждого элемента множества в строку. См. также: Array#join
# File lib/set.rb, line 397
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 438
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 291
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? { |o| set.include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является истинным подмножеством заданного множества.
# File lib/set.rb, line 265
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? { |o| include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является истинным надмножеством заданного множества.
# File lib/set.rb, line 417
def reject!(&block)
block or return enum_for(__method__) { size }
n = size
delete_if(&block)
self if size != n
end Эквивалентно Set#delete_if, но возвращает nil, если изменений не было. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 180
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.
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 540
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 Сбрасывает внутреннее состояние после изменения существующих элементов и возвращает self.
Элементы будут переиндексированы и дубликаты будут удалены.
# File lib/set.rb, line 426
def select!(&block)
block or return enum_for(__method__) { size }
n = size
keep_if(&block)
self if size != n
end Эквивалентно Set#keep_if, но возвращает nil, если изменений не было. Возвращает перечислитель, если блок не задан.
# File lib/set.rb, line 154 def size @hash.size end
Возвращает количество элементов.
# File lib/set.rb, line 278
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? { |o| set.include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является подмножеством заданного множества.
# File lib/set.rb, line 450
def subtract(enum)
do_with_enum(enum) { |o| delete(o) }
self
end Удаляет каждый элемент, который появляется в заданном перечислимом объекте, и возвращает self.
# File lib/set.rb, line 252
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? { |o| include?(o) }
else
raise ArgumentError, "value must be a set"
end
end Возвращает true, если множество является надмножеством заданного множества.
# File lib/set.rb, line 195 def to_a @hash.keys end
Преобразует множество в массив. Порядок элементов не определён.
Set[1, 2].to_a #=> [1, 2] Set[1, 'c', :s].to_a #=> [1, "c", :s]
# File ext/json/lib/json/add/set.rb, line 25 def to_json(*args) as_json.to_json(*args) end
Возвращает значение JSON.
# File lib/set.rb, line 204 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 460 def |(enum) dup.merge(enum) end
Возвращает новое множество, созданное путём объединения множества и элементов заданного перечислимого объекта.
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}>
Ruby Core © 1993–2020 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.