更改克隆对象的值

时间:2014-09-23 11:11:45

标签: ruby arrays

我有一个对象,它有一个名为my_array的实例变量数组。我是通过attr_accessor声明的。

my_object.my_array = [1,2,3] # <= I don't know the max size of my_array(it can be dynamic)

我希望使用my_object创建相同的对象,并仅使用一个元素填充其my_array。该元素内的值是来自my_array元素(来自my_object)的每个值。由于my_array的大小是动态的,我想我需要通过each进行迭代。

这是我的尝试:

my_object.my_array.each do |element|          # <= my_object is a special object
  new_object = nil unless new_object.nil?
  new_object = my_object.clone # <= I create new object with same class with my_object
  new_object.my_array.clear   # <= clear all element inside it.
  new_object.my_array.push(element)  # assign element value to the first element.
  # rest of code #
  new_object = nil 
end

迭代没有正确迭代。 my_object.my_array的大小为3,那么它应该迭代三次,但不是,它只迭代一次。我相信它是因为new_object.my_array.clear,但我是从my_object克隆的,为什么会发生这种情况呢?

2 个答案:

答案 0 :(得分:1)

当您指定一个数组时,它只复制引用,并且它们都指向相同的引用, 因此,当您打印其中任何一个时,都会反映出一个变化:

orig_array = [1,2,3,4]<br>
another_array = orig_array

puts orig_array.unshift(0).inspect
puts another_array.inspect

哪个输出:

[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4]

为避免这种情况,您可以使用Marshal从原始数组进行复制,而不会影响复制到的对象。 原始数组中的任何更改都不会更改复制到的对象。

orig_array = [1,2,3,4]
another_array = Marshal.load(Marshal.dump(orig_array))

puts orig_array.unshift(0).inspect
puts another_array.inspect

哪个输出:

[0, 1, 2, 3, 4]
[1, 2, 3, 4]

答案 1 :(得分:0)

问题是,clone将构成浅层克隆,而不是深层克隆。换句话说,my_array是一个引用,克隆的实例引用内存中的相同数组。考虑:

class MyClass
  attr_accessor :my_array
end

a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]

b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array              # also changed!
#=> [1, 2, 3, 4]

为了解决这个问题,您需要扩展initialize_copy方法以克隆数组:

class MyClass
  attr_accessor :my_array

  def initialize_copy(orig)
    super
    self.my_array = orig.my_array.clone
  end
end

a = MyClass.new
a.my_array = [1, 2, 3]
a.my_array
#=> [1, 2, 3]

b = a.clone
b.my_array.push(4)
b.my_array
#=> [1, 2, 3, 4]
a.my_array              # did not change, works as expected
#=> [1, 2, 3]