§ 9.1. Программирование циклических алгоритмов. Цикл по переменной (for)

Школьный курс python
Содержание

Цикл по переменной

Цикл по переменной (или с параметром) for применяется в том случае, когда прежде выполнения инструкций тела цикла, становится известным количество шагов, которые должен выполнить этот цикл. Название цикла связано с тем, что в заголовке этого цикла всегда используется счетчик. Счетчику (i) присваивается начальное значение (a). В зависимости от языка, на каждом шаге цикла счетчик получает приращение (n, целое или дробное). Цикл заканчивает свою работу, когда значение счетчика приобретает максимально допустимое значение (b), определяемое в условии. Обычно блок-схема цикла изображается с помощью блока “модификация”:

Инструкция for

В C++ цикл по переменной реализует инструкция for. Синтаксис этой инструкции выглядит следующим образом:

for ([инициализация]; [условие]; [модификация]) {
    Тело цикла;
}

Заголовок цикла содержит три необязательных раздела: выражения-инициализации, выражения-условия и выражения-модификации. Выражения, находящееся в этих разделах, можно опускать, но нельзя опускать ";". Если отсутствуют все три раздела, то цикл превратится в “бесконечный цикл”, аналогичный while (true):

for (;;) {}

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

  • В начале происходит инициализация переменной-счетчика. Инициализация производится единожды, до выполнения цикла;
  • Затем проверяется выражение-условие. Если выражение имеет значение true, будет выполняться тело цикла;
  • После выполнения инструкций тела цикла производится модификация (изменение) величины счетчика (обычно для этого используются операции инкремента или декремента).
В C++ цикл for универсален. Переменная счетчика может быть любого арифметического типа. В процессе работы цикла for не рекомендуется (но не запрещено) изменять значение счетчика и других переменных влияющих на условие завершения цикла. Но, в таком случае, увеличится как сложность программирования алгоритма, так и сложность его чтения.

В C++ является правилом создавать определение переменной-счетчика в заголовке цикла. Но это не обязательно! (См. программу 9.1.3). Однако использование описания переменной-счетчика в заголовке цикла приводит к описанию локальной переменной, уничтожаемой автоматически при завершении работы цикла. Поэтому, без крайней необходимости, описание переменной-счетчика вне цикла 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.

Программа 9.1.1
#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 (целую степень действительного числа).

Программа 9.1.2
#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.

Программа 9.1.3
#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 не относится к телу цикла! (В конце заголовка – ;). Таким образом, данный цикл в теле имеет пустую инструкцию, а все выражения вычисляются в заголовке.

Часто значение счетчика используется в теле цикла для изменения какой-либо величины (в программе 9.1.3 – для накопления значения факториала). Приведем пример с черепахой. В этой программе значение счетчика изменяет величину радиуса окружности на каждой итерации.
Задача 3. Построить в центре холста n разноцветных окружностей с общим центром так, чтобы окружность меньшего радиуса находилась поверх окружности с большим радиусом. Цвета окружностей взять случайным образом. Радиус r самой большой окружности ввести с клавиатуры.

Программа 9.1.4
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. Вывести все делители этого числа.

Программа 9.1.5
#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++ существует специальная форма цикла forrange-based for (цикл for основанный на диапазоне или диапазонный for). Она вошла в стандарт C++11. Синтаксис этой формы цикла for:

for (range_declaration : range_expression) {
    Тело цикла
}

Нам пока неизвестно понятие массива, поэтому мы рассмотрим пример работы цикла range-based for на примере списка инициализации. Список инициализации (англ. – List initialization) – это объект похожий на массив. Он хранит элементы, которые заключены в фигурные скобки и используются для инициализации других объектов. В программе ниже, элементами списка будет инициализироваться переменная i на каждом шаге цикла.

Программа 9.1.6
#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).

Программа 9.1.7
#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
Программа 9.1.8
#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 – это логические переменные.

Программа 9.1.9
#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 с максимальной суммой делителей

Программа 9.1.10
#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. Составить программу мощения черепахой равносторонней треугольной плиткой всей площади холста. Размер стороны плитки определить с клавиатуры.

Программа 9.1.11
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

Замечаем, что в этой программе тоже наличествует повторяющийся код. На следующем уроке мы решим эту проблему.

Приложение

Примеры решения задач
Вопросы
Темы сообщений
Задания А
Задания Б
Задания С
Ссылки
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 5,00 из 5)
Загрузка...

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.


Обсуждение закрыто.