На одной из предыдущих страниц мы рассмотрели оператор цикла с параметром, поэтому пришло время изучить другие типы циклов – операторы while и repeat.
✎ Операторы цикла while и repeat используются тогда, когда заранее не известно общее количество итераций (повторений вычислений) цикла, а завершение вычислений зависит от некоего условия. Если условие ставится вначале цикла (на входе), то используется оператор While, если на выходе – Repeat.
А теперь расшифруем сказанное: сначала while, а потом ниже repeat.
Оператор цикла while имеет такую структуру:
Это надо понимать так: пока истинно условие, стоящее между while и do, выполняется оператор после do, называемый телом цикла. Перед каждым заходом в цикл проверяется условие: если оно истинно, то выполняется оператор, если ложно, то автоматически осуществляется выход из цикла.
Если оператор в цикле состоит из нескольких операторов, то поместить их нужно в операторные скобки begin - end (сравните оператор цикла for). Не стоит также забывать, что сами операторы разделяются оператором "точка с запятой" (но перед закрывающим END в конце ставить её не обязательно).
Продемонстрируем сказанное на примере вычисления суммы кубов всех чисел от 1 до 10. Из кода видно, что пока b ≤ 10 (строка 7), будем выполнять тело цикла, в котором на каждой итерации к сумме sum прибавляем b3. При последнем вычислении при b=10 вычислим сумму (строка 9), а ниже увеличим b на 1: b=11, поэтому следующим шагом будет выход из цикла, поскольку условие входа b ≤ 10 нарушится.
var
b, sum: integer;
begin
b := 1;
sum := 0; // начальная сумма
while b <= 10 do
begin
sum := sum + b*b*b;
inc(b)
end;
writeln('sum = ', sum); // выводим результат
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647**procedure** Inc(**var** i: integer);
: Увеличивает значение переменной i на 1 Рассмотрим хорошо известный пример с разложением функции ex в ряд Тейлора:
Будем вычислять его значение с точностью, например, 0.000001 (одна миллионная), а само значение обозначим как S. как видно, первое значение ряда (суммы) равно a0=1, а для вычисления каждого последующего члена an+1 предыдущий an нужно умножить на x и разделить на n. Действительно, an+1 = xn+1/(n+1)! = xn·x/(n!·n) = an·x/n. Это и продемонстрировано в программе ниже.
Смысл таков: пока члены ряда больше 0.000001, будет выполняться тело цикла, в котором вычисляется указанная сумма. Как только член ряда a станет меньше или равен 0.000001, происходит выход из цикла, и в конце выводится результат.
var
a, x, S: real;
n: integer;
begin
x := 0.5;
n := 0; // начальный номер
a := 1; // начальное значение члена ряда
S := 0; // начальная сумма
while a > 0.0000001 do
begin
S := S + a;
inc(n);
a := a * x / n
end;
writeln('S = ', S:0:6);
readln
end.
**type** real;
: Представляет число двойной точности с плавающей запятой.Размер: 8 байт Количество значащих цифр: 15 - 16 Диапазон значений: -1.8∙10308 .. 1.8∙10308**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647**procedure** Inc(**var** i: integer);
: Увеличивает значение переменной i на 1 Нужно избегать случая, когда условие входа в цикл всегда истинно, ибо в тогда программа зациклится. Такая ситуация называется "бесконечным циклом". Приведем пример.
begin
while 1 < 100 do
writeln('Hello!')
end.
Данная программа будет выводить приветствие "Hello!" бесконечно, то есть до тех пор, пока вы её не остановите. Происходит это потому, что условие 1 < 100 всегда истинно.
Посмотрите ещё пример с гармоническим рядом или другие программы из раздела While задачника Абрамяна.
Выведите наименьший делитель числа x, отличный от 1
var
x, d: integer;
begin
write('Введите x --> ');
readln(x);
d := 2; { <- минимальный делитель отличный от 1 }
while (x mod d <> 0) do inc(d);
writeln('d = ', d);
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647A **mod** B
- остаток при целочисленном делении А на В**procedure** Inc(**var** i: integer);
: Увеличивает значение переменной i на 1 Напечатать минимальное число, большее 200, которое нацело делится на 17
var
n: integer;
begin
n := 201; { <- минимальное число большее 200 }
while (n mod 17 <> 0) do inc(n);
writeln('Ответ: ', n);
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647A **mod** B
- остаток при целочисленном делении А на В**procedure** Inc(**var** i: integer);
: Увеличивает значение переменной i на 1 Оператор цикла repeat имеет такую структуру:
Отличие оператора цикла repeat от while состоит в том, что в нем условие проверяется на выходе из цикла: если оно не выполняется, то цикл продолжается, если выполнится – сразу выход из цикла. Таким образом, пока условие истинно, программа идет на следующую итерацию, условие нарушается – выходим. Поэтому оператор repeat ещё называют оператором выхода. Ещё в операторе repeat не нужны операторные скобки begin - end для нескольких операторов:
Ещё одной особенностью оператора repeat - until является то, что по крайней мере один раз оператор в теле цикла выполнится, поскольку условие выхода проверяется в конце. Эта особенность приводит к тому, что любой оператор с предусловием while легко может быть преобразован в оператор с послеусловием repeat, а вот далеко не каждый оператор repeat легко записывается с помощью while.
Вычислить корень квадратный из введенного с клавиатуры числа. Запустите программу и попробуйте ввести отрицательное число: каждый раз вас будет возвращать в начало цикла, поскольку выйти из цикла можно, если ввести неотрицательное число x: x >= 0
.
var
x: integer;
begin
repeat
readln(x)
until x >= 0; { <-- выходим, если x>=0 }
writeln('Квадратный корень: ', sqrt(x):0:4);
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647**function** Sqrt(x: real): real;
: Возвращает квадратный корень числа x. Вводить с клавиатуры числа до тех пор, пока их сумма не превысит заданное наперед число. Как видим в строке 11, если сумма sum превысит число М, то выходим из цикла и выводим результат:
var
x, M, sum: real;
begin
write('Введите контрольное число --> ');
readln(M);
sum := 0; { <- начальное значение суммы }
repeat
readln(x); { <- вводим x }
sum := sum + x { <- к сумме прибавляем x }
until sum > M; { <== выходим, если сумма превысит M }
writeln('Результат: ', sum);
readln
end.
**type** real;
: Представляет число двойной точности с плавающей запятой.Размер: 8 байт Количество значащих цифр: 15 - 16 Диапазон значений: -1.8∙10308 .. 1.8∙10308 Вводится последовательность чисел, 0-конец последовательности. Определить, содержит ли последовательность хотя бы два равных соседних числа.
var
n, pre: integer;
twoEqual: boolean;
begin
twoEqual := false; { <- нету пока равных чисел }
pre := 0; { <- начальное значение предыдущего числа }
writeln('Введите элементы последовательности:');
repeat
read(n);
{ Вычисление twoEqual имеет смысл, если мы ещё не встретили два
равных соседних числа (т.е. при twoEqual=false). В таком случае,
если последнее введенное число n равно предыдущему pre (pre=n),
то индикатор twoEqual станет истинным и больше не изменится: }
if not twoEqual then twoEqual := (n = pre);
pre := n { <- предыдущим pre становится n }
until n = 0; { <- выходим при вводе 0 }
writeln;
if twoEqual then writeln('Содержит')
else writeln('Не содержит');
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647**type** boolean;
: Представляет логическое значение.**const** false = False;
: Представляет логическое значение. Вводится последовательность из N целых чисел. Найти наибольшее из всех отрицательных чисел. Первый вариант решения.
var
N, i, a, maxNegNum: integer;
begin
i := 0; { <-- Номера вводимых чисел }
N := 10; { <-- Количество вводимых чисел }
{ Начальное значение максимального отрицательного числа: }
maxNegNum := -MaxInt-1;
repeat
inc(i); { <-- Увеличиваем номер вводимого числа }
read(a); { <-- Ввордим число }
{ Поскольку нужно найти максимальное число только среди
отрицательных элементов, то положительные или нулевые
элементы нас не интересуют: для этого достаточно добавить
условие a<0. Потом при каждой итерации будем сравнивать
введенное число а с максимальным maxNegNum: если оно(число)
больше максимального, то заменим ним максииальное: maxNegNum=а. }
if (a < 0) and (a > maxNegNum) then maxNegNum := a
until i = N; { <-- Выходим из цикла, если введены все N чисел }
writeln('Ответ: ', maxNegNum); { <-- Выводим результат }
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647**procedure** Inc(**var** i: integer);
: Увеличивает значение переменной i на 1 Вводится последовательность из N целых чисел. Найти наибольшее из всех отрицательных чисел. Второй вариант решения.
var
N, i, a, maxNegNum: integer;
begin
i := 0; { <-- Количество введенных чисел }
N := 10; { <-- Количество чисел для ввода }
maxNegNum := 0;{ <-- Максимальное значение }
writeln('Введите ', N, ' целых чисел:');
repeat
inc(i);
write(i, ') ');
readln(a);
if (a < 0) then { <-- Проверяем только отрицательные числа }
{ Когда в наборе мы нашли первое отрицательное число,
величина maxNegNum равна 0, поэтому меняем её на а – это
и будет первое ненулевое значение для maxNegNum: }
if maxNegNum = 0 then maxNegNum := a
else { В остальных случаях maxNegNum сравниваем с а и находим большее: }
if (a > maxNegNum) then maxNegNum := a
until i = N; { <-- Выходим, когда введены все числа }
{ Когда переменная maxNegNum отрицательная, то это означает, что в наборе
чисел есть отрицательные – тогда выводим максимальное из них, в противном
случае сообщаем об отсутствии отрицательных чисел: }
if maxNegNum < 0 then writeln('Максимальное отрицательное число: ', maxNegNum)
else writeln('В последовательности нет отрицательных чисел');
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647**procedure** Inc(**var** i: integer);
: Увеличивает значение переменной i на 1 Вводится последовательность из N целых чисел. Найти наибольшее из всех отрицательных чисел. Третий способ решения.
Этот вариант решения – это по сути перефразировка предыдущего способа, только здесь используется дополнительная логическая переменная-индикатор bln
для указания присутствия или отсутствия отрицательных чисел. Сначала bln
ставим false
(строка 8). Заходим в цикл, вводим числа, и как только нашли отрицательное число (строка 14), первый раз, когда ещё bln=false
, (или not bln
), запоминаем это число как maxNegNum, а значение логической переменной меняем: bln=true
(строки 15 – 18), что означает наличие отрицательных чисел в наборе. Для остальных отрицательных элементов сравниваем введенное а и maxNegNum, и запоминаем максимальное из них как maxNegNum – оно и будет максимальным среди отрицательных (строки 19 – 20).
var
N, i, a, maxNegNum: integer;
bln: boolean;
begin
i := 0;
N := 10;
bln := false;
writeln('Введите ', N, ' целых чисел:');
repeat
inc(i);
write(i, ') ');
readln(a);
if (a < 0) then
if not bln then begin
maxNegNum := a;
bln := true
end
else
if (a > maxNegNum) then maxNegNum := a
until i = N;
if not bln then writeln('Нет отрицательных чисел')
else writeln('Ответ: ', maxNegNum);
readln
end.
**type** integer;
: Представляет 32-битовое целое число со знаком.Диапазон значений: -2 147 483 648 .. 2 147 483 647**type** boolean;
: Представляет логическое значение.**const** false = False;
: Представляет логическое значение.**procedure** Inc(**var** i: integer);
: Увеличивает значение переменной i на 1**const** true = True;
: Представляет логическое значение. Это коротко об операторах цикла while и repeat. Что еще упущено выше, так это возможность каждый цикл с параметром for преобразовать в оператор с предусловием while или послеусловием repeat. Другие задачи на использование этих операторов вы найдете в разделе while по ссылке Задачник, или разделе Proc (процедуры и функции) того же раздела. Если что не понятно, то комментарии находятся ниже.