While это инструкция цикла с предусловием

В этом параграфе мы познакомимся с операторами ветвления if и switch, циклами while, do-while и for, а также с оператором goto.

Оператор if

Условный оператор if записывается так:

if (condition) {
    // код, который исполнится в случае, когда условие condition истинно
}

Дополнительно можно добавить ветку кода для случая, когда условие ложно:

if (condition) {
    // код, который исполнится, если condition истинно
} else {
    // код, который исполнится, если condition ложно
}

Также можно выстроить цепочку условных операторов:

if (condition1) {
    // случай, когда condition1 истинно
} else if (condition2) {
    // случай, когда condition1 ложно, а condition2 истинно
} else if (contition3) {
    // случай, когда condition1 и condition2 ложны, а condition3 истинно
} else {
    // случай, когда condition1, condition2 и condition3 ложны
}

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

На месте condition может стоять любое выражение логического типа. Простейшие примеры таких выражений — это проверка на равенство (==) и неравенство (!=), а также сравнения на меньше / больше (<, <=, > и >=):

int main() {
    int x;
    std::cin >> x;
    if (x <= 0) {
        std::cout << "zero or negativen";
    } else if (x == 1) {
        std::cout << "onen";
    } else if (x == 2) {
        std::cout << "twon";
    } else {
        std::cout << "manyn";
    }
}

Сложные условия

Условия можно комбинировать с помощью логических операторов && (И), || (ИЛИ) и ! (НЕ). Рассмотрим пример, где проверяется принадлежность точки разным интервалам на прямой.

int main() {
    int a, b, x;
    /* Тут должна быть логика заполнения объявленных переменных,
    но мы её опустили, чтобы не отвлекаться */

    if (a <= x && x <= b) {
        // точка x лежит на отрезке [a; b]
    } else {
        // точка x лежит вне отрезка [a; b]
    }

    // то же самое можно было бы проверить так:
    if (!(x < a || x > b)) {  // отрицание
        // точка x лежит на отрезке [a; b]
    } else {
        // точка x лежит вне отрезка [a; b]
    }
}

Обратите внимание, что двойное неравенство некорректно проверять через a <= x <= b. Так можно написать, но смысл будет совсем другим: результат сравнения a <= x будет приведён к нулю или единице, и полученное число будет сравниваться с b.

Операторы сравнения имеют больший приоритет, чем логические операторы, поэтому скобки вокруг элементарных сравнений в сложных условиях не обязательны. А вот оператор отрицания имеет высокий приоритет, поэтому в последнем примере он применяется к условию в скобках. Таблицу приоритета операторов C++ можно посмотреть здесь.

Операторы && и || ведут себя лениво: если первого аргумента уже достаточно для ответа, то второй аргумент вычисляться не будет. Например, в выражении condition1 && condition2 второе условие не вычисляется, если первое ложно. Это часто используют для проверок корректности:

    int a, b;
    // ...
    if (a != 0 && b % a == 0) {
        // b делится на a
    }

Сравнение чисел с плавающей точкой

Рассмотрим программу, которая проверяет равенство 0.1 + 0.2 == 0.3:

#include <iostream>

int main() {
    double x = 0.1, y = 0.2;
    if (x + y == 0.3) {
        std::cout << "EQUAL ";
    } else {
        std::cout << "NOT EQUAL ";
    }

    std::cout << x + y << "n";
}

Логично было бы предположить, что программа выведет EQUAL 0.3, потому что $0.1 + 0.2 = 0.3$. Однако программа напечатает NOT EQUAL 0.3. Данная «ошибка вычисления» встречается в большинстве современных языков программирования и обусловлена погрешностью представления этих чисел. Если повысить число знаков дробной части в выводе, мы увидим, что 0.1 + 0.2 == 0.30000000000000004. Подробнее об этом можно прочитать здесь и в этой статье.

Поскольку операции над числами с плавающей точкой могут содержать погрешность, обычное сравнение через == некорректно. Поэтому правильнее сравнивать модуль разности величин с некой допустимой для нас погрешностью. Модуль дробного числа можно получить с помощью функции std::abs из заголовочного файла cmath. Исходную программу можно было бы переписать так:

#include <cmath>
#include <iostream>

int main() {
    double delta = 0.000001;

    double x = 0.1, y = 0.2;
    double sum = x + y;

    if (std::abs(sum - 0.3) < delta) {
        std::cout << "EQUAL ";
    } else {
        std::cout << "NOT EQUAL ";
    }

    std::cout << sum << "n";
}

Теперь программа выведет EQUAL 0.3.

Оператор switch

Рассмотрим простейший калькулятор, считывающий число, затем знак арифметической операции, а затем другое число и печатающий результат. Напишем сначала программу с помощью if и else.

#include <cstdint>
#include <iostream>

int main() {
    int64_t a, b;
    char operation;
    std::cin >> a >> operation >> b;

    int64_t result = 0;
    if (operation == '+') {
        result = a + b;
    } else if (operation == '-') {
        result = a - b;
    } else if (operation == '*') {
        result = a * b;
    } else if (operation == '/' || operation == ':') {
        result = a / b;
    } else if (operation == '%') {  // остаток от деления
        result = a % b;
    }

    std::cout << result << "n";
}

Вопросы для самопроверки

Что будет, если ввести 2 / 3?

  • Так как всё вычисление происходит в целых числах, то напечатается 0. Деление целых положительных чисел в C++ — это всегда неполное частное.

Что будет, если ввести 2 @ 3?

  • Обработку значка @ мы не предусмотрели. Программа напечатает 0, но тут необходимо напомнить, что у локальных переменных типа int нет дефолтных значений, и если бы мы не присвоили изначально result = 0, то программа напечатала бы какое-то неизвестное заранее число, лежащее в ячейках памяти, в которых поселился result.

Перепишем эту программу через оператор switch. Этот оператор следует рассматривать как условный прыжок на соответствующую метку в зависимости от значения выражения.

#include <cstdint>
#include <iostream>

int main() {
    int64_t a, b;
    char operation;
    std::cin >> a >> operation >> b;

    int64_t result;
    switch (operation) {
        case '+':
            result = a + b;
            break;  // если не написать этот break, программа просто пойдёт дальше в код следующего блока case
        case '-':
            result = a - b;
            break;
        case '*':
            result = a * b;
            break;
        case '/':
        case ':':
            result = a / b;
            break;
        case '%':
            result = a % b;
            break;
        default:  // здесь обрабатывается случай, когда ни один case не сработал.
            result = 0;
    }

    std::cout << result << "n";
}

Выражения внутри скобок оператора switch и в блоках case должны быть простого целочисленного или символьного типа. В приведённой выше программе значение + относится к типу char. Использование сложных типов (например, строк) приведёт к ошибке компиляции:

int main() {
    std::string name;
    std::cin >> name;
    switch (name) {  // ошибка компиляции
        case "Alice":
           std::cout << "Hello, Alice!n";
           break;
    }
}

Оператор goto

Есть шутка, что оператор безусловного перехода goto — это ругательное слово из четырёх букв. Безусловные переходы ломают иерархичность программы, затрудняют чтение и отладку. Доказано, что любую программу можно переписать без оператора goto. Поэтому во многих современных языках goto отсутствует.

Однако в C++ этот оператор есть по следующим причинам:

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

Мы познакомимся с его синтаксисом, но дальше использовать нигде не будем — и вам не советуем.

int main() {
    again:  // метка — это произвольное имя с двоеточием

    std::cout << "How old are you?n";
    int age;
    std::cin >> age;

    if (age < 0 || age >= 128) {
        std::cout << "Wrong age...n";
        goto again;  // безусловный прыжок в место, помеченное меткой
    }

    std::cout << "Your age is " << age << ".n";

    // ...
}

Здесь вводится метка again, на которую осуществляется переход, если возраст введён некорректно. Ниже мы покажем, как можно избавиться от оператора goto.

С помощью оператора goto нельзя выйти из функции или зайти в неё, а также нельзя перепрыгнуть через объявления переменных (кроме тривиальных случаев):

#include <iostream>

int main() {
    goto label;
    int x = 42;
    label:  // ошибка компиляции!
    std::cout << x << "n";
}

Цикл while

В C++ существует несколько видов циклов. Цикл while — это цикл с предусловием. Перед очередной итерацией проверяется условие, и если оно истинно, то цикл продолжается. Рассмотрим пример печати таблицы квадратов чисел от 1 до 10:

#include <iostream>

int main() {
    int n = 1;
    while (n <= 10) {
        std::cout << n << "t" << n * n << "n";  // выводим число и его квадрат через табуляцию
        ++n;
    }
}

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

1	1
2	4
3	9
4	16
5	25
6	36
7	49
8	64
9	81
10	100

Цикл do-while

Это цикл с постусловием. Отличие от цикла while заключается в том, что первая итерация всегда выполняется безусловно. Только после её завершения проверяется условие цикла. Если оно истинно, то цикл продолжается.

#include <iostream>

int main() {
    int n = 1;
    do {
        std::cout << n << "t" << n * n << "n";
        ++n;
    } while (n <= 10);
}

Без особых причин пользоваться этим видом циклов не стоит, старайтесь использовать циклы while или for.

Цикл for

Цикл for — самый гибкий. Он записывается так:

for (initialization; condition; action) {
    // тело цикла
}

Как правило, с циклом ассоциируется некоторый параметр, который меняется от итерации к итерации, а цикл выполняется до тех пор, пока некоторое условие на этот параметр истинно.

Начальное значение такого параметра можно задать в разделе initialization, условие — в condition, а действие над параметром, выполняющееся после каждой итерации, — в action.

Напечатаем таблицу квадратов через цикл for:

#include <iostream>

int main() {
    for (int i = 1; i <= 10; ++i) {
        std::cout << i << "t" << i * i << "n";
    }
}

Напомним, что ++i — традиционная краткая форма записи для выражения i = i + 1.

Цикл for эквивалентен такому циклу while:

{
    initialization;
    while (condition) {
        // тело цикла
        action;
    }
}

Цикл range-based for

Этот цикл применим к контейнерам разной природы (массивам, векторам, спискам и т. д.), с которыми мы познакомимся позже. Пока рассмотрим его на примере строк. Цикл позволяет удобно проитерироваться по символам строки, не используя индексов. В этом примере мы считываем строку и печатаем отдельно все символы строки и их ASCII-коды:

#include <iostream>
#include <string>

int main() {
    std::string line;
    std::getline(std::cin, line);
    for (char symbol : line) {
        std::cout << symbol << "t" << static_cast<int>(symbol) << "n";
    }
}

Здесь оператор static_cast преобразует символ к числовому типу int, чтобы получить его код. Результат для строки Hello, world! выглядит так:

H	72
e	101
l	108
l	108
o	111
,	44
 	32
w	119
o	111
r	114
l	108
d	100
!	33

Обратите внимание, что std::string хранит внутри байты. Если вы вводите символы русского алфавита и у вас используется кодировка UTF-8, ставшая де-факто стандартом, то эти символы будут кодироваться парами байтов. И при такой итерации вы увидите отдельные байты, а не символы.

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

Циклы могут быть вложенными. Напечатаем таблицу умножения:

#include <iostream>

int main() {
    for (int i = 1; i <= 10; ++i) {
        for (int j = 1; j <= 10; ++j) {
            std::cout << i * j << "t";
        }
        std::cout << "n";
    }
}

Результат:

1	2	3	4	5	6	7	8	9	10	
2	4	6	8	10	12	14	16	18	20	
3	6	9	12	15	18	21	24	27	30	
4	8	12	16	20	24	28	32	36	40	
5	10	15	20	25	30	35	40	45	50	
6	12	18	24	30	36	42	48	54	60	
7	14	21	28	35	42	49	56	63	70	
8	16	24	32	40	48	56	64	72	80	
9	18	27	36	45	54	63	72	81	90	
10	20	30	40	50	60	70	80	90	100	

Операторы break и continue

Оператор break досрочно заканчивает текущий цикл. Оператор continue прыгает в самый конец тела цикла и, если условие цикла позволяет, переходит на следующую итерацию.

Типичный пример использования оператора break — выход из формально бесконечного цикла:

while (true) {
    // ...
    if (condition) {
        break;
    }
    // ...
}

Кстати, рассмотрим другие способы записать бесконечный цикл.

Через цикл do-while:

do {
    // ...
} while (true);

Через цикл for:

for (;;) {
    // ...
}

Напишем программу, которая считывает числа с клавиатуры до тех пор, пока пользователь не введёт ноль, а затем печатает их сумму:

#include <iostream>

int main() {
    int sum = 0;
    while (true) {
        int x;
        std::cin >> x;
        if (x == 0) {
            break;
        }
        sum += x;
    }
    std::cout << sum << "n";
}

(Здесь, конечно, неявно предполагается, что и сами числа, и результат суммирования помещаются в тип int, и в ходе вычислений не происходит переполнений.)

Считывание до конца ввода

В предыдущем примере мы считывали числа до тех пор, пока на вход не поступит ноль. Это не очень удобно. Предположим, что вместо ввода с клавиатуры мы читали бы эти числа из файла. Это легко сделать, перенаправив файл на стандартный ввод при запуске программы из консоли:

./a.out < input.txt

Следующий цикл считывает числа до тех пор, пока поступающие данные не закончатся:

#include <iostream>

int main() {
    int sum = 0;
    int x;
    while (std::cin >> x) {
        sum += x;
    }
    std::cout << sum << "n";
}

Здесь вместо условия цикла подставлено выражение std::cin >> x. Кроме считывания x это выражение преобразуется к логическому типу, показывающему, есть ли ещё данные в потоке ввода.

При вводе данных не из файла, а с клавиатуры можно сымитировать конец ввода комбинацией клавиш Ctrl+D в Linux и macOS или Ctrl+Z в Windows.

Аналогично можно прочитать строки до конца ввода с помощью std::getline:

#include <iostream>
#include <string>

int main() {
    std::string name;
    while (std::getline(std::cin, name)) {
        std::cout << "Hello, " << name << "!n";
    }
}

У этого термина существуют и другие значения, см. Цикл.

Пример цикла While.

Цикл — разновидность управляющей конструкции в высокоуровневых языках программирования, предназначенная для организации многократного исполнения набора инструкций. Также циклом может называться любая многократно исполняемая последовательность инструкций, организованная любым способом (например, с помощью условного перехода).

Определения[править | править код]

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

Исполнение любого цикла включает первоначальную инициализацию переменных цикла, проверку условия выхода, исполнение тела цикла и обновление переменной цикла на каждой итерации. Кроме того, большинство языков программирования предоставляет средства для досрочного управления циклом, например, операторы завершения цикла, то есть выхода из цикла независимо от истинности условия выхода (в языке Си — break) и операторы пропуска итерации (в языке Си — continue).

Виды циклов[править | править код]

Безусловные циклы[править | править код]

Иногда в программах используются циклы, выход из которых не предусмотрен логикой программы. Такие циклы называются безусловными, или бесконечными. Специальных синтаксических средств для создания бесконечных циклов, ввиду их нетипичности, языки программирования не предусматривают, поэтому такие циклы создаются с помощью конструкций, предназначенных для создания обычных (или условных) циклов. Для обеспечения бесконечного повторения проверка условия в таком цикле либо отсутствует (если позволяет синтаксис, как, например, в цикле LOOP ... END LOOP языка Ада), либо заменяется константным значением (while true do ... в Паскале). В языке С используется цикл for(;;) с незаполненными секциями или цикл while (1).

Цикл с предусловием[править | править код]

Цикл с предусловием — цикл, который выполняется, пока истинно некоторое условие, указанное перед его началом. Это условие проверяется до выполнения тела цикла, поэтому тело может быть не выполнено ни разу (если условие с самого начала ложно). В большинстве процедурных языков программирования реализуется оператором while, отсюда его второе название — while-цикл.
На языке Pascal цикл с предусловием имеет следующий вид:

while <условие> do
begin   
  <тело цикла> 
end;

На языке Си:

while (<условие>) {
   <тело цикла>
}

Цикл с постусловием[править | править код]

Цикл с постусловием — цикл, в котором условие проверяется после выполнения тела цикла. Отсюда следует, что тело всегда выполняется хотя бы один раз. В языке Паскаль этот цикл реализует оператор repeat..until; в Си — do…while.

На языке Pascal цикл с постусловием имеет следующий вид::

repeat
    <тело цикла>
until <условие выхода>

На языке Си:

do {
    <тело цикла>
} while (<условие продолжения цикла>)

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

Цикл с выходом из середины[править | править код]

Цикл с выходом из середины — наиболее общая форма условного цикла. Синтаксически такой цикл оформляется с помощью трёх конструкций: начала цикла, конца цикла и команды выхода из цикла. Конструкция начала маркирует точку программы, в которой начинается тело цикла, конструкция конца — точку, где тело заканчивается. Внутри тела должна присутствовать команда выхода из цикла, при выполнении которой цикл заканчивается и управление передаётся на оператор, следующий за конструкцией конца цикла. Естественно, чтобы цикл выполнился более одного раза, команда выхода должна вызываться не безусловно, а только при выполнении условия выхода из цикла.

Принципиальным отличием такого вида цикла от рассмотренных выше является то, что часть тела цикла, расположенная после начала цикла и до команды выхода, выполняется всегда (даже если условие выхода из цикла истинно при первой итерации), а часть тела цикла, находящаяся после команды выхода, не выполняется при последней итерации.

Легко видеть, что с помощью цикла с выходом из середины можно легко смоделировать и цикл с предусловием (разместив команду выхода в начале тела цикла), и цикл с постусловием (разместив команду выхода в конце тела цикла).

Часть языков программирования содержит специальные конструкции для организации цикла с выходом из середины. Так, в языке Ада для этого используется конструкция LOOP ... END LOOP и команда выхода EXIT или EXIT WHEN:

LOOP
  ... Часть тела цикла
  EXIT WHEN <условие выхода>;
  ... Часть тела цикла
  IF <условие выхода> THEN 
    EXIT; 
  END;
  ... Часть тела цикла
END LOOP:

Здесь внутри цикла может быть любое количество команд выхода обоих типов. Сами команды выхода принципиально не различаются, обычно EXIT WHEN применяют, когда проверяется только условие выхода, а просто EXIT — когда выход из цикла производится в одном из вариантов сложного условного оператора.

В тех языках, где подобных конструкций не предусмотрено, цикл с выходом из середины может быть смоделирован с помощью любого условного цикла и оператора досрочного выхода из цикла (такого, как break в Си, exit в Турбо Паскале и т. п.), либо оператора безусловного перехода goto.

Цикл со счётчиком (или цикл для)[править | править код]

Цикл со счётчиком — цикл, в котором некоторая переменная изменяет своё значение от заданного начального значения до конечного значения с некоторым шагом, и для каждого значения этой переменной тело цикла выполняется один раз. В большинстве процедурных языков программирования реализуется оператором for, в котором указывается счётчик (так называемая «переменная цикла»), требуемое количество проходов (или граничное значение счётчика) и, возможно, шаг, с которым изменяется счётчик. Например, в языке Оберон-2 такой цикл имеет вид:

 FOR v := b TO e BY s DO
   ... тело цикла
 END 

здесь v — счётчик, b — начальное значение счётчика, e — граничное значение счётчика, s — шаг).

Неоднозначен вопрос о значении переменной по завершении цикла, в котором эта переменная использовалась как счётчик. Например, если в программе на языке Паскаль встретится конструкция вида:

i := 100;
for i := 0 to 9 do
begin
  ... тело цикла
end;
k := i;

возникает вопрос: какое значение будет в итоге присвоено переменной k: 9, 10, 100, может быть, какое-то другое? А если цикл завершится досрочно? Ответы зависят от того, увеличивается ли значение счётчика после последней итерации и не изменяет ли транслятор это значение дополнительно. Ещё один вопрос: что будет, если внутри цикла счётчику будет явно присвоено новое значение? Различные языки программирования решают данные вопросы по-разному. В некоторых поведение счётчика чётко регламентировано. В других, например, в том же Паскале, стандарт языка не определяет ни конечного значения счётчика, ни последствий его явного изменения в цикле, но не рекомендует изменять счётчик явно и использовать его по завершении цикла без повторной инициализации. Программа на Паскале, игнорирующая эту рекомендацию, может давать разные результаты при выполнении на разных системах и использовании разных трансляторов.

Радикально решён вопрос в языках Ада и Kotlin: счётчик считается описанным в заголовке цикла, и вне его просто не существует. Даже если имя счётчика в программе уже используется, внутри цикла в качестве счётчика используется отдельная переменная. Счётчику запрещено явно присваивать какие бы то ни было значения, он может меняться только внутренним механизмом оператора цикла.

В результате конструкция на Аде:

i := 100;
for i in (0..9) loop
  ... тело цикла
end loop;
k := i;

И на Котлине:

val i = 100;
for (i in 0..9){
    ... тело цикла
}
val k = i;

внешне аналогичная вышеприведённому циклу на Паскале, трактуется однозначно: переменной k будет присвоено значение 100, поскольку переменная i, используемая вне данного цикла, не имеет никакого отношения к счётчику i, который создаётся и изменяется внутри цикла. Подобное обособление счётчика удобно и безопасно: не требуется отдельное описание для него и минимальна вероятность случайных ошибок, связанных со случайным разрушением внешних по отношению к циклу переменных. Если программисту требуется включить в готовый код цикл со счётчиком, то он может не проверять, существует ли переменная с именем, которое он выбрал в качестве счётчика, не добавлять описание нового счётчика в заголовок соответствующей процедуры, не пытаться использовать один из имеющихся, но в данный момент «свободных» счётчиков. Он просто пишет цикл с переменной-счётчиком, имя которой ему удобно, и может быть уверен, что никакой коллизии имён не произойдёт.

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

Никлаус Вирт одно время называл цикл со счётчиком «маргинальным», утверждая, что такая конструкция является излишней и должна быть исключена из синтаксиса языков программирования как несистемная. В соответствии с этим представлением в языке программирования Оберон цикла со счётчиком не было. Однако в языке Оберон-2, созданном Виртом и Мёссенбёком в развитие Оберона, цикл со счётчиком FOR появился снова в интересах практического удобства использования[1].

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

for (i = 0; i < 10; ++i)
{
  ... тело цикла 
}

фактически представляет собой другую форму записи конструкции[2]:

i = 0;
while (i < 10)
{
  ... тело цикла 
  ++i;
}

То есть в конструкции for сначала пишется произвольное предложение инициализации цикла, затем — условие продолжения и, наконец, выполняемая после каждого тела цикла некоторая операция (это не обязательно должно быть изменение счётчика; это может быть правка указателя или какая-нибудь совершенно посторонняя операция). Для языков такого вида вышеописанная проблема решается очень просто: переменная-счётчик ведёт себя совершенно предсказуемо и по завершении цикла сохраняет своё последнее значение.

Совместный цикл[править | править код]

Ещё одним вариантом цикла является цикл, задающий выполнение некоторой операции для объектов из заданного множества, без явного указания порядка перечисления этих объектов. Такие циклы называются совместными (а также циклами по коллекции, циклами просмотра) и представляют собой формальную запись инструкции вида: «Выполнить операцию X для всех элементов, входящих во множество M». Совместный цикл, теоретически, никак не определяет, в каком порядке операция будет применяться к элементам множества, хотя конкретные языки программирования, разумеется, могут задавать конкретный порядок перебора элементов. Произвольность даёт возможность оптимизации исполнения цикла за счёт организации доступа не в заданном программистом, а в наиболее выгодном порядке. При наличии возможности параллельного выполнения нескольких операций возможно даже распараллеливание выполнения совместного цикла, когда одна и та же операция одновременно выполняется на разных вычислительных модулях для разных объектов, при том, что логически программа остаётся последовательной.

Совместные циклы имеются в некоторых языках программирования (C#, Eiffel, Java, JavaScript, Perl, Python, PHP, LISP, Tcl и др.) — они позволяют выполнять цикл по всем элементам заданной коллекции объектов. В определении такого цикла требуется указать только коллекцию объектов и переменную, которой в теле цикла будет присвоено значение обрабатываемого в данный момент объекта (или ссылка на него). В различных языках программирования синтаксис оператора различен:

C++:

for (type &item : set) //поддерживается, начиная со стандарта C++11
{
    //использование item
}

C#:

foreach (type item in set) 
{
    //использование item
}

Delphi:

for item in [1..100] do
begin
  //Использование item (Работоспособность кода проверялась в Delphi 2010) 
end;

Perl (строгий порядок «от первого до последнего»):

foreach (@set) 
{
    #использование $_
}
# или
for (@set) 
{
    #использование $_
}
# или
foreach $item (@set) 
{
    #использование $item
}

Eiffel:

across set as cursor loop
    -- использование cursor.item
end

Java:

for (type item : set) 
{
    //использование item
}

JavaScript:

for (txtProperty in objObject)
  {
  /*
  использование:
  objObject [txtProperty]
  */
  }

PHP:

foreach ($arr as $item) {
    /* использование $item*/
}
//или
foreach ($arr as $key=>$value) {
    /* использование значений индекса $key и его значения $value*/
}

//или
foreach ($arr as &$item) {
    /*использование $item по ссылке*/
}

Visual Basic.NET:

For Each item As type In set
    'использование item
Next item

Windows PowerShell:

foreach ($item in $set) {
  # операции с $item
} 

или

$set | ForEach-Object {
  # операции с $_
}

Python

for item in iterator_instance:
    # использование item

Ruby

iterator_instance.each do |item|
    # использование item
end

Досрочный выход и пропуск итерации[править | править код]

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

Досрочный выход из цикла[править | править код]

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

Команда досрочного выхода обычно называется EXIT или break, а её действие аналогично действию команды безусловного перехода (goto) на команду, непосредственно следующую за циклом, внутри которого эта команда находится. Так, в языке Си два нижеприведённых цикла работают совершенно одинаково:

// Применение оператора break
while(<условие>) {
  ... операторы
  if (<ошибка>) break;
  ... операторы
}
... продолжение программы

// Аналогичный фрагмент без break
while(<условие>) {
  ... операторы
  if (<ошибка>) goto break_label;
  ... операторы 
}
break_label:
... продолжение программы

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

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

Пропуск итерации[править | править код]

Данный оператор применяется, когда в текущей итерации цикла необходимо пропустить все команды до конца тела цикла. При этом сам цикл прерываться не должен, условия продолжения или выхода должны вычисляться обычным образом.

В языке Си и его языках-потомках в качестве команды пропуска итерации используется оператор continue в конструкции цикла. Действие этого оператора аналогично безусловному переходу на строку внутри тела цикла, следующую за последней его командой. Например, код на Си, находящий сумму элементов массива и сумму всех положительных элементов массива, может иметь следующий вид:

int arr[ARRSIZE];
...
// Суммирование отдельно всех и только положительных
// элементов массива arr с применением continue.
int sum_all = 0;
int sum_pos = 0;
for (int i = 0 ; i < ARRSIZE; ++i)
{
    sum_all += arr[i];
    if (arr[i] <= 0) continue;
    sum_pos += arr[i];
}

// Аналогичный код c goto
int sum_all = 0;
int sum_pos = 0;
for (int i = 0 ; i < ARRSIZE; ++i)
{
    sum_all += arr[i];
    if (arr[i] <= 0) goto cont_label;
    sum_pos += arr[i];
cont_label:
}

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

Необходимость[править | править код]

С точки зрения структурного программирования команды досрочного выхода из цикла и пропуска итерации являются избыточными, поскольку их действие может быть легко смоделировано чисто структурными средствами. Более того, по мнению ряда теоретиков программирования (в частности, Эдсгера Дейкстры), сам факт использования в программе неструктурных средств, будь то классический безусловный переход или любая из его специализированных форм, таких как break или continue, является свидетельством недостаточно проработанного алгоритма решения задачи.

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

// Досрочный выход из цикла без break
bool flag = false; // флаг досрочного завершения
while(<условие> && !flag) {
  ... операторы
  if (<ошибка>) {
    flag = true;
  } else {
    ... операторы
  }
}
... продолжение программы

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

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

int arr[ARRSIZE];
...
// Суммирование отдельно всех и только положительных
// элементов массива arr с заменой continue
int sum_all = 0;
int sum_pos = 0;
for (int i = 0 ; i < ARRSIZE; ++i)
{
    sum_all += arr[i];
    if (arr[i] > 0) // Условие заменено на противоположное!
    {
      sum_pos += arr[i];
    }
}

Как видим, достаточно было заменить проверяемое условие на противоположное и поместить заключительную часть тела цикла в условный оператор. Можно заметить, что программа стала короче (за счёт удаления команды пропуска итерации) и одновременно логичнее (из кода непосредственно видно, что суммируются положительные элементы).

Кроме того, использование команды пропуска итерации в цикле с условием (while-цикле) может также спровоцировать неочевидную ошибку: если тело цикла, как это часто бывает, завершается командами изменения переменной (переменных) цикла, то команда пропуска итерации пропустит и эти команды тоже, в результате чего (в зависимости от условия, по которому происходит пропуск) может произойти зацикливание или не соответствующий алгоритму повтор итерации. Так, если заменить в вышеприведённом примере цикл for на while, получится следующее:

int arr[ARRSIZE];
...
int sum_all = 0;
int sum_pos = 0;
int i = 0;
while (i < ARRSIZE) // Цикл внешне аналогичен предыдущему for ...
{
    sum_all += arr[i];
    if (arr[i] <= 0) continue;
    sum_pos += arr[i];
    ++i; // ... но эта команда будет пропущена при выполнении continue 
         // и программа зациклится
}

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

Вложенные циклы[править | править код]

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

Полное число исполнений тела внутреннего цикла не превышает произведения числа итераций внутреннего и всех внешних циклов. Например, взяв три вложенных друг в друга цикла, каждый по 10 итераций, получим 10 исполнений тела для внешнего цикла, 100 для цикла второго уровня и 1000 в самом внутреннем цикле.

Одна из проблем, связанных с вложенными циклами — организация досрочного выхода из них. Во многих языках программирования есть оператор досрочного завершения цикла (break в Си, exit в Турбо Паскале, last в Perl и т. п.), но он, как правило, обеспечивает выход только из цикла того уровня, откуда вызван. Вызов его из вложенного цикла приведёт к завершению только этого внутреннего цикла, внешний же цикл продолжит выполняться. Проблема может показаться надуманной, но она действительно иногда возникает при программировании сложной обработки данных, когда алгоритм требует немедленного прерывания в определённых условиях, наличие которых можно проверить только в глубоко вложенном цикле.

Решений проблемы выхода из вложенных циклов несколько.

  • Простейший — использовать оператор безусловного перехода goto для выхода в точку программы, непосредственно следующую за вложенным циклом. Этот вариант критикуется сторонниками структурного программирования, как и все конструкции, требующие использования goto. Некоторые языки программирования, например, Модула-2, просто не имеют оператора безусловного перехода, и в них подобная конструкция невозможна.
  • Альтернатива — использовать штатные средства завершения циклов, в случае необходимости устанавливая специальные флаги, требующие немедленного завершения обработки. Недостаток — усложнение кода, снижение производительности.
  • Размещение вложенного цикла в процедуре. Идея состоит в том, чтобы всё действие, которое может потребоваться прервать досрочно, оформить в виде отдельной процедуры, и для досрочного завершения использовать оператор выхода из процедуры (если такой есть в языке программирования). В языке Си, например, можно построить функцию с вложенным циклом, а выход из неё организовать с помощью оператора return. Недостаток — выделение фрагмента кода в процедуру не всегда логически обосновано, и не все языки имеют штатные средства досрочного завершения процедур.
  • Воспользоваться механизмом генерации и обработки исключений (исключительных ситуаций), который имеется сейчас в большинстве языков высокого уровня. В этом случае в нештатной ситуации код во вложенном цикле возбуждает исключение, а блок обработки исключений, в который помещён весь вложенный цикл, перехватывает и обрабатывает его. Недостаток — реализация механизма обработки исключений в большинстве случаев такова, что скорость работы программы уменьшается. Правда, в современных условиях это не особенно важно: практически потеря производительности столь мала, что имеет значение лишь для очень немногих приложений.
  • Наконец, существуют специальные языковые средства для выхода из вложенных циклов. Так, в языке Ада программист может пометить цикл (верхний уровень вложенного цикла) меткой, и в команде досрочного завершения цикла указать эту метку. Выход произойдёт не из текущего цикла, а из всех вложенных циклов до помеченного, включительно[3]. Язык PHP предоставляет возможность указать число прерываемых циклов после команды break — так break 2 прервёт сам цикл и вышестоящий над ним, а break 1 эквивалентно простой записи команды break[4].

Циклы с несколькими охраняемыми ветвями[править | править код]

Цикл Дейкстры[править | править код]

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

 do
   P1 → S1,
     …
   Pn → Sn
 od

Здесь do — маркер начала конструкции цикла, od — маркер завершения конструкции цикла, Pi — iохраняющее условие (логическое выражение, которое может иметь значение «истинно» или «ложно»), Si — iохраняемая команда. Цикл состоит из одной или нескольких ветвей (охраняемых выражений), каждая из которых представляет собой пару из охраняющего условия (или, коротко, «охраны») и охраняемой команды (понятно, что в реальности команда может быть сложной).

При выполнении цикла Дейкстры в каждой итерации происходит вычисление охраняющих условий. Если хотя бы одно из них истинно, выполняется соответствующая охраняемая команда, после чего начинается новая итерация (если истинны несколько охраняющих условий, выполняется только одна охраняемая команда). Если все охраняющие условия ложны, цикл завершается. Нетрудно заметить, что цикл Дейкстры с одним охраняющим условием и одной охраняемой командой представляет собой, по сути, обычный цикл с предусловием (цикл «пока»).

Хотя цикл Дейкстры был изобретён ещё в 1970-х годах, специальных конструкций для его создания в языках программирования не содержится. Единственным исключением стал недавно созданный Оберон-07 — первый реальный язык программирования, явно поддерживающий цикл с несколькими охраняемыми ветвями. Впрочем, цикл Дейкстры может быть без больших затруднений смоделирован с помощью традиционных конструкций структурных языков программирования. Вот пример его реализации одним из возможных способов на языке Ада:

loop
  if P1 then 
    S1;
    ...
  elsif Pn then 
    Sn;
  else
    exit;
  end if;
end loop;

Здесь P1—Pn — охраняющие условия, а S1—Sn — соответствующие охраняемые команды.

Цикл Дейкстры удобен при реализации некоторых специфических повторяющихся вычислений, которые неудобно описывать с помощью более традиционных циклических конструкций. Например, этим циклом естественно представляется конечный автомат — каждая ветвь соответствует одному состоянию автомата, охраняемые условия строятся так, чтобы в текущей итерации выбиралась ветвь, соответствующая текущему состоянию автомата, а код охраняемой команды обеспечивает выполнение вычислений в текущем состоянии и переход в следующее (то есть такое изменение переменных, после которого на следующей итерации будет истинным охраняющее условие нужной ветви).

Цикл «паук»[править | править код]

Легко видеть, что цикл Дейкстры не содержит явного условия продолжения или выхода, что не всеми теоретиками программирования рассматривается как благо. Поэтому была предложена усложнённая конструкция цикла Дейкстры, получившая название «цикл-‘паук’». В той же нотации она выглядит следующим образом:

 do
   P1→S1,
     …
   Pn→Sn
 out
   Q1→T1,
     …
   Qn→Tn
 else
   E
 od

Здесь после маркера out добавлены ветви завершения, состоящие из условий выхода Qi и команд завершения Ti. Кроме того, добавлена ветвь альтернативного завершения else с командой E.

Цикл-‘паук’ выполняется так:

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

Структура цикла-‘паука’ позволяет предельно строго описать условия выполнения цикла. Согласно теоретическим положениям, ветвь альтернативного завершения не должна использоваться в качестве одного из вариантов корректного прекращения работы цикла (все такие варианты должны быть оформлены в виде соответствующих ветвей завершения с явным условием), она служит только для того, чтобы отследить ситуацию, когда по каким-то причинам цикл начал выполняться нештатно. То есть команда альтернативного завершения может лишь анализировать причины ошибки и представлять результаты анализа.

Хотя явной поддержки на уровне синтаксиса для этого цикла не существует ни в одном языке программирования, цикл-‘паук’, как и цикл Дейкстры, может быть смоделирован с помощью традиционных структурных конструкций.

Методы оптимизации циклов[править | править код]

эквивалентными преобразованиями исходного кода
  • Разбиение цикла на блоки
  • Размотка цикла
  • Размыкание цикла
компилятором
  • Расщепление цикла
  • Расщепление тела цикла
  • Слияние циклов

См. также[править | править код]

  • Инварианты цикла (используются при проектировании и верификации циклов)
  • Рекурсия
  • Итератор (программирование)

Примечания[править | править код]

  1. Оберон — воплощение мечты Никлауса Вирта
  2. Строго говоря, тождество не полное, так как по-другому будет работать оператор continue.
  3. Loops/Nested at Rosetta Code (англ.)
  4. PHP Manual, break (англ.)

Ссылки[править | править код]

  • Подробное описание циклических процессов в Паскале
 

Рассмотрим третью алгоритмическую структуру — цикл.
Циклом называется блок кода, который для решения задачи требуется повторить несколько раз.

Каждый цикл состоит из

  • блока проверки условия повторения цикла
  • тела цикла
 

Цикл выполняется до тех пор, пока блок проверки условия возвращает истинное значение.
Тело цикла содержит последовательность операций, которая выполняется в случае истинного условия повторения цикла. После выполнения последней операции тела цикла снова выполняется операция проверки условия повторения цикла. Если это условие не выполняется, то будет выполнена операция, стоящая непосредственно после цикла в коде программы.

В языке Си следующие виды циклов:

  • while — цикл с предусловием;
  • do…while — цикл с постусловием;
  • for — параметрический цикл (цикл с заданным числом повторений).

Общая форма записи

while (Условие)
{
  БлокОпераций;
}

Если Условие выполняется (выражение, проверяющее Условие, не равно нулю), то выполняется БлокОпераций, заключенный в фигурные скобки, затем Условие проверяется снова.
Последовательность действий, состоящая из проверки Условия и выполнения БлокаОпераций, повторяется до тех пор, пока выражение, проверяющее Условие, не станет ложным (равным нулю). При этом происходит выход из цикла, и производится выполнение операции, стоящей после оператора цикла.

Пример на Си: Посчитать сумму чисел от 1 до введенного k

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  int k;  // объявляем целую переменную key
  int i = 1;
  int sum = 0; // начальное значение суммы равно 0
  printf(«k = «);
  scanf(«%d», &k);   // вводим значение переменной k
  while (i <= k)     // пока i меньше или равно k
  {
    sum = sum + i; // добавляем значение i к сумме
    i++;           // увеличиваем i на 1
  }
  printf(«sum = %dn», sum); // вывод значения суммы
  getchar(); getchar();
  return 0;
}

Результат выполнения
Цикл while
При построении цикла while, в него необходимо включить конструкции, изменяющие величину проверяемого выражения так, чтобы в конце концов оно стало ложным (равным нулю). Иначе выполнение цикла будет осуществляться бесконечно (бесконечный цикл).

Пример бесконечного цикла

1
2
3
4

while (1)
{
  БлокОпераций;
}

while — цикл с предусловием, поэтому вполне возможно, что тело цикла не будет выполнено ни разу если в момент первой проверки проверяемое условие окажется ложным.

Например, если в приведенном выше коде программы ввести k=-1, то получим результат
Цикл while может не выполниться ни разу

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

Общая форма записи

do {
  БлокОпераций;
while (Условие);

Цикл do…while — это цикл с постусловием, где истинность выражения, проверяющего Условие проверяется после выполнения Блока Операций, заключенного в фигурные скобки. Тело цикла выполняется до тех пор, пока выражение, проверяющее Условие, не станет ложным, то есть тело цикла с постусловием выполнится хотя бы один раз.

Использовать цикл do…while лучше в тех случаях, когда должна быть выполнена хотя бы одна итерация, либо когда инициализация объектов, участвующих в проверке условия, происходит внутри тела цикла.

Пример на Си. Проверка, что пользователь ввел число от 0 до 10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
#include <stdlib.h> // для использования функции system()
int main() {
  int num;             // объявляем целую переменную для числа
  system(«chcp 1251»); // переходим на русский язык в консоли
  system(«cls»);       // очищаем экран
  do {
    printf(«Введите число от 0 до 10: «); // приглашение пользователю
    scanf(«%d», &num); // ввод числа
  } while ((num < 0) || (num > 10)); // повторяем цикл пока num<0 или num>10
  printf(«Вы ввели число %d», num); // выводим введенное значение num — от 0 до 10
  getchar(); getchar();
  return 0;
}

Результат выполнения:
Цикл do...while

Параметрический цикл for

Общая форма записи

for (Инициализация; Условие; Модификация)
{
  БлокОпераций;
}

for — параметрический цикл (цикл с фиксированным числом повторений). Для организации такого цикла необходимо осуществить три операции:

  • Инициализация — присваивание параметру цикла начального значения;
  • Условие — проверка условия повторения цикла, чаще всего — сравнение величины параметра с некоторым граничным значением;
  • Модификация — изменение значения параметра для следующего прохождения тела цикла.
 

Эти три операции записываются в скобках и разделяются точкой с запятой ;;. Как правило, параметром цикла является целочисленная переменная.
Инициализация параметра осуществляется только один раз — когда цикл for начинает выполняться.
Проверка Условия повторения цикла осуществляется перед каждым возможным выполнением тела цикла. Когда выражение, проверяющее Условие становится ложным (равным нулю), цикл завершается. Модификация параметра осуществляется в конце каждого выполнения тела цикла. Параметр может как увеличиваться, так и уменьшаться.

Пример на Си: Посчитать сумму чисел от 1 до введенного k

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  int k;  // объявляем целую переменную key
  int sum = 0; // начальное значение суммы равно 0
  printf(«k = «);
  scanf(«%d», &k);   // вводим значение переменной k
  for(int i=1; i<=k; i++) // цикл для переменной i от 1 до k с шагом 1
  {
    sum = sum + i; // добавляем значение i к сумме
  }
  printf(«sum = %dn», sum); // вывод значения суммы
  getchar(); getchar();
  return 0;
}

Результат выполнения
Цикл while
В записи цикла for можно опустить одно или несколько выражений, но нельзя опускать точку с запятой, разделяющие три составляющие цикла.
Код предыдущего примера можно представить в виде

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  int k;  // объявляем целую переменную key
  int sum = 0; // начальное значение суммы равно 0
  printf(«k = «);
  scanf(«%d», &k);   // вводим значение переменной k
  int i=1;
  for(; i<=k; i++) // цикл для переменной i от 1 до k с шагом 1
  {
    sum = sum + i; // добавляем значение i к сумме
  }
  printf(«sum = %dn», sum); // вывод значения суммы
  getchar(); getchar();
  return 0;
}

Параметры, находящиеся в выражениях в заголовке цикла можно изменить при выполнении операции в теле цикла, например

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  int k;  // объявляем целую переменную key
  int sum = 0; // начальное значение суммы равно 0
  printf(«k = «);
  scanf(«%d», &k);   // вводим значение переменной k
  for(int i=1; i<=k; ) // цикл для переменной i от 1 до k с шагом 1
  {
    sum = sum + i; // добавляем значение i к сумме
    i++;           // добавляем 1 к значению i

  }
  printf(«sum = %dn», sum); // вывод значения суммы
  getchar(); getchar();
  return 0;
}

В цикле for может использоваться операция запятая, — для разделения нескольких выражений. Это позволяет включить в спецификацию цикла несколько инициализирующих или корректирующих выражений. Выражения, к которым применяется операция запятая, будут вычисляться слева направо.

Пример на Си:

1
2
3
4
5
6
7
8
9
10
11
12
13

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  int k;  // объявляем целую переменную key
  printf(«k = «);
  scanf(«%d», &k);   // вводим значение переменной k
  for(int i=1, j=2; i<=k; i++, j+=2) // цикл для переменных
  {                                  // (i от 1 до k с шагом 1) и (j от 2 с шагом 2)
    printf(«i = %d   j = %dn», i, j); // выводим значения i и j
  }
  getchar(); getchar();
  return 0;
}

Результат выполнения
Цикл for

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

В Си допускаются вложенные циклы, то есть когда один цикл находится внутри другого:

for (i = 0; i<n; i++)  // внешний цикл — Цикл1
{     
  for (j = 0; j<n; j++)   // вложенный цикл — Цикл2
  {
    ;        // блок операций Цикла2
  }
  // блок операций Цикла1;
}

Пример: Вывести числа от 0 до 99, по 10 в каждой строке

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  for(int i=0; i<10; i++) // цикл для десятков
  {                                  
    for (int j = 0; j < 10; j++) // цикл для единиц
    {
      printf(«%2d «, i * 10 + j); // выводим вычисленное число (2 знакоместа) и пробел
    }
    printf(«n»); // во внешнем цикле переводим строку
  }
  getchar(); // scanf() не использовался,
  return 0;  // поэтому консоль можно удержать одним вызовом getchar()
}

Результат выполнения
Вложенные циклы: вывод чисел от 0 до 99

Рекомендации по выбору цикла

При выборе цикла необходимо оценить необходимость проверки условия при входе в цикл или по завершении прохождения цикла.
Цикл с постусловием удобно применять в случаях, когда для проверки условия требуется вычислить значение выражения, которое затем будет размещено в теле цикла (см. выше пример ввода числа от 0 до 10).
Цикл c предусловием используется в случае если все переменные, участвующие в выражении, проверяющем условие, проинициализированы заранее, но точное число повторений цикла неизвестно или предполагается сложная модификация переменных, участвующих в формировании условия повторения цикла.
Если цикл ориентирован на работу с параметром, для которого заранее известно число повторений и шаг изменения, то более предпочтительным является параметрический цикл. Очень удобно использовать параметрический цикл при работе с массивами для перебора элементов.

Операторы прерывания и продолжения цикла break и continue

В теле любого цикла можно использовать операторы прерывания цикла — break и продолжения цикла — continue.

Оператор break позволяет выйти из цикла, не завершая его.
Оператор continue позволяет пропустить часть операторов тела цикла и начать новую итерацию.

Пример на Си: Вывести числа от 0 до 99 ниже главной диагонали

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  for(int i=0; i<10; i++) // цикл для десятков
  {                                  
    for (int j = 0; j < 10; j++) // цикл для единиц
    {
      if (j > i) // если число единиц больше числа десятков в числе
        break// выходим из вложенного цикла и переходим к новой строке
      printf(«%2d «, i * 10 + j); // выводим вычисленное число (2 знакоместа) и пробел
    }
    printf(«n»); // во внешнем цикле переводим строку
  }
  getchar(); // scanf() не использовался,
  return 0;  // поэтому консоль можно удержать одним вызовом getchar()
}

Результат выполнения
Оператор break

Пример на Си: Вывести числа от 0 до 99 исключая числа, оканчивающиеся на 5 или 8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  for(int i=0; i<10; i++) // цикл для десятков
  {                                  
    for (int j = 0; j < 10; j++) // цикл для единиц
    {
      if ((j == 5) || (j == 8)) // если число единиц в числе равно 5 или 8,
        continue;             // переходим к следующей итерации цикла
      printf(«%2d «, i * 10 + j); // выводим вычисленное число (2 знакоместа) и пробел
    }
    printf(«n»); // во внешнем цикле переводим строку
  }
  getchar(); // scanf() не использовался,
  return 0;  // поэтому консоль можно удержать одним вызовом getchar()
}

Результат выполнения
Оператор continue

При вложенных циклах действия операторов break и continue распространяется только на самую внутреннюю структуру, в которой они содержатся.

Оператор безусловного перехода goto

Общая форма записи

goto Метка;
. . .
Метка : Операция;

Выполнение оператора goto вызывает передачу управления в программе операции, помеченной Меткой. По сути Метка является идентификатором адреса операции, которой должно быть передано управление. Для отделения Метки от Операции используется двоеточие — :.
Метка может располагаться в программе как до оператора goto, так и после него. Имена Меток образуются по тем же правилам, что и имена переменных.

Пример на Си: Вывести все целые числа от 5 до 0.

1
2
3
4
5
6
7
8
9
10
11
12

#define _CRT_SECURE_NO_WARNINGS // для возможности использования scanf
#include <stdio.h>
int main() {
  int k = 5;
M1: if (k < 0) // если k<0,
    goto M2;   // переходим на метку M2 (выходим из программы)
  printf(«%d «, k); // выводим значение k
  k—;              // уменьшаем k на 1
  goto M1;          // переходим на метку M1 (повторяем операции выше)
M2: getchar();
  return 0;
}

Результат выполнения
Оператор goto

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

Назад: Язык Си

From Wikipedia, the free encyclopedia

While loop flow diagram

In most computer programming languages, a while loop is a control flow statement that allows code to be executed repeatedly based on a given Boolean condition. The while loop can be thought of as a repeating if statement.

Overview[edit]

The while construct consists of a block of code and a condition/expression.[1] The condition/expression is evaluated, and if the condition/expression is true,[1] the code within all of their following in the block is executed. This repeats until the condition/expression becomes false. Because the while loop checks the condition/expression before the block is executed, the control structure is often also known as a pre-test loop. Compare this with the do while loop, which tests the condition/expression after the loop has executed.

For example, in the C programming language (as well as Java, C#,[2] Objective-C, and C++, which use the same syntax in this case), the code fragment

int x = 0;

while (x < 5) {
    printf ("x = %dn", x);
    x++;
}

first checks whether x is less than 5, which it is, so then the {loop body} is entered, where the printf function is run and x is incremented by 1. After completing all the statements in the loop body, the condition, (x < 5), is checked again, and the loop is executed again, this process repeating until the variable x has the value 5.

Note that it is possible, and in some cases desirable, for the condition to always evaluate to true, creating an infinite loop. When such a loop is created intentionally, there is usually another control structure (such as a break statement) that controls termination of the loop.
For example:

while (true) {
    // do complicated stuff
    if (someCondition)
        break;
    // more stuff
}

Demonstrating while loops[edit]

These while loops will calculate the factorial of the number 5:

ActionScript 3[edit]

var counter: int = 5;
var factorial: int = 1;

while (counter > 1) {
    factorial *= counter;
    counter--;
}

Printf("Factorial = %d", factorial);

Ada[edit]

with Ada.Integer_Text_IO;

procedure Factorial is
    Counter   : Integer := 5;
    Factorial : Integer := 1;
begin
    while Counter > 0 loop
        Factorial := Factorial * Counter;
        Counter   := Counter - 1;
    end loop;

    Ada.Integer_Text_IO.Put (Factorial);
end Factorial;

APL[edit]

counter  5
factorial  1

:While counter > 0
    factorial × counter
    counter - 1    
:EndWhile

  factorial

or simply

AutoHotkey[edit]

counter := 5
factorial := 1

While counter > 0
    factorial *= counter--    

MsgBox % factorial

Microsoft Small Basic[edit]

counter = 5    ' Counter = 5
factorial = 1  ' initial value of variable "factorial"

While counter > 0
    factorial = factorial * counter
    counter = counter - 1
    TextWindow.WriteLine(counter)
EndWhile

Visual Basic[edit]

Dim counter As Integer = 5    ' init variable and set value
Dim factorial As Integer = 1  ' initialize factorial variable

Do While counter > 0
    factorial = factorial * counter
    counter = counter - 1
Loop     ' program goes here, until counter = 0

'Debug.Print factorial         ' Console.WriteLine(factorial) in Visual Basic .NET

Bourne (Unix) shell[edit]

counter=5
factorial=1
while [ $counter -gt 0 ]; do
    factorial=$((factorial * counter))
    counter=$((counter - 1))
done

echo $factorial

C or C++[edit]

int main() {
    int count = 5;
    int factorial = 1;

    while (count > 1)
        factorial *= count--;

    printf("%d", factorial);
}

CFML[edit]

Script syntax[edit]

counter = 5;
factorial = 1;
 
while (counter > 1) {
    factorial *= counter--;
}

writeOutput(factorial);

Tag syntax[edit]

<cfset counter = 5>
<cfset factorial = 1>
<cfloop condition="counter GT 1">
    <cfset factorial *= counter-->
</cfloop>
<cfoutput>#factorial#</cfoutput>

Fortran[edit]

program FactorialProg
    integer :: counter = 5
    integer :: factorial = 1

    do while (counter > 0)
        factorial = factorial * counter
        counter = counter - 1
    end do

    print *, factorial
end program FactorialProg

Go[edit]

Go does not have a while statement, but it has the function of a for statement if you omit some elements of the for statement.

counter, factorial := 5, 1

for counter > 1 {
	counter, factorial = counter-1, factorial*counter
}

Java, C#, D[edit]

The code for the loop is the same for Java, C# and D:

int counter = 5;
int factorial = 1;

while (counter > 1)
    factorial *= counter--;

JavaScript[edit]

let counter = 5;
let factorial = 1;

while (counter > 1)
    factorial *= counter--;

console.log(factorial);

Lua[edit]

counter = 5
factorial = 1

while counter > 0 do
  factorial = factorial * counter
  counter = counter - 1
end

print(factorial)

MATLAB & GNU Octave[edit]

counter = 5;
factorial = 1;

while (counter > 0)
    factorial = factorial * counter;      %Multiply
    counter = counter - 1;                %Decrement
end

factorial

Mathematica[edit]

Block[{counter=5,factorial=1},  (*localize counter and factorial*)
    While[counter>0,            (*While loop*)
        factorial*=counter;     (*Multiply*)
        counter--;              (*Decrement*)
    ];

    factorial
]

Oberon, Oberon-2 (programming language), Oberon-07, or Component Pascal[edit]

MODULE Factorial;
IMPORT Out;
VAR
    Counter, Factorial: INTEGER;
BEGIN
    Counter := 5;
    Factorial := 1;

    WHILE Counter > 0 DO
        Factorial := Factorial * Counter;
        DEC(Counter)
    END;
    
    Out.Int(Factorial,0)
END Factorial.

Maya Embedded Language[edit]

int $counter = 5;
int $factorial = 1;

int $multiplication;

while ($counter > 0) {
    $multiplication = $factorial * $counter;

    $counter -= 1;
    
    print("Counter is: " + $counter + ", multiplication is: " + $multiplication + "n");
}

Nim[edit]

var
  counter = 5            # Set counter value to 5
  factorial = 1          # Set factorial value to 1

while counter > 0:       # While counter is greater than 0
    factorial *= counter # Set new value of factorial to counter.
    dec counter          # Set the counter to counter - 1.

echo factorial

Non-terminating while loop:

while true:
  echo "Help! I'm stuck in a loop!"

Pascal[edit]

Pascal has two forms of the while loop, while and repeat. While repeats one statement (unless enclosed in a begin-end block) as long as the condition is true. The repeat statement repetitively executes a block of one or more statements through an until statement and continues repeating unless the condition is false. The main difference between the two is the while loop may execute zero times if the condition is initially false, the repeat-until loop always executes at least once.

program Factorial1;
var
    Fv: integer;

    procedure fact(counter:integer);
    var
        Factorial: integer;

    begin       
         Factorial := 1;
    
         while Counter > 0 do
         begin
             Factorial := Factorial * Counter;
             Counter := Counter - 1
         end;

         WriteLn(Factorial)
     end;

begin
    Write('Enter a number to return its factorial: ');
    readln(fv);
    repeat
         fact(fv);
         Write('Enter another number to return its factorial (or 0 to quit): '); 
     until fv=0;
end.

Perl[edit]

my $counter   = 5;
my $factorial = 1;

while ($counter > 0) {
    $factorial *= $counter--; # Multiply, then decrement
}

print $factorial;

While loops are frequently used for reading data line by line (as defined by the $/ line separator) from open filehandles:

open IN, "<test.txt";

while (<IN>) {
    print;
}

close IN;

PHP[edit]

$counter = 5;
$factorial = 1;

while ($counter > 0) {
    $factorial *= $counter--; // Multiply, then decrement.
}

echo $factorial;

PL/I[edit]

declare counter   fixed initial(5);
declare factorial fixed initial(1);

do while(counter > 0)
    factorial = factorial * counter;
    counter = counter - 1;
end;

Python[edit]

counter = 5                           # Set the value to 5 
factorial = 1                         # Set the value to 1

while counter > 0:                    # While counter(5) is greater than 0  
    factorial *= counter              # Set new value of factorial to counter.
    counter -= 1                      # Set the counter to counter - 1.

print(factorial)                      # Print the value of factorial.

Non-terminating while loop:

while True:
    print("Help! I'm stuck in a loop!")

Racket[edit]

In Racket, as in other Scheme implementations, a named-let is a popular way to implement loops:

#lang racket
(define counter 5)
(define factorial 1)
(let loop ()
    (when (> counter 0)
        (set! factorial (* factorial counter))
        (set! counter (sub1 counter))
        (loop)))
(displayln factorial)

Using a macro system, implementing a while loop is a trivial exercise (commonly used to introduce macros):

#lang racket
(define-syntax-rule (while test body ...) ; implements a while loop
    (let loop () (when test body ... (loop))))
(define counter 5)
(define factorial 1)
(while (> counter 0)
    (set! factorial (* factorial counter))
    (set! counter (sub1 counter)))
(displayln factorial)

But note that an imperative programming style is often discouraged in Racket (as in Scheme).

Ruby[edit]

# Calculate the factorial of 5
i = 1
factorial = 1

while i <= 5
  factorial *= i
  i += 1
end

puts factorial

Rust[edit]

fn main() {
    let mut counter = 5;
    let mut factorial = 1;

    while counter > 1 {
        factorial *= counter;
        counter -= 1;
    }

    println!("{}", factorial);
}

Smalltalk[edit]

Contrary to other languages, in Smalltalk a while loop is not a language construct but defined in the class BlockClosure as a method with one parameter, the body as a closure, using self as the condition.

Smalltalk also has a corresponding whileFalse: method.

| count factorial |
count := 5.
factorial := 1.
[count > 0] whileTrue: 
    [factorial := factorial * count.
    count := count - 1].
Transcript show: factorial

Swift[edit]

var counter = 5                 // Set the initial counter value to 5 
var factorial = 1               // Set the initial factorial value to 1

while counter > 0 {             // While counter(5) is greater than 0  
    factorial *= counter        // Set new value of factorial to factorial x counter.
    counter -= 1                // Set the new value of counter to  counter - 1.
}

print(factorial)                // Print the value of factorial.

Tcl[edit]

set counter 5
set factorial 1

while {$counter > 0} {
    set factorial [expr $factorial * $counter] 
    incr counter -1 
}

puts $factorial

VEX[edit]

int counter = 5;
int factorial = 1;

while (counter > 1)
    factorial *= counter--;

printf("%d", factorial);

PowerShell[edit]

$counter = 5
$factorial = 1

while ($counter) {
    $factorial *= $counter--
}

$factorial

While programming language[edit]

The While programming language[3] is a simple programming language constructed from assignments, sequential composition, conditionals and while statements, used in the theoretical analysis of imperative programming language semantics.[4][5]

C := 5;
F := 1;

while (C > 1) do
    F := F * C;
    C := C - 1;

See also[edit]

  • Do while loop
  • For loop
  • Foreach
  • LOOP (programming language) – a programming language with the property that the functions it can compute are exactly the primitive recursive functions

References[edit]

  1. ^ a b «The while and do-while Statements (The Java™ Tutorials > Learning the Java Language > Language Basics)». Dosc.oracle.com. Retrieved 2016-10-21.
  2. ^ «while (C# reference)». Msdn.microsoft.com. Retrieved 2016-10-21.
  3. ^ «Chapter 3 : The While programming language» (PDF). Profs.sci.univr.it. Retrieved 2016-10-21.
  4. ^ Flemming Nielson; Hanne R. Nielson; Chris Hankin (1999). Principles of Program Analysis. Springer. ISBN 978-3-540-65410-0. Retrieved 29 May 2013.
  5. ^ Illingworth, Valerie (11 December 1997). Dictionary of Computing. Oxford Paperback Reference (4th ed.). Oxford University Press. ISBN 9780192800466.

Цикл заставляет интерпретатор JavaScript многократно выполнять один и тот же блок кода, называемый телом цикла. Каждое отдельное исполнение инструкций в теле цикла называется итерацией. В JavaScгipt доступны четыре инструкции циклов: while, do/while, for и for/in.

Цикл while

Инструкция while (англ. до тех пор, пока) создает цикл с предусловием. В скобках после слова while указывают некоторое логическое выражение или значение. Цикл начнется если значение этого выражения равно true и будет работать до тех пор, пока это условие не обратится в false. Общий синтаксис этого цикла выглядит так:

while (условие) {
  инструкция
}

Циклы while|for

Следующий цикл while исполняется, пока значение переменной i меньше 3:

После окончания третьей итерации условие i < 3 больше не является истинным, поэтому цикл завершается.

Для создания бесконечного цикла в качестве условия достаточно задать булево значение true:

while (true) {
  инструкция
}

Это общепринятый способ создания бесконечного цикла. В прочих случаях (к примеру, если в рассмотренном нами примере убрать в коде i++) возможен вариант (в теории) создания бесконечного цикла. На практике, браузер выведет сообщение о «зависшем» скрипте и посетитель его остановит.

Цикл do…while

Инструкция do…while (англ. делай до тех пор, пока) отличается от цикла while тем, что в do…while сначала выполняется тело цикла, а затем проверяется условие продолжения цикла. Из-за такой особенности do…while называют циклом с постусловием. Таким образом, если условие do…while заведомо ложное, то хотя бы один раз блок операторов в теле цикла do…while всё равно выполнится.

Инструкция do…while представляет собой конструкцию из двух операторов, используемых совместно. Синтаксис этой конструкции следующий:

do {
  // тело цикла
} while (условие);

Пример do…while:

Этот цикл продолжается, пока переменная i меньше 4. Она равна 0 в начале цикла и увеличивается на 1 на каждой итерации.

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

Цикл for

Инструкция for – это вариант цикла с предусловием, который состоит из трех необязательных выражений, заключенных в круглые скобки и разделенных точками с запятой, за которым следует оператор (обычно оператор блока), который должен выполняться в цикле. Она имеет следующий синтаксис:

for (инициализация; выражение; обновление) {
  // ... тело цикла ...
}

Описание синтаксиса:

  1. Инициализация. Присваивается первоначальное значение переменной, обычно – счетчика. Выполняется только один раз в начале выполнения оператора. Областью действия этой переменной будет тело цикла.
  2. Выражение – булево выражение, которое вычисляется на каждой итерации цикла. Представляет собой условие продолжения работы оператора цикла. После того, как значение счетчика достигнет указанного предела, цикл завершится.
  3. Обновление – это значение, на которое будет увеличиваться или уменьшаться счетчик цикла. Вычисляется по завершении каждой итерации цикла. Чтобы оно было полезным, как и выражение инициализации, оно должно иметь побочные эффекты. В общем случае таким побочным эффектом служит операция присваивания, инкремента или декремента.

Пример цикла for:

Рассмотрим выполнение этого цикла более подробно:

  1. Инициализация: Переменная-счетчик, в данном случае х, инициализируется значением 1. Выполняется один-единственный раз, при заходе в цикл.
  2. Выражение: x < 4 – это условие продолжения цикла for, оно проверяется перед каждой итерацией и при входе в цикл на истинность. Если это так, то выполняются инструкции тела цикла (в данном случае – инструкция alert( x + » » );).
  3. Обновление: x++ – изменяет значение переменной-счетчика. Выполняется после тела на каждой итерации, но перед проверкой условия x < 4.
  4. Тело цикла: alert( x + » » ) – тело цикла обрамляется фигурными скобками, если тело цикла состоит из одной операции, то фигурные скобки можно опустить.

Иными словами, поток выполнения цикла: Инициализация → (если условие выражения true → тело цикла → обновление (x++)) → (если условие выражения true → тело цикла → обновление (x++)) → … и так далее, пока верно условие – x < 4.

Циклы for могут быть более сложными, чем в приведенных выше примерах. В некоторых циклах на каждой итерации может изменяться одновременно несколько переменных. В таких циклах часто применяется оператор «запятая» – он позволяет объединить несколько выражений инициализации и инкрементирования в одно, например:

Использование for без блоков

Все три выражения в заголовке цикла for являются необязательными.

Например, в блоке инициализации не требуется инициализировать переменные. Цикл в примере ниже полностью идентичен приведённому выше:

Как и блок инициализации, блок выражения также не является обязательным. Если вы опускаете выражение, то вы должны обязательно разбить цикл в теле, чтобы не создавать бесконечный цикл:

Можно опустить все три блока. Для избежания зацикливания используйте break для завершения цикла, а также изменяйте (увеличивайте или уменьшайте) переменную-счётчик, так чтобы условие для оператора break в какой-то момент сработало:

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

Цикл for…in

Цикл for…in используется для перебора всех свойств из объекта в случайном порядке и имеет следующий синтаксис:

for (variable in object) {
  инструкция /* ... делать что-то с object[variable] ... */
}

В качестве nеременной (variable) можно подставить имя переменной или инструкцию var, объявляющую одну переменную. Переменной перед началом каждой итерации цикла присваивается в виде строки имя одного из свойств объекта. Как и в цикле for, оператор var здесь не обязателен, но его рекомендуется использовать, чтобы переменная была локальной. Справа от ключевого слова in указывается объект, свойства которого будут перебираться циклом. Если переменная, представляющая объект, будет иметь значение null или undefined цикл не выполнится ни разу. И как обычно, инструкция – это инструкция или блок инструкций, образующих тело цикла.

Пример итерации по свойствам объекта:

Как отмечалось ранее, если имя свойства хранится в переменной, то обратиться к нему можно только через квадратные скобки (myCar[prop]), а не через оператор «точка».

Свойства объектов в JavaScript не упорядочены, поэтому порядок возврата их имен в цикле for…in предсказать сложно. Все перечислимые свойства будут возвращены, но порядок их вывода может зависеть от браузера.

Согласно стандарта ECMAScript, если имя свойства – нечисловая строка, то такие свойства всегда перебираются в том же порядке, в каком присваивались. Так получилось в примере выше при выводе свойств объекта myCar.
С другой стороны, если в качестве имени свойства выступает число или строка, которая может быть преобразована в числовой фомат, то браузеры осуществлят сортировку таких свойств в целях внутренней оптимизации. Вывод таких имен свойств объекта не будет соответствовать их оригинальному расположению.

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

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

Пример вложенного цикла:

Теперь попытаемся разобраться, как это работает. Первый (внешний) цикл после каждой итерации увеличивает значение переменной i, а второй (внутренний) – переменной j. За одну итерацию внешнего цикла внутренний выполняется девять раз. По условию (i < 10) внешний цикл выполнится 9 раз. Соответственно вложенный цикл будет выполнятся тоже 9 раз, а код внутри него – 9*9 итого 81 раз.

Иными словами, код читаем так: натыкаемся на внешний цикл, делаем первый проход, во время прохода натыкаемся на еще один цикл (внутренний), делаем девять проходов по нему, каждый раз выводя текущее значение переменной j. Выводим значение i и далее возвращаемся в начало внешнего цикла для второго прохода и так 9 раз.

Операторы break и continue

Циклом можно управлять с помощью операторов break и continue.

Оператор break приводит к выходу из цикла или инструкции switch и передает управление
операторам, следующим за ними.

В следующем примере создаётся счётчик, значения которого должны изменяться от 1 до 99, однако оператор break прерывает цикл после 4 итераций:

Для вложенных циклов оператор break используется с меткой, с помощью которой завершается работа «меченой» инструкции. Метка позволяет выйти из любого блока кода. Метка имеет вид "имя_метки:", имя должно быть уникальным. Она ставится перед циклом или блоком инструкций, которые нужно завершить с помощью break:

Указание имени метки (без двоеточия) за ключевым словом break приводит к выходу из цикла или инструкции. Между ключевым словом break и именем метки не допускается перевод строки. Вызов break inner завершит вложенный цикл, а break outer ищет ближайший внешний цикл с такой меткой и переходит в его конец.

Оператор continue прерывает текущее выполнение цикла и переходит к выполнению следующего шага этого цикла. При этом, цикл while возвращается непосредственно к своему условию, а цикл for сначала вычисляет выражение инкремента, а затем возвращается к условию.

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

В теле цикла инструкция if с помощью оператора (%) проверяет, является ли число четным. Если да, итерация цикла завершается до увеличения переменной num, но цикл продолжается со следующей итерации со значением i, увеличенным на единицу. Затем цикл выполняется до естественного завершения при значении i, равном 10. Переменная num подсчитывает количество итераций цикла. Окончательное значение num равно 5, а не 9, потому что четные операции инкремента пропускаются из-за оператора continue.

Оператор continue, как и break, можно использовать вместе с «помеченными» инструкциями для
возврата к конкретному месту в коде. Чаще всего это делается во вложенных циклах, например:

В этом примере для внешнего цикла for добавлена метка outer_mask. Каждый цикл включает 5 итераций, то есть инструкция num++ предположительно должна быть выполнена 25 раз, после чего переменная num должна быть равна 25. Оператор continue завершает выполнение внутреннего цикла, начиная новую итерацию внешнего. Она выполняется, когда j равно 3, то есть пропускаются две итерации внутреннего цикла, из-за чего num в итоге имеет значение 23.


Задачи

  • Число «задом наперед»

    Пользователь вводит целое число. Напишите код, который выведет число, обратное по порядку составляющих его цифр введенному. Например, введено 9876, надо вывести 6789. Решите задачу через цикл while.

    Показать решение

    Решение:

    1. Найдем остаток от деления на 10 исходного числа num1. Получим его последнюю цифру digit.
    2. Добавим эту цифру к новому числу num2.
    3. Разделим нацело на 10 исходное число num1 и округлим до целых. Тем самым избавимся от последней цифры в нем. Полученное число сохраним в num1.
    4. Снова найдем остаток от деления на 10 того, что осталось от первого числа num1. Запомним эту цифру.
    5. Умножим на 10 второе число. Тем самым увеличим его разрядность до двух и сдвинем первую цифру в разряд десятков.
    6. Добавим к полученному второму числу запомненную ранее цифру digit из первого числа.
    7. Будем повторять перечисленные действия пока исходное число не уменьшится до нуля, т.е. пока не избавимся от всех его разрядов.
  • Сумма свойств объекта

    Есть объект users в которм перечислен возраст участников. Напишите код, который выведет суммарный возраст всех участников. Решите задачу через цикл for…in.

    
    var users = {
      "John": 28,
      "Mark": 30,
      "David": 25,
      "Richard": 42
    };
    
    /* ...ваш код... */
    

    Показать решение

  • Найти сумму четных цифр числа

    Пользователь вводит натуральное число. Найти сумму четных цифр, входящих в его состав.

    Показать решение

    Решение:

    1. В переменную sum будем суммировать все четные числа.
    2. Пока значение введенного числа (n) больше нуля выполнять пункты 3 – 5.
    3. Извлекать последнюю цифру числа (n) (путем нахождения остатка от деления на 10) и проверять ее на четность (путем нахождения остатка от деления на 2).
    4. Если цифра четная, то добавлять ее к sum.
    5. Избавиться от последней цифры числа путем ее деления нацело на 10.
    6. Вывести значение sum в консоль. Перейдите во вкладку Console инструментов разработчика ( Ctrl+Shift+J / Cmd+Shift+J ).
  • Повторять цикл, пока пользователь не введёт 0

    Напишите цикл, который предлагает prompt ввести число. Если посетитель ввёл число – попросить ввести ещё раз, и так далее.
    Цикл должен спрашивать ввести число пока либо посетитель не введёт 0, либо не нажмёт кнопку Cancel (ESC). После ввода нуля, показать на экран количество чисел, которые были введены, их общую сумму и среднее арифметическое.

  • Сумма нечётных чисел в диапазоне

    Необходимо суммировать все нечётные целые числа в диапазоне, который введёт пользователь с клавиатуры.

  • Равнобедренный треугольник из символов

    Нарисовать равнобедренный треугольник из символов *. Высоту выбирает пользователь. Например, при высоте = 6, на экране треугольник должен выглядеть так:

    Равнобедренный треугольник

  • Найти наибольшую цифру натурального числа

    Пользователь вводит с клавиатуры натуральное число. Найдите его наибольшую цифру и выведите её через alert.
    Например, введено число 987560. Наибольшая цифра в нем 9.

    Показать решение

    Решение:

    1. Переменная n – заданное число, а переменная m – максимальная цифра в этом числе.
    2. Допустим, что последняя цифра числа и есть максимальная. Извлечем ее используя оператор нахождения остатка при делении на 10. Присвоим значение переменной m.
    3. Избавимся от последней цифры с помощью операции деления на 10 и округления при помощи побитового оператора.
    4. Далее в цикле будем извлекать с конца числа поочерёдно каждую его цифру и сравнивать со значением m. Если очередная цифра будет больше, чем максимальное значение, то будем присваивать ее переменной m.
    5. Также в цикле будем каждый раз избавляться от последней, уже учтенной, цифры. Цикл завершит свою работу, когда переменная n станет равной нулю, т. е. после того, когда будут перебраны все цифры числа.
    6. В конце кода через alert выведем значение m на экран. Это и будет наибольшая цифра введенного числа.

Понравилась статья? Поделить с друзьями:
  • Weishaupt wm g20 2 a инструкция
  • Weishaupt wg20n 1 c инструкция
  • Weishaupt wg10n 1 d инструкция
  • Weishaupt gl8 1 d инструкция
  • Weishaupt gl3 1 e инструкция