改变Ruby对象的状态会改变其他类成员吗? [英] Changing state of Ruby objects changes other class members?

查看:67
本文介绍了改变Ruby对象的状态会改变其他类成员吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类,该类主要实现围绕多维数组的某种逻辑,多维数组本质上是一个数字网格.这些数字可以交换位置,等等.但是,当它们交换时,同一类的其他对象也似乎已被修改.我不确定为什么.

I have a class that primarily implements some logic around a multi-dimensional array, which is essentially a grid of numbers. These numbers can swap positions, etc.. However, when they swap, other objects of the same class also appear to be modified. I'm not sure why.

我正在使用实例变量来存储网格,所以我不明白为什么更改显然会影响其他类成员.

I'm using instance variables to store the grid, so I don't understand why changes are apparently affecting other class members.

这是一个简化的示例;

Here's a simplified example;

class TestGrid
attr_accessor :grid
@grid = []

def initialize(newgrid)
    @grid = newgrid
end

def to_s
    out = ""
    @grid.each{|row|
      out += row.join("|") + "\n"
    }
    out
end

def swap(x, y)
    @grid[x[0]][x[1]], @grid[y[0]][y[1]] = @grid[y[0]][y[1]], @grid[x[0]][x[1]]
end

end

当我们与IRB中的单个实例进行交互时,一切看起来都很好;

When we interact with a single instance in IRB, things look fine;

1.9.3-p385 :001 > require './TestGrid.rb'
 => true 
1.9.3-p385 :002 > x = TestGrid.new([[1,2],[3,4]])
 => 1|2
 3|4

1.9.3-p385 :003 > x.swap([0,1],[1,1])
 => [4, 2] 
1.9.3-p385 :004 > puts  x
1|4
3|2
 => nil 

但是,如果我通过克隆或复制创建第二个实例;

However, if I create a second instance by cloning or duping;

1.9.3-p385 :006 >   x = TestGrid.new([[1,2],[3,4]])
 => 1|2
3|4

1.9.3-p385 :007 > y = x.clone
 => 1|2
3|4

1.9.3-p385 :008 > x.swap([0,1],[1,1])
 => [4, 2] 
1.9.3-p385 :009 > puts x 
1|4
3|2
 => nil 
1.9.3-p385 :010 > puts y
1|4
3|2
 => nil 

为什么我对x所做的更改也被应用到y?根据我对Object#Clone的理解,这些假设应该是互不相关的不同实例.他们的对象ID似乎可以支持这种期望;

Why are my changes to x also being applied to y? From my understanding of Object#Clone, theses are supposed to be distinct instance, unrelated to each other. Their object ID's would seem to support that expectation;

1.9.3-p385 :012 > puts "#{x.object_id} #{y.object_id}"
70124426240320 70124426232820

作为参考,我最终创建了一个initialize_copy方法,该方法确保深度复制受影响的参数.我真的不喜欢将对象编组的想法只是为了深深地复制数组,所以我决定这样做.

For reference, I ended up creating an initialize_copy method which ensures the affected parameter is deep copied. I didn't really like the idea of Marshalling objects around just to copy an array deeply, so I decided on this instead.

def initialize_copy(original)
  super
  @grid = []
  original.grid.each{|inner|
    @grid << inner.dup
  }
 end

推荐答案

默认情况下,dupclone生成对其调用的对象的 shallow 副本.这意味着示例中的xy仍引用相同的内存区域.

By default, dup and clone produce shallow copies of the objects they are invoked on. Meaning that x and y in your example still reference the same area of memory.

http://ruby-doc.org/core- 2.0/Object.html#method-i-dup

http://ruby-doc.org/core- 2.0/Object.html#method-i-clone

您可以在自定义类中覆盖它们,以在不同的内存区域中生成 deep 副本.

You can override them inside of your customized class to produce a deep copy in a different area of memory.

Ruby中一个常见的习惯用法是使用Object超类的Marshal#loadMarshal#dump方法来生成深层副本. (注意:这些方法通常用于序列化/反序列化对象).

A common idiom in Ruby is to use the Marshal#load and Marshal#dump methods of the Object superclass to produce deep copies. (Note: these methods are normally used to serialize/deserialze objects).

 def dup
   new_grid = Marshal.load( Marshal.dump(@grid) )

   new_grid
 end

irb(main):007:0> x = TestGrid.new([[1,2],[3,4]])
=> 1|2
3|4

irb(main):008:0> y = x.dup
=> [[1, 2], [3, 4]]
irb(main):009:0> x.swap([0,1],[1,1])
=> [4, 2]
irb(main):010:0> puts x
1|4
3|2
=> nil
irb(main):011:0> y
=> [[1, 2], [3, 4]]
irb(main):012:0> puts y
1
2
3
4
=> nil
irb(main):013:0>

交换后

y保持不变.

或者,创建一个新数组,遍历@grid并将其子数组推入该数组.

Alternatively, create a new array, iterate through @grid and push its subarrays into the array.

 def dup
   new_grid = []

   @grid.each do |g|
      new_grid << g
   end

   new_grid
 end

这篇关于改变Ruby对象的状态会改变其他类成员吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆