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

时间:2016-08-12 11:34:54

标签: 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 '@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, "x"], [3, 4, "x"], [5, 6, "x"]] # <= Why did it change?
@board: [[1, 2, "x"], [3, 4, "x"], [5, 6, "x"]]

我可以做些什么来确保温度不会被修改?

4 个答案:

答案 0 :(得分:1)

你有阵列数组,所以你复制了第一个数组,但是在对象里面指向同一个实例。在这种情况下,您只需修改相同的源。

喜欢这里:

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

arr2[0] << 1

p arr
# => [[1, 2, 3, 1]]
p arr2
# => [[1, 2, 3, 1]]

所以你必须对这样的所有数组实例使用dup

arr = [[1, 2, 3]]
arr3 = arr.map(&:dup)
arr3[0] << 1

p arr
# => [[1, 2, 3]]
p arr3
# => [[1, 2, 3, 1]]

在您的情况下,使用此map

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"]]

答案 1 :(得分:0)

原因是clonedup产生浅拷贝。因此,内部数组的对象id仍然是相同的:那些是相同的数组。

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

答案 2 :(得分:0)

来自docs

  

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

说:你需要制作阵列的深层副本。

当您使用ActiveSupport gem(Rails)时,您可以使用其deep_dup方法而不是仅调用dup

temp = @board.deep_dup

没有宝石mashaling可能是一个简单的解决方案,深入复制几乎所有东西,而不知道对象的内部:

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

答案 3 :(得分:0)

您可以为嵌套数组添加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"]]