Цикл while
Цикл while в R выполняет своё тело, пока условие остаётся TRUE. Там, где цикл for выполняется по одному разу на элемент чего-либо, while выполняется неизвестное число раз — пока не изменится условие:
Каждый цикл while работает благодаря трём частям, и все три здесь видны:
- Подготовка перед циклом —
count <- 1даёт условию то, что можно проверить. - Условие —
count <= 5, построенное из обычных операторов сравнения, проверяется перед каждым проходом. - Обновление внутри тела —
count <- count + 1двигает цикл к остановке.
Уберите третью часть — и условие будет TRUE вечно: цикл никогда не закончится. Это определяющий риск while, и оставшаяся часть страницы во многом о том, как им управлять.
Условие проверяется перед каждым проходом
while сначала проверяет условие, а потом выполняет тело. Отсюда два следствия. Если условие уже ложно на старте, тело выполняется ноль раз:
И проверка происходит между полными проходами, а не посреди тела: раз проход начался, он выполняется до конца, даже если условие стало FALSE на полпути. Если нужно выйти посреди тела, для этого есть break (ниже).
Разобранный пример: деление пополам до порога
Естественный дом для while — это «продолжать, пока не»: заранее нельзя знать, сколько потребуется проходов, поэтому for не подходит. Сколько раз можно поделить 1000 пополам, прежде чем результат станет меньше 1?
Десять делений, финал примерно 0.977. Этот шаблон — переменная состояния, которую тело уменьшает, и счётчик, который оно наращивает, — покрывает огромную долю реальных циклов while: численные методы, работающие до достаточно малой погрешности; симуляции, идущие до исчерпания ресурса; повторные попытки, продолжающиеся до успешного вызова.
repeat + break: аналог do-while в R
В R нет ключевого слова do-while — нет цикла, проверяющего условие после тела. Зато есть repeat, который крутится безусловно и вечно, и break, который немедленно выходит из любого цикла. Поставьте проверку с break в конце тела repeat — и вы построили ровно do-while, цикл, чьё тело гарантированно выполнится хотя бы раз:
Тело всегда выполняется до первой проверки, поэтому n удваивается минимум один раз; цикл выходит на 128 — первом удвоении, превысившем 100. Используйте эту форму, когда честное описание звучит как «сделай, а потом реши, делать ли снова»: чтение ввода до корректного, генерация выборок до подходящей. repeat без достижимого break — бесконечный цикл по построению, так что пишите сначала break, а потом заполняйте тело.
break работает одинаково в циклах while и for — это общий выход «здесь мы закончили», и он естественно сочетается с конструкциями if, проверяющими условие посреди тела.
next внутри цикла while
next пропускает остаток текущего прохода и возвращается к проверке условия. В while он работает так же, как в for, — с одной специфичной для while ловушкой: если шаг обновления идёт после next, то его пропуск означает, что переменная цикла никогда не двигается, и вы построили бесконечный цикл. Ставьте обновление перед next:
Это печатает чётные числа с 2 по 10. Переместите i <- i + 1 ниже строки с next — и первое же нечётное значение навсегда пропустит обновление. В циклах for такой опасности нет (переменная цикла продвигается автоматически), что ещё раз говорит в пользу for, когда вы просто идёте по последовательности.
Защита от бесконечных циклов
Любой бесконечный цикл — это один и тот же баг: ничто в теле не меняет переменные, от которых зависит условие, — или меняет их не в ту сторону. В интерактивном режиме можно прервать через Esc или Ctrl+C, но в скрипте процесс просто зависает. Предотвращают это две привычки.
Во-первых, написав условие, сразу спросите себя: «какая строка тела двигает его к FALSE?» — и убедитесь, что эта строка выполняется на каждом пути через тело (помните ловушку с next выше).
Во-вторых, для циклов, чьё завершение трудно доказать, добавляйте ограничитель числа итераций, чтобы ошибка в логике давала диагностируемый результат, а не зависшую сессию. Здесь он защищает цикл в духе Коллатца — многократно делить чётные числа пополам и превращать нечётное x в 3 * x + 1, пока не дойдём до 1:
Стартуя с 10, цикл достигает 1 за 6 шагов. Если бы какое-то начальное значение никогда не доходило до 1, оговорка steps < 10000 всё равно завершила бы цикл, а напечатанное состояние подсказало бы, что что-то пошло не так, — видимый неверный ответ лучше программы, которая не отвечает никогда. Здесь уместен именно сокращённый &&: обе стороны — одиночные значения, и защита читается как одно предложение.
while против for: как выбрать
Правило большого пальца — о том, что вы знаете заранее:
- Вы знаете, что перебираете (элементы вектора, числа от 1 до n): используйте цикл for — переменная цикла управляет собой сама.
- Вы знаете только условие остановки (до сходимости, до порога, до корректного значения): используйте
while. - Тело должно выполниться хотя бы раз, прежде чем проверка обретёт смысл: используйте
repeatс завершающимbreak.
Если вы ловите себя на том, что вручную ведёте счётчик в while только ради обхода вектора, — это переодетый цикл for, переходите на него.
Что вы уносите с собой
while (condition) { ... }сначала проверяет, потом выполняет — ложное условие на старте означает ноль проходов.- Тело должно двигать условие к
FALSE; тройка «подготовка / условие / обновление» — вся дисциплина целиком. - В R нет ключевого слова do-while: идиома —
repeat { body; if (done) break }, гарантирующая хотя бы один проход. breakвыходит из любого цикла; при использованииnextвwhileобновляйте переменную цикла до пропуска, иначе она никогда не продвинется.- Ограничитель числа итераций (
&& steps < 10000) превращает потенциальное зависание в видимый и отлаживаемый неверный ответ.
Дальше: векторы — структура данных, по которой ходили все циклы на этой странице, и причина, по которой многие циклы в R могут исчезнуть совсем.
Часто задаваемые вопросы
Как работает цикл while в R?
while (condition) { ... } сначала проверяет условие и выполняет тело, только пока оно остаётся TRUE. Что-то в теле должно рано или поздно сделать условие FALSE — обычно обновление счётчика или уменьшение значения, — иначе цикл никогда не завершится. Если условие ложно с самого начала, тело выполняется ноль раз.
Есть ли в R цикл do-while?
Как ключевого слова — нет. Идиома такова: repeat { ...body...; if (condition) break } — repeat крутится безусловно, поэтому тело гарантированно выполняется хотя бы раз, а break в конце играет роль проверки выхода из do-while.
Как остановить бесконечный цикл в R?
В интерактивном режиме нажмите Esc (RStudio) или Ctrl+C (терминал), чтобы прервать выполнение. Чтобы вообще его не допустить: убедитесь, что тело обновляет переменные из условия на каждом проходе, и добавьте ограничитель числа итераций — while (working && steps < 10000), — чтобы ошибка в логике давала неверный результат, а не зависшую сессию.