2.4. Вложенные циклы

Тут — разберёмся, что такое вложенные циклы, а также как пользоваться флагами и операторами break и continue.

В прошлом параграфе мы рассмотрели циклы for и while. Их используют, когда необходимо многократно повторить действия в программе. Часто в программировании решаются задачи, в которых требуется использовать цикл внутри другого цикла. Такие циклы называют вложенными.

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

for i in range(26):
    for j in range(26):
        print(f"{chr(ord('a') + i)}{chr(ord('a') + j)}")

Задача решена полным перебором букв алфавита. В начале программы запускается внешний цикл, который отвечает за генерацию первой буквы. Внешний цикл фиксирует очередное значение итерируемой переменной i — это будет смещение в алфавите относительно начальной буквы.

Далее запускается внутренний цикл, отвечающий за генерацию второй буквы и изменяющий значение итерируемой переменной j. Она также содержит смещение в алфавите, но уже для второй буквы. Таким образом, за одну итерацию внешнего цикла внутренний цикл совершает все свои итерации.

Процесс повторяется, пока все свои итерации не совершит внешний цикл. Внутри функции print() использованы известные нам функции chr() и ord(). Функция ord() использована для возврата кода начальной буквы алфавита («a»), к нему прибавляется текущее смещение, задаваемое итерируемыми переменными i и j. А далее для полученных кодов функция chr() возвращает буквы.

Циклы могут иметь большую степень вложенности. Однако следует учитывать, что общее количество итераций при использовании вложенных циклов равно произведению итераций всех циклов, что приводит к увеличению времени работы программы.

Например, в предыдущей программе количество всех итераций составит 26 * 26 = 676 итераций. Поэтому следует внимательно относиться к выбору алгоритма и не использовать вложенные циклы, если есть возможность решить задачу без них.

Циклы for и while можно останавливать при наступлении определённого условия. Для этого используется оператор break.

Рассмотрим следующий пример:

password = "right_password"
while True:
    if input("Введите пароль: ") == password:
        print("Пароль принимается")
        break

В примере мы запускаем бесконечный цикл, в котором просим пользователя ввести пароль и сравниваем результат с верным паролем. В случае если введённый пароль совпал с верным, то выводим фразу «Пароль принимается» и останавливаем цикл.

Так как операторов break в цикле может быть много, то анализировать такие программы становится тяжело из-за того, что сложно сказать, где будет точка выхода из цикла для разных входных данных. Поэтому использованием данного оператора лучше не злоупотреблять.

При использовании оператора break во вложенных циклах он останавливает только тот цикл, в котором непосредственно вызывается.Дополним первый пример с генерацией двухбуквенных строк условием: сгенерировать последовательно двухбуквенные строки по алфавиту до строки «ya».

flag = False
for i in range(26):
    for j in range(26):
        text = f"{chr(ord('a') + i)}{chr(ord('a') + j)}"
        if text == "ya":
            print(text)
            flag = True
            break
        print(text)
    if flag:
        break

В программе по-прежнему используются вложенные циклы. Генерация и вывод происходят без изменений. Обратите внимание на переменную-флаг flag логического (булева) типа. Она нужна для сигнала, что программа дошла до заданной комбинации букв и требуется остановить внешний цикл, так как break во внутреннем цикле остановит только внутренний цикл.

Обычно флаг устанавливают в начальное значение False (флаг опущен), а при выполнении какого-то условия в программе флаг устанавливают в значение True (флаг поднят). При генерации комбинации «ya» происходит вывод этой комбинации, «поднятие» флага и остановка внутреннего цикла. После завершения внутреннего цикла происходит проверка состояния флага, и если флаг поднят, то останавливается и внешний цикл.

В циклах for и while можно останавливать текущую итерацию и переходить к следующей с помощью оператора continue. При использовании вложенных циклов оператор continue действует только на тот цикл, в котором непосредственно находится.

Перепишем программу из первого примера так, чтобы не выводить комбинации с одинаковыми буквами («aa», «bb» и т. д.):

for i in range(26):
    for j in range(26):
        if i == j:
            continue
        print(f"{chr(ord('a') + i)}{chr(ord('a') + j)}")

Как уже было сказано, не стоит злоупотреблять операторами break и continue. На самом деле последнюю программу можно написать проще, без использования оператора continue:

for i in range(26):
    for j in range(26):
        if i != j:
            print(f"{chr(ord('a') + i)}{chr(ord('a') + j)}")

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

while input("Введите строку (СТОП для остановки): ") != "СТОП":
    pass
else:
    print("Цикл завершён")

После завершения цикла сработает оператор else, и код внутри него выведет строку «Цикл завершён».

Оператор break влияет на поведение оператора else в циклах. Если в цикле сработал оператор break, то цикл сразу завершается, а код в операторе else выполняться не будет. Перепишем предыдущий пример, добавив проверку: если введённое значение равно "ignore_else", то остановим цикл с помощью break:

while (text := input("Введите строку (СТОП для остановки): ")) != "СТОП":
    if text == "ignore_else":
        break
else:
    print("Цикл завершён")

Когда пользователь введёт «СТОП», цикл попадёт в блок else, и в терминале появится строка «Цикл завершён». А при вводе "ignore_else" сработает оператор break, и цикл завершится, не выполняя код в else.

Отмечайте параграфы как прочитанные чтобы видеть свой прогресс обучения

Вступайте в сообщество хендбука

Здесь можно найти единомышленников, экспертов и просто интересных собеседников. А ещё — получить помощь или поделиться знаниями.
Вступить
Сообщить об ошибке
Предыдущий параграф2.3. Циклы

В этом параграфе мы рассмотрим синтаксис, назначение и примеры использования циклов while и for.

Следующий параграф3.1. Строки, кортежи, списки

Рассмотрим упорядоченные коллекции, индексацию, методы и функции для их обработки.