# helpers: freeze # Portions Copyright (c) 2002-2013 Akinori MUSHA class ::Set include ::Enumerable def self.[](*ary) new(ary) end def initialize(enum = nil, &block) @hash = {} return if enum.nil? ::Kernel.raise ::ArgumentError, 'value must be enumerable' unless ::Enumerable === enum if block enum.each { |item| add yield(item) } else merge(enum) end end def dup result = self.class.new result.merge(self) end def -(enum) unless enum.respond_to? :each ::Kernel.raise ::ArgumentError, 'value must be enumerable' end dup.subtract(enum) end def inspect "#" end def ==(other) if equal?(other) true elsif other.instance_of?(self.class) @hash == other.instance_variable_get(:@hash) elsif other.is_a?(::Set) && size == other.size other.all? { |o| @hash.include?(o) } else false end end def add(o) @hash[o] = true self end def classify(&block) return enum_for(:classify) unless block_given? result = ::Hash.new { |h, k| h[k] = self.class.new } each { |item| result[yield(item)].add item } result end def collect!(&block) return enum_for(:collect!) unless block_given? result = self.class.new each { |item| result << yield(item) } replace result end 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 def compare_by_identity? @hash.respond_to?(:compare_by_identity?) && @hash.compare_by_identity? end def delete(o) @hash.delete(o) self end def delete?(o) if include?(o) delete(o) self end end def delete_if return enum_for(:delete_if) unless block_given? # @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 def freeze return self if frozen? @hash.freeze `$freeze(self)` end def keep_if return enum_for(:keep_if) unless block_given? reject { |o| yield o }.each { |o| @hash.delete(o) } self end def reject!(&block) return enum_for(:reject!) unless block_given? before = size delete_if(&block) size == before ? nil : self end def select!(&block) return enum_for(:select!) unless block_given? before = size keep_if(&block) size == before ? nil : self end def add?(o) if include?(o) nil else add(o) end end def each(&block) return enum_for(:each) unless block_given? @hash.each_key(&block) self end def empty? @hash.empty? end def eql?(other) @hash.eql?(other.instance_eval { @hash }) end def clear @hash.clear self end def include?(o) @hash.include?(o) end def merge(enum) enum.each { |item| add item } self end def replace(enum) clear merge(enum) self end def size @hash.size end def subtract(enum) enum.each { |item| delete item } self end def |(enum) unless enum.respond_to? :each ::Kernel.raise ::ArgumentError, 'value must be enumerable' end dup.merge(enum) end %x{ function is_set(set) { #{`set`.is_a?(::Set) || ::Kernel.raise(::ArgumentError, 'value must be a set')} } } def superset?(set) `is_set(set)` return false if size < set.size set.all? { |o| include?(o) } end def proper_superset?(set) `is_set(set)` return false if size <= set.size set.all? { |o| include?(o) } end def subset?(set) `is_set(set)` return false if set.size < size all? { |o| set.include?(o) } end def proper_subset?(set) `is_set(set)` return false if set.size <= size all? { |o| set.include?(o) } end def intersect?(set) `is_set(set)` if size < set.size any? { |o| set.include?(o) } else set.any? { |o| include?(o) } end end def disjoint?(set) !intersect?(set) end def to_a @hash.keys end alias + | alias < proper_subset? alias << add alias <= subset? alias > proper_superset? alias >= superset? alias difference - alias filter! select! alias length size alias map! collect! alias member? include? alias union | end module ::Enumerable def to_set(klass = Set, *args, &block) klass.new(self, *args, &block) end end