Какие классы Ruby поддерживают .clone?
Вопрос
Ruby определяет #clone
в объекте .
К моему удивлению, некоторые классы вызывают исключения при вызове.
Я обнаружил, что NilClass , TrueClass , FalseClass , Fixnum имеют такое поведение.
1) Существует ли полный список классов (по крайней мере, базовых классов), которые не допускают #clone
?
Или есть способ определить, поддерживает ли определенный класс #clone
?
2) Что не так с 42.clone
?
Решение
Я не думаю, что существует формальный список, по крайней мере, если вы не считаете чтение источника. Причина 2) не работает из-за оптимизации, примененной к Fixnums. Они хранятся / передаются внутри как их фактические значения (так же как true, false и nil), а не как указатели. Наивное решение состоит в том, чтобы 42.clone
возвращал тот же 42
, но затем инвариант obj.clone.object_id! = Obj.object_id
больше не будет храниться, 42.clone
фактически не будет клонировать. Р>
Другие советы
Fixnum - это особый класс, которому уделяется особое внимание в языке. С момента запуска вашей программы существует ровно один Fixnum для каждого числа, которое может представлять класс, и им дается специальное представление, которое не занимает дополнительного пространства & # 8212; таким образом, основные математические операции не распределяют и не освобождают память как сумасшедшие. Из-за этого не может быть больше одного 42.
Для всех остальных у них есть одна общая черта: они одиночки. По определению существует только один экземпляр одноэлементного класса, поэтому попытка его клонировать является ошибкой.
Я до сих пор не знаю, как правильно проверить на клонируемость, но вот очень неуклюжий, злой способ проверить на клонируемость с помощью перехвата ошибок:
def clonable?(value)
begin
clone = value.clone
true
rescue
false
end
end
А вот как вы можете клонировать даже неклонируемого. По крайней мере, для тех немногих классов, с которыми я устал.
def super_mega_clone(value)
eval(value.inspect)
end
Вот несколько примеров тестирования:
b = :b
puts "clonable? #{clonable? b}"
b = proc { b == "b" }
puts "clonable? #{clonable? b}"
b = [:a, :b, :c]
c = super_mega_clone(b)
puts "c: #{c.object_id}"
puts "b: #{b.object_id}"
puts "b == c => #{b == c}"
b.each_with_index do |value, index|
puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}"
end
b[0] = :z
puts "b == c => #{b == c}"
b.each_with_index do |value, index|
puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}"
end
b = :a
c = super_mega_clone(b)
puts "b: #{b.object_id} c: #{c.object_id}"
> clonable? false
> clonable? true
> c: 2153757040
> b: 2153757480
> b == c => true
> [0] b: 255528 c: 255528
> [1] b: 255688 c: 255688
> [2] b: 374568 c: 374568
> b == c => false
> [0] b: 1023528 c: 255528
> [1] b: 255688 c: 255688
> [2] b: 374568 c: 374568
> b: 255528 c: 255528
Я сделал git grep " не могу клонировать "
исходного кода YARV и получил
lib/singleton.rb: raise TypeError, "can't clone instance of singleton #{self.class}"
object.c: rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
test/test_singleton.rb: expected = "can't clone instance of singleton TestSingleton::SingletonTest"
Первая и третья строки означают, что вы не можете клонировать синглтон.
Вторая строка ссылается на rb_special_const_p (obj)
. Но это выходит за рамки моего кен.
Вы не можете клонировать неизменяемые классы. То есть у вас может быть только один экземпляр объекта 42 (как Fixnum), но может быть много экземпляров " 42 " (потому что строка изменчива). Вы также не можете клонировать символы, так как они являются чем-то вроде неизменяемых строк.
Вы можете проверить это в IRB с помощью метода object_id. (символы и фиксированные номера будут давать вам один и тот же object_id после повторных вызовов)
Rails, по-видимому, расширяет упомянутые вами классы с помощью функции «duplicable? ()» quot; Метод. р>