Циклы: while, until, loop, times
Цикл — это повторение действия. В Ruby есть классические while и until, но в идиоматическом коде их часто заменяют итераторами вроде times и each.
Суть:whileповторяет тело, пока условие истинно;until— пока ложно; аn.times— самый ruby-style способ повторить действие ровно n раз.
В большинстве языков цикл — основной инструмент перебора. В Ruby философия другая: вместо ручного управления счётчиком вы чаще «отправляете сообщение» коллекции или числу, и оно само организует повторение. Но базовые циклы знать нужно — они незаменимы там, где число итераций заранее неизвестно.
i = 0
while i < 3
puts "шаг #{i}"
i += 1
end
# until — пока НЕ выполнено условие
balance = 100
until balance <= 0
balance -= 30
end
Разбор: times и управление циклом
Самый частый случай — «повторить N раз». Здесь идиоматичен n.times, который к тому же даёт индекс. Внутри любого цикла работают break (выйти полностью) и next (перейти к следующей итерации).
3.times { |i| puts "итерация #{i}" } # => 0,1,2
(1..10).each do |n|
next if n.even? # пропустить чётные
break if n > 7 # выйти после 7
puts n # => 1,3,5,7
end
loop do
print "введите 'стоп': "
break if gets.chomp == "стоп"
end
Как работает под капотом
Все эти конструкции — про поток управления: исполнение «крутится» в теле, пока действует условие или пока не встретит break. Метод loop — бесконечный цикл, его всегда нужно прерывать вручную. Важная деталь: loop тихо завершается, если внутри возникает исключение StopIteration — это делает его удобным для работы с энумераторами.
вход в цикл
|
v
[ условие? ] --ложь--> выход
| истина
v
тело цикла
|
next? --> назад к условию (пропуск остатка тела)
break? --> немедленный выход
|
+--> назад к условию
Частые ошибки
- Бесконечный цикл. Забыли
i += 1или условие никогда не станет ложным — программа зависнет. - Off-by-one. Путаница с
<и<=в условии даёт на одну итерацию больше или меньше. - Ручной while там, где нужен each. Перебирать массив через индексы и
while— не по-рубишному и подвержено ошибкам.
Best practices
- Если число повторений известно — берите
n.times, а неwhileсо счётчиком. - Для перебора коллекций используйте
eachи другие итераторы (о них — следующий раздел), а не индексные циклы. while/untilоставьте для случаев, где условие выхода динамическое (ввод пользователя, опрос состояния).
Глубже: почему Ruby не любит классический for
Внимательный читатель заметит, что мы почти не упоминаем цикл for, хотя в Ruby он есть. Это не случайность. Идиоматический Ruby избегает for по нескольким причинам. Во-первых, переменная цикла в for «утекает» наружу и остаётся видимой после цикла, тогда как в блоке each она локальна — это чище и безопаснее. Во-вторых, for не даёт ничего, чего не давал бы each, зато выглядит чужеродно на фоне остального кода, построенного на блоках и итераторах. В-третьих, переход на мышление «отправь сообщение коллекции» вместо «управляй счётчиком вручную» — это ключевой сдвиг, который делает из вас рубиста. Поэтому правило простое: для перебора коллекций берите each и методы Enumerable, для повторения N раз — times, для условного повторения — while/until, а про for можете забыть. Этот сдвиг в привычках окупится в следующем разделе, где блоки и Enumerable раскроются во всю мощь и заменят ручные циклы почти полностью.
Итог. while и until управляют повторением по условию, n.times повторяет фиксированное число раз, а break и next управляют ходом цикла. В рубишном коде ручные циклы уступают место итераторам.