Set 類別
此函式庫提供 Set
類別,實作一個無序值集合,且沒有重複值。它結合了 Array 直覺的互操作功能和 Hash 快速查詢的功能。
to_set
方法已新增至 Enumerable
以供使用。
Set
容易與 Enumerable
物件(實作 each
)一起使用。除了集合和陣列之外,大部分的初始化器方法和二元運算子都接受一般 Enumerable
物件。可以使用 to_set
方法將 Enumerable
物件轉換為 Set
。
-
元素的相等性是由
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
聯絡¶ ↑
-
Akinori MUSHA <[email protected]>(目前維護者)
此處內容¶ ↑
首先,其他地方的內容。類別 Set
-
繼承自 類別 Object。
-
包含 模組 Enumerable,提供數十個其他方法。
特別是,類別 Set 沒有許多用於擷取或反覆運算的方法。相反地,它依賴於 Enumerable 中的方法。
在此,類別 Set 提供對下列事項有用的方法
建立 Set 的方法¶ ↑
Set 運算的方法¶ ↑
-
&(別名為
intersection
):傳回一個包含self
和指定可列舉中所有共用元素的新 Set。 -
-(別名為
difference
):傳回self
的一份拷貝,其中已移除指定可列舉中的所有元素。 -
^:傳回一個包含
self
和指定可列舉中所有元素的新 Set,但排除兩者共有的元素。
比較的方法¶ ↑
-
<=>:傳回 -1、0 或 1,表示
self
小於、等於或大於指定的物件。 -
==:傳回
self
和指定的列舉物件是否相等,由Object#eql?
決定。 -
compare_by_identity?
:傳回集合在比較元素時是否只考慮身分。
查詢的方法¶ ↑
-
empty?
:傳回集合是否沒有元素。 -
proper_subset?
(別名為 <):傳回指定的列舉物件是否為集合的真子集合。 -
proper_superset?
(別名為 >):傳回指定的列舉物件是否為集合的真超集合。 -
disjoint?
:如果集合和指定的列舉物件沒有共同元素,傳回true
,否則傳回false
。 -
intersect?
:如果集合和指定的列舉物件:有任何共同元素,傳回true
,否則傳回false
。 -
compare_by_identity?
:傳回集合在比較元素時是否只考慮身分。
指定的方法¶ ↑
-
add?
:如果指定的物件不是集合中的元素,則將其新增並傳回self
;否則傳回nil
。 -
merge
:將每個指定的列舉物件的元素合併到集合中;傳回self
。 -
replace
:以給定可列舉內容取代集合內容。
刪除方法¶ ↑
-
clear
:移除集合中所有元素;傳回self
。 -
delete
:從集合中移除給定物件;傳回self
。 -
delete?
:如果給定物件是集合中的元素,則移除它並傳回self
;否則,傳回nil
。 -
subtract
:從集合中移除每個給定物件;傳回self
。 -
delete_if
- 移除由給定區塊指定的元素。 -
keep_if
:移除未由給定區塊指定的元素。 -
reject!
移除由給定區塊指定的元素。
轉換方法¶ ↑
-
classify
:傳回一個雜湊,分類元素,由給定區塊決定。 -
flatten
:傳回一個新的集合,是self
的遞迴扁平化。flatten!
:以self
中每個巢狀集合的元素取代self
中的每個巢狀集合。 -
join
:傳回一個包含所有元素的字串,視需要轉換為字串,並以給定的記錄分隔符號串接。 -
to_a
:傳回包含所有集合元素的陣列。 -
to_set
:如果未給定參數和區塊,則傳回self
;如果給定區塊,則傳回一個包含區塊傳回值的新集合。
迭代方法¶ ↑
-
each
:以每個連續元素呼叫區塊;傳回self
。
其他方法¶ ↑
-
reset
:重設內部狀態;如果物件在集合中的元素已修改,此方法會很有用。
常數
- VERSION
公開類別方法
建立一個新的集合,包含指定的物件。
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 228 def self.[](*ary) new(ary) end
請參閱 as_json
。
# File ext/json/lib/json/add/set.rb, line 9 def self.json_create(object) new object['a'] 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 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
公開執行個體方法
傳回一個新的集合,包含集合和指定可列舉物件共有的元素。
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 640 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[1, 5] #=> #<Set: {3}> Set['a', 'b', 'z'] - ['a', 'c'] #=> #<Set: {"b", "z"}>
# File lib/set.rb, line 630 def -(enum) dup.subtract(enum) end
如果集合相等,則傳回 0;如果集合是指定集合的真子集合/超集合,則傳回 -1/+1;如果兩個集合都有獨特的元素,則傳回 nil。
# File lib/set.rb, line 453 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
如果兩個集合相等,則傳回 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 lib/set.rb, line 674 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;否則傳回 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
傳回一個新的集合,包含集合和指定可列舉物件之間獨有的元素。(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 661 def ^(enum) n = Set.new(enum) each { |o| n.add(o) unless n.delete?(o) } n 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 511 def add(o) @hash[o] = true self 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 523 def add?(o) add(o) unless include?(o) end
方法 Set#as_json
和 Set.json_create
可用於序列化和反序列化 Set 物件;請參閱 Marshal
。
方法 Set#as_json
會序列化 self
,回傳一個表示 self
的 2 元素雜湊。
require 'json/add/set' x = Set.new(%w/foo bar baz/).as_json # => {"json_class"=>"Set", "a"=>["foo", "bar", "baz"]}
方法 JSON.create
會反序列化此類雜湊,回傳一個 Set 物件。
Set.json_create(x) # => #<Set: {"foo", "bar", "baz"}>
# File ext/json/lib/json/add/set.rb, line 28 def as_json(*) { JSON.create_id => self.class.name, 'a' => to_a, } 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 743 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
移除所有元素並回傳自身。
set = Set[1, 'c', :s] #=> #<Set: {1, "c", :s}> set.clear #=> #<Set: {}> set #=> #<Set: {}>
# File lib/set.rb, line 316 def clear @hash.clear self end
使用 collect()
回傳的元素取代元素。如果未提供區塊,則回傳一個列舉器。
# File lib/set.rb, line 564 def collect! block_given? or return enum_for(__method__) { size } set = self.class.new each { |o| set << yield(o) } replace(set) end
讓集合根據元素的身分進行比較,並回傳自身。此方法可能不受 Set
的所有子類別支援。
# File lib/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
如果集合將根據元素的身分進行比較,則回傳 true。另請參閱 Set#compare_by_identity
。
# File lib/set.rb, line 268 def compare_by_identity? @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? end
從集合中刪除給定的物件並回傳自身。使用 subtract
一次刪除多個項目。
# File lib/set.rb, line 529 def delete(o) @hash.delete(o) self end
從集合中刪除給定的物件並傳回自身。如果物件不在集合中,傳回 nil。
# File lib/set.rb, line 536 def delete?(o) delete(o) if include?(o) end
刪除集合中區塊評估為 true 的每個元素,並傳回自身。如果未提供區塊,傳回列舉器。
# File lib/set.rb, line 543 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。此方法與 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 lib/set.rb, line 492 def disjoint?(set) !intersect?(set) end
根據給定區塊定義的共性,將集合分割成一組子集合。
如果區塊的元數為 2,則元素 o1 和 o2 在區塊呼叫 (block.call(o1, o2)) 為 true 時為共用。否則,如果區塊呼叫 (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 lib/set.rb, line 771 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
對集合中的每個元素呼叫給定的區塊一次,將元素作為參數傳遞。如果未提供區塊,傳回列舉器。
# File lib/set.rb, line 499 def each(&block) block_given? or return enum_for(__method__) { size } @hash.each_key(&block) self end
如果集合不包含任何元素,傳回 true。
# File lib/set.rb, line 307 def empty? @hash.empty? end
傳回一個新的集合,為集合的副本,遞迴式地平坦化每個包含的集合。
# File lib/set.rb, line 377 def flatten self.class.new.flatten_merge(self) end
等於 Set#flatten
,但會將接收器取代為結果。如果未進行任何修改,傳回 nil。
# File lib/set.rb, line 383 def flatten! replace(flatten()) if any?(Set) end
如果集合包含指定的物件,則傳回 true。
請注意,include?
和 member?
不同於其他可列舉物件,它們不會使用 ==
來測試成員相等性。
另請參閱 Enumerable#include?
# File lib/set.rb, line 393 def include?(o) @hash[o] end
複製內部雜湊。
# File lib/set.rb, line 290 def initialize_clone(orig, **options) super @hash = orig.instance_variable_get(:@hash).clone(**options) end
複製內部雜湊。
# File lib/set.rb, line 284 def initialize_dup(orig) super @hash = orig.instance_variable_get(:@hash).dup end
傳回包含集合人類可讀表示形式的字串(“#<Set: {element1, element2, …}>”)。
# File lib/set.rb, line 811 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
如果集合和指定的可列舉物件至少有一個共同元素,則傳回 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 lib/set.rb, line 470 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
傳回透過將集合的每個元素轉換為字串所建立的字串。另請參閱: Array#join
# File lib/set.rb, line 803 def join(separator=nil) to_a.join(separator) end
刪除集合中區塊評估為 false 的每個元素,並傳回 self。如果未提供區塊,則傳回列舉器。
# File lib/set.rb, line 554 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
將指定可列舉物件的元素合併到集合中,並傳回 self。
# File lib/set.rb, line 595 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
如果集合是指定集合的真子集合,則傳回 true。
# File lib/set.rb, line 438 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
如果集合是指定集合的真超集合,則傳回 true。
# File lib/set.rb, line 412 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
等同於 Set#delete_if
,但如果沒有任何變更,則會傳回 nil。如果未提供區塊,則會傳回一個列舉器。
# File lib/set.rb, line 574 def reject!(&block) block_given? or return enum_for(__method__) { size } n = size delete_if(&block) self if size != n 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 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
在修改現有元素後重設內部狀態,並傳回 self。
元素將會重新編製索引並去重。
# File lib/set.rb, line 699 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
等同於 Set#keep_if
,但如果沒有任何變更,則會傳回 nil。如果未提供區塊,則會傳回一個列舉器。
# File lib/set.rb, line 583 def select!(&block) block_given? or return enum_for(__method__) { size } n = size keep_if(&block) self if size != n end
如果集合是給定集合的子集合,則傳回 true。
# File lib/set.rb, line 425 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
刪除出現在給定可列舉物件中的每個元素,並傳回 self。
# File lib/set.rb, line 609 def subtract(enum) do_with_enum(enum) { |o| delete(o) } self end
如果集合是給定集合的超集合,則傳回 true。
# File lib/set.rb, line 399 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
將集合轉換為陣列。元素的順序不確定。
Set[1, 2].to_a #=> [1, 2] Set[1, 'c', :s].to_a #=> [1, "c", :s]
# File lib/set.rb, line 342 def to_a @hash.keys end
傳回代表 self
的 JSON
字串
require 'json/add/set' puts Set.new(%w/foo bar baz/).to_json
輸出
{"json_class":"Set","a":["foo","bar","baz"]}
# File ext/json/lib/json/add/set.rb, line 44 def to_json(*args) as_json.to_json(*args) end
如果沒有給定引數,則傳回 self。否則,使用 klass.new(self, *args, &block)
將集合轉換為另一個集合。
在子類別中,傳回 klass.new(self, *args, &block)
,除非覆寫。
# File lib/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