Поскольку @Tobias ответил на ваш вопрос, я хотел бы потратить некоторое время, чтобы предложить, как вы могли бы сделать свой код более Ruby -подобным.
Во-первых, в то время как вы могли бы использовать while
или until
l oop, я предлагаю вам полагаться в основном на метод Kernel # l oop для большинства циклов, которые вы напишите. Это просто заставляет цикл продолжаться в пределах блока loop
, пока не будет найдено ключевое слово break
1 . Это очень похоже на while true
или until false
(обычно используется в некоторых языках), но я думаю, что это читается лучше. Что еще более важно, использование loop
защищает вычисления в его блоке от посторонних глаз. (См. Раздел Другие соображения ниже для примера этой точки.)
Вы также можете выйти из блока loop
, выполнив return
или exit
, но обычно вы будет использовать break
.
Мое второе основное предложение заключается в том, что для этого типа проблемы вы используете case statement
, а не if/elsif/else/end
конструкцию. Давайте сначала сделаем это, используя диапазоны.
Используйте оператор case с диапазонами
my_num = rand(831)
guess_count = 0
loop do
print "Pick a number between 0 and 830: "
guess_count += 1
case gets.chomp.to_i
when my_num
puts "you got it!"
break
when 0..my_num-1
puts "higher"
else
puts "lower"
end
end
Здесь следует отметить несколько моментов.
- Я использовал
print
вместо puts
, поэтому пользователь будет вводить свой ответ в той же строке, что и приглашение. guess_count
увеличивается независимо от ответа пользователя, так что это можно сделать до выполняется оператор case. - нет необходимости присваивать ответ пользователя (
gets.chomp.to_i
) переменной. - операторы case сравнивают значения с соответствующим методом равенства регистра
===
.
Что касается последнего пункта, то здесь мы сравниваем целое число (gets.chomp.to_i
) с другим целым числом (my_num
) и с диапазоном (0..my_num-1)
. В первом случае используется Integer#===
, что эквивалентно Integer#==
. Для диапазонов используется метод Range # === .
Предположим, например, что my_num = 100
и gets.chomp.to_i #=> 50
В этом случае выражение case читается следующим образом.
case 50
when 100
puts "you got it!"
break
when 0..99
puts "higher"
else
puts "lower"
end
Здесь мы находим, что 100 == 50 #=> false
и (0..99) === 50 #=> true
, поэтому отображается puts "higher"
. (0..99) === 50
возвращает true
, поскольку целое число (справа от ===
) покрыто диапазоном (слева). Это не то же самое, что 50 === (0..90)
, который свободно читает: «(0..99)
- это член из 50
», поэтому возвращается false
.
Вот пара больше примеров того, как операторы case могут быть использованы для получения преимущества, поскольку они опираются на метод тройного равенства.
case obj
when Integer
obj + 10
when String
obj.upcase
when Array
obj.reverse
...
end
case str
when /\A#/
puts "A comment"
when /\blaunch missiles\b/
big_red_button.push
...
end
Использование оператор case с оператором космического корабля <=>
оператор космического корабля используется Ruby '* Array # sort и Enumerable # Сортировать методов, но имеет другое применение, как в случае операторов. Здесь мы можем использовать Integer # <=> для сравнения двух целых чисел.
my_num = rand(831)
guess_count = 0
loop do
print "Pick a number between 0 and 830: "
case gets.chomp.to_i <=> my_num
when 0
puts "you got it!"
break
when -1
puts "higher"
else # 1
puts "lower"
end
end
В других приложениях оператор космического корабля может использоваться для сравнения строк ( String # <=> ), массивы ( Array # <=> ), Date
объекты ( Date # <=> ) и т. Д.
Используйте ха sh
Хэши часто можно использовать в качестве альтернативы case statements
. Здесь мы могли бы написать следующее.
response = { -1=>"higher", 0=>"you got it!", 1=>"lower" }
my_num = rand(831)
guess_count = 0
loop do
print "Pick a number between 0 and 830: "
guess = gets.chomp.to_i
puts response[guess <=> my_num]
break if guess == my_num
end
Здесь нам нужно значение gets.chomp.to_i
дважды, поэтому я сохранил его в переменной.
Прочие соображения
Предположим, мы пишем следующее:
i = 0
while i < 5
i += 1
j = i
end
j #=> 5
j
после l oop равно 5
.
Если вместо этого использовать loop
:
i = 0
loop do
i += 1
j = i
break if i == 5
end
j #=> NameError (undefined local variable or method 'j')
Хотя while
и loop
оба имеют доступ к i
, но loop
ограничивает значения локальных переменных, созданных в его блоке, блоком. Это потому, что блоки создают новый scope , что является хорошей практикой кодирования. while
и until
не используют блоки. Обычно мы не хотим, чтобы код, следующий за l oop, имел доступ к локальным переменным, созданным в l oop, что является одной из причин предпочтения loop
над while
и until
.
* 1153. * Наконец, ключевое слово
break
также может использоваться с аргументом, значение которого возвращается
loop
. Например:
def m
i = 0
loop do
i += 1
break 5*i if i == 10
end
end
m #=> 50
или
i = 0
n = loop do
i += 1
break 5*i if i == 10
end
n #=> 50
1. Если вы изучите do c для Kernel#loop
, вы увидите, что выполнение break
из блока loop
эквивалентно вызову исключения StopIteration
.