【问题标题】:Ruby: Disallow updating of an array that is a class variableRuby:不允许更新作为类变量的数组
【发布时间】:2016-08-25 22:08:47
【问题描述】:

我正在编写一个简单的井字游戏,其中我有一个关于棋盘、玩家、计算机和游戏本身的类。在我的 Board 类中,我将类变量 @board(它是一个数组)设置为 attr_reader,这应该不允许直接写入它。尽管以下内容不起作用(按预期)

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

以下确实有效,我不想发生这种情况

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

如何阻止类数组变量@board 被写入?当前类代码如下:

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!

我尝试在谷歌上搜索,但无济于事,但我是一个完整的初学者,所以我可能没有使用正确的搜索词

【问题讨论】:

  • 搜索“不可变数组”和“冻结数组”。不变性必须由特定对象强加,因为 x.y = ax.y.z = b 中的 assignment 没有任何关系或想法(除了 x.y 之前已解析为一个对象)。
  • 术语说明:@board 不是类变量,而是实例变量。类变量以两个@s 开头,例如@@board.

标签: arrays ruby class


【解决方案1】:

我相信您需要使数组不可变。

您可以使用Array#freeze 来实现。

之后您的代码应如下所示:

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

在运行您的第一个示例时:

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

在运行第二个示例时:

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

【讨论】:

    【解决方案2】:

    仅定义attr_reader,不定义attr_writer,将阻止仅对@board 变量进行赋值。换句话说,您的Board 类不会公开一个接口来修改存储在@board 中的内容,但不会阻止修改初始值。

    你可以使用freeze:

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

    (另外,你不需要map

    冻结顶级数组和嵌套数组会按照您的描述进行,但我想它也会破坏您的游戏,因为修改将完全不可能。

    我的建议是完全不要公开 @board 并将其视为私有的。然后,您应该公开一个接口来设置板中的值,并提供一个方法来返回板的只读表示。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-09
      • 2019-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多