3

我认为to_s为自定义类定义方法意味着在该类上调用该puts方法将返回由to_s. 然而,在这个程序中,我只有在编写puts bingo_board.to_s. 到底是怎么回事?

class BingoBoard < Array
  @@letters = %w[B I N G O]

  def initialize
    # populates an 5x5 array with numbers 1-100
    # to make this accessible across your methods within this class, I made
    # this an instance variable. @ = instance variable
    @bingo_board = Array.new(5) {Array.new(5)}
    @bingo_board.each_with_index do |column, i|
      rangemin = 15 * i + 1
      @bingo_board[i] = (rangemin..(rangemin+14)).to_a.sample(5)
    end
    @bingo_board[2][2] = "X" # the 'free space' in the middle
    @game_over = false
  end

  def game_over?
    @game_over
  end

  def generate_call
    ....
  end

  def compare_call(call)
    @bingo_board[@@letters.index(call[0])].include? call[1]
  end

  def react_to_call(call)
    ...
  end

  def check_board
    ...
  end

  def show_column(num)
    ...
  end

  def to_s
    result = ""
    0.upto(4) do |val|
      result += " " + @@letters[val] + " "
    end
    result += "\n\n"
    0.upto(4) do |row|
      0.upto(4) do |col|
        val = @bingo_board[col][row]
        result += " " if val.to_i < 10
        result += val.to_s + " "
      end
      result += "\n"
    end
    result
  end
end

my_board = BingoBoard.new
counter = 0
until my_board.game_over?
  puts my_board.to_s # renders the board in accordance with my to_s method
  call = my_board.generate_call
  counter += 1
  puts "\nThe call \# #{counter} is #{call[0]} #{call[1]}"
  my_board.react_to_call(call)
  gets.chomp
end
puts my_board  # renders bubkes (i.e., nothing)
puts "\n\n"
puts "Game over"
4

4 回答 4

2

这是因为您是从 Array 扩展而来的。这就是为什么你会出现奇怪的行为。我看不出你需要从哪里扩展,所以只需删除它,事情就会如你所愿。

如果您想知道为什么会这样,这里有一个更详细的答案。基本上 puts 对数组进行了例外处理,因此当传递数组时,将在每个成员上调用 puts。Ruby Array#puts 不使用重写的实现?

于 2014-10-19T17:16:45.357 回答
2

正如@jörgwmittag 所说,这是一个特例。该IO#puts方法以不同的方式处理数组 - 这意味着任何响应的东西to_ary。它首先调用to_ary,然后遍历结果数组的每个元素,并且只调用to_s它们。它从不调用to_s数组本身。

如果您委托给成员数组而不是从Array继承,您可以更细粒度地控制“继承”(委托)的内容。to_ary然后您可以从委托中排除,这将防止puts将您的对象视为数组并触发此行为。

其他通用解决方案:

  1. 使用字符串插值或显式to_s调用,以便puts接收的内容已经是字符串:

    puts "#{bingo_board}"
    puts bingo_board.to_s
    
  2. 使用printorprintf代替puts

    print bingo_board,"\n"
    printf "%s\n",bingo_board
    
于 2017-01-14T14:47:00.520 回答
1

如果对象是一个Array或可以转换为一个(即它实现to_ary),则puts不会调用to_s该对象,而是遍历该对象并通过调用它来打印其中to_s每个对象。

看:

puts [1, 2]
# 1
# 2

[1, 2].to_s
# => '[1, 2]'

实际上已记录在案,尽管有些隐含:

如果使用数组参数调用,则将每个元素写入新行。

于 2014-10-19T19:06:52.433 回答
0

看起来它运行Array#inspect方法而不是您的 custom to_salias_method :inspect, :to_s在定义结束后添加to_s将对您有所帮助。

但它只适用于p, 因为puts运行each(&:inspect).

于 2014-10-19T17:04:20.707 回答