2017-08-20 72 views
0

我完成了tic tac toe的胸肌,並且一直致力於改進我的電腦播放器。這需要從舊板對象創建新的板對象的副本。我無法創建電路板的深層副本。對象的深度複製Ruby

這裏是有問題的代碼:

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只是複製對象它不會工作,但我不明白爲什麼我當前的實現不起作用。我應該存儲當前對象的網格,然後使用同一個網格創建一個新對象。有什麼想法嗎?

回答

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

但這些陣列的元素仍然不是活得T個不同的對象,如果我們不是有串:

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 

,它應該工作。