2014-10-19 238 views
2

我认为定义一个自定义类的to_s方法意味着调用该类的puts方法将返回to_s指定的输出。然而,在这个程序中,如果我写puts bingo_board.to_s,我只会得到我渴望的结果。到底是怎么回事?为什么不是put方法调用我的.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" 

回答

2

它是因为你是从数组扩展。这就是为什么你会得到这种奇怪的行为。我没有看到你需要的扩展,所以只需删除它,事情就会按照你的预期工作。

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

+0

哇!这是一个快速解决方案。但是Array有自己的to_s方法。为什么我的自定义to_s方法无法覆盖它,@jwater? – pgblu 2014-10-19 17:21:21

+0

tl;你离开我的链接的博士:数组的处理方式不同,没有人真正知道为什么。这是一个公平的总结吗? 另外:是否准确地说数组to_s方法不能被覆盖? – pgblu 2014-10-19 17:29:23

+1

@pgblu - 不完全是,它被覆盖,如果你使用其他方法如say,print,它会调用你的to_s,但只要你使用puts方法,那么你是对的。 – jwater 2014-10-19 17:38:31

0

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

但它只能与p一起使用,因为puts会运行each(&:inspect)

+0

好的。现在,而不是纯粹的泡沫我得到[] – pgblu 2014-10-19 17:05:32

+0

我更新了答案。 – Takyo 2014-10-19 17:28:34

1

如果对象是一个Array或可被转化为一个(即它实现to_ary),然后puts不会在对象上调用to_s,而是迭代在物体和打印由内每个对象打电话给to_s就可以了。

参见:

puts [1, 2] 
# 1 
# 2 

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

This is actually documented,虽然有点隐含:

如果调用数组参数,在一个新行写入每个元素。

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 
    
相关问题