Мини-игра. Руби трудный путь упражнение 35

В настоящее время я изучаю Ruby по руководству "Узнай Ruby на собственном опыте". И в этом упражнении автор просит нас добавить вещи в простую игру. Однако я пытался улучшить метод bear_room, делая что-то вроде этого:

while true
print "> "
choice = gets.chomp.downcase!
if choice.include? ("taunt")
  dead("The bear looks at you then slaps your face off.")
elsif choice.include? "taunt" && !bear_moved
  puts "The bear has moved from the door. You can go through it now."
  bear_moved = true
elsif choice.include? "taunt" && bear_moved
  dead("The bear gets pissed off and chews your leg off.")
elsif choice.include? "open" && bear_moved

Однако, когда я пишу это:

choice = gets.chomp.downcase!

Это дает мне эту ошибку при выполнении:

ex35.rb:44:in `bear_room': undefined method `include?' for nil:NilClass (NoMethodError)
from ex35.rb:99:in `start'
from ex35.rb:109:in `<main>'

Но если сделать что-то вроде этого:

choice = gets.chomp.downcase

или это:

choice = gets.chomp
choice.downcase!

Оно работает. Почему так? Буду признателен за любую помощь. Кроме того, как работает бит while true? Это меня действительно сбивает с толку.

Вот остальная часть программы на случай, если она вам понадобится. Я собираюсь оставить упомянутый метод «отдельным», чтобы его было легче читать.

# Creates the 'gold_room' method, so it can be called later.
def gold_room
  puts "This room is full of gold. How much do you take?"

  print "> "
  choice = gets.chomp
  # Converts the 'choice' variable to integer type.
  choice.to_i

  # Checks if 'choice' is equals to 0, OR greater or equal to 1.
  if choice == '0' || choice >= '1'
  # Saves the integer 'choice' variable in the 'how_much' variable.
    how_much = choice.to_i
  else
    dead("Man, learn to type a number.")
  end

  # Checks if the 'how_much' variable is lesser than 50, and executes the code below if so.
  if how_much < 50
    puts "Nice, you're not greedy, you win!"
    exit(0)
  elsif how_much >= 50 && how_much < 100
    puts "Mmm, ok that's enough. Get out!"
  else
    dead("You greedy bastard!")
  end
end


################### bear_room method ###################


# Creates the 'bear_room' method.
def bear_room
puts "There is a bear here."
puts "The bear has a bunch of honey."
puts "The fat bear is in front of another door."
puts "How are you going to move the bear?"
puts "1. Taunt the bear."
puts "2. Steal the bear's honey. "

# Declares the 'bear_moved' variable as a boolean, initialize it to false.
   bear_moved = false

  while true
    print "> "
    choice = gets.chomp.downcase
    if choice.include? ("taunt")
      dead("The bear looks at you then slaps your face off.")
    elsif choice.include? "taunt" && !bear_moved
      puts "The bear has moved from the door. You can go through it now."
      bear_moved = true
    elsif choice.include? "taunt" && bear_moved
      dead("The bear gets pissed off and chews your leg off.")
    elsif choice.include? "open" && bear_moved
      gold_room
    else
      puts "I got no idea what that means."
    end
  end
end

############### end of method ###############





# Defines the 'cthulhu_room' method.
def cthulhu_room
  puts "Here you see the great evil Cthulhu."
  puts "He, it, whatever stares at you and you go insane."
  puts "Do you flee for your life or eat your head?"

  print "> "
  choice = gets.chomp

  # Checks if the user's input contains the word 'flee'. If so, executes the code below.
  if choice.include? "flee"
    start
  # Checks if the user's input contains the word 'head'. If so, executes the code below instead.
  elsif choice.include? "head"
    dead("Well that was tasty!")
  else
    # Otherwise, calls the 'cthulhu_room' method again.
    cthulhu_room
  end
end

# Defines the 'dead' method. It takes one argument (why). Example: dead("Well that was tasty!")
def dead(why)
  puts why, "Nice."
  # Succesfully finish the program.
  exit(0)
end

# Defines the 'start' method, wich is where the game begins. Duh.
def start
  puts "You are in a dark room."
  puts "There is a door to your right and left."
  puts "Which one do you take?"

  print "> "
  choice = gets.chomp

  # Start the branching. It checks the user's input, and saves that on the 'choice' variable, which is used along the whole program in the other methods.
  if choice == "left"
    # Calls the 'bear_room' method.
    bear_room
  elsif choice == "right"
    # Calls the 'cthulhu_room' method.
    cthulhu_room
  else
    dead("You stumble around the room until you starve.")
  end
end

# Start the game.
start

---------------------------------------------------------------------------

TL;DR: В чем разница между choice = gets.chomp.downcase! и

choice = gets.chomp
choice.downcase!

PS: комментарии являются частью упражнения. Пожалуйста, если у вас есть какие-либо исправления (о комментариях, о том, как я сделал вопрос, о коде в целом и т. д.), пожалуйста, сообщите мне, чтобы я мог исправиться. Спасибо и извините за длину!

-------------------------------------------------------------------------


person Leonardo Guedez    schedule 05.03.2015    source источник


Ответы (1)


Это потому, что если строка не изменилась при вызове downcase!, она возвращает nil. Таким образом, когда вы пытаетесь вызвать include?, он говорит, что nil не имеет такого метода.

Таким образом, безопаснее всего использовать «неразрушающую» версию downcase. Метод downcase! изменяет строку на месте, если это возможно.

Ознакомьтесь с документами для дальнейшего чтения.

person squiguy    schedule 05.03.2015
comment
Но если downcase! изменяет объект, к которому он применяется, почему gets.chomp.downcase! не работает? - person Leonardo Guedez; 05.03.2015
comment
@LeonardoGuédez Это потому, что вы присваиваете ему значение. Как вы указали, если вы вызываете choice.downcase! отдельно, вы ничему не присваиваете значение, а вместо этого choice изменяется неявно. - person squiguy; 05.03.2015