Содержание
Цикл по переменной
Цикл по переменной (или с параметром) for
применяется в том случае, когда прежде выполнения инструкций тела цикла, становится известным количество шагов, которые должен выполнить этот цикл. Название цикла связано с тем, что в заголовке этого цикла всегда используется счетчик. Счетчику (i
) присваивается начальное значение (a
). В зависимости от языка, на каждом шаге цикла счетчик получает приращение (n
, целое или дробное). Цикл заканчивает свою работу, когда значение счетчика приобретает максимально допустимое значение (b
), определяемое в условии. Обычно блок-схема цикла изображается с помощью блока “модификация”:
Инструкция for
В C++ цикл по переменной реализует инструкция for
. Синтаксис этой инструкции выглядит следующим образом:
for ([инициализация]; [условие]; [модификация]) { Тело цикла; }
Заголовок цикла содержит три необязательных раздела: выражения-инициализации, выражения-условия и выражения-модификации. Выражения, находящееся в этих разделах, можно опускать, но нельзя опускать ";"
. Если отсутствуют все три раздела, то цикл превратится в “бесконечный цикл”, аналогичный while (true)
:
for (;;) {}
Как мы уже отмечали для цикла while
, выход из такого цикла программируется в теле цикла.
Цикл for
выполняет работу в такой порядке:
- В начале происходит инициализация переменной-счетчика. Инициализация производится единожды, до выполнения цикла;
- Затем проверяется выражение-условие. Если выражение имеет значение true, будет выполняться тело цикла;
- После выполнения инструкций тела цикла производится модификация (изменение) величины счетчика (обычно для этого используются операции инкремента или декремента).
for
универсален. Переменная счетчика может быть любого арифметического типа. В процессе работы цикла for
не рекомендуется (но не запрещено) изменять значение счетчика и других переменных влияющих на условие завершения цикла. Но, в таком случае, увеличится как сложность программирования алгоритма, так и сложность его чтения.
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
.
Задача 1. Вывести на экран все степени числа 2
от 1
до 10
.
#include <iostream> #include <iomanip> using namespace std; int main() { unsigned p = 1; for (int i = 1; i <= 10; i++) { p *= 2; cout << "2 ^ " << setw(2) << i << " = " << setw(4) << p << endl; } return 0; }
Вывод
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
Если тело цикла содержит несколько инструкций, то блок заключается в фигурные скобки. Скобки можно опустить, если в теле цикла только одна инструкция. Рассмотрим пример из учебника.
Задача 2. Дано целое n
и действительное a
. Вычислить an (целую степень действительного числа).
#include <iostream> #include <iomanip> using namespace std; int main() { double p = 1., a; int n; cout << "a = "; cin >> a; cout << "n = "; cin >> n; for (int i = 0; i < n; i++) p *= a; cout << a << "^" << n << " = " << fixed << setprecision(5) << p << endl; return 0; }
Вывод
a = 2.1 n = 5 2.1^5 = 40.84101
Блок-схема
C++ поддерживает несколько выражений в разделах инициализации и модификации в заголовке инструкции for
. При этом выражение условия должно быть одно! (Заметим, что это может усложнить чтение алгоритма).
Составим программу вычисления факториала с помощью цикла for
.
#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
;
). Таким образом, данный цикл в теле имеет пустую инструкцию, а все выражения вычисляются в заголовке.Часто значение счетчика используется в теле цикла для изменения какой-либо величины (в программе 9.1.3 – для накопления значения факториала). Приведем пример с черепахой. В этой программе значение счетчика изменяет величину радиуса окружности на каждой итерации.
Задача 3. Построить в центре холста n
разноцветных окружностей с общим центром так, чтобы окружность меньшего радиуса находилась поверх окружности с большим радиусом. Цвета окружностей взять случайным образом. Радиус r
самой большой окружности ввести с клавиатуры.
import turtle as t from random import random, seed ##################### Параметры холста ###################### seed() w = t.Screen() w.reset() w.title('Окружности') w.bgcolor('black') w.setup(width = 600, height = 600, startx = -10, starty = 10) #################### Параметры черепахи ##################### t.speed(10) t.hideturtle() ###################### Решение задачи ####################### r = int(input('r = ')) # Радиус самой большой окружности n = int(input('n = ')) # Количество окружностей t.up() t.goto(0, -r) # Устанавливаем по центру t.down() for i in range(n): R = random() G = random() B = random() t.fillcolor(R, G, B) t.begin_fill() t.circle(r - 30 * i) # Рисуем окружность t.end_fill() t.up() # Переходим к следующей t.lt(90) t.fd(30) t.rt(90) t.down() w.exitonclick() t.mainloop()
Возможный вывод
r = 250
n = 9
Рассмотрим пример задачи с циклом в котором используется условная инструкция.
Задача 4. Дано натуральное число 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 8000
Цикл for основанный на диапазоне (range-based for)
Для перебора последовательности элементов (массива или контейнера) приходится выполнять однотипные действия и, при этом, использовать громоздкий код. Для упрощения работы с массивами в C++ существует специальная форма цикла for
– range-based for
(цикл for
основанный на диапазоне или диапазонный for
). Она вошла в стандарт C++11. Синтаксис этой формы цикла for
:
for (range_declaration : range_expression) { Тело цикла }
Нам пока неизвестно понятие массива, поэтому мы рассмотрим пример работы цикла range-based for
на примере списка инициализации. Список инициализации (англ. – List initialization) – это объект похожий на массив. Он хранит элементы, которые заключены в фигурные скобки и используются для инициализации других объектов. В программе ниже, элементами списка будет инициализироваться переменная i
на каждом шаге цикла.
#include <iostream> using namespace std; int main() { for (auto i : {'a', 'b', 'c', 'd'}) cout << i << " => " << static_cast<int>(i) << endl; return 0; }
Вывод
a => 97 b => 98 c => 99 d => 100
Для автоматического выведения типа, в этом цикле, используется спецификатор auto
. Параметр цикла (i
) поочередно, на каждом шаге, принимает значения элементов последовательности. Данная программа выводит символы списка и их коды. Поскольку за символом скрывается его код в кодовой таблице, мы можем получить этот код путем преобразования символьного типа в целочисленный тип с использованием операции static_cast
. Более подробно работу этого типа цикла мы рассмотрим позднее, при изучении массивов.
Вложенные циклы for
Для вложенных циклов, работающих от счетчика, чаще всего применяется цикл for
, так как он представляет эту структуру в более компактной форме. Приведем простой пример.
Задача 5. Вывести таблицу умножения шестнадцатеричных чисел от 1
до 10
(1610).
#include <iostream> #include <iomanip> using namespace std; int main() { for (int i = 1; i < 17; i++) { for (int j = 1; j < 17; j++) { cout << hex // выводить в 16-ой системе << 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
Блок-схема
Обратите внимание, что переменные внешнего и вложенного циклов должны иметь разные имена!
Вывод по образцу с помощью for
тоже возможен.
Задача 6. Вывести числовой треугольник по образцу:
1 2 1 3 2 1 4 3 2 1 5 4 3 2 1 6 5 4 3 2 1 7 6 5 4 3 2 1 8 7 6 5 4 3 2 1 9 8 7 6 5 4 3 2 1
#include <iostream> using namespace std; int main() { for (int i = 0; i < 10; i++) { for (int j = 0; j < 10 - i; j++) cout << i << " "; cout << "\n"; } return 0; }
Вывод см. выше
Рассмотрим более сложный пример переборного алгоритма реализованного с помощью вложенных for
.
Задача 7. Вывести таблицу истинности для логического выражения:
not(a v b) v (a ^ c)
,
где a
, b
и c
– это логические переменные.
#include <iostream> #include <iomanip> using namespace std; int main() { cout << "a" << setw(3) << "b" << setw(3) << "c" << setw(3) << "f" << endl; for (auto a : {0, 1}) for (auto b : {0, 1}) for (auto c : {0, 1}) cout << a << setw(3) << b << setw(3) << c << setw(3) << (!(a || b) || (a && c)) << "\n"; return 0; }
Вывод
a b c f 0 0 0 1 0 0 1 1 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 1 1 1 0 0 1 1 1 1
Как видите, мы оказались правы – переборные циклы выполненные с помощью for
выглядят значительно компактнее аналогичных c циклом while
.
Заметим, что цикл одного типа может быть вложен в цикл другого типа. Рассмотрим пример.
Задача 8. Найти натуральное число от 100
до 1000
с максимальной суммой делителей
#include <iostream> using namespace std; int main() { int Max = 0, k; for (int j = 100; j < 1001; j++) { int s = 0, i = 2; while (i <= j / 2) { if (j % i == 0) s += i; ++i; } if (s > Max) { Max = s; k = j; } } cout << k << endl; return 0; }
Вывод
960
Рассмотрим пример с черепахой, которая мостит треугольную плитку на холсте с помощью вложенных циклов for
.
Задача 9. Составить программу мощения черепахой равносторонней треугольной плиткой всей площади холста. Размер стороны плитки определить с клавиатуры.
import turtle as t from math import sqrt ##################### Параметры холста ###################### w = t.Screen() w.reset() w.title('Плитка') w.bgcolor('black') w.setup(width = 600, height = 600, startx = -10, starty = 10) #################### Параметры черепахи ##################### t.speed(10) t.hideturtle() t.goto(-300,300) ###################### Решение задачи ####################### d = int(input("Длина стороны -> ")) n = 600 // d # Определяем количество плиток m = 600 // int(d * sqrt(3)) # количество рядов for i in range(m + 1): for j in range(n + 1): # за один шаг рисуем 2 ряда t.fillcolor("DeepSkyBlue4") t.begin_fill() for k in range(3): t.fd(d) t.rt(120) t.end_fill() t.rt(60) t.fillcolor("dark turquoise") t.begin_fill() for k in range(3): t.fd(d) t.rt(120) t.end_fill() t.fd(d) t.fillcolor("dark turquoise") t.begin_fill() for k in range(3): t.fd(d) t.rt(120) t.end_fill() t.rt(60) t.fillcolor("DeepSkyBlue4") t.begin_fill() for k in range(3): t.fd(d) t.rt(120) t.end_fill() t.lt(180) t.fd(d) t.rt(60) t.bk((n + 1) * d) # Перемещаемся к следующему ряду t.rt(120) t.fd(d) t.lt(60) t.fd(d) t.lt(60) w.exitonclick() t.mainloop()
Вывод
d = 70
Замечаем, что в этой программе тоже наличествует повторяющийся код. На следующем уроке мы решим эту проблему.