Python
2024-06-14 20:09

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

В прошлом параграфе мы рассмотрели циклы 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)}")