【问题标题】:Deep Copying of Objects RubyRuby 对象的深度复制
【发布时间】:2018-01-28 23:20:42
【问题描述】:

我完成了井字游戏的训练,并且正在努力改进我的电脑播放器。这需要从旧板对象创建新板对象的副本。我在创建板的深层副本时遇到问题。

这里是有问题的代码:

  Class Board
  attr_accessor :grid

  def initialize(grid = Array.new(3){ Array.new(3)})
    @grid = grid
  end

  def place_mark(cords, sym)
    self[cords] = sym
    @grid 
  end

  def [](pos)
    row, col = pos
    @grid[row][col]
  end

  def []=(pos, mark)
    row, col = pos
    @grid[row][col] = mark
  end

 def new_state
    grid = @grid.dup
    Board.new(grid)
  end
end 

board = Board.new
new_state = board.new_state # ==> A different object
new_state.place_mark([0,0], :X) # ==> Object with x placed at 0,0
board # ==> Object with x placed at 0,0

现在,当我实现一个 new_state 然后在 new_state 上放置一个标记时,它也会在复制它的状态上放置一个标记。

我明白为什么如果我只通过复制对象来设置我的 new_state 它将不起作用。但我不明白为什么我当前的实现不起作用。我应该存储当前对象的网格,然后使用相同的网格创建一个新对象。有什么想法吗?

【问题讨论】:

    标签: ruby clone deep-copy tic-tac-toe dup


    【解决方案1】:

    在 Ruby 中,dup 会生成一个浅克隆,并且不会复制它们引用的对象。由于@grid 是一个数组数组,所以@grid 数组中的每个数组都不会被复制。这可能还不清楚,所以希望代码有所帮助:

    grid = [ [:first_row], [:second_row] ]
    copy = grid.dup
    grid.object_id == copy.object_id # => false, so you have a different array
    grid[0].object_id == copy[0].object_id # => true, so the inner array is the same
    
    copy[0][0] = :test_change
    grid # => [[:test_change], [:second_row]]
    copy # => [[:test_change], [:second_row]]
    

    所以内部数组是同一个对象,从一个地方修改它会修改引用它的所有内容。但是,如果您修改“外部”数组,它会像您期望的那样工作:

    copy[0] = [:updates_correctly]
    copy # => [[:updates_correctly], [:second_row]]
    grid # => [[:test_change], [:second_row]]
    

    所以,知道了这一切,希望你能更清楚地解决这个问题,你只需要在“内部”数组上调用dup。我们也将在这里使用collect,它返回一个新数组,因此我们根本不需要在“外部”数组上调用dup

    copy = grid.collect(&:dup) # => [[:test_change], [:second_row]]
    copy[0][0] = :another_change
    copy # => [[:another_change], [:second_row]]
    grid # => [[:test_change], [:second_row]]
    

    但如果我们有字符串,这些数组的元素仍然不是不同的对象:

    grid = [ ['first row'] ]
    copy = grid.collect(&:dup)
    

    并修改了字符串,我们最终会同时更改:

    copy[0][0].upcase!
    copy # => [["FIRST ROW"]]
    grid # => [["FIRST ROW"]]
    

    您需要走多远取决于您的特定需求。

    所以,TL;DR您需要将new_state 更改为:

    def new_state
      grid = @grid.collect(&:dup)
      Board.new(grid)
    end
    

    它应该可以工作。

    【讨论】:

      猜你喜欢
      • 2012-12-15
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 2012-07-03
      • 2014-11-03
      • 2016-07-07
      • 2015-01-08
      • 1970-01-01
      相关资源
      最近更新 更多