1. Введение
Начнем с вопроса: для чего вообще нужно вкладывать один цикл внутрь другого? Дело в том, что часто наши данные или задачи организованы не просто в одну линию, а, например, в таблицу, сетку или даже в многомерную структуру. Допустим, вы хотите вывести на экран таблицу умножения, пройтись по двумерному массиву или пересчитать пересечения между всеми парами элементов. Здесь вам одного цикла явно не хватит — нужен цикл в цикле.
В программировании вложенный цикл — это как два будильника: наружный начинает звонить, а внутри него запускается еще один, который будет звенеть каждый раз, пока первый активен. Так, пока идет одна "внешняя" итерация, внутренняя проходит весь свой путь (и делает это снова и снова для каждой итерации внешнего).
Хороший пример — часы и минуты. Часы — это внешний цикл от 0 до 23, минуты — внутренний цикл от 0 до 59. На каждое изменение внешнего цикла внутренний успевает пробежать все свои значения.
2. Синтаксис вложенных циклов
В C# синтаксис вложенных циклов ничем не отличается от обычных циклов — вы просто пишете один цикл внутри тела другого. Посмотрим на примеры с for и while:
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
Console.Write($"{i},{j} ");
}
Console.WriteLine();
}
Здесь внешний цикл контролирует переменную i (от 0 до 2), а внутренний — переменную j (от 0 до 3). Для каждого значения i внутренний цикл полностью проходит от j=0 до j=3. Если вы выполните этот код, увидите приятную табличку координат:
0,0 0,1 0,2 0,3
1,0 1,1 1,2 1,3
2,0 2,1 2,2 2,3
Аналогичный пример с использованием while:
int i = 0;
while (i < 3)
{
int j = 0;
while (j < 4)
{
Console.Write($"{i},{j} ");
j++;
}
Console.WriteLine();
i++;
}
Обратите внимание: внутри каждого прохода внешнего цикла переменная внутреннего цикла (j) должна быть заново инициализирована, иначе мы увидим только одну строку!
3. Примеры работы вложенных циклов
Пример 1: вывод шахматной доски (8x8)
Пусть наша первая задача — вывести на экран классическую шахматную доску в виде черных и белых клеток (пусть # — черная, _ — белая). Реализуем с помощью вложенных циклов for:
for (int row = 0; row < 8; row++)
{
for (int col = 0; col < 8; col++)
{
if ((row + col) % 2 == 0)
Console.Write("_");
else
Console.Write("#");
}
Console.WriteLine();
}
Результат:
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
_#_#_#_#
#_#_#_#_
Важный момент: вложенность обеспечивает то, что для каждой строки (row) мы полностью проходим по всем столбцам (col). Без вложенности мы не получили бы структуру доски — только одну строку или одну колонку.
Пример 2: таблица умножения
Один из классических кандидатов на вложенные циклы! Давайте выведем таблицу умножения 1-9:
for (int i = 1; i <= 9; i++)
{
for (int j = 1; j <= 9; j++)
{
Console.Write($"{i * j,3} ");
}
Console.WriteLine();
}
Форматирование ${i * j,3} добавляет отступы, чтобы таблица выглядела аккуратно.
Результат:
1 2 3 4 5 6 7 8 9
2 4 6 8 10 12 14 16 18
3 6 9 12 15 18 21 24 27
4 8 12 16 20 24 28 32 36
...
9 18 27 36 45 54 63 72 81
4. Вложенные циклы и управление ими — нюансы
О влиянии break и continue во вложенных циклах
Вот тут многие новички попадаются! Если вы используете break или continue во внутреннем цикле, они влияют только на этот цикл. Внешний продолжает свою работу как ни в чем не бывало.
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
if (j == 3)
break; // выскакиваем только из внутреннего цикла!
Console.Write($"{i},{j} ");
}
Console.WriteLine();
}
Результат:
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
Если вам нужно выйти сразу из двух вложенных циклов (например, срочно завершить поиск при первом успешном совпадении в таблице), обычно используют флаг или специальный прием (например, return — если всё происходит внутри функции).
5. Визуализация вложенных циклов
Иногда бывает сложно "увидеть" последовательность исполнения вложенных циклов. Взглянем на следующую блок-схему:
В табличной форме — сколько всего итераций будет при i от 1 до 3, j от 1 до 4?
| i | j (перебирается для каждого i) | Итераций внутреннего цикла |
|---|---|---|
| 1 | 1, 2, 3, 4 | 4 |
| 2 | 1, 2, 3, 4 | 4 |
| 3 | 1, 2, 3, 4 | 4 |
| Всего: 3 × 4 = 12 |
6. Ошибки и подводные камни при работе с вложенными циклами
Распространенная ошибка — неверно инициализировать переменную внутреннего цикла. Например, объявить её вне внешнего цикла, но не сбрасывать на каждом шаге. В итоге внутренний цикл может не выполняться вовсе, или выполняться неправильно.
int j = 0;
for (int i = 0; i < 3; i++)
{
while (j < 4) // Ой! j уже мог быть равен 4 после первой итерации.
{
Console.Write($"{i},{j} ");
j++;
}
Console.WriteLine();
}
Здесь цикл отработает только один раз. Не забывайте инициализировать переменные внутренних циклов внутри внешних!
Также, если случайно написать две вложенных цикла с одинаковыми переменными, компилятор вас поругает: переменная уже определена.
for (int i = 0; i < 3; i++)
{
for (int i = 0; i < 3; i++)
{
Console.Write($"{i},{i} ");
}
Console.WriteLine();
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ