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: виведення шахової дошки (8×8)
Перше завдання — вивести на екран класичну шахову дошку у вигляді чорних і білих клітинок (нехай # — чорна, _ — біла). Зробімо це за допомогою вкладених циклів 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();
}
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ