ruby - 更改 Ruby 对象的状态会更改其他类成员吗?

标签 ruby object instance-variables

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

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

这是一个简化的示例;

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 中的单个实例交互时,一切看起来都很好;

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 

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

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 似乎支持这种期望;

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

作为引用,我最终创建了一个initialize_copy方法,该方法确保受影响的参数被深度复制。我真的不喜欢仅仅为了深入复制数组而编码对象的想法,所以我决定这样做。

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

最佳答案

默认情况下,dupclone 会生成调用它们的对象的副本。这意味着示例中的 x 和 y 仍然引用相同的内存区域。

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

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

您可以在自定义类中重写它们,以在不同的内存区域中生成深层副本。

Ruby 中的一个常见习惯用法是使用 Object 父类(super class)的 Marshal#loadMarshal#dump 方法来生成深拷贝。 (注意:这些方法通常用于序列化/反序列化对象)。

 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 并将其子数组插入该数组。

 def dup
   new_grid = []

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

   new_grid
 end

关于ruby - 更改 Ruby 对象的状态会更改其他类成员吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17137440/

相关文章:

python:当类属性、实例属性和方法都同名时会发生什么?

html - Ruby cgi 表错误

unit-testing - 如何测试 Controller 实例变量中的属性值?

ruby - 在安装 puppet 客户端后执行 puppet -V 时出现与 ruby​​ 相关的错误

javascript - 如何使用变量内容读取对象数组的内容

javascript - 创建一个 addAffair 函数,该函数将在给定日期添加新事务

javascript - 如何在代码中显示我已完成的任务?

java - 创建类类型变量的实例

ruby - 如何使用 Mechanize 获取元素节点

ruby - 如何计算 ruby​​ 中某个类的现有实例?