Root /ArchiveAbout
()

Операторы цикла while и repeat

Операторы цикла while и repeat

На одной из предыдущих страниц мы рассмотрели оператор цикла с параметром, поэтому пришло время изучить другие типы циклов – операторы 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 в ряд Тейлора:

Разложение функции e^x в ряд в Pascal

Будем вычислять его значение с точностью, например, 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 647 A **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 647 A **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 (процедуры и функции) того же раздела. Если что не понятно, то комментарии находятся ниже.