Вопрос

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; Метод.

http://api.rubyonrails.org/ файлы / ActiveSupport / Библиотека / active_support / core_ext / объект / duplicable_rb.html

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top