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