Ruby-在更改实例变量时修改局部变量

Ruby-在更改实例变量时修改局部变量,ruby,variables,scope,instance-variables,Ruby,Variables,Scope,Instance Variables,temp获取@board.dup,并修改@board数组。但是,temp也会被修改!我试着阅读了所有相关的文档,但仍然找不出解释 class Test def initialize @board = [[1,2],[3,4], [5,6]] end def modify temp = @board.dup #Also tried .clone print 'temp: ';p temp print '@b

temp
获取
@board.dup
,并修改
@board
数组。但是,
temp
也会被修改!我试着阅读了所有相关的文档,但仍然找不出解释

class Test
    def initialize
        @board = [[1,2],[3,4], [5,6]]
    end

    def modify
        temp = @board.dup #Also tried .clone

        print 'temp: ';p temp
        print '@board: ';p @board

        @board.each do |x|
            x << "x"
        end

        print "\ntemp: ";p temp
        print '@board: ';p @board
    end
end

x = Test.new
x.modify
类测试
def初始化
@董事会=[[1,2]、[3,4]、[5,6]]
结束
def修改
temp=@board.dup#也尝试过。克隆
打印“温度:”;p温度
打印“@board:”;p@board
@董事会,每个董事会| x|

x原因是
clone
dup
产生浅拷贝。因此,内部数组的对象ID仍然是相同的:它们是相同的数组

你需要的是一个深度克隆。标准库中没有内置方法可以做到这一点。

来自:

dup
生成obj的浅层副本obj的实例变量被复制,但它们引用的对象不被复制

这就是说:您需要制作阵列的深度副本

使用ActiveSupport gem(Rails)时,您可以使用其
deep\u dup
方法,而不只是调用
dup

temp = @board.deep_dup
如果不使用gems,mashaling可能是一个简单的解决方案,可以在不了解对象内部结构的情况下实现几乎所有的深度dup:

temp = Marshal.load(Marshal.dump(@board))  

可以为嵌套数组添加deep_dup方法:

class Array
  def deep_dup
    map {|x| x.deep_dup}
  end
end

# To handle the exception when deepest array contains numeric value
class Numeric
  def deep_dup
    self
  end
end

class Test
    def initialize
        @board = [[1,2], [3,4], [5,6]]
    end

    def modify
        temp = @board.deep_dup
        ...
    end
end

x = Test.new
x.modify

# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2], [3, 4], [5, 6]]

# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]]

您有一个带数组的数组,所以您复制了第一个数组,但在对象内部指向同一个实例。在这种情况下,您只需修改相同的源

就像这里:

arr = [[1, 2, 3]]
arr2 = arr.dup

arr2[0] << 1

p arr
# => [[1, 2, 3, 1]]
p arr2
# => [[1, 2, 3, 1]]
在您的情况下,请使用此
映射

class Test
  def initialize
    @board = [[1,2],[3,4], [5,6]]
  end

  def modify
    temp = @board.map(&:dup) # dup all

    print 'temp: ';p temp
    print '@board: ';p @board

    @board.each do |x|
      x << "x"
    end

    print "\ntemp: ";p temp
    print '@board: ';p @board
  end
end

x = Test.new
x.modify
# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2], [3, 4], [5, 6]]
# 
# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]]
类测试
def初始化
@董事会=[[1,2]、[3,4]、[5,6]]
结束
def修改
温度=@board.map(&:dup)#dup all
打印“温度:”;p温度
打印“@board:”;p@board
@董事会,每个董事会| x|

它是相同的参考,例如,
temp
指向相同的阵列
@board
是。如果你想保留原始版本,那么你必须克隆它。对不起,编辑了我的问题。我确实在代码中执行了
@board.dup
。但是临时工也换了。(也尝试了
.clone
,仍然是相同的输出)你必须克隆deep。
封送
在这种情况下是OP。@LukasBaliak:但是香草ruby没有比这更好的了。@SergioTulentsev完全转储所有deep对象是的,但在这种情况下我认为是一个很小的OP。@LukasBaliak:我有点同意你的看法。但我更喜欢写的答案不仅适用于特定的例子。未来的其他读者可能会有一个稍微不同的问题,可能会对更普遍的解决方案感兴趣。
arr = [[1, 2, 3]]
arr3 = arr.map(&:dup)
arr3[0] << 1

p arr
# => [[1, 2, 3]]
p arr3
# => [[1, 2, 3, 1]]
class Test
  def initialize
    @board = [[1,2],[3,4], [5,6]]
  end

  def modify
    temp = @board.map(&:dup) # dup all

    print 'temp: ';p temp
    print '@board: ';p @board

    @board.each do |x|
      x << "x"
    end

    print "\ntemp: ";p temp
    print '@board: ';p @board
  end
end

x = Test.new
x.modify
# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2], [3, 4], [5, 6]]
# 
# temp: [[1, 2], [3, 4], [5, 6]]
# @board: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]]