зеркало из https://github.com/github/ruby.git
[ruby/set] Remove SortedSet implementations
It required RBTree to perform decently and the external dependency was not suitable for a standard library. The pure ruby fallback implementation was originally meant to be an example of how to write a subclass of Set, and its poor performance was not suitable for use in production. I decided it should be distributed as an external library instead of bundling it with Set. https://github.com/ruby/set/commit/dfcc8e568b
This commit is contained in:
Родитель
46fc8d78a5
Коммит
a3db08d7b6
247
lib/set.rb
247
lib/set.rb
|
@ -16,12 +16,9 @@
|
|||
#
|
||||
# This library provides the Set class, which deals with a collection
|
||||
# of unordered values with no duplicates. It is a hybrid of Array's
|
||||
# intuitive inter-operation facilities and Hash's fast lookup. If you
|
||||
# need to keep values sorted in some order, use the SortedSet class.
|
||||
# intuitive inter-operation facilities and Hash's fast lookup.
|
||||
#
|
||||
# The method +to_set+ is added to Enumerable for convenience.
|
||||
#
|
||||
# See the Set and SortedSet documentation for examples of usage.
|
||||
|
||||
|
||||
#
|
||||
|
@ -667,154 +664,6 @@ class Set
|
|||
end
|
||||
end
|
||||
|
||||
#
|
||||
# SortedSet implements a Set that guarantees that its elements are
|
||||
# yielded in sorted order (according to the return values of their
|
||||
# #<=> methods) when iterating over them.
|
||||
#
|
||||
# All elements that are added to a SortedSet must respond to the <=>
|
||||
# method for comparison.
|
||||
#
|
||||
# Also, all elements must be <em>mutually comparable</em>: <tt>el1 <=>
|
||||
# el2</tt> must not return <tt>nil</tt> for any elements <tt>el1</tt>
|
||||
# and <tt>el2</tt>, else an ArgumentError will be raised when
|
||||
# iterating over the SortedSet.
|
||||
#
|
||||
# == Example
|
||||
#
|
||||
# require "set"
|
||||
#
|
||||
# set = SortedSet.new([2, 1, 5, 6, 4, 5, 3, 3, 3])
|
||||
# ary = []
|
||||
#
|
||||
# set.each do |obj|
|
||||
# ary << obj
|
||||
# end
|
||||
#
|
||||
# p ary # => [1, 2, 3, 4, 5, 6]
|
||||
#
|
||||
# set2 = SortedSet.new([1, 2, "3"])
|
||||
# set2.each { |obj| } # => raises ArgumentError: comparison of Fixnum with String failed
|
||||
#
|
||||
class SortedSet < Set
|
||||
@@setup = false
|
||||
@@mutex = Mutex.new
|
||||
|
||||
class << self
|
||||
def [](*ary) # :nodoc:
|
||||
new(ary)
|
||||
end
|
||||
|
||||
def setup # :nodoc:
|
||||
@@setup and return
|
||||
|
||||
@@mutex.synchronize do
|
||||
# a hack to shut up warning
|
||||
alias_method :old_init, :initialize
|
||||
|
||||
begin
|
||||
require 'rbtree'
|
||||
|
||||
module_eval <<-END, __FILE__, __LINE__+1
|
||||
def initialize(*args)
|
||||
@hash = RBTree.new
|
||||
super
|
||||
end
|
||||
|
||||
def add(o)
|
||||
o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
|
||||
super
|
||||
end
|
||||
alias << add
|
||||
END
|
||||
rescue LoadError
|
||||
module_eval <<-END, __FILE__, __LINE__+1
|
||||
def initialize(*args)
|
||||
@keys = nil
|
||||
super
|
||||
end
|
||||
|
||||
def clear
|
||||
@keys = nil
|
||||
super
|
||||
end
|
||||
|
||||
def replace(enum)
|
||||
@keys = nil
|
||||
super
|
||||
end
|
||||
|
||||
def add(o)
|
||||
o.respond_to?(:<=>) or raise ArgumentError, "value must respond to <=>"
|
||||
@keys = nil
|
||||
super
|
||||
end
|
||||
alias << add
|
||||
|
||||
def delete(o)
|
||||
@keys = nil
|
||||
@hash.delete(o)
|
||||
self
|
||||
end
|
||||
|
||||
def delete_if
|
||||
block_given? or return enum_for(__method__) { size }
|
||||
n = @hash.size
|
||||
super
|
||||
@keys = nil if @hash.size != n
|
||||
self
|
||||
end
|
||||
|
||||
def keep_if
|
||||
block_given? or return enum_for(__method__) { size }
|
||||
n = @hash.size
|
||||
super
|
||||
@keys = nil if @hash.size != n
|
||||
self
|
||||
end
|
||||
|
||||
def merge(enum)
|
||||
@keys = nil
|
||||
super
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
block or return enum_for(__method__) { size }
|
||||
to_a.each(&block)
|
||||
self
|
||||
end
|
||||
|
||||
def to_a
|
||||
(@keys = @hash.keys).sort! unless @keys
|
||||
@keys.dup
|
||||
end
|
||||
|
||||
def freeze
|
||||
to_a
|
||||
super
|
||||
end
|
||||
|
||||
def rehash
|
||||
@keys = nil
|
||||
super
|
||||
end
|
||||
END
|
||||
end
|
||||
# a hack to shut up warning
|
||||
remove_method :old_init
|
||||
|
||||
@@setup = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(*args, &block) # :nodoc:
|
||||
SortedSet.setup
|
||||
@keys = nil
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
module Enumerable
|
||||
# Makes a set from the enumerable object with given arguments.
|
||||
# Needs to +require "set"+ to use this method.
|
||||
|
@ -822,97 +671,3 @@ module Enumerable
|
|||
klass.new(self, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
# =begin
|
||||
# == RestricedSet class
|
||||
# RestricedSet implements a set with restrictions defined by a given
|
||||
# block.
|
||||
#
|
||||
# === Super class
|
||||
# Set
|
||||
#
|
||||
# === Class Methods
|
||||
# --- RestricedSet::new(enum = nil) { |o| ... }
|
||||
# --- RestricedSet::new(enum = nil) { |rset, o| ... }
|
||||
# Creates a new restricted set containing the elements of the given
|
||||
# enumerable object. Restrictions are defined by the given block.
|
||||
#
|
||||
# If the block's arity is 2, it is called with the RestrictedSet
|
||||
# itself and an object to see if the object is allowed to be put in
|
||||
# the set.
|
||||
#
|
||||
# Otherwise, the block is called with an object to see if the object
|
||||
# is allowed to be put in the set.
|
||||
#
|
||||
# === Instance Methods
|
||||
# --- restriction_proc
|
||||
# Returns the restriction procedure of the set.
|
||||
#
|
||||
# =end
|
||||
#
|
||||
# class RestricedSet < Set
|
||||
# def initialize(*args, &block)
|
||||
# @proc = block or raise ArgumentError, "missing a block"
|
||||
#
|
||||
# if @proc.arity == 2
|
||||
# instance_eval %{
|
||||
# def add(o)
|
||||
# @hash[o] = true if @proc.call(self, o)
|
||||
# self
|
||||
# end
|
||||
# alias << add
|
||||
#
|
||||
# def add?(o)
|
||||
# if include?(o) || !@proc.call(self, o)
|
||||
# nil
|
||||
# else
|
||||
# @hash[o] = true
|
||||
# self
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# def replace(enum)
|
||||
# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable"
|
||||
# clear
|
||||
# enum.each_entry { |o| add(o) }
|
||||
#
|
||||
# self
|
||||
# end
|
||||
#
|
||||
# def merge(enum)
|
||||
# enum.respond_to?(:each) or raise ArgumentError, "value must be enumerable"
|
||||
# enum.each_entry { |o| add(o) }
|
||||
#
|
||||
# self
|
||||
# end
|
||||
# }
|
||||
# else
|
||||
# instance_eval %{
|
||||
# def add(o)
|
||||
# if @proc.call(o)
|
||||
# @hash[o] = true
|
||||
# end
|
||||
# self
|
||||
# end
|
||||
# alias << add
|
||||
#
|
||||
# def add?(o)
|
||||
# if include?(o) || !@proc.call(o)
|
||||
# nil
|
||||
# else
|
||||
# @hash[o] = true
|
||||
# self
|
||||
# end
|
||||
# end
|
||||
# }
|
||||
# end
|
||||
#
|
||||
# super(*args)
|
||||
# end
|
||||
#
|
||||
# def restriction_proc
|
||||
# @proc
|
||||
# end
|
||||
# end
|
||||
|
||||
# Tests have been moved to test/test_set.rb.
|
||||
|
|
118
test/test_set.rb
118
test/test_set.rb
|
@ -796,116 +796,6 @@ class TC_Set < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
class TC_SortedSet < Test::Unit::TestCase
|
||||
def test_sortedset
|
||||
s = SortedSet[4,5,3,1,2]
|
||||
|
||||
a = s.to_a
|
||||
assert_equal([1,2,3,4,5], a)
|
||||
a << -1
|
||||
assert_equal([1,2,3,4,5], s.to_a)
|
||||
|
||||
prev = nil
|
||||
s.each { |o| assert(prev < o) if prev; prev = o }
|
||||
assert_not_nil(prev)
|
||||
|
||||
s.map! { |o| -2 * o }
|
||||
|
||||
assert_equal([-10,-8,-6,-4,-2], s.to_a)
|
||||
|
||||
prev = nil
|
||||
ret = s.each { |o| assert(prev < o) if prev; prev = o }
|
||||
assert_not_nil(prev)
|
||||
assert_same(s, ret)
|
||||
|
||||
s = SortedSet.new([2,1,3]) { |o| o * -2 }
|
||||
assert_equal([-6,-4,-2], s.to_a)
|
||||
|
||||
s = SortedSet.new(['one', 'two', 'three', 'four'])
|
||||
a = []
|
||||
ret = s.delete_if { |o| a << o; o.start_with?('t') }
|
||||
assert_same(s, ret)
|
||||
assert_equal(['four', 'one'], s.to_a)
|
||||
assert_equal(['four', 'one', 'three', 'two'], a)
|
||||
|
||||
s = SortedSet.new(['one', 'two', 'three', 'four'])
|
||||
a = []
|
||||
ret = s.reject! { |o| a << o; o.start_with?('t') }
|
||||
assert_same(s, ret)
|
||||
assert_equal(['four', 'one'], s.to_a)
|
||||
assert_equal(['four', 'one', 'three', 'two'], a)
|
||||
|
||||
s = SortedSet.new(['one', 'two', 'three', 'four'])
|
||||
a = []
|
||||
ret = s.reject! { |o| a << o; false }
|
||||
assert_same(nil, ret)
|
||||
assert_equal(['four', 'one', 'three', 'two'], s.to_a)
|
||||
assert_equal(['four', 'one', 'three', 'two'], a)
|
||||
end
|
||||
|
||||
def test_each
|
||||
ary = [1,3,5,7,10,20]
|
||||
set = SortedSet.new(ary)
|
||||
|
||||
ret = set.each { |o| }
|
||||
assert_same(set, ret)
|
||||
|
||||
e = set.each
|
||||
assert_instance_of(Enumerator, e)
|
||||
|
||||
assert_nothing_raised {
|
||||
set.each { |o|
|
||||
ary.delete(o) or raise "unexpected element: #{o}"
|
||||
}
|
||||
|
||||
ary.empty? or raise "forgotten elements: #{ary.join(', ')}"
|
||||
}
|
||||
|
||||
assert_equal(6, e.size)
|
||||
set << 42
|
||||
assert_equal(7, e.size)
|
||||
end
|
||||
|
||||
def test_freeze
|
||||
orig = set = SortedSet[3,2,1]
|
||||
assert_equal false, set.frozen?
|
||||
set << 4
|
||||
assert_same orig, set.freeze
|
||||
assert_equal true, set.frozen?
|
||||
assert_raise(FrozenError) {
|
||||
set << 5
|
||||
}
|
||||
assert_equal 4, set.size
|
||||
|
||||
# https://bugs.ruby-lang.org/issues/12091
|
||||
assert_nothing_raised {
|
||||
assert_equal [1,2,3,4], set.to_a
|
||||
}
|
||||
end
|
||||
|
||||
def test_freeze_dup
|
||||
set1 = SortedSet[1,2,3]
|
||||
set1.freeze
|
||||
set2 = set1.dup
|
||||
|
||||
assert_not_predicate set2, :frozen?
|
||||
assert_nothing_raised {
|
||||
set2.add 4
|
||||
}
|
||||
end
|
||||
|
||||
def test_freeze_clone
|
||||
set1 = SortedSet[1,2,3]
|
||||
set1.freeze
|
||||
set2 = set1.clone
|
||||
|
||||
assert_predicate set2, :frozen?
|
||||
assert_raise(FrozenError) {
|
||||
set2.add 5
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
class TC_Enumerable < Test::Unit::TestCase
|
||||
def test_to_set
|
||||
ary = [2,5,4,3,2,1,3]
|
||||
|
@ -920,13 +810,5 @@ class TC_Enumerable < Test::Unit::TestCase
|
|||
|
||||
assert_same set, set.to_set
|
||||
assert_not_same set, set.to_set { |o| o }
|
||||
|
||||
set = ary.to_set(SortedSet)
|
||||
assert_instance_of(SortedSet, set)
|
||||
assert_equal([1,2,3,4,5], set.to_a)
|
||||
|
||||
set = ary.to_set(SortedSet) { |o| o * -2 }
|
||||
assert_instance_of(SortedSet, set)
|
||||
assert_equal([-10,-8,-6,-4,-2], set.sort)
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче