Как понять, что #dup и #clone работают с объектами, которые ссылаются на другие объекты?

Я не уверен в значении "... но не в объектах, на которые они ссылаются" как в документах ruby, так и rubinus.

В ruby-doc есть объяснение #clone и #dup поведение, говорящее:

Создает поверхностную копию obj — копируются переменные экземпляра obj, но не объекты, на которые они ссылаются. Копирует замороженное и испорченное состояние объекта obj. См. также обсуждение в разделе Object#dup.

То же самое повторяется в реализации Rubinius:

Копирует переменные экземпляра, но не рекурсивно копирует объекты, на которые они ссылаются. Копирует испорченность.

Я попробовал следующий код, но поведение не соответствует моим ожиданиям.

class Klass
   attr_accessor :array
end

s1 = Klass.new
ar = [1, 2, 3]
s1.array = [ar]

s2 = s1.clone
# according to the doc,
# s2.array should be initialized with empty Array
# however the array is recursivley copied too

s2.array.equal? s1.array # true

person steveyang    schedule 24.04.2013    source источник


Ответы (2)


В Ruby все объекты являются ссылками. Взгляните на следующий пример:

class Klass
  attr_accessor :a
end

s1 = Klass.new
a = [1,2,3]
s1.a = a
s2 = s1.clone
s1.a.object_id  #=> 7344240 
s2.a.object_id  #=> 7344240 

Вы можете видеть, что оба массива являются одним и тем же объектом и являются ссылками на массив, находящийся где-то в куче. В глубокой копии сам массив был бы скопирован, и у нового s2 был бы свой собственный отдельный массив. Массив не копируется, а просто ссылается.

Примечание. Вот как это выглядит при глубоком копировании:

s3 = Marshal.load(Marshal.dump(s1)) #=> #<Klass:0x00000000bf1350 @a=[1, 2, 3, 4], @bork=4> 
s3.a << 5 #=> [1, 2, 3, 4, 5] 
s1 #=> #<Klass:0x00000000e21418 @a=[1, 2, 3, 4], @bork=4> 
person Linuxios    schedule 24.04.2013
comment
Я думаю, что второй s1.a.object_id #=> 7344240 должен быть s2.a.object_id. Хотя они ссылаются на один и тот же объект массива. - person steveyang; 24.04.2013
comment
Итак... не рекурсивно копирует объекты, на которые они ссылаются. означает, копировать только реферер (указатель), а не ссылку, верно? - person steveyang; 24.04.2013
comment
@steven.yang: Упс! Спасибо. - person Linuxios; 24.04.2013
comment
Итак, поведение, которое я написал в вопросе, - это именно то, что должно быть реализовано. Понятно. - person steveyang; 24.04.2013

"Равный?" сравнение проверяет, являются ли они точно одним и тем же объектом:

  • Сравнение == проверяет, равны ли два значения
  • экл? проверяет, равны ли два значения и имеют ли они один и тот же тип
  • равный? проверяет, являются ли две вещи одним и тем же объектом.

Например :

a=[1,2]
=> [1, 2] 
a == [1,2]
=> true 
a.eql? [1,2]
=> true 
a.equal? [1,2]
=> false 
a.equal? a
=> true

Как вы тестируете равные? это показывает, что копия не создала объект с неинициализированным массивом, но она заставила скопированный объект указывать на тот же массив, что и оригинал. Если бы он рекурсивно скопировал объекты, s2.array имел бы то же содержимое, что и s1.array, но был бы другим объектом, поэтому:

s2.array.equal? s1.array # false
s2.array.eql? s1.array # true
person Ian Kenney    schedule 24.04.2013
comment
Как это отвечает на вопрос? - person Linuxios; 24.04.2013