§8 Инструкции циклов. Инструкции while, do while, break и continue. Вложенные циклы

Инструкции циклов

Циклы предназначены для повторяющегося выполнения одних и тех же инструкций по условию. В C++ имеется несколько разновидностей инструкций циклов:

  • Цикл с предусловием while
  • Цикл с постусловием do while
  • Цикл с параметром for

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

Инструкция while

Алгоритмическую структуру “цикл с предусловием” в языке программирования C++ реализует инструкция while. Блок-схема:
15
Синтаксис

while (условие) {
    Инструкции тела цикла
}
  • Условие выхода из цикла должно быть скалярным выражением. Пока условие имеет значение, отличное от нуля или true выполняется тело цикла;
  • Если проверяемое условие ложно изначально, то инструкции, входящие в тело цикла, не выполняются, а выполняются инструкции, находящиеся за блоком;
  • Если в теле цикла только одна инструкция, то { } можно опустить;
  • “Бесконечный цикл” можно организовать следующими условиями: while (true) {} или while (1) {}

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

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
	int n, p = 1, i = 0;
	cout << "Введите число > 0 и < 31" << endl;
	cout << "n = "; cin >> n;
	cout.setf(ios::left);
	while (i < n) {
		++i;
		p *= 2; 
		cout << "2 ^ " << setw(2)
			 << i << " = "
			 << p << endl;
	}
	return 0;
}
Введите число > 0 и < 31
n = 10
2 ^ 1  = 2
2 ^ 2  = 4
2 ^ 3  = 8
2 ^ 4  = 16
2 ^ 5  = 32
2 ^ 6  = 64
2 ^ 7  = 128
2 ^ 8  = 256
2 ^ 9  = 512
2 ^ 10 = 1024

Эту программу можно переписать с использованием операции побитового сдвига влево на 1 (количество сдвигов будет равносильно возведению в соответствующую степень числа 2). Тогда инструкцию в строке 15 нужно переписать так:

p = p << 1; // или p <<= 1;

Данная операция низкоуровневая и, следовательно, выполняется гораздо быстрее. При побитовом сдвиге происходит сдвиг битов исходного числа (влево на 1, для данного примера).
Тот факт, что в качестве условия может быть любое выражение скалярного типа широко используется для того, чтобы сделать код программы более компактным. Например: while((ch = cin.get()) != '#'), или while(cin >> str), или while(n--). Такой подход очень удобен, но при этом не следует забывать, что код программы должен быть понятен. К тому же сложные условия таят "подводные камни". Неверно составленный программный код может привести к "зацикливанию" (невозможности выхода из цикла). Рассмотрим пример.
Программа 8.2 Дано целое число N и набор из N вещественных чисел (вводятся с клавиатуры). Вывести сумму и произведение чисел из данного набора.

#include <iostream>
using namespace std;

int main() {
	int n, s = 0, p = 1, d;
	cout << "n = "; cin >> n;
	while (n--) {
		cin >> d;
		s += d;
		p *= d;
    }
	cout << "s = " << s << endl
		 << "p = " << p << endl;
    return 0;
}
n = 10
1 2 3 4 5 6 7 8 9 2
s = 47
p = 725760

Инструкция do while

Алгоритмическую структуру "цикл с постусловием" в языке программирования C++ реализует инструкция do while. Блок-схема:
16
Синтаксис

do {
    Инструкции тела цикла
} while (условие)
  • Тело цикла (одна или более инструкций) будет выполнено хотя бы один раз;
  • Пока условие имеет значение, отличное от нуля или true - выполняется тело цикла;
  • Если проверяемое условие ложно изначально, то инструкции, входящие в тело цикла, будут выполнены один раз, затем произойдет выход из цикла.

Эту разновидность цикла удобно использовать, когда необходимо анализировать входные данные.
В составленной нами ранее программе 8.1 есть явный изъян: мы просим пользователя не вводить число больше определенного значения, но сам ввод не контролируем. Исправить ситуацию поможет цикл do while:
Программа 8.3 См. пр. 8.1

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
	int n, p = 1, i = 0;
	do {
		cout << "n = ";
		cin >> n;
		if (n > 30 || n <= 0)
			cout << "Введите число меньше 30"
					"но больше 0"
				 << endl;
	} while (n > 30 || n <= 0);
	cout.setf(ios::left);
	while (i < n) {
		++i;
		p *= 2;
		cout << "2 ^ " << setw(2)
			 << i << " = "
			 << p << endl;
	}
	return 0;
}

Теперь программа не продолжит свою работу до тех пор, пока пользователь не выполнит обязательные требования. Ещё один пример.
Программа 8.4 Вводится последовательность чисел, 0 - конец последовательности. Определить, содержит ли последовательность хотя бы два равных соседних числа. В последовательности должно быть больше 2-х чисел.

#include <iostream>
using namespace std;

int main() {
	int a, b, i = 0;
	bool f = false;
	cout << "Введите числа последовательности:" << endl;
	cout << "[" << ++i << "] => "; cin >> a;
	cout << "[" << ++i << "] => "; cin >> b;
	do {
		if (a == b) f = true;
		a = b;
		cout << "[" << ++i << "] => "; cin >> b;
	} while (b != 0);
	if (f) cout << "Найдены равные соседи.";
	else   cout << "Равных соседей нет.";
	return 0;
}
Введите числа последовательности:
[1] => 3
[2] => 4
[3] => 7
[4] => 2
[5] => 2
[6] => 9
[7] => 5
[8] => 0
Найдены равные соседи.

Разработаем с помощью цикла do while и функции random() небольшую игровую программу "Угадай число!". Суть игры в том, чтобы за n-е число попыток пользователь смог угадать, какое число (случайное число генерируемое random()) загадал компьютер. Если игрок не уложится в необходимое число попыток, то выиграл компьютер, иначе победил игрок.
Программа 8.5

#include <iostream>
#include <random>
#include <ctime>
using namespace std;

int main() {
    int n = 10, k, a;
    default_random_engine rnd(time(0));
    uniform_int_distribution<unsigned> d(1, 100);
    k = d(rnd);
    cout << "У вас " << n << " попыток" << endl;
    do {
        cout << "Попытка " << 11 - n
             << ". Какое число загадал компьютер?\n -> ";
        cin >> a;
        if (a > k)
            cout << " много" << endl;
        else
            if (a < k)
                cout << " мало" << endl;
            else
                cout << "Вы выиграли!" << endl;
        --n;
        if (!n) {cout << "Вы проиграли!"; break;}
    } while (a != k);
    return 0;
}
У вас 10 попыток
Попытка 1. Какое число загадал компьютер?
 -> 50
 мало
Попытка 2. Какое число загадал компьютер?
 -> 60
 мало
Попытка 3. Какое число загадал компьютер?
 -> 70
Вы выиграли!

Инструкции break и continue

В программе 8.5 для того чтобы прервать игру, если превышено число попыток, используется инструкция break. Инструкция break прерывает выполнение циклов while, do while и for, а также инструкцию выбора switch и передает управление программой инструкции, следующей за блоком, в котором она находится.
Программа 8.6 Дано натуральное число n. Определить, является ли оно простым.

#include <iostream>
using namespace std;

int main() {
	int n, i = 2;
	bool k = true;
	cout << "n = "; cin >> n;
	while (i <= n / 2) {
		if (!(n % i)) {
			k = false;
			break;
		}
		i++;
	}
	cout << "Число "
		 << n
		 << (k ? " простое" : " не является простым");
	return 0;
}
n = 15
Число 15 не является простым

Если найден делитель числа, то нет необходимости просматривать все возможные делители числа, достигнув граничного условия. Нужно немедленно прервать выполнение цикла.
В отличие от break - continue прерывает выполнение только текущей итерации и передает выполнение на вычисление условия. При этом инструкции, следующие за continue до конца блока, игнорируются. Данная инструкция применяется не часто, но может быть использована для контроля допустимости значений в выражениях тела цикла.
Требуется вывести значения функции f(x) = 1 / x с шагом 1 на отрезке от -20 до 20. Значение x == 0, в котором функция неопределена, должно быть пропущено.
Программа 8.7

#include <iostream>
using namespace std;

int main() {
	int i = -20;
	while (i <= 20) {
		if (!i) {
			i++;
			continue;
		}
		cout << showpos
			 << scientific
			 << 1 / double(i)
			 << endl;
		i++;
	}
	return 0;
}
-5.000000e-02
-5.263158e-02
...
-1.000000e+00
+1.000000e+00
...
+5.263158e-02
+5.000000e-02

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

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

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

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
	int i = 1;
	while (i < 17) {
		i++;
		int j = 1;
		while (j < 17) {
			j++;
			cout << hex
				 << uppercase
				 << setw(4)
				 << i * j;
		}
		cout << '\n';
	}
	return 0;
}
   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F  10
   2   4   6   8   A   C   E  10  12  14  16  18  1A  1C  1E  20
   3   6   9   C   F  12  15  18  1B  1E  21  24  27  2A  2D  30
   4   8   C  10  14  18  1C  20  24  28  2C  30  34  38  3C  40
   5   A   F  14  19  1E  23  28  2D  32  37  3C  41  46  4B  50
   6   C  12  18  1E  24  2A  30  36  3C  42  48  4E  54  5A  60
   7   E  15  1C  23  2A  31  38  3F  46  4D  54  5B  62  69  70
   8  10  18  20  28  30  38  40  48  50  58  60  68  70  78  80
   9  12  1B  24  2D  36  3F  48  51  5A  63  6C  75  7E  87  90
   A  14  1E  28  32  3C  46  50  5A  64  6E  78  82  8C  96  A0
   B  16  21  2C  37  42  4D  58  63  6E  79  84  8F  9A  A5  B0
   C  18  24  30  3C  48  54  60  6C  78  84  90  9C  A8  B4  C0
   D  1A  27  34  41  4E  5B  68  75  82  8F  9C  A9  B6  C3  D0
   E  1C  2A  38  46  54  62  70  7E  8C  9A  A8  B6  C4  D2  E0
   F  1E  2D  3C  4B  5A  69  78  87  96  A5  B4  C3  D2  E1  F0
  10  20  30  40  50  60  70  80  90  A0  B0  C0  D0  E0  F0 100

Один вид цикла может быть вложен в цикл другого вида. Рассмотрим классическую задачу разложения на простые множители.
Программа 8.9 Дано целое число. Разложить число на простые множители.

#include <iostream>
using namespace std;

int main() {
	int n;
	cout << "n = "; cin >> n;
	int k = n;
	for (int i = 2; i <= n / 2; i++) {
		while (k % i == 0) {
			k /= i;
			cout << i << " ";
		}
	}
	return 0;
}

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

Вопросы

1. Назовите отличия циклов while и do while. Можно ли заменить цикл while на do while? А наоборот?
2. Можно ли заменить инструкции break и continue на другие конструкции языка?
3. Когда применяется структура вложенных циклов?
4. Что такое итерация?

Домашняя работа
  1. Зад. 9 учебника. Напишите программу, которая выводит все пятизначные числа, которые при делении на 133 дают в остатке 125, а при делении на 134 дают в остатке 111.
  2. Зад. 13. Напишите программу, которая считает количество четных цифр введенного целого числа N, 0 < N < 4000000000
  3. Даны целые положительные числа N и K. Найти сумму 1K + 2K + … + NK. (Использовать структуру вложенных циклов). Чтобы избежать целочисленного переполнения, вычислять слагаемые этой суммы с помощью вещественной переменной и выводить результат как вещественное число.
Учебник

§58

Презентация к уроку

lesson-8

Литература
  1. Лафоре Р. Объектно-ориентированное программирование в C++ (4-е изд.). Питер: 2004
  2. Прата, Стивен. Язык программирования C++. Лекции и упражнения, 6-е изд.: Пер. с англ. — М.: ООО «И.Д. Вильяме», 2012
  3. Липпман Б. Стенли, Жози Лажойе, Барбара Э. Му. Язык программирования С++. Базовый курс. Изд. 5-е. М: ООО "И. Д. Вильямс", 2014
  4. Эллайн А. C++. От ламера до программера. СПб.: Питер, 2015
  5. Шилдт Г. С++: Базовый курс, 3-изд. М.: Вильямс, 2010


Print Friendly, PDF & Email

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