2016-04-30 11 views
8

Sto scrivendo un semplice gioco Tic Tac Toe, in cui ho una classe per la scheda, il giocatore, il computer e il gioco stesso. Nella mia classe Board, ho impostato una variabile di classe @board (che è un array) come attr_reader, che non consente di scrivere direttamente su di essa. Anche se la seguente non funziona (come previsto)Ruby: non consentire l'aggiornamento di un array che è una variabile di classe

game_board = Board.new 
game_board.board = "some junk" 

Il seguente funziona, che io non voglio che accada

game_board = Board.new 
game_board.board[0] = "some junk" 

Come si interrompe il @board variabile array di classe di essere scritta a? codice della classe corrente al di sotto:

class Board 
    attr_reader :board 

    def initialize 
    create_board 
    end 

    private 
    def create_board 
    @board = Array.new(3).map{Array.new(3)} 
    end 
end 

game_board = Board.new 
game_board.board 
#=> [[nil,nil,nil],[nil,nil,nil],[nil,nil,nil]] 
game_board.board = "junk" 
#=> undefined method 'board =' 

game_board.board[0] = "junk" 
game_board.board 
#=> ["junk",[nil,nil,nil],[nil,nil,nil]] #I don't want to allow this! 

ho provato googling questo, ma senza alcun risultato, ma io sono un principiante assoluto, quindi non può utilizzare i termini di ricerca corretti

+1

Ricerca "array immutable" e "freeze array". L'immutabilità deve essere imposta dall'oggetto specifico come 'x.y = a' non ha alcuna relazione o idea sull'assegnazione * * in' x.y.z = b' (eccetto che 'x.y' è stato risolto in precedenza su un oggetto). – user2864740

+1

Nota di terminologia: '@ board' non è una variabile di classe, è una variabile di istanza. Le variabili di classe iniziano con due '@' s, ad es. '@@ board'. –

risposta

7

Credo che hai bisogno di fare il matrice immutabile.

Per raggiungere questo obiettivo è possibile utilizzare Array#freeze.

tuo codice dopo che dovrebbe essere simile:

class Board 
    attr_reader :board 

    def initialize 
    create_board 
    end 

    private 
    def create_board 
    @board = Array.new(3).map{Array.new(3).freeze}.freeze 
    end 
end 

sulla gestione del primo esempio:

>> game_board = Board.new 
#<Board:0x00000001648b50 @board=[[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]> 
>> game_board.board = "some junk" 
NoMethodError: undefined method `board=' for #<Board:0x00000001648b50> 
    from (irb):14 
    from /home/alfie/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>' 

sulla gestione del secondo esempio:

>> game_board = Board.new 
#<Board:0x00000001639e48 @board=[[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]> 
>> game_board.board[0] = "some junk" 
RuntimeError: can't modify frozen Array 
    from (irb):16 
    from /home/alfie/.rvm/rubies/ruby-2.1.3/bin/irb:11:in `<main>' 
6

Definire solo attr_reader, senza attr_writer, impedirà l'assegnazione di ents alla variabile @board. In altre parole, la tua classe Board non espone un'interfaccia per modificare ciò che è memorizzato in @board, ma non fa nulla per impedire modifiche del valore iniziale.

Si potrebbe utilizzare freeze:

def create_board 
    @board = Array.new(3) { Array.new(3).freeze } 
    @board.freeze 
end 

(anche, non hai bisogno di map lì)

congelamento dell'array di primo livello e quelli annidati farà ciò che si descrive, ma credo che spezzerà anche il tuo gioco perché le modifiche saranno completamente impossibili.

Quello che suggerirei è di non esporre @board a tutti e considerarlo privato. Dovresti quindi esporre un'interfaccia per impostare i valori nella scheda e fornire un metodo per restituire una rappresentazione di sola lettura della scheda.

class Board 

    def initialize 
    create_board 
    end 

    def []=(x, y, value) 
    @board[x][y] = value 
    end 

    def board 
    @board.map { |a| a.dup.freeze }.freeze 
    end 

    private 

    def create_board 
    @board = Array.new(3) { Array.new(3) } 
    end 
end 


b = Board.new 
b.board 
# => [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]] 

b[1,2] = "x" 
b[0,0] = "o" 
b.board 
# => [["o", nil, nil], [nil, nil, "x"], [nil, nil, nil]] 

b.board[0] = "junk" 
# RuntimeError: can't modify frozen Array 
b.board[0][1] = "junk" 
# RuntimeError: can't modify frozen Array 
b.board 
# => [["o", nil, nil], [nil, nil, "x"], [nil, nil, nil]]