Marat Sadykov
41 уровень

Оператор while

Пост из группы Java Developer
1709 участников

Введение

Наши самые первые программы представляли собой последовательность инструкций, которые выполняются одна за другой. Без развилок. Это и HelloWorld, выводящий в консоль фразу приветствия, арифметические вычисления.
После первых программ мы научились ветвлению, то есть программа выполняла те или иные действия в зависимости от условий. Вот как можно было бы закодировать включение кондиционера на обогрев и охлаждение:
if (tempRoom>tempComfort)
    airConditionerOn();
if (tempRoom<tempComfort
    heaterOn();
Сделаем следующий шаг. В быту мы часто выполняем однообразные повторяющиеся действия, например, чистим яблоки для пирога. Этот увлекательный процесс можно описать как:
  1. Если в тазике есть яблоки, то выполняем шаги с 1.1 по 1.4:

    1. 1.1. Берём яблоко
    2. 1.2. Чистим и нарезаем его дольками
    3. 1.3. Помещаем на основание пирога из теста на сковороде
    4. 1.4. Возвращаемся на шаг 1.
Допустим у вас 10 яблок, 2 руки и один нож. В жизни вы последовательно чистите весь десяток, руководствуясь одним и тем же алгоритмом. А как заставить программу делать повторяющееся действие с каждым яблоком?
  • Привяжем себя к числу яблок,но если у нас их мало – часть команд выполнялось бы вхолостую без «полезной нагрузки» (и, возможно, порезались, очищая кожуру несуществующего яблока).
  • Если яблок больше, чем наших команд обработки – часть продуктов пропала бы необработанной.
  • Подобный «код» тяжело читать, в нём много повторов, его трудно модифицировать.

Циклы – операторы многократного выполнения действий

Цикл while Java (цикл вайл) хорошо подойдёт в нашем случае. Эта конструкция оформляет многократные действия в лаконичную и понятную структуру. Алгоритм по нарезке яблок для пирога в виде while в Java мог бы выглядеть как:
while(числоЯблокВТазике>0) {
    яблоко = тазик.взятьОчередноеЯблоко();
    положитьВПирог(яблоко.чистить().нарезать());
    числоЯблокВТазике--;//-- это декремент, уменьшает количество яблок на единицу
}
System.out.println('Яблоки для пирога обработаны.');

Синтаксис команды

Первый способ описания оператора while следующий:
while(Логическое выражение) {
	// Тело цикла - периодически выполняемые оператор(ы)
}
Выполняется следующим образом (по шагам):
  1. Вычисляем Логическое условие, следующее в скобках за while.
  2. Если логическое условие истинно, то выполняются операторы в теле цикла, после выполнения последнего оператора в теле цикла, переходим на шаг 1
  3. Если логическое условие ложно, то переходим к первому оператору за пределами цикла while.

Цикл с предусловием

Так как перед выполнением тела цикла мы всегда предварительно вычисляем логическое выражение (условие входа в цикл), то этот вид while часто называют циклом с предусловием. Построим таблицу первых десяти целых, положительных степеней числа:
public static void main(String[] args) {
    int number = 3; // Возводимое в степень число
    int result = 1; // Результат возведения в степень
    int power = 1; // Начальный показатель степени
    while(power <= 10) { // условие входа в цикл
        result = result * number;
        System.out.println(number + " в степени " + power + " = " + result);
        power++;
    }
}
Результат вывода на консоль:
3 в степени 1 = 3
3 в степени 2 = 9
3 в степени 3 = 27
3 в степени 4 = 81
3 в степени 5 = 243
3 в степени 6 = 729
3 в степени 7 = 2187
3 в степени 8 = 6561
3 в степени 9 = 19683
3 в степени 10 = 59049
Process finished with exit code 0

Цикл с постусловием

Второй вид цикла:
do {
    // Тело цикла - периодически выполняемые оператор(ы)
}while (Логическое выражение);
Выполняется следующим образом (шаги):
  1. Выполняется тело цикла (сразу после ключевого слова do).
  2. Вычисляем Логическое условие, следующее в скобках за while.
  3. Если логическое условие истинно, то переходим на шаг 1
  4. Если логическое условие ложно, то переходим к первому оператору за пределами цикла while.
Два основных отличия от предыдущего вида цикла: тело цикла как минимум выполняется один раз и логическое условие проверяется после выполнения тела цикла. Поэтому этот вид цикла while называют циклом с постусловием. На этот раз выведем таблицу степеней числа, не превышающую 10000:
public static void main(String[] args) {
    int number = 3;// Возводимое в степень число
    int result = number;// Результат возведения в степень
    int power = 1;// Начальный показатель степени
    do {
        System.out.println(number + " в степени " + power + " = " + result);
        power++;
        result = result * number;
    }while (result < 10000); // условие выхода из цикла
Результат вывода на консоль:
3 в степени 1 = 3
3 в степени 2 = 9
3 в степени 3 = 27
3 в степени 4 = 81
3 в степени 5 = 243
3 в степени 6 = 729
3 в степени 7 = 2187
3 в степени 8 = 6561
Process finished with exit code 0
Обратите внимания на изменения в коде, сравнив с вариантом цикла с предусловием.

Интересные факты о работе с циклами

Управляющие команды в теле цикла

Существуют две команды, влияющие на ход выполнения цикла: break, особенности применения которого мы покажем в следующей главе, и continue.
  • continue – прекращает выполнение тела текущего цикла и осуществляет переход к логическому выражению оператора while. Если вычисленное выражение будет истинно – выполнение цикла будет продолжено.
  • break – немедленно прекращает выполнение текущего цикла и осуществляет переход к первой команде за его пределами. Таким образом, выполнение текущего цикла прерывается. Подробнее мы рассмотрим её в следующей теме.
Вспомним про наш фруктовый пример. Если мы не уверены в качестве предложенных яблок, то могли бы изменить код с применением команды continue:
while(числоЯблокВТазике>0) {
    яблоко = тазик.взятьОчередноеЯблоко();
    числоЯблокВТазике--;//-- это декремент, уменьшает количество яблок на единицу
    if (яблоко.плохое()) {  // метод вернет true для гнилого и т.п. яблока
        яблоко.выкинутьВМусор();
        continue; // продолжим цикл, перейдем к условию числоЯблокВТазике>0
    }
    положитьВПирог(яблоко.чистить().нарезать());
}
Конструкцию continue часто применяют, когда в теле цикла необходимо выполнять команды при наступлении некоторого условия, например, выполнить действия при срабатывании датчика в оборудовании (а иначе просто продолжить цикл считывания его показателей) или вычислить выражение только на определенных шагах цикла. Пример для последнего случая – вычисление в цикле while суммы кубов натуральных чисел, у которых квадрат меньше их количества:
public static void main(String[] args) {
    int sum = 0;    // итоговая сумма
    int i = 0;      // стартовое число ряда
    int count = 20; // количество чисел
    while(i<=count) {
        i++;        // берем очередное число, i++ эквивалентно i=i+1
        if (i*i=<count)  // если квадрат числа меньше
            continue;   // количества чисел - сумму не считаем
                        // переходим к следующему числу в цикле
        sum += i*i*i; // иначе считаем сумму кубов чисел
    } // sum += i*i*i - форма записи, аналогичная sum = sum + i*i*i
    System.out.println(sum);// печатаем результат
}

Бесконечный цикл

Данные управляющие команды чаще всего находят применение в бесконечном цикле. Его так называют, потому что логическое условие выхода никогда не выполняется. В коде он выглядит примерно как:
while(true) {
    // Тело цикла
}
В этом случае и пригодится применение команды break для организации выхода из него. Этот вид цикла имеет место при ожидании внешних условий, которые формируются за пределами логики тела цикла. Например, в играх, эмулирующих виртуальный мир вокруг героя (выход из цикла = выход из игры), операционных системах. Или при использовании алгоритмов, возможно, улучшающих результат с каждым последующим вызовом в цикле, но ограничивая их по времени или наступлению внешнего события (шашки, шахматы или предсказание погоды). Следует помнить, что в обычных условиях бесконечные циклы – одна из проблем неустойчивости программы. Для демонстрации вернёмся к степеням числа:
public static void main(String[] args) {
    int number = 3; // Возводимое в степень число
    int result = 1; // Результат возведения в степень
    int power = 1; // Начальный показатель степени
    while(true) {
        result = result * number;
        System.out.println(number + " в степени " + power + " = " + result);
        power++;
        if (power>10)
            break; // выход из цикла
    }
}
Результат вывода на консоль:
3 в степени 1 = 3
3 в степени 2 = 9
3 в степени 3 = 27
3 в степени 4 = 81
3 в степени 5 = 243
3 в степени 6 = 729
3 в степени 7 = 2187
3 в степени 8 = 6561
3 в степени 9 = 19683
3 в степени 10 = 59049
Process finished with exit code 0

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

Вот мы и подошли к завершающей теме о наших циклах. Вспомним о яблочном пироге (надеюсь, вы не голодны в данный момент) и наш «цикл»:
  1. Если в тазике есть яблоки, выполняем шаги с 1.1 по 1.4:

    1. 1.1. Берем яблоко
    2. 1.2. Чистим и нарезаем его дольками
    3. 1.3. Помещаем на основание пирога из теста на сковороде
    4. 1.4. Возвращаемся на шаг 1.
Подробнее распишем процесс нарезания дольками:
  1. Число долек = 0
  2. Пока число долек < 12, выполнить шаги с 2.1 по 2.3

    1. 2.1. Отрезать очередную дольку от яблока
    2. 2.2. Кол-во долек ++
    3. 2.3. Возвращаемся на шаг 2
И вставим в наш кондитерский алгоритм:
  1. Если в тазике есть яблоки, то выполняем шаги с 1.1 по 1.6:

    1. 1.1. Берем яблоко
    2. 1.2. Очищаем его от кожуры
    3. 1.3. Число долек = 0
    4. 1.4. Пока число долек < 12, выполнить шаги с 1.4.1 по 1.4.3
      1. 1.4.1. Отрезать очередную дольку от яблока
      2. 1.4.2. Кол-во долек ++
      3. 1.4.3. Возвращаемся на шаг 1.4
    5. 1.5. Помещаем дольки на тестовое основание пирога из теста на сковороде
    6. 1.6. Возвращаемся на шаг 1.
Получили цикл в цикле. Подобные конструкции весьма частые. Для завершающего примера построим таблицу умножения, которые школьники 90-х видели на обложках тетрадей в младших классах.
public static void main(String[] args) {
    // Выводим значения второго множителя в строке
    System.out.println("    2  3  4  5  6  7  8  9");
    int i = 2;      // первый множитель, присваиваем стартовое значение
    while(i<10) {   // Первый цикл, выполняем пока первый множитель меньше 10
        System.out.print(i + " | ");// Выводим первый множитель в начало строки
        int j = 2;                  // второй множитель, стартовое значение
        while (j<10) { // Второй цикл, выполняем пока второй множитель меньше 10
            int mul=i*j; // Считаем произведение множителей
            if (mul<10)  // Если содержит одну цифру-после него выводим два пробела
                System.out.print(mul + "  ");
            else   // иначе выводим произведение и после него - один пробел
                System.out.print(mul + " ");
            j++;     // Увеличиваем на единицу второй множитель,
        }            // Переходим к началу второго цикла (while (j<10 ).... )
        System.out.println(); // Перейдем на следующую строку вывода
        i++;                  // Увеличиваем на единицу первый множитель,
    }                         // Перейдем к началу первого цикла (while ( i<10 ) ....
}
Результат вывода на консоль:      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 5 | 10 15 20 25 30 35 40 45 6 | 12 18 24 30 36 42 48 54 7 | 14 21 28 35 42 49 56 63 8 | 16 24 32 40 48 56 64 72 9 | 18 27 36 45 54 63 72 81 Process finished with exit code 0 Циклы (в частности, оператор while) – один из фундаментальных кирпичиков построения программ. Решая задачи на JavaRush, вы изучите все их разнообразие, разберёте тонкости функционирования и получите практические навыки их применения.