§9 Инструкция цикла с параметром for. Цикл for основанный на диапазоне

Цикл с параметром. Инструкция for

Инструкция цикла for реализует алгоритмическую структуру цикл с параметром (или цикл со счетчиком). Цикл for применяется в том случае, когда в программе, прежде выполнения инструкций цикла, становится известным (или заранее определено) количество шагов этого цикла. В блок-схеме инструкция for изображается следующим образом:
17
Синтаксис:

for (инициализация; условие; модификация) {
    Инструкции тела цикла;
}

Если в теле цикла одна инструкция, то { } можно опустить. Переменная-параметр цикла (счетчик) может быть любого числового типа. Это делает цикл for C++ таким же универсальным, как и цикл типа while. В разделе модификации чаще всего используется операция постфиксного или префиксного инкремента (или декремента), но может использоваться любое выражение с присваиванием, изменяющее значение параметра цикла. Цикл работает следующим образом:

  • В начале происходит описание и инициализация переменной-счетчика
  • Далее проверка условия: если выражение имеет значение true, произойдет итерация
  • После выполнения инструкций тела цикла производится модификация величины счетчика

Примечание: в C++ является правилом делать описание переменной-счетчика в заголовке цикла. Но это не обязательно, тем более, если планируется инициализировать несколько переменных в разделе инициализации так, как это реализовано в программе 9.2. Однако, использование описания переменной-счетчика в заголовке цикла приводит к описанию локальной переменной, уничтожаемой автоматически при завершении работы цикла. Поэтому, без крайней необходимости, описание переменной-счетчика вне цикла for производить не следует.
В процессе работы цикла for не рекомендуется изменять операнды в выражениях заголовка цикла – это приведет к разного рода ошибкам! Но сами значения переменных (или констант), в том числе изменяемые значения (счетчик), использовать можно. Рассмотрим классический пример.

Программа 9.1 Дано натуральное число N. Вывести все делители этого числа.

#include <iostream>
using namespace std;

int main() {
	int N;
	cout << "N = "; cin >> N;
	for (int i = 2; i < N / 2; i++) {
		if (N % i == 0)
			cout << i << " ";
	}
	return 0;
}
N = 16000
2 4 5 8 10 16 20 25 32 40 50 64 80 100 125 128 160 200 250 320 400 500 640 800 1000 1600 2000 3200 4000 

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

При использовании инструкции continue в цикле for необходимо учитывать особенности работы этого цикла:

  • Инструкции, следующие после continue, будут пропущены
  • Затем происходит модификация счетчика
  • Переход к выполнению следующей итерации (иначе, проверки условия)
Покажем это на примере:

int main() {
	for (int i = 1; i < 20; i++) {
		if (i % 2 == 0) continue;
		cout << i << " ";
	}
1 3 5 7 9 11 13 15 17 19 

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

int main() {
	for (int i = 1; i < 20; i += 2) 
		cout << i << " ";

Несколько выражений в разделе инициализации и модификации

Как мы уже отметили ранее в заголовке инструкции for должно быть три раздела. Выражения, находящееся в этих разделах, можно опускать, но нельзя опускать ";". В конце концов, можно оставить только ;. Заголовок в виде:

for (;;) { ... }

является заголовком “бесконечного” цикла. (Выход из цикла должен программироваться внутри тела цикла).
C++ поддерживает несколько выражений в разделах инициализации и модификации в заголовке инструкции for. При этом условие продолжения цикла должно быть одно!
Например. Постановка задачи: Вычислить факториал числа, не превосходящий 20.
Программа 9.2

#include <iostream>
using namespace std;

int main() {
	unsigned long long n;
	int i, k;
	cout << "k = "; cin >> k; // 0 <= k <= 20
	for(n = 1, i = 1; i <= k; n *= i, ++i);
	cout << k << "! = " << n << endl;
	return 0;
}
k = 20
20! = 2432902008176640000

Примечание: обратите внимание, что поток вывода в строке 12 не относится к телу цикла! (В конце заголовка – ;). Таким образом, данный цикл в теле имеет пустую инструкцию, а все выражения вычисляются в заголовке. Программа 9.2 правильно вычисляет факториал числа от 0 до 20.

Цикл for основанный на диапазоне (range-based for)

Для перебора элементов массива или контейнера приходится выполнять однотипные действия, при этом использовать громоздкий код. Для упрощения работы с контейнерами в C++ существует специальная форма цикла for range-based for (цикл for основанный на диапазоне или диапазонный for).
Синтаксис:

for (объявление : имя_последовательности) 
   loop_statement

Использование range-based for на примере C-массива:
Программа 9.3

#include <iostream>
using namespace std;

int main() {
	int x[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (auto &s : x) {
		cout << s << " ";
	}
	return 0;
}

Чтобы элементы массива можно было изменять – переменная s должна быть ссылочной переменной (как в примере выше). Если переменная не является ссылкой, то данные будут копироваться. Для автоматического выведения типа в этом цикле используется спецификатор auto. range-based for имеет ограничение на работу с динамическими массивами: он не поддерживает изменение размера массива, так как содержит фиксированный указатель конца массива. При работе с массивами, имеющими фиксированный размер, диапазонный for является прекрасной и безопасной альтернативой обычному for.

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

Так же, как и другие инструкции циклов, for поддерживает структуру вложенных циклов. Применение вложенных циклов for для организации ввода и вывода двумерных массивов выглядит гораздо компактнее, чем при использовании цикла while.
Однако, при решении задач обхода таких массивов, необходимо избегать применение условной инструкции if. Зачастую, задачу можно реализовать более рационально, путем манипуляции индексами (переменными цикла i и j). То есть, поставить в зависимость изменение одного индекса, от значения величины другого. Рассмотрим два примера.
Программа 9.4 Дана квадратная матрица размера n, элементы которой равны 0. Заполнить элементы, лежащие ниже и на самой главной диагонали единицами.

#include <iostream>
using namespace std;

int main() {
	int n;
	cout << "n = "; cin >> n;
	int mas[n][n];
	// Заполняем нулями
	for(int i = 0; i < n; i++)
		for(int j = 0; j < n; j++)
			mas[i][j] = 0;
	// Реализация
	for(int i = 0; i < n; i++)
		for(int j = 0; j <= i; j++)
			mas[i][j] = 1;
	// Вывод
	for(int i = 0; i < n; i++) {
		for(int j = 0; j < n; j++) {
			cout.width(2);
			cout << mas[i][j];
		}
		cout << '\n';
	}
	return 0;
}
n = 10
 1 0 0 0 0 0 0 0 0 0
 1 1 0 0 0 0 0 0 0 0
 1 1 1 0 0 0 0 0 0 0
 1 1 1 1 0 0 0 0 0 0
 1 1 1 1 1 0 0 0 0 0
 1 1 1 1 1 1 0 0 0 0
 1 1 1 1 1 1 1 0 0 0
 1 1 1 1 1 1 1 1 0 0
 1 1 1 1 1 1 1 1 1 0
 1 1 1 1 1 1 1 1 1 1

Программа 9.5 Составить программу заполнения массива числами треугольника Паскаля и вывода этого массива. Треугольник паскаля имеет вид:
64515410
В этом треугольнике на вершине и по бокам стоят единицы (в программе 9.5 треугольник “положен на бок” – стороны треугольника: первый столбец и главная диагональ). Каждое число равно сумме двух чисел, расположенных над ним. Строки треугольника симметричны относительно вертикальной оси и содержат биноминальные коэффициенты.

#include <iostream>
using namespace std;

int main() {
	int n;
	cout << "n = "; cin >> n;
	int pas[n][n];

	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			pas[i][j] = 0;
	pas[0][0] = 1;
	for (int i = 1; i < n; i++) {
		pas[i][0] = 1;
		for (int j = 1; j <= i; j++) {
			pas[i][j] = pas[i - 1][j - 1] + pas[i - 1][j];
		}
	}
	for (int i = 0; i < n; i++) {
		for (int j = 0; j <= i; j++) {
			cout.width(4);
			cout << pas[i][j];
		}
		cout << '\n';
	}
	return 0;
}
n = 12
   1
   1   1
   1   2   1
   1   3   3   1
   1   4   6   4   1
   1   5  10  10   5   1
   1   6  15  20  15   6   1
   1   7  21  35  35  21   7   1
   1   8  28  56  70  56  28   8   1
   1   9  36  84 126 126  84  36   9   1
   1  10  45 120 210 252 210 120  45  10   1
   1  11  55 165 330 462 462 330 165  55  11   1
Вопросы
  1. Может ли быть заменена в программе инструкция цикла for на инструкцию цикла while? Всегда ли это можно сделать?
  2. Когда удобнее применять для организации циклов инструкцию for? while?
  3. Возможны ли в заголовке инструкции for следующие выражения:
        a) for (;a > b && !(a % 2);)
        b) for (a > b;;)
        c) for (;;i = 0)
        d) for (;i = 0;)
        e) for (;;i++, --b)
        f) for (--i;;)
        g) for (b = 0; b != a;) ?
  4. Переменная i – параметр внешнего цикла, а j – вложенного. Доступна ли будет переменная j во внешнем цикле? i во вложенном цикле?
Учебник

§58

Домашняя работа
  1. Зад. 29. Напишите программу, в которой вводятся натуральные числа a и b, а на дисплей выводятся все простые числа в диапазоне от a до b (идея алгоритма Программа 8.5)
  2. Зад. 30. Совершенным называется число, равное сумме всех своих делителей, меньших его самого (например, число 6 = 1 + 2 + 3). Напишите программу, которая вводит натуральное число N и определяет, является ли число N совершенным.
  3. Напишите программу, которая выводит на экран квадратную числовую таблицу размера n x n, имеющую следующий вид при n = 10:
    1  *  *  *  *  *  *  *  *  *
    *  2  *  *  *  *  *  *  *  *
    *  *  3  *  *  *  *  *  *  *
    *  *  *  4  *  *  *  *  *  *
    *  *  *  *  5  *  *  *  *  *
    *  *  *  *  *  6  *  *  *  *
    *  *  *  *  *  *  7  *  *  *
    *  *  *  *  *  *  *  8  *  *
    *  *  *  *  *  *  *  *  9  *
    *  *  *  *  *  *  *  *  * 10
    
Литература
  1. Лафоре Р. Объектно-ориентированное программирование в C++ (4-е изд.). Питер: 2004
  2. Прата, Стивен. Язык программирования C++. Лекции и упражнения, 6-е изд.: Пер. с англ. — М.: ООО «И.Д. Вильяме», 2012
  3. Липпман Б. Стенли, Жози Лажойе, Барбара Э. Му. Язык программирования С++. Базовый курс. Изд. 5-е. М: ООО “И. Д. Вильямс”, 2014
  4. Эллайн А. C++. От ламера до программера. СПб.: Питер, 2015
  5. Шилдт Г. С++: Базовый курс, 3-изд. М.: Вильямс, 2010


Print Friendly, PDF & Email

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