2014-10-19 236 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 
    
相關問題