Статья Автор: Александр Ф. Алейников

5.4 Вложенные циклы

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

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

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

for i in range(3):
    for j in range(4):
        print('i =', i, 'j =', j)

При запуске программы выполняется первая строка for i in range(3). В данный момент i = 0, и со следующей строки начинает выполняться тело этого цикла. В прошлых уроках мы узнали, что переход на следующую итерацию будет осуществлён, когда полностью выполнятся все инструкции тела цикла. В данном случае в теле цикла находится другой цикл. То есть пока не завершится вложенный цикл for j in range(4), переход на следующую итерацию внешнего цикла for i in range(3) осуществлён не будет. Таким образом при i = 0 переменная j примет значения 0,1,2,3 по очереди, при этом будет выполнена соответствующая функция print(). Далее будет осуществлён переход на новую итерацию внешнего цикла, переменная i станет равна единице, снова запустится цикл for j in range(4) и т.д. В конечном итоге на экран будет выведен следующий текст:

i = 0 j = 0
i = 0 j = 1
i = 0 j = 2
i = 0 j = 3
i = 1 j = 0
i = 1 j = 1
i = 1 j = 2
i = 1 j = 3
i = 2 j = 0
i = 2 j = 1
i = 2 j = 2
i = 2 j = 3

— Обратите внимание на то, что в случае вложенных циклов мы используем разные переменные цикла.

— Выполнение всех итераций вложенного цикла — всего лишь одна итерация цикла, который находится на уровень выше.

 

На этом сайте вы можете выполнить приведённый выше код пошагово, чтобы лучше понять, в каком состоянии находится программа в каждый момент времени. По кнопкам Next и Prev осуществляется переход на шаг вперёд и назад соответственно. Зелёная и красная стрелки указывают на только что выполнившуюся и следующую инструкции, а в окне справа можно увидеть то, что будет выведено на экран к текущему шагу.

Рассмотрим чуть более осмысленный пример. Предположим, мы являемся товароведами в местном магазине «Шестёрочка». В конце дня мы с коллегой Галиной проводим ревизию остатков товара на складе. Галя нам говорит сначала названия отделов, затем перечисляет продукцию в каждом из них в формате «наименование товара — количество оставшейся продукции в у.е. (условных единицах)».

Наша задача — вывести количество оставшейся продукции для каждого отдела в формате «отдел — остаток товара». Притом заведомо известно, что всего отделов 4, в каждом из них по 3 товара.

Напишем программу для решения данной задачи:

n = 4
k = 3
for i in range(n):
    name = input()  # считываем название отдела
    sum_balance = 0  # остаток товара для очередного отдела
    for j in range(k):
        product = input()  # считываем название товара
        balance = int(input())  # считываем остаток товара
        sum_balance += balance  # увеличиваем остаток товаров отдела
    print(name, '-', sum_balance)  # выводим остаток для текущего отдела

В данной программе в теле цикла for i in range(n) мы считываем название отдела, при этом указываем, что остаток товара пока что равен нулю. Для этого отдела мы запускаем цикл для пересчёта остатка товара (сначала даётся наименование, затем количество. Поэтому два раза используется функция input()). В переменную sum_balance мы прибавляем остаток каждого товара в данном отделе. Когда цикл с переменной j полностью выполнится, это означает, что мы посчитали все товары конкретного отдела, и можем выводить данные на экран. После выполнения функции print() осуществляется переход на следующую итерацию цикла с переменной i, снова вводится название отдела и (важно) снова задаётся sum_balance = 0, потому что в новом отделе суммарный остаток пока что нулевой. Далее программа выполняется до своего логического завершения и постепенно выводятся остатки для каждого отдела в требуемом формате.

Обратите внимание на то, что переменная product никак не используется в выводе текста на экран, но она необходима, чтобы удовлетворить условия считывания данных.

Если есть хоть какие-то сомнения по работе вышеприведённого кода, то настоятельно рекомендуем скопировать его в свою среду разработки и «поиграться» со входными данными. Также можно воспользоваться визуализатором кода.

Рассмотрим пример, использующий большую вложенность. Представим, что после долгого изучения языка Python, вы отправились в десятичасовой перелёт в очень жаркие страны. Но из-за джетлага (синдрома смены часового пояса) вы забыли код от чемодана, в котором у вас все вещи (а главное — купальник и плавки).

Смоделируем подбор четырёхзначного шифра при помощи вложенных циклов:

for i in range(10):
    for j in range(10):
        for k in range(10):
            for m in range(10):
                print(i, j, k, m)

При выполнении первой строки фиксируется i = 0 и начинает выполняться тело цикла. В нём запускается новый цикл, фиксируется j = 0. В нём начинается цикл с k = 0 и наконец в нём запускается цикл, на первой итерации которого m = 0. Таким образом мы дошли до строки print(i, j, k, m) и вывели на экран 0 0 0 0. После вывода первого числа заканчивается первая итерация цикла for m in range(10). Теперь осуществляется переход на вторую итерацию, то есть m становится единицей (заметьте, что остальные переменные всё ещё равны нулю). По итогу вторым напечатанным числом станет 0 0 0 1. Затем выведутся коды 0 0 0 2, 0 0 0 3 и т.д. На последней итерации четвёртого цикла выведется 0 0 0 9. На данном этапе последний цикл завершается, то есть заканчивает выполняться блок команд, который находится внутри цикла for k in range(10). Следовательно, осуществляется переход на следующую итерацию этого цикла: k становится единицей и начинает выполняться тело этого цикла. В нём запускается цикл for m in range(10), который снова начинает выводить значения на экран: 0 0 1 0, 0 0 1 1, 0 0 1 2 ... 0 0 1 9. Завершается четвёртый цикл, k меняется на двойку и уже понятно, что будет дальше. В какой-то момент будет выведено 0 0 9 9. И только тогда завершается первая итерация цикла for j in range(10). Далее j становится единицей и всё начинается «с начала» для циклов с переменными k и m.

Таким образом данный код выведет на экран

0 0 0 0
0 0 0 1
0 0 0 2
0 0 0 3
0 0 0 4
0 0 0 5
0 0 0 6
0 0 0 7
0 0 0 8
0 0 0 9
0 0 1 0
0 0 1 1
.......
9 9 9 9

Рассмотрим пример построения части таблицы умножения с использованием вложенных циклов.

Этот код:

for i in range(10):
    for j in range(7):
        print(j, '*', i, '=', j * i, end='    ')  # заменяем перенос строки на несколько пробелов
    print()  # добавляем перенос строки в конце очередной итерации внешнего цикла

выводит

0 * 0 = 0    1 * 0 = 0    2 * 0 = 0    3 * 0 = 0    4 * 0 = 0    5 * 0 = 0    6 * 0 = 0    
0 * 1 = 0    1 * 1 = 1    2 * 1 = 2    3 * 1 = 3    4 * 1 = 4    5 * 1 = 5    6 * 1 = 6    
0 * 2 = 0    1 * 2 = 2    2 * 2 = 4    3 * 2 = 6    4 * 2 = 8    5 * 2 = 10    6 * 2 = 12    
0 * 3 = 0    1 * 3 = 3    2 * 3 = 6    3 * 3 = 9    4 * 3 = 12    5 * 3 = 15    6 * 3 = 18    
0 * 4 = 0    1 * 4 = 4    2 * 4 = 8    3 * 4 = 12    4 * 4 = 16    5 * 4 = 20    6 * 4 = 24    
0 * 5 = 0    1 * 5 = 5    2 * 5 = 10    3 * 5 = 15    4 * 5 = 20    5 * 5 = 25    6 * 5 = 30    
0 * 6 = 0    1 * 6 = 6    2 * 6 = 12    3 * 6 = 18    4 * 6 = 24    5 * 6 = 30    6 * 6 = 36    
0 * 7 = 0    1 * 7 = 7    2 * 7 = 14    3 * 7 = 21    4 * 7 = 28    5 * 7 = 35    6 * 7 = 42    
0 * 8 = 0    1 * 8 = 8    2 * 8 = 16    3 * 8 = 24    4 * 8 = 32    5 * 8 = 40    6 * 8 = 48    
0 * 9 = 0    1 * 9 = 9    2 * 9 = 18    3 * 9 = 27    4 * 9 = 36    5 * 9 = 45    6 * 9 = 54   

Не обращайте внимание на неровное построение таблицы. На данном этапе это не так важно. 

Оператор break

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

Рассмотрим пример:

for i in range(4):
    for j in range(20):
        print('i =', i, 'j =', j)
        if j > 2:
            break

При запуске данной программы получим

i = 0 j = 0
i = 0 j = 1
i = 0 j = 2
i = 0 j = 3
i = 1 j = 0
i = 1 j = 1
i = 1 j = 2
i = 1 j = 3
i = 2 j = 0
i = 2 j = 1
i = 2 j = 2
i = 2 j = 3
i = 3 j = 0
i = 3 j = 1
i = 3 j = 2
i = 3 j = 3

То есть при достижении j = 3 сначала выполнится инструкция print(), т.к. она находится до проверки условия, а далее условие станет истинным, break прервёт ближайший цикл (цикл с переменной j) и мы «досрочно» перейдём на следующую итерацию цикла с переменной i.

Печать