为什么put方法调用我的.to_s方法?

时间:2014-10-19 16:59:14

标签: ruby puts

我认为为自定义类定义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 个答案:

答案 0 :(得分:2)

因为你从Array扩展了。这就是为什么你会得到奇怪的行为。我不知道你需要从哪里扩展,所以只需删除它就可以按照你的预期运行。

如果你想知道为什么会这样,那么这里有一个更详细的答案。基本上puts会使数组成为例外,因此当传递数组时会在每个成员上调用puts。 Ruby Array#puts not using overridden implementation?

答案 1 :(得分: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. 使用printprintf代替puts

    print bingo_board,"\n"
    printf "%s\n",bingo_board
    

答案 2 :(得分:1)

如果对象是Array或者可以转换为一个(即它实现to_ary),那么puts将不会在对象上调用to_s,而是迭代覆盖对象,并通过调用在中打印每个对象

请参阅:

to_s

This is actually documented,虽然有些暗示:

  

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

答案 3 :(得分:0)

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

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