【问题标题】:Why isn't the puts method calling my .to_s method?为什么 puts 方法不调用我的 .to_s 方法?
【发布时间】:2014-12-14 16:32:24
【问题描述】:

我认为为自定义类定义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"

【问题讨论】:

    标签: ruby puts


    【解决方案1】:

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

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

    【讨论】:

    • 哇!这是一个快速修复。但是 Array 有自己的 to_s 方法。为什么我的自定义 to_s 方法没有覆盖它,@jwater?
    • tl;dr of the link 你给我的:数组的处理方式不同,没有人真正知道为什么。这是一个公平的总结吗?另外:那么说数组 to_s 方法不能被覆盖是否准确?
    • @pgblu - 不完全是,它被覆盖了,如果你使用其他方法,比如 print,它会调用你的 to_s,但只要你使用 puts 方法,那么你是对的。
    【解决方案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
      

    【讨论】:

      【解决方案3】:

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

      见:

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

      This is actually documented,虽然有些含蓄:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        • 好的。现在我得到 [] 而不是纯 bubkes
        猜你喜欢
        • 2013-12-01
        • 2013-04-17
        • 1970-01-01
        • 2014-09-28
        • 2011-12-09
        • 2013-07-09
        • 2016-03-07
        • 2015-07-01
        • 2018-03-26
        相关资源
        最近更新 更多