2014-10-19 2 views
2

Я думал, что определение метода to_s для пользовательского класса означало, что вызов метода puts в этом классе возвращал бы выход, как указано to_s. Однако в этой программе я получаю только результат, который я жажду, если напишу puts bingo_board.to_s. Что происходит?Почему метод puts не вызывает мой метод .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

+0

tl; dr ссылки, которую вы оставили мне: массивы обрабатываются по-разному, и никто не знает почему. Это справедливое резюме? Также: правильно ли сказать, что метод to_s массива не может быть переопределен? – pgblu

+1

@pgblu - не совсем, он перезаписан, и если вы используете какой-либо другой метод, например say, print, он будет называть ваш to_s, но пока вы используете метод puts, тогда вы правы. – jwater

0

Похоже, он работает Array#inspect метод вместо пользовательских to_s. Добавление alias_method :inspect, :to_s сразу после окончания определения to_s поможет вам.

Но он будет работать только с p, потому что puts работает each(&:inspect).

+0

ОК. Теперь вместо чистых пузырей я получаю [] – pgblu

+0

Я обновил ответ. – Takyo

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. Использование print или printf вместо puts:

    print bingo_board,"\n" 
    printf "%s\n",bingo_board 
    
Смежные вопросы