Как учиться

Циклы: как не запутаться

В тетради дачника записано: «Понедельник — полил грядки. Вторник — полил грядки. Среда — полил грядки». Дальше то же самое ещё четыре строки, потом неделя повторяется. Человек, которому приходится так писать, на второй неделе начнёт сокращать: «пн–вс: полил». Это и есть цикл. Одна запись вместо многих, и в ней указано, сколько раз повторять и что именно.

Для программ он нужен по той же причине, но с одним отличием: компьютер не устаёт и повторяет хоть миллион раз. Поэтому у него сложности другого рода, не со скукой, а с границами: когда остановиться и сколько раз выполнить. Какая именно беда случится, зависит от того, какой из двух циклов вы взяли, и ниже сначала сравнение «руками и циклом», потом история про цикл, который не останавливается. Про общую карту предмета рассказано в статье про информатику в школе, а до этой темы стоит дойти через условия и ветвления: внутри цикла почти всегда стоит проверка.

Повторить руками

Задача простая: напечатать на экране записи о поливе на семь дней. Руками это выглядит так:

  • print("День 1: полив")
  • print("День 2: полив")
  • print("День 3: полив")

и дальше ещё четыре строки такого же вида. Работает, и для семи дней даже терпимо. Проблемы две. Первая: если дней становится тридцать, надо писать ещё двадцать три строки, и в одной из них обязательно найдётся опечатка, например «День 17» вместо «День 18», причём программа ничего не заметит. Вторая: если вместо слова «полив» надо написать «полив и подкормка», править придётся каждую строку, и пропустить одну проще, чем кажется.

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

Повторить циклом for

Та же задача одной строкой: for day in range(1, 8): print("День", day, "— полив"). Слово for значит «для каждого», day — имя переменной, которая на каждом витке получает новое значение, а range(1, 8) выдаёт числа подряд: 1, 2, 3, и так до 7.

Именно здесь ждёт главная ловушка цикла. Конец диапазона в range не входит: range(1, 8) заканчивается на семёрке, и восьмёрка в него не попадает. Начинающий пишет «до семи» и ставит семь, и получает шесть дней. Если написать только range(7), счёт начнётся от нуля и пойдёт от 0 до 6, тоже семь значений, но другие. Ошибки на единицу в обе стороны — одна из самых частых причин неверных ответов в задачах с циклами, и в программах подсчёта они называются просто «ошибка на единицу». Лечит их одна привычка: перед запуском записать на бумаге, какие значения примет переменная на первом и на последнем витке.

Плюс цикла в том, что семь дней превращаются в тридцать заменой одной цифры. Это не мелочь: любое изменение условий задачи делается в одном месте.

Сдвиг тела, о котором шла речь в статье про условия, в циклах решает больше, чем там: строка, сдвинутая под for, повторяется на каждом витке, а строка без сдвига выполняется один раз после цикла. Поэтому итоговая фраза «Все семь дней политы» должна стоять без сдвига, иначе она напечатается семь раз, и это одна из самых частых ошибок на первых занятиях: код правильный, а вывод странный, и непонятно, где искать.

Подсчёт шагов: цикл while

Иногда число повторений заранее неизвестно. В бочке сто литров воды, на каждый полив уходит пятнадцать. Сколько полных поливов выйдет? Ответ можно найти и арифметикой (о ней ниже), но сейчас важнее устройство цикла, а на таком примере оно видно лучше всего: поливать надо, пока воды хватает.

Для этого есть цикл while, «пока»: water = 100, days = 0, затем while water >= 15: water = water - 15; days = days + 1. Читается так: пока воды не меньше пятнадцати литров, убавить воду на пятнадцать и прибавить день. После цикла print(days) выводит 6, а в бочке остаётся 10 литров. Седьмой полив уже не выйдет, и в этом смысл условия со знаком больше или равно.

Хорошая проверка, что вы поняли такой цикл, — прокрутить его на бумаге. Две колонки, water и days, и по строке на каждый проход: 100 и 0, потом 85 и 1, потом 70 и 2, и так, пока условие не станет ложным. Если у вас получилась та же шестёрка, что и у программы, понимание есть; если другая, то видно, на каком проходе вы расходитесь.

Когда цикл не нужен

Прежде чем садиться за цикл, полезно спросить себя, нельзя ли обойтись формулой. Полные поливы из бочки в сто литров находятся делением без остатка, оно разобрано в статье про переменные и типы данных: 100 // 15 даёт те же шесть, и ни одного витка не понадобилось.

Цикл нужен там, где формулу записать трудно. Пусть расход растёт на два литра каждый день: в первый день уходит пятнадцать, во второй семнадцать, в третий девятнадцать. Здесь проще всего пройти по дням. Перед циклом заводим use = 15; условие остаётся прежним, while water >= 15, а в теле цикла делаем три действия через точку с запятой: water = water - use; use = use + 2; days = days + 1. Для исходных ста литров ответ пять поливов, и порядок действий важен: сначала вычитаем текущий расход, потом увеличиваем его на два.

Цикл, который не кончается

Теперь о том, что пугает сильнее всего: программа зависла, ничего не пишет, а у ноутбука вдруг загудел вентилятор. Скорее всего, это бесконечный цикл, и сделать его проще, чем кажется. Уберите из нашего while действие, которое меняет воду: while water >= 15: days = days + 1. Условие смотрит на water, а внутри цикла water никто не меняет, и значит, оно остаётся истинным вечно. Программа не сломана, она аккуратно выполняет то, что ей сказали: считает дни без конца.

Вторая причина коварнее, потому что цикл выглядит правильным. Пусть мы написали вместо «воды не меньше пятнадцати» условие «вода не равна нулю»: while water != 0. По смыслу кажется, что так даже понятнее, пока вода не кончится. Но вода идёт по значениям 100, 85, 70 и так далее до 10, потом падает до минус пяти и больше нуля не возвращается. Ноль она перепрыгнула, а условие «не ноль» остаётся истинным всегда.

Вывод из этой истории касается привычки: когда значение меняется шагами, условие остановки надёжнее писать через «больше» или «меньше», чем через «равно» и «не равно». Условие «пока больше нуля» заметит перепрыгнутое значение, условие «пока не нуль» его пропустит.

Теперь о том, что делать, когда программа уже висит. В терминале её прерывает сочетание Ctrl и C, в учебных средах вроде IDLE работает то же сочетание, а в Thonny есть кнопка остановки (о средах — в обзоре школьной информатики). Закрывать окно целиком не нужно: вы потеряете несохранённый код. После остановки добавьте в тело цикла печать значений, которые должны меняться, print(water), и посмотрите, меняются ли они. Обычно хватает одного взгляда на первые пять строк.

Для самых недоверчивых есть и страховка: счётчик витков, после которого цикл принудительно прерывается командой break. В готовой программе она ни к чему, при отладке спасает от зависаний.

Напоследок о чтении чужого кода. Цикл, присланный репетитору или найденный в сборнике, читается двумя вопросами. Если он завис, в сообщении пригодится одна строчка: при каком вводе и какое значение не меняется. Что меняется на каждом витке? И когда оно перестанет меняться так, что условие станет ложным? Если на первый вопрос ответа нет, цикл бесконечный; если на второй ответ «никогда», то тоже. Остальное, включая то, что цикл печатает, вторично, и на этих двух вопросах строится чтение любой программы с повторениями.

Что выбрать

Выбор подсказывает задача. Если число повторений известно заранее (дни недели, элементы списка, числа от 1 до 100, буквы слова), берите for: он кончается сам, и бесконечным его сделать почти нельзя. Если повторять нужно «пока что-то не случится», а число шагов неизвестно, как с водой в бочке, тогда while, и на нём нужно особенно следить за тем, что внутри цикла меняется условие.

Конструкцию while True с выходом через break новичкам я бы не давал: она работает, но приучает не думать об условии остановки, а в этой теме условие остановки и есть главное. Придёт время, и её освоят, но не в первые месяцы.

Куда это дальше

Цикл по числам скоро заменяется циклом по элементам списка, и это тема про списки. Когда тело цикла разрастается до десятка строк, его удобно вынести в отдельный блок с именем: так появляются функции, о них в статье про функции и аргументы. В экзамене циклы встречаются в задачах на перебор, где время работы программы тоже имеет значение; как это связано с выбором языка, сказано в статье о структуре ЕГЭ по информатике, а какие задания первой части решаются циклами, видно по карте первой части и демоверсии своего года.

Начать поиск:

Вопросы и ответы

Что выбрать в самом начале, for или while? For. Он кончается сам и не даёт завесить программу, поэтому на нём удобнее учиться. While подключайте, когда столкнётесь с задачей, где число повторений неизвестно заранее: как в примере с бочкой.

Почему range(1, 8) заканчивается на семи? Так устроен диапазон: начало входит, конец нет. Привычка пригодится позже: со срезами списков то же самое.

Программа зависла. Как её остановить и не потерять написанное? Нажмите кнопку остановки в вашей среде или Ctrl и C в терминале. Файл с программой при этом остаётся целым, если вы успели его сохранить. Поэтому сохраняйте файл перед каждым запуском цикла: привычка простая, а однажды она спасёт десять строк кода.

Можно ли выйти из цикла раньше, чем кончится условие? Можно, командой break: она выходит из ближайшего цикла сразу. Пользоваться ею в первые месяцы стоит осторожно: условие остановки теряется из виду, а в этой теме оно главное. Если выход нужен, сначала проверьте, нельзя ли выразить его самим условием.

Оставить заявку

Ещё в рубрике «Как учиться»