Глава 4б — Другие формы цикла for


Область видимости, несколько переменных, continue и обратный отсчёт

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

Переменная цикла после завершения

Если объявить i внутри заголовка for, обратиться к ней после цикла нельзя. Если значение нужно дальше, объявите переменную перед циклом:

int i;
for (i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}
Console.WriteLine("Индекс после цикла: " + i);

Что выведет программа?

Ответ
0
1
2
3
4
Индекс после цикла: 5

После последнего выполнения тела i++ увеличивает значение до 5. Затем проверка i < 5 возвращает false.

Область видимости определяет, где можно обратиться к переменной по имени. Локальная переменная, объявленная внутри блока { ... }, недоступна за его пределами. У переменной, объявленной в заголовке for, область видимости ограничена этим циклом. Объявляйте переменную перед циклом, только если она нужна и после него.

Несколько переменных в одном заголовке

Можно объявить и инициализировать несколько переменных одного типа, а затем обновлять их через запятую:

for (int i = 0, j = 10; i < j; i++, j--)
{
    Console.WriteLine($"i = {i}, j = {j}");
}

На каждом повторении i увеличивается, а j уменьшается. Последняя выведенная пара — i = 4, j = 6. После обновления оба значения равны 5, поэтому i < j становится ложным.

Запись $"i = {i}, j = {j}" называется интерполяцией строк: C# подставляет значения выражений из фигурных скобок. Это удобная альтернатива соединению строк оператором +.

Составное условие и несколько обновлений

В средней части по-прежнему одно логическое выражение, но его можно составить с помощью && или ||:

int a = 0, b = 10;
for (; a < 5 && b > 5; a++, b -= 2)
{
    Console.WriteLine($"a={a}, b={b}");
}

Здесь обе части условия должны быть истинны. Получатся пары 0, 10, 1, 8 и 2, 6. После следующего обновления b станет равным 4, и цикл завершится.

Пропуск инициализации

Если переменная уже получила начальное значение, первую часть заголовка можно оставить пустой. Точку с запятой сохраняем:

int i = 3;
for (; i < 6; i++)
{
    Console.WriteLine(i);
}

Программа выведет 3, 4, 5.

Обновление внутри тела

Третью часть заголовка тоже можно оставить пустой и перенести обновление в тело:

for (int i = 0; i < 3;)
{
    Console.WriteLine(i);
    i++; // Обновление находится внутри тела.
}

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

Изменение i для повторного ввода

Рассмотрим программу, которая принимает три положительных числа. При вводе нуля или отрицательного числа она просит повторить ввод:

for (int i = 0; i < 3; i++)
{
    Console.Write("Введите положительное число: ");
    int n = int.Parse(Console.ReadLine());
    if (n <= 0)
    {
        Console.WriteLine("Число не подходит. Попробуйте ещё раз.");
        i--; // Компенсируем предстоящее i++ в заголовке.
    }
}

Если n <= 0, уменьшение i внутри тела и увеличение в заголовке взаимно компенсируются. Поэтому неудачный ввод не засчитывается. Программа предполагает ввод целых чисел; буквенный ввод требует отдельной проверки.

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

continue: пропуск оставшейся части тела

continue пропускает оставшиеся команды текущего повторения. В цикле for после этого выполняется обновление из заголовка, затем снова проверяется условие:

for (int i = 0; i < 5; i++)
{
    if (i % 2 == 0)
        continue; // Пропускаем вывод чётного числа.

    Console.WriteLine(i);
}

Будут выведены 1 и 3. Для этой задачи можно сразу перебирать только нечётные числа:

for (int i = 1; i < 5; i += 2)
{
    Console.WriteLine(i);
}

Вторая версия прямо показывает, какие значения нужны. Используйте continue, когда пропуск оставшейся части тела действительно помогает понять решение.

Обратный отсчёт

Для движения от большего значения к меньшему используйте i-- и подходящее условие:

for (int i = 3 - 1; i >= 0; i--)
{
    Console.WriteLine(i);
}

В Visual Studio шаблон обратного цикла можно вставить сокращением forr, затем дважды нажать Tab.

Сколько раз выполнится тело? Какие числа появятся на экране?

Ответ

Три раза:

2
1
0

После вывода 0 значение уменьшается до -1, и условие i >= 0 становится ложным.

Как читать разные формы for

Форма записи Значение и что проверить
Несколько переменных через запятую Они объявляются с одним типом. Проследите изменение каждой переменной.
for (; i < 10;) Инициализация и обновление отсутствуют в заголовке. Проверьте, где i получает и меняет значение.
for (int i = 0;; i++) Условие отсутствует и считается истинным. Без выхода из тела цикл сам не остановится.
i-- внутри тела Изменяет обычный ход счётчика. Учитывайте и это изменение, и обновление в заголовке.
continue Пропускает остаток тела, затем выполняются обновление и проверка условия.
for (int i = 2; i >= 0; i--) Обратный отсчёт от 2 до 0 включительно.

Допустимый синтаксис ещё не гарантирует понятный код. Для обычного повторения действий используйте привычную форму с начальным значением, условием и обновлением в заголовке. Читателю должно быть легко увидеть, какие значения перебираются и почему цикл завершится.

Дальше