Как правильно составить цикл с условием

В этой главе мы познакомимся с операторами ветвления 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";
    }
}

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

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

Данная методика рассчитана на слабых студентов. Как правило, сильные на этой теме не застревают и особенных методик для них придумывать не нужно. Второстепенная цель статьи — перевести эту методику из класса «работает на всех студентах, но только у одного преподавателя» в класс «работает у всех студентов, всех преподавателей». На абсолютную оригинальность не претендую. Если вы уже применяете похожую методику для обучения этой теме, напишите, пожалуйста, чем ваш вариант отличается. Если решите применить, расскажите по итогам, как все прошло. Если похожая методика описана в какой-нибудь книжке, напишите, пожалуйста, название.

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

Зачем столько букв? Циклы это же элементарно!

Как я уже писал выше, для практикующих разработчиков и для сильных студентов, сложность концепции циклов может быть недооценена. Например можно устроить долгую лекцию, увидеть кивающие головы и умные глаза. Но при попытке решить какую-нибудь задачу, начинается ступор и необъяснимые проблемы. После лекции у студентов наверняка сложилось лишь частичное понимание. Ситуация усугубляется тем, что студенты не могут сами озвучить, в чем именно их заблуждение.
Однажды я осознал, что студенты воспринимают мои примеры как иероглифы. То есть как неделимые куски текста, в которых нужно дописать какую-то «магическую» букву и оно заработает.
Иногда я замечал, что студенты думают, что для решения конкретной задачи нужна какая-то еще конструкция, которую я просто еще не рассказал. Хотя для решения требовалось лишь немного модифицировать пример.

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

Кому и зачем я преподаю

Поскольку вступительных экзаменов нет, то на занятиях могут быть как сильные, так и очень слабые студенты. Более подробно о моих студентах можно почитать в статье Портрет слушателей вечерних курсов
Я стремился к тому, чтобы программирование осваивали все, кто этого хочет.
Мои занятия проходят индивидуально и студент платит свои деньги за каждое. Казалось бы, студенты будут оптимизировать затраты и требовать минимум. Однако люди ходят на очные занятия с живым преподавателем не за самими знаниями, а за уверенностью в том что они успели усвоить, за ощущением прогресса и за одобрением от эксперта (преподавателя). Если студенты не будут чувствовать прогресса в своем обучении, они будут уходить. В целом можно построить занятия так, чтобы студенты ощущали прогресс в увеличении количества знакомых конструкций. То есть сначала подробно изучаем while, потом изучаем for, потом do while и вот у нас готов курс на тысячу и одну ночь, в котором два месяца изучаются одни только циклы, а на выходе — студент, который под диктовку написал стандартную библиотеку. Однако для того, чтобы решать практические задачи, нужно не только знание материала, но и самостоятельность в его применении и в поиске новой информации. Поэтому для очных курсов считаю правильным принцип — научить минимуму и поощрять самостоятельное изучение нюансов и смежных тем. В теме про циклы, минимумом я считаю конструкцию while. На ней можно понять принцип. Зная принцип можно освоить и for и do-while самостоятельно.

Чтобы добиться освоения материала слабыми студентами, описать синтаксис недостаточно. Нужно давать побольше простых, но разнообразных заданий и расписывать примеры более подробно. В конечном итоге скорость освоения ограничивается способностью студента к преобразованию выражений и поиску закономерностей. Для сообразительных студентов, большинство заданий будут скучными. При занятиях с ними, можно не настаивать на решении 100% задач. Мой материал можно посмотреть на моем гитхабе. Правда репозиторий больше похож на гримуар чернокнижника — никто, кроме меня, не поймет что где находится

, а если провалить проверку, то можно сойти с ума

Методика ориентирована на практику

Теория объясняется на примере решения задачи. На занятиях по основам программирования, где изучаются ветвления и циклы, просто не получится устроить полезную лекцию по одной теме на целый час. 15-20 минут хватает, чтобы объяснить концепцию. Основные сложности появляются при выполнении практических заданий.
Начинающие преподаватели могут вывалить операторы, ветвления, циклы и массивы за одну лекцию. Вот только студенты у них столкнутся с проблемой усвоения этой информации.
Нужно ведь не только рассказать материал, но и убедиться, что слушатели его поняли.

Факт усвоения темы определяется по тому, как студент справляется с самостоятельной работой.
Если студенту удалось решить задачу по теме без помощи преподавателя, значит тема усвоена. Чтобы обеспечить самостоятельную проверку, у каждой задачи описывается таблица с тестовыми сценариями. У задач есть ярко выраженный порядок. Пропускать задачи не рекомендуется. Если текущая задача слишком сложная, то к следующей переходить бесполезно. Она ещё сложнее. Чтобы студент мог осилить текущую сложную задачу, ему объясняется несколько приемов на примере первой задачи. Собственно, все содержание темы сводится к приемам преодоления трудностей. Циклы это, скорее, побочный эффект.

Первая задача всегда пример. Вторая отличается незначительно и выполняется «самостоятельно» сразу же после первой под присмотром преподавателя. Все последующие задачи направлены на то, чтобы обратить внимание на разные мелочи, которые могут вызывать заблуждения.

Объяснение примера представляет собой диалог, в котором у студента нужно вызывать back propagation и кросс-валидацию чтобы убедиться в усвоении порции материала.

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

While или for?

Один из спорных вопросов — выбор конструкции для примера: while или for. Однажды мой знакомый практикующий разработчик без опыта преподавания целый час убеждал меня, что цикл for — самый простой для понимания. Аргументы сводились к «в нем все понятно и по местам разложено». Однако первопричина затруднений настоящих новичков в самой идее цикла, а не в его написании. Если человек не поймет эту идею, то у него будут затруднения с синтаксисом. Как только идея осознана, то проблемы оформления кода исчезают сами.

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

Второй мой аргумент в том, что while требует меньше оформления, чем for. Меньше оформления — меньше глупых ошибок с пропущенными запятыми и скобочками. У новичков еще не настолько развита внимательность и дотошность, чтобы автоматически избегать синтаксических ошибок.
Третий аргумент — во многих хороших книгах while объясняется первым.

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

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

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

Явное лучше неявного

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

Ура, работает!
Ура, работает!
Ура, работает!
Ура, работает!
Ура, работает!
Ура, работает!
Ура, работает!
Ура, работает!

Такой вариант плох тем, что в выводе не видно значения счетчика. Это проблема для начинающих. Не стоит ее недооценивать. Поначалу эта задача была первой, а задача про вывод ряда чисел по возрастанию — второй. Приходилось вводить дополнительные термины «цикл N раз» и «цикл от A до B», которые по сути одно и то же. Чтобы не плодить лишних сущностей, я решил показывать только пример с выводом ряда чисел. Немногим удается без подготовки научиться держать в голове счетчик и моделировать поведение программы в голове. Некоторые студенты впервые сталкиваются с моделированием «в уме» именно на теме про циклы.
После некоторой практики, задачу на повторение одинакового текста я даю на самостоятельное решение. Если давать сначала видимый счетчик, а потом невидимый, то у студентов возникает меньше проблем. Иногда достаточно подсказки «не пиши счетчик на экран».

Как объясняется у других?

В большинстве учебных материалов в интернете, синтаксис цикла дается в составе «лекции». Например на developer.mozilla.org (в настоящий момент) вместе с циклом while описываются еще несколько конструкций. При этом даются исключительно сами конструкции в виде шаблонов. Результат их запуска описывается словами, а иллюстрация отсутствует. На мой взгляд, такая подача темы умножает на ноль полезность таких материалов. Ученик может переписать код и запустить его сам, но эталон для сравнения все равно нужен. Как понять, что пример переписан правильно, если не с чем сравнить результат?
Когда дается только шаблон, без примера, студенту становится еще сложнее. Как понять, что фрагменты кода расставлены в шаблоне правильно? Можно попробовать написать как-нибудь, а потом запустить. Но если нет эталона для сравнения результата, то запуск тоже не поможет.

В курсе по C++ на интуите синтаксис цикла закопан в третьей странице лекции 4 по теме «операторы». При объяснении синтаксиса циклов делают особый упор на термин «оператор». Термин подается в виде набора фактов вроде «символ; это оператор», “{} это составной оператор”, «тело цикла должно быть оператором». Мне такой подход не нравится тем, что он как бы прячет важные взаимосвязи за одним термином. Разбор исходного кода программы на термы на таком уровне нужен разработчикам компиляторов для реализации спецификации языка, но никак не студентам в первом приближении. Новички в программировании редко обладают достаточной дотошностью, чтобы настолько внимательно относиться к терминам. Редкий человек запоминает и понимает новые слова с первого раза. Практически никто не может правильно применить термин, который только что узнал. Поэтому у студентов возникает куча ошибок вроде «написал while(a<7);{, а программа не работает».
На мой взгляд, в начале лучше дать синтаксис конструкции сразу со скобочками. Вариант без скобочек объяснять только если у ученика возник конкретный вопрос «почему тут без скобочек и работает».

В книге Окулова «Основы программирования» 2012 г. знакомство с циклами начинается с шаблона for, затем даются рекомендации по его использованию, а потом сразу идет экспериментальный раздел занятия. Я так понимаю, что книга писалась для того меньшинства очень способных учеников, которые редко приходят ко мне на занятия.

В популярных книгах всегда пишется результат фрагментов кода. Например у Шилдта «Java 8. Полное руководство» 2015 года издания. Сначала дается шаблон, потом пример программы и сразу после него — результат выполнения.

В качестве примера рассмотрим цикл while, в котором выполняется обратный
отсчет, начиная с 10, и выводится ровно 10 строк «тактов»:

//Продемонстрировать применение оператора цикла while
class While {
    public static void main(String args []) {
        int n = 10;
        while (n > 0) {
            System.out.println("такт " + n);
            n--;
        }
    }
}

После запуска эта программа выводит десять «тактов» следующим образом:
такт 10
такт 9
такт 8
такт 7
такт 6
такт 5
такт 4
такт 3
такт 2
такт 1

Подход с описанием шаблона, примера программы и результата работы этой программы используется также в книге «Javascript для детей» и в курсе js на w3schools.com. Формат веб-страницы даже позволяет сделать этот пример интерактивным.

В книге Страуструпа «Принципы и практика с использованием C++» 2016 г. автор пошел еще дальше. Первым делом объясняется какой результат должен получиться, а уже после этого — показывают текст программы. Причем в качестве примера берут не просто случайную программу, а дают экскурс в историю. Это помогает обратить внимание на нее «Смотри, это не просто какой то бесполезный текст. Ты видишь что то значимое».

В качестве примера итерации рассмотрим первую программу, выполненную на машине с хранимой программой (EDSAC). Она была написана Дэвидом Уилером (David Wheeler) в компьютерной лаборатории Кэмбриджского университета (Cambridge University, England) 6 мая 1949 года. Эта программа вычисляет и распечатывает простой список квадратов.
0 0
1 1
2 4
3 9
4 16
...
98 9604
99 9801

Здесь в каждой строке содержится число, за которым следуют знак табуляции (‘t’) и квадрат этого числа. Версия этой программы на языке C++ выглядит так:

//Вычисляем и распечатываем таблицу квадратов чисел 0-99
int main()
{
    int i = 0; // Начинаем с нуля
    while(i < 100){
        cout << i << 't' << square(i) << 'n';
        ++i;
    }
}

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

Как объясняю я сам

Подход Страуструпа: описание результата, затем решение задачи, а потом самостоятельный анализ студентом — выглядит самым продуманным. Поэтому я решил взять за основу именно его, но рассказывать на менее историчном примере — задаче по выводу «оглавления». Она формирует узнаваемый якорь, чтобы потом говорить «вспомни задачу про оглавление» и чтобы студенты вспоминали именно ее. В своем примере я постарался предупредить еще два из самых распространенных заблуждений. Далее я напишу о них подробнее.

На этой задаче мы знакомимся с приемами решения сложных задач. Первоначальное решение нужно сделать примитивно и просто. Ну а затем можно подумать, как улучшить это решение.
Введение
Глава 1
Глава 2
Глава 3
Глава 4
Глава 5
Глава 6
Глава 7
Заключение

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

Console.WriteLine("Введение");
Console.WriteLine("Глава 1");
Console.WriteLine("Глава 2");
Console.WriteLine("Глава 3");
Console.WriteLine("Глава 4");
Console.WriteLine("Глава 5");
Console.WriteLine("Глава 6");
Console.WriteLine("Глава 7");
Console.WriteLine("Заключение");

Как его можно улучшить?
Заменить однообразные действия на цикл.
Какие действия тут повторяются подряд без изменений?
В этом фрагменте таких нет. Впрочем команды по выводу слова «Глава» с номером очень похожи друг на друга.
Поэтому следующий этап — поиск разницы между фрагментами. Это только в этой задаче все очевидно, потом повторяться будут не одиночные команды, а блоки кода по 5 строк и более. Искать придется не просто в списке команд, а конструкциях ветвления или цикла.
В примере разница между командами в числе после слова «Глава».
Как только разница найдена, нужно понять закономерность изменения. Отличающийся фрагмент это число? Оно постоянно увеличивается или уменьшается? Как меняется значение числа между двумя командами рядом?
В примере число после слова «Глава» увеличивается с шагом 1. Разница найдена, закономерность выявлена. Теперь можно заменить различающийся фрагмент на переменную.
Объявлять такую переменную нужно перед первым из повторяющихся фрагментов. Такую переменную обычно называют I или j или как-то более развернуто. Её начальное значение должно быть равно первому выводимому на экран значению. В примере первое значение это 1.
Какое начальное значение нужно взять для вывода ряда чисел «100, 101, 102, 103, 104, 105»?
В этом ряду первое число 100.
После каждой команды вывода нужно увеличить значение этой переменной на 1. Эта единица — шаг изменения.
Какой шаг будет в ряду чисел «100, 102, 104, 106»?
В этом ряду шаг 2.
После замены различающегося фрагмента на переменную, код будет выглядеть так:

Console.WriteLine("Введение");
int i;
i = 0;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Глава " + i);
i = i + 1;
Console.WriteLine("Заключение");

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

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

  1. Решить «в лоб» множеством отдельных команд
  2. Найти закономерность
  3. Выразить закономерность переменной
  4. Оформить в виде цикла

Далее вводится новые термины, чтобы студент не оказался в ситуации «все понимаю, но сказать не могу»:
— счётчик — всегда переменная, которая нужна для отслеживания количества шагов цикла. Обычно целое число, которое сравнивается с ограничением.
— шаг счётчика — описание закономерности изменения счётчика.
— ограничение — число или переменная, с которой сравнивается счётчик, чтобы алгоритм был конечным. Значение счётчика меняется так, чтобы приближаться к ограничению.
— тело цикла — набор команд, которые будут повторяться. Когда говорится «команда написана внутри цикла», то имеют в виду именно тело.
— итерация цикла — однократное выполнение тела цикла.
— условие цикла — логическое выражение, от которого зависит, будет ли выполняться ещё одна итерация. (Тут возможна путаница с конструкциями ветвления)
Нужно быть готовым к тому, что первое время студенты будут применять термины не по назначению. Это относится как к сильным, так и к слабым. Налаживание общего языка это целое искусство. Сейчас напишу кратко: нужно ставить задачу «выдели фрагмент кода с <термин>» и самому правильно использовать эти термины в разговоре.
После преобразования с циклом получается фрагмент:

Console.WriteLine("Введение");
int i = 0;
while (i < 7) {
    Console.WriteLine("Глава " + i);
    i = i + 1;
}
Console.WriteLine("Заключение");

Главное заблуждение

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

;
int i = 0;
while (i < 7) {
    Console.WriteLine("Введение")
    Console.WriteLine("Глава " + i);
    i = i + 1;
    Console.WriteLine("Заключение");
}

Ученики постоянно натыкаются на эту проблему, как в начале, так и в более сложных задачах.
Коронная подсказка в этом случае:

Сколько раз нужно повторять написание команды: один раз или много?

Команды вывода слов «Введение» и «Заключение», а также объявление и инициализация переменной i не похожи на другие повторяющиеся действия. Они выполняются всего по одному разу, значит их нужно писать за пределами тела цикла.

В коде должны остаться все три этапа решения, чтобы потом ссылаться на них в случае затруднений. Первые два варианта достаточно закомментировать, чтобы они не мешали.
Внимание студента нужно обратить на следующие факты:
— В условии цикла обычно сравнивается счётчик и ограничение. Счётчик может меняться в теле цикла, а ограничение — нет. Чтобы нарушить это правило, нужно сформулировать веские причины.
— Команды для вывода слов «Введение» и «Заключение» находятся за пределами тела цикла. Нам их нужно выполнить 1 раз. «Введение» — до повторения действий, «Заключение» — после.
В процессе закрепления этой темы, освоения следующих, а также разбирательств с затруднениями даже сильным студентам полезно задавать вопрос: «А вот это действие сколько раз нужно выполнять? Один или много?».

Развитие дополнительных навыков

В процессе изучения циклов, у студентов ещё тренируется навык диагностики и решения проблем. Для проведения диагностики, студенту нужно представить желаемый результат и сравнить его с фактическим результатом. От разницы между ними зависят действия для исправления.
Поскольку студенты на этом этапе ещё плохо представляют себе «желаемый» результат, то они могут ориентироваться на тестовые данные. Как правило никто на этом этапе ещё не понимает, что может пойти не так и как с этим бороться. Поэтому я даю под запись в тетрадь описание типичных проблем и несколько способов их решения. Выбор наиболее подходящего из них — задача самого студента.
Запись нужна чтобы спрашивать «получилось то, что ожидалось?», «Какая из этих ситуаций сейчас получилась?», «Помогло ли примененное решение?».

  1. Количество действий на 1 меньше или больше, чем ожидается. Способы решения:
    — увеличить начальное значение счётчика на 1.
    — заменить строгий оператор сравнения (< или >) на нестрогий (<= или >=).
    — изменить значение ограничения на 1.
  2. Действия в цикле выполняются без остановки, бесконечно. Способы решения:
    — добавить команду изменения счётчика, если она отсутствует.
    — исправить команду изменения счётчика так, чтобы его значение становилось ближе к ограничению.
    — убрать команду изменения ограничения, если она в теле цикла.
  3. Количество действий в цикле более чем на 1 меньше или больше, чем ожидалось. Действие в цикле не выполнилось ни разу. Сначала нужно выяснить фактические значения переменных непосредственно перед началом цикла. Способы решения:
    — изменить начальное значение ограничения
    — изменить начальное значение счетчика

Обычно проблема 3 связана с использованием не той переменной или не-обнулением счётчика.

После этого объяснения, у студента все ещё могут быть различные заблуждения по поводу работы циклов.
Чтобы развеять самые распространенные, даю задачи:

  1. В которой ограничение, начальное значение счётчика или шаг счетчика вводится пользователем.
  2. В которой значение счётчика нужно использовать в каком-то арифметическом выражении. Желательно со счётчиком в подкоренном выражении или в знаменателе, чтобы разница была нелинейная.
  3. В которой значение счётчика не выводится на экран в процессе работы цикла. Например вывод нужного количества одинаковых фрагментов текста или нарисовать фигуру черепашьей графикой.
  4. В которой нужно выполнить сначала одни повторяющиеся действия, а потом другие.
  5. В которой нужно выполнить другие действия до и после повторяющихся

Для каждой задачи нужно привести тестовые данные и ожидаемый результат.

Чтобы понять, насколько быстро можно двигаться, нужно дать прочитать условия этих задач и спросить: «чем они отличаются от примера?», «Что нужно изменить в примере, чтобы решить их?». Если студент осмысленно отвечает, тогда пусть решит хотя бы одну на занятии, а остальные — дома самостоятельно. Если решение будет успешно, то можно начать объяснение про условия внутри циклов.
Если с самостоятельным решением затруднения, то нужно все отрабатывать на занятии. Чтобы решение задачи не напоминало рисование совы, я рекомендую сначала решить задачу не универсально. То есть так, чтобы решение проходило первый тест и не использовало конструкцию цикла. Ну а потом уже применять преобразования, чтобы добиться универсальности решения.

Циклы и ветвления

На мой взгляд, полезно дать тему «циклы внутри ветвлений» отдельно. Так, чтобы потом было видно разницу между многократной проверкой условия и однократной.
Задачи для закрепления будут про вывод чисел от А до В, которые вводятся пользователем:
— всегда по возрастанию.
— по возрастанию или по убыванию в зависимости от значений А и В.

К теме «ветвления внутри циклов» нужно переходить только после того, как студент освоил приемы: «замена закономерности на переменную» и ” замена повторяющихся действий на цикл”.
Главная причина применения ветвлений внутри циклов — аномалии в закономерности. В середине она нарушается в зависимости от исходных данных.
Тем студентам, которые способны искать решение путем комбинирования простых приемов, достаточно сказать «ветвления можно писать внутри циклов» и дать задачу «для примера» полностью на самостоятельное решение.
Задача для примера:

Пользователь вводит число Х. Вывести в столбик числа от 0 до 9 и поставить знак ‘+’ напротив того числа, которое равно Х.

Если было введено 0

0+
1
2
3
4
5
6
7
8
9

Если было введено 6

0
1
2
3
4
5
6+
7
8
9

Если было введено 9

0
1
2
3
4
5
6
7
8
9+

Если было введено 777

0
1
2
3
4
5
6
7
8
9

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

Желаемый

string temp;
temp = Console.ReadLine();
int x;
x = int.Parse(temp);
if (x==0) {
    Console.WriteLine(0 + "+");
} else {
    Console.WriteLine(0);
}
if (x==1) {
    Console.WriteLine(1 + "+");
} else {
    Console.WriteLine(1);
}
if (x==2) {
    Console.WriteLine(2 + "+");
} else {
    Console.WriteLine(2);
}
if (x==3) {
    Console.WriteLine(3 + "+");
} else {
    Console.WriteLine(3);
}
if (x==4) {
    Console.WriteLine(4 + "+");
} else {
    Console.WriteLine(4);
}
if (x==5) {
    Console.WriteLine(5 + "+");
} else {
    Console.WriteLine(5);
}
if (x==6) {
    Console.WriteLine(6 + "+");
} else {
    Console.WriteLine(6);
}
if (x==7) {
    Console.WriteLine(7 + "+");
} else {
    Console.WriteLine(7);
}
if (x==8) {
    Console.WriteLine(8 + "+");
} else {
    Console.WriteLine(8);
}
if (x==9) {
    Console.WriteLine(9 + "+");
} else {
    Console.WriteLine(9);
}

Возможный

string temp;
temp = Console.ReadLine();
int x;
x = int.Parse(temp);
if (x==0) {
    Console.WriteLine("0+n1n2n3n4n5n6n7n8n9");
}
if (x==1) {
    Console.WriteLine("0n1+n2n3n4n5n6n7n8n9");
}
if (x==2) {
    Console.WriteLine("0n1n2+n3n4n5n6n7n8n9");
}
if (x==3) {
    Console.WriteLine("0n1n2n3+n4n5n6n7n8n9");
}
if (x==4) {
    Console.WriteLine("0n1n2n3n4+n5n6n7n8n9");
}
if (x==5) {
    Console.WriteLine("0n1n2n3n4n5+n6n7n8n9");
}
if (x==6) {
    Console.WriteLine("0n1n2n3n4n5n6+n7n8n9");
}
if (x==7) {
    Console.WriteLine("0n1n2n3n4n5n6n7+n8n9");
}
if (x==8) {
    Console.WriteLine("0n1n2n3n4n5n6n7n8+n9");
}
if (x==9) {
    Console.WriteLine("0n1n2n3n4n5n6n7n8n9+");
}

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

Циклы внутри циклов

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

Объяснение темы про циклы внутри циклов лучше всего начать с объяснения важности обнуления счетчика.
Задача для примера:

Пользователь вводит два числа: R и T. Вывести две строки символов “#”. В первой строке должно быть R штук символов. Во второй строке T штук. Если какое-либо число будет отрицательно, вывести сообщение об ошибке.

R=5, T=11

#####
###########

R=20, T=3

####################
###

R=-1, T=6

Значение R должно быть неотрицательно

R=6, T=-2

Значение T должно быть неотрицательно

Очевидно, что у этой задачи тоже есть как минимум два варианта решения.

Желаемый

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
int i = 0;
while (i < R)
{
    Console.Write("#");
    i = i + 1;
}
Console.WriteLine();
i = 0;
while (i < T)
{
    Console.Write("#");
    i = i + 1;
}

Возможный №1

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
int i = 0;
while (i < R)
{
    Console.Write("#");
    i = i + 1;
}
Console.WriteLine();
int j = 0;
j = 0;
while (j < T)
{
    Console.Write("#");
    j = j + 1;
}

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

Типичная проблема с использованием одной переменной-счётчика для двух циклов проявляется вот так:

Количество символов во второй строке не соответствует значению T. Если с этой проблемой нужна помощь, то нужно «ткнуть носом» в конспект про типичные проблемы с циклами. Это симптом №3. Диагностируется если добавить вывод значения счётчика непосредственно перед вторым циклом. Исправляется обнулением. Но это лучше сразу не рассказывать. Студент должен попытаться сформулировать хотя бы одну гипотезу.

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

Возможный №2

string temp;
int R;
int T;
temp = Console.ReadLine();
R = int.Parse(temp);
temp = Console.ReadLine();
T = int.Parse(temp);
Console.WriteLine(new String('#', R));
Console.WriteLine(new String('#', T));

Следующая обязательная задача:

Выведите на экран цифры от 0 до 9. Каждая цифра должна быть на своей строчке. Количество цифр в строчке (W) вводится с клавиатуры.

W=10

0000000000
1111111111
2222222222
3333333333
4444444444
5555555555
6666666666
7777777777
8888888888
9999999999

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

Спасибо за внимание.

Ставьте лайки, подписывайтесь на канал.

P.S. Если вы нашли опечатки или ошибки в тексте, пожалуйста, сообщите мне. Это можно сделать выделив часть текста и нажав в Mac «⌘ + Enter», а на классических клавиатурах «Ctrl / Enter», либо через личные сообщения. Если же эти варианты недоступны, напишите об ошибках в комментариях. Спасибо!

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

Опрос для читателей без кармы


33.33%
Преподаю профессионально, +1
9


7.41%
Преподаю профессионально, -1
2


51.85%
Не преподаю, +1
14

Проголосовали 27 пользователей.

Воздержались 6 пользователей.

Циклы с условием

Если
заранее неизвестно число повторений
цикла, то используются циклы с условием.
В Паскале имеется два типа таких циклов.
Это циклы с предусловием и постусловием.

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

WHILE
<логическое выражение> DO
<оператор>;

Выполнение
оператора цикла с предусловием начинается
с проверки условия, записанного после
слова while. Если оно соблюдается, то
выполняется <тело цикла>, а затем
вновь проверяется условие и т.д. Как
только на очередном шаге окажется, что
условие не соблюдается, то выполнение
<тела цикла> прекратится.

Если
<тело цикла> состоит из нескольких
операторов, то они объединяются
операторными скобками.

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

Пример
4.

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

program
DemoWhile;

const

Limit
=10; {Ограничение
на количество вводимых чисел}

var
Count, Item, Sum: integer;

begin

Count:=0;
{Счетчик
чисел}

Sum:=
0; {Сумма
чисел}

while
(Count < Limit) do {Условие
выполнения
цикла}

begin

Count:=
Count+1;

Write(‘Введите
‘, Count, ‘ – e целое
число:
‘);

Readln(Item);{Ввод
очередного числа с клавиатуры}

Sum:=
Sum+Item;

end;

Writeln(‘Сумма
введенных чисел равна ‘, Sum)
;

end.

В
данном примере в разделе описания
констант описана константа Limit=10,
задающая ограничение на количество
вводимых чисел. В разделе описания
переменных описаны переменные Count,
Item, Sum

целочисленного типа. В начале выполнения
программы обнуляются значения счетчика
введенных чисел Count
и их суммы Sum.
Затем выполняются цикл ввода 10 чисел и
их суммирование. Вначале оператор
условия
while

проверяет условие Count
< Limit
.
Если условие верно, то выполняется
составной оператор в теле цикла:

begin

Count:=
Count+1;

Write(‘Введите
‘, Count, ‘-e целое
число:
‘);

Readln(Item)
;

Sum:=
Sum+Item;

End;

в
котором вводится значение очередного
числа, и на это значение увеличивается
значение суммы. После этого управление
в программе вновь передается оператору
цикла while,
опять проверяется условие Count
< Limit
.
Если условие верно, то выполняется
составной оператор и т. д., пока значение
переменной Count
будет меньше 10. Как только значение
Count
станет
равно 10 и условие Count
< Limit

не будет соблюдено, выполнение цикла
завершится, а управление в программе
будет передано на оператор, находящийся
за словом end,
т. e. первый оператор за границей while.
Это вызов процедуры Writeln,
которая выведет сообщение ‘Сумма
введенных чисел равна’

и напечатает значение переменной Sum.

Пример
5
:
Нахождение наибольшего общего делителя
двух целых чисел с помощью алгоритма
Евклида.

program
Evklid;

var
a,b,c:integer;

begin

write(‘введите
два целых числа : ‘);

readln(a,b);

while
b<>0 do

begin

c:=a
mod b;

a:=b;

b:=c;

end;

writeln(‘наибольший
общий делитель = ‘,a);

readln

end.

Пример
6
: Пары
неотрицательных вещественных чисел
вводятся с клавиатуры. Посчитать
произведение для каждой пары и сумму
всех чисел.

program
cycle_while;

var
x,y,sum:real; otv:char;

begin

sum:=0;

otv=’Д’;

while
(otv=’Д’)
or (otv=’д’)
do

begin

write(‘Введите
числа
x,y > 0 ‘);

readln(x,y);

writeln(‘Их
произведение = ‘,x*y:8:3);

sum:=sum+x+y;

write(‘Завершить
программу (Д/Н)? ‘);

readln(otv);

end;

writeln(‘Общая
сумма
= ‘,sum:8:3);

readln

end.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

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

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

Циклы в python

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

Преимущества циклов

В Python преимущества циклов, как и в других язвках программирования, заключаются в следующем:

  1. Это обеспечивает возможность повторного использования кода.
  2. Используя циклы, нам не нужно писать один и тот же код снова и снова.
  3. С помощью циклов мы можем перебирать элементы структур данных (массивов или связанных списков).

В Python существуют следующие операторы циклов.

Оператор цикла Описание
for Цикл for используется в том случае, когда необходимо выполнить некоторую часть кода до тех пор, пока не будет выполнено заданное условие. Цикл for также называют циклом c предусловием. Лучше использовать цикл for, если количество итераций известно заранее.
while Цикл while используется в сценарии, когда мы не знаем заранее количество итераций. Блок операторов в цикле while выполняется до тех пор, пока не будет выполнено условие, указанное в цикле while. Его также называют циклом с предварительной проверкой условия.
do-while Цикл do-while продолжается до тех пор, пока не будет выполнено заданное условие. Его также называют циклом с пстусловием. Он используется, когда необходимо выполнить цикл хотя бы один раз.

Цикл for в Python

Цикл for в Python используется для многократного повторения операторов или части программы. Он часто используется для обхода структур данных, таких как список, кортеж или словарь.

Синтаксис цикла for в python приведен ниже.

for iterating_var in sequence:    
    statement(s)    

Цикл for в Python

Цикл For с использованием последовательности

Пример 1: Итерация строки с помощью цикла for

str = "Python"  
for i in str:  
    print(i)  

Вывод:

P
y
t
h
o
n

Пример 2: Программа для печати таблицы заданного числа.

list = [1,2,3,4,5,6,7,8,9,10]  
n = 5  
for i in list:  
    c = n*i  
    print(c)  

Вывод:

5
10
15
20
25
30
35
40
45
50s

Пример 3: Программа для печати суммы заданного списка.

list = [10,30,23,43,65,12]  
sum = 0  
for i in list:  
    sum = sum+i  
print("The sum is:",sum)  

Вывод:

The sum is: 183

Цикл For с использованием функции range()

Функция range()

Функция range() используется для генерации последовательности чисел. Если мы передадим range(10), она сгенерирует числа от 0 до 9. Синтаксис функции range() приведен ниже.

range(start,stop,step size)  
  • Start означает начало итерации.
  • Stop означает, что цикл будет повторяться до stop-1. range(1,5) будет генерировать числа от 1 до 4 итераций. Это необязательный параметр.
  • Размер шага используется для пропуска определенных чисел в итерации. Его использование необязательно. По умолчанию размер шага равен 1. Это необязательно.

Рассмотрим следующие примеры:

Пример 1: Программа для печати чисел по порядку.

for i in range(10):  
    print(i,end = ' ')  

Вывод:

0 1 2 3 4 5 6 7 8 9 

Пример 2: Программа для печати таблицы заданного числа.

n = int(input("Enter the number "))  
for i in range(1,11):  
    c = n*i  
    print(n,"*",i,"=",c)  

Вывод:

Enter the number 10
10 * 1 = 10
10 * 2 = 20
10 * 3 = 30
10 * 4 = 40
10 * 5 = 50
10 * 6 = 60
10 * 7 = 70
10 * 8 = 80
10 * 9 = 90
10 * 10 = 100

Пример 3: Программа для печати четного числа с использованием размера шага в range().

n = int(input("Enter the number "))  
for i in range(2,n,2):  
    print(i)  

Вывод:

Enter the number 20
2
4
6
8
10
12
14
16
18

Мы также можем использовать функцию range() с последовательностью чисел. Функция len() сочетается с функцией range(), которая выполняет итерацию по последовательности с использованием индексации. Рассмотрим следующий пример.

list = ['Peter','Joseph','Ricky','Devansh']  
for i in range(len(list)):  
    print("Hello",list[i])  

Вывод:

Hello Peter
Hello Joseph
Hello Ricky
Hello Devansh

Вложенный цикл for в python

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

for iterating_var1 in sequence:  #outer loop  
    for iterating_var2 in sequence:  #inner loop  
        #block of statements     
#Other statements    

Пример 1: Вложенный цикл for

# User input for number of rows  
rows = int(input("Enter the rows:"))  
# Outer loop will print number of rows  
for i in range(0,rows+1):  
# Inner loop will print number of Astrisk  
    for j in range(i):  
        print("*",end = '')  
    print()  

Вывод:

Enter the rows:5
*
**
***
****
*****

Пример 2: Программа для печати пирамиды чисел.

rows = int(input("Enter the rows"))  
for i in range(0,rows+1):  
    for j in range(i):  
        print(i,end = '')  
    print()  

Вывод:

1
22
333
4444
55555

Использование оператора else в цикле for

В отличие от других языков, таких как C, C++ или Java, Python позволяет нам использовать оператор else с циклом for, который может быть выполнен только тогда, когда все итерации исчерпаны. Здесь мы должны заметить, что если цикл содержит какой-либо оператор break, то оператор else не будет выполнен.

Пример 1

for i in range(0,5):    
    print(i)    
else:  
    print("for loop completely exhausted, since there is no break.")  

Вывод:

0
1
2
3
4
for loop completely exhausted, since there is no break.

Цикл for полностью исчерпал себя, так как нет прерывания.

Пример 2

for i in range(0,5):    
    print(i)    
    break;    
else:print("for loop is exhausted");    
print("The loop is broken due to break statement...came out of the loop")  

В приведенном выше примере цикл прерван из-за оператора break, поэтому оператор else не будет выполнен. Будет выполнен оператор, находящийся непосредственно рядом с блоком else.

Вывод:

0

Цикл был прерван, благодаря оператору break.

Цикл while в Python

Цикл while позволяет выполнять часть кода до тех пор, пока заданное условие не станет ложным. Он также известен как цикл с предварительной проверкой условия.

Его можно рассматривать как повторяющийся оператор if. Когда мы не знаем количество итераций, цикл while является наиболее эффективным.

Синтаксис приведен ниже.

while expression:    
    statements    

Здесь утверждения могут быть одним утверждением или группой утверждений. Выражение должно быть любым допустимым выражением Python, приводящим к true или false. Истиной является любое ненулевое значение, а ложью – 0.

Цикл while в Python

Операторы управления циклом

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

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

# prints all letters except 'a' and 't'   
i = 0  
str1 = 'javatpoint'  
  
while i < len(str1):   
    if str1[i] == 'a' or str1[i] == 't':   
        i += 1  
        continue  
    print('Current Letter :', a[i])   
    i += 1  

Вывод:

Current Letter : j
Current Letter : v
Current Letter : p
Current Letter : o
Current Letter : i
Current Letter : n

2. Оператор break – Когда встречается оператор break, он выводит управление из цикла.

Пример:

# The control transfer is transfered  
# when break statement soon it sees t  
i = 0  
str1 = 'javatpoint'  
  
while i < len(str1):   
    if str1[i] == 't':   
        i += 1  
        break  
    print('Current Letter :', str1[i])   
    i += 1  

Вывод:

Current Letter : j
Current Letter : a
Current Letter : v
Current Letter : a

3. Оператор pass – Оператор pass используется для объявления пустого цикла. Он также используется для определения пустого класса, функции и оператора управления. Давайте разберем следующий пример.

# An empty loop   
str1 = 'javatpoint'  
i = 0  
  
while i < len(str1):   
    i += 1  
    pass  
print('Value of i :', i)  

Вывод

Value of i : 10

Пример 1: Программа для печати от 1 до 10 с использованием цикла while

i=1  
#The while loop will iterate until condition becomes false.  
While(i<=10):    
    print(i)   
    i=i+1   

Вывод

1
2
3
4
5
6
7
8
9
10

Пример 2: Программа для печати таблицы заданных чисел.

i=1    
number=0    
b=9    
number = int(input("Enter the number:"))    
while i<=10:    
    print("%d X %d = %d n"%(number,i,number*i))    
    i = i+1    

Вывод

Enter the number:10
10 X 1 = 10 

10 X 2 = 20 

10 X 3 = 30 

10 X 4 = 40 

10 X 5 = 50 

10 X 6 = 60 

10 X 7 = 70 

10 X 8 = 80 

10 X 9 = 90 

10 X 10 = 100 

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

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

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

Пример 1

while (1):    
    print("Hi! we are inside the infinite while loop"

Вывод

Hi! we are inside the infinite while loop
Hi! we are inside the infinite while loop

Пример 2

var = 1    
while(var != 2):    
    i = int(input("Enter the number:"))    
    print("Entered value is %d"%(i))    

Вывод

Enter the number:10
Entered value is 10
Enter the number:10
Entered value is 10
Enter the number:10
Entered value is 10
Infinite time

Использование else в цикле while

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

i=1   
while(i<=5):    
    print(i)    
    i=i+1    
else:  
    print("The while loop exhausted")    
i=1    
while(i<=5):    
    print(i)    
    i=i+1    
    if(i==3):    
        break   
else:  
    print("The while loop exhausted")  

Вывод

1
2

В приведенном выше коде, когда встречается оператор break, цикл while останавливает свое выполнение и пропускает оператор else.

Программа для печати чисел Фибоначчи до заданного предела

terms = int(input("Enter the terms "))  
# first two intial terms  
a = 0  
b = 1  
count = 0  
  
# check if the number of terms is Zero or negative  
if (terms <= 0):  
   print("Please enter a valid integer")  
elif (terms == 1):  
   print("Fibonacci sequence upto",limit,":")  
   print(a)  
else:  
   print("Fibonacci sequence:")  
   while (count < terms) :  
       print(a, end = ' ')  
       c = a + b  
       # updateing values  
       a = b  
       b = c  
     
    count += 1  
Enter the terms 10
Fibonacci sequence:
0 1 1 2 3 5 8 13 21 34 

Оператор прерывания в Python

Break – это ключевое слово в python, которое используется для вывода управления программой из цикла. Оператор break разрывает циклы по одному, то есть в случае вложенных циклов он сначала разрывает внутренний цикл, а затем переходит к внешним циклам. Другими словами, можно сказать, что break используется для прерывания текущего выполнения программы, и управление переходит на следующую строку после цикла.

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

Синтаксис прерывания приведен ниже.

#оператор цикла
break;   

Пример:

list =[1,2,3,4]  
count = 1;  
for i in list:  
    if i == 4:  
        print("item matched")  
        count = count + 1;  
        break  
print("found at",count,"location");  

Вывод:

item matched
found at 2 location

Пример:

str = "python"  
for i in str:  
    if i == 'o':  
        break  
    print(i);  

Вывод:

p
y
t
h

Пример: оператор break с циклом while

i = 0;  
while 1:  
    print(i," ",end=""),  
    i=i+1;  
    if i == 10:  
        break;  
print("came out of while loop");  

Вывод:

0  1  2  3  4  5  6  7  8  9  came out of while loop

Пример

n=2  
while 1:  
    i=1;  
    while i<=10:  
        print("%d X %d = %dn"%(n,i,n*i));  
        i = i+1;  
    choice = int(input("Do you want to continue printing the table, press 0 for no?"))  
    if choice == 0:  
        break;      
    n=n+1  

Вывод:

2 X 1 = 2

2 X 2 = 4

2 X 3 = 6

2 X 4 = 8

2 X 5 = 10

2 X 6 = 12

2 X 7 = 14

2 X 8 = 16

2 X 9 = 18

2 X 10 = 20

Do you want to continue printing the table, press 0 for no?1

3 X 1 = 3

3 X 2 = 6

3 X 3 = 9

3 X 4 = 12

3 X 5 = 15

3 X 6 = 18

3 X 7 = 21

3 X 8 = 24

3 X 9 = 27

3 X 10 = 30

Do you want to continue printing the table, press 0 for no?0

Оператор continue в Python

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

#loop statements    
continue  
#the code to be skipped     

Оператор continue в Python

Рассмотрим следующие примеры.

Пример

i = 0                     
while(i < 10):                
   i = i+1  
   if(i == 5):  
      continue  
   print(i)  

Вывод:

1
2
3
4
6
7
8
9
10

Обратите внимание на вывод приведенного выше кода, значение 5 пропущено, потому что мы предоставили условие if с помощью оператора continue в цикле while. Когда оно совпадает с заданным условием, управление передается в начало цикла while, и он пропускает значение 5 из кода.

Давайте посмотрим на другой пример:

Пример

str = "JavaTpoint"  
for i in str:  
    if(i == 'T'):  
        continue  
    print(i)  

Вывод:

J
a
v
a
p
o
i
n
t

Оператор pass в python

Оператор pass является нулевым оператором (null operation), поскольку при его выполнении ничего не происходит. Он используется в тех случаях, когда оператор синтаксически необходим, но мы не хотим использовать вместо него какой-либо исполняемый оператор.

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

Pass также используется в тех случаях, когда код будет записан где-то, но еще не записан в программном файле. Рассмотрим следующий пример.

list = [1,2,3,4,5]    
flag = 0    
for i in list:    
    print("Current element:",i,end=" ");    
    if i==3:    
        pass    
        print("nWe are inside pass blockn");    
        flag = 1    
    if flag==1:    
        print("nCame out of passn");    
        flag=0   

Вывод:

Current element: 1 Current element: 2 Current element: 3 
We are inside pass block


Came out of pass

Current element: 4 Current element: 5 

Python цикл Do While

В Python нет цикла do while. Но мы можем создать подобную программу.

Цикл do while используется для проверки условия после выполнения оператора. Он похож на цикл while, но выполняется хотя бы один раз.

Общий синтаксис цикла Do While (не отностится к python)

do {  
     //statement  
} while (condition);  

Пример: цикл do while в Python

i = 1  
  
while True:  
    print(i)  
    i = i + 1  
    if(i > 5):  
        break  

Вывод:

1  
2  
3  
4  
5  

#Руководства

  • 4 апр 2023

  • 0

Они есть практически в каждом языке программирования, но в Python с ними работать приятнее всего. Как, впрочем, и со всем остальным.

Иллюстрация: Катя Павловская для Skillbox Media

Иван Стуков

Журналист, изучает Python. Любит разбираться в мелочах, общаться с людьми и понимать их.

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

Например, циклы выполняют один и тот же блок кода несколько раз. В Python есть два основных вида циклов: while и for. О них и поговорим.

  • Как работают циклы
  • Цикл while в Python
  • Цикл for в Python
  • Функция range()
  • Однострочный цикл: генератор списков
  • Прерывание цикла: ключевое слово break
  • Пропуск части цикла: ключевое слово continue
  • Последнее действие в цикле: ключевое слово else
  • Бесконечный цикл
  • Как сделать аналог do while в Python
  • Вложенные циклы в Python

Любой цикл состоит из двух обязательных элементов:

  • условие — начальный параметр; цикл запустится только при его выполнении и закончится, как только условие перестанет выполняться;
  • тело — сама программа, которая выполняется внутри цикла.

Схематически его можно представить так:

Инфографика: Майя Мальгина для Skillbox Media

В синтаксисе Python в конце строки с условием ставится двоеточие, а всё тело выделяется отступом (табуляцией или четырьмя пробелами).

программа до цикла
условие:
	первая строка тела
	вторая строка тела
программа после цикла

While — наиболее простой и понятный вид цикла. Его ещё называют циклом с предусловием.

x = 1
while x < 5:
    print(x)
    x += 1  # Означает то же самое, что x = x + 1
>>> 1
>>> 2
>>> 3
>>> 4

С языка Python на русский программу можно перевести так: «Пока икс меньше пяти, печатай икс и прибавляй к нему единицу».

Но в простоте while кроется и опасность: его легко сделать бесконечным. Например, если в коде выше мы уберём x += 1, то получится вот так:

# Этот код будет выполняться бесконечно
x = 1
while x < 5:
    print(x)

Здесь с переменной x ничего не происходит. Она всегда равна единице, поэтому условие цикла никогда не перестанет выполняться. Соответственно, он никогда не завершится.

Чтобы избежать таких ситуаций, при использовании while нужно следить: перестанет ли когда-нибудь выполняться условие? Ещё можно использовать оператор break — о нём мы расскажем чуть позже.

Цикл for программисты используют куда чаще, чем while. Для него мы устанавливаем не условие в чистом виде, а некий массив данных: список, кортеж, строку, словарь, диапазон или любой другой итерируемый объект.

На каждой итерации цикла программа как бы спрашивает: «Остались ли в объекте ещё элементы, по которым я не прошла?»

Допустим, у нас есть список с числами: [14, 101, -7, 0]. Мы можем использовать его вместе с for, чтобы напечатать каждый элемент по отдельности.

num_list = [14, 101, -7, 0]
for number in num_list:
    print(number)
>>> 14
>>> 101
>>> -7
>>> 0

Здесь переменная number обновляется при каждом новом витке цикла. Сначала она хранит в себе первый элемент, потом второй, и так — пока список не закончится.

Как и любую другую переменную, мы могли назвать number как угодно. Часто используют буквы i, j и k. Если внутри цикла мы ни разу не обращаемся к этой переменной, то среди питонистов её принято обозначать символом нижнего подчёркивания _.

Когда нужно применить for к числовому промежутку, его можно задать диапазоном. Для этого используют функцию range(). В неё можно передать от одного до трёх аргументов.

Если аргумент один, то сформируется диапазон от нуля до числа, предшествующего значению аргумента.

for i in range(3):
    print(i)
>>> 0
>>> 1
>>> 2

Если аргумента два, то сформируется диапазон от значения первого аргумента до числа, предшествующего значению второго аргумента.

for i in range(23, 26):
    print(i)
>>> 23
>>> 24
>>> 25

Если аргумента три, то первые два работают, как в прошлом случае. Третий же означает шаг, с которым числа следуют друг за другом.

for i in range(10, 20, 3):
    print(i)
>>> 10
>>> 13
>>> 16
>>> 19

Если в теле цикла for выполняется всего одно действие, синтаксис Python позволяет сократить его запись до:

i for i in iterable_object

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

num_list = [i for i in range(1, 11)]
print(num_list)
>>> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

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

print([i for i in range(1, 11) if i % 2 == 0])
>>> [2, 4, 6, 8, 10]

Конструкция if i % 2 == 0 означает: «если при делении i на 2 остаток равен 0».

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

print([i ** 2 for i in range(1, 11) if i % 2 == 0])
>>> [4, 16, 36, 64, 100]

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

Бывают случаи, когда нужно завершить цикл принудительно, даже если его условие всё ещё выполняется. В таких случаях используют ключевое слово break.

Возьмём строку Hi, loop! и будем выводить каждый её символ. Если встретим запятую, досрочно завершим цикл.

string = 'Hi, loop!'
for i in string:
    if i == ',':
        break
    print(i)
>>> H
>>> i

Если же в строке запятой не будет, то цикл пройдёт по каждому её символу — и только потом завершится.

string = 'Hi loop!'
for i in string:
    if i == ',':
        break
    print(i)
>>> H
>>> i
>>>  
>>> l
>>> o
>>> o
>>> p
>>> !

Иногда возникает необходимость принудительно начать следующий шаг цикла, пропустив часть строк в его теле. Для таких случаев существует ключевое слово continue.

Возьмём числа от 1 до 10 включительно и выведем из них только те, которые не делятся ни на 2, ни на 3.

for i in range(1, 10):
    if i%2 == 0 or i%3 == 0:
        continue
    print(i)
>>> 1
>>> 5
>>> 7

Как видим, если срабатывает условие if (то есть если число делится на 2 или на 3 без остатка), то оставшаяся часть тела не работает — и i не печатается.

Обычно ключевое слово else употребляют в связке с if, но у него есть и другое применение. Его можно использовать вместе с while или for. В таком случае else-код выполнится после того, как пройдут все витки цикла.

Если же цикл досрочно прервётся из-за break, то часть программы в else не выполнится.

Вспомним наш код со строкой Hi, loop! и добавим к нему else.

string = 'Hi, loop!'
for i in string:
    if i == ',':
        break
    print(i)
else:
    print('Цикл завершился без break')
>>> H
>>> i

В строке была запятая, сработал break — не выполнилось else-условие. Теперь уберём из неё запятую и посмотрим, что получится.

string = 'Hi loop!'
for i in string:
    if i == ',':
        break
    print(i)
else:
    print('Цикл завершился без break')
>>> H
>>> i
>>>  
>>> l
>>> o
>>> o
>>> p
>>> !
>>> Цикл завершился без break

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

while 1 == 0:
    print('Эта строка никогда не выполнится')
else:
    print('Цикл завершился без break')
>>> Цикл завершился без break

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

Чтобы цикл был бесконечным, его условие должно выполняться всегда. Это можно сделать разными способами.

# Способ №1 — «пока истинно»
while True:
	pass  # pass — оператор-заглушка, который ничего не делает

Если сделать while False, то цикл, наоборот, никогда не начнётся.

# Способ №2 — «пока проверяемое значение — любое ненулевое число»
while 1:
	pass
while -4:
	pass
while 2023:
	pass

Если сделать while 0, то цикл никогда не начнётся.

# Способ №3 — «пока проверяемое значение — непустой элемент»
while 'string':
	pass
while [False, 'list', 0]:
	pass

Если после while поставить пустой элемент — например, строку str() или список list(), то цикл никогда не начнётся.

# Способ №4 — корректное уравнение
while 1 == 1:
	pass
while 0 != 1:
	pass

Альтернативный подход — поместить после while переменную, в которой лежит подходящее условие. Например:

# Способ №1
condition = True
while condition:
	pass
# Способ №2
condition = 1
while condition:
	pass
# Способ №3
condition = 'string'
while condition:
	pass

В таких случаях для выхода из цикла можно не использовать оператор break, а заменить значение в переменной condition на False, 0, None или любой пустой элемент. Цикл закончит последний виток и завершится, потому что условие больше не будет выполняться.

Такой подход применяется, когда нужно завершить цикл из других мест программы: например, из функции или вложенного цикла. Ещё одно отличие: так как цикл завершается «естественно», без оператора break, в нём выполнится else-код (если он есть).

condition = True
x = 0
while condition:
    print(x)
    x += 1
    if x == 3:
        condition = False
else:
    print('Цикл завершился без break')
>>> 0
>>> 1
>>> 2
>>> Цикл завершился без break

В некоторых языках программирования есть ещё один вид цикла — с постусловием. Он всегда проходит хотя бы один виток и только после этого проверяет, выполняется ли условие. Вот его схема:

Инфографика: Майя Мальгина для Skillbox Media

В Java и C++ такое достигается с помощью конструкции do while, но в Python её нет. Зато можно сделать аналог. Для этого нужно использовать бесконечный цикл, а внутри его тела прописать условие завершения.

x = 12
while True:
    x += 1
    print(x)
    if x > 5:
        break
>>> 13

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

Циклы в Python можно вкладывать друг в друга — то есть в теле одного цикла вызывать другой. Логика программы при этом усложняется.

Инфографика: Майя Мальгина для Skillbox Media

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

while condition:
    pass
    while inner_condition:
        pass
    pass

Напишем программу, которая будет выводить номер итерации внешнего и внутреннего цикла.

for i in range(3):
    print(f'Итерация внешнего цикла: {i}')
    for j in range(2):
        print(f'Итерация внутреннего цикла: {j}')
>>> Итерация внешнего цикла: 0
>>> Итерация внутреннего цикла: 0
>>> Итерация внутреннего цикла: 1
>>> Итерация внешнего цикла: 1
>>> Итерация внутреннего цикла: 0
>>> Итерация внутреннего цикла: 1
>>> Итерация внешнего цикла: 2
>>> Итерация внутреннего цикла: 0
>>> Итерация внутреннего цикла: 1

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

Научитесь: Профессия Python-разработчик
Узнать больше

Добавить комментарий