Циклы в Pascal: for, while и repeat-until с примерами

Циклы в Pascal: for, while и repeat-until с примерами Полезное

Цикл в Pascal — это оператор, который выполняет фрагмент кода повторно: заданное число раз или пока истинно условие. В языке три вида циклов: for (со счётчиком), while ... do (условие проверяется до тела) и repeat ... until (условие проверяется после тела). Ниже — синтаксис каждого, правило выбора цикла по признаку задачи, таблица сравнения, примеры на Free Pascal с выводом и три типичные ошибки с исправлением.

Что такое Pascal и зачем он сегодня

Pascal — язык программирования, который Никлаус Вирт разработал в 1970 году как учебный язык для структурного программирования. Он строгий к типам и читается почти как псевдокод, поэтому до сих пор используется в школах и на первых курсах: базовые конструкции (ветвления, циклы, процедуры, массивы) в нём видны «в чистом виде». В заданиях ЕГЭ по информатике фрагменты программ приводятся в том числе на Pascal.

В коммерческой разработке язык встречается редко, но живые реализации есть: открытый компилятор Free Pascal с IDE Lazarus (кроссплатформенный, активно поддерживается), Delphi (коммерческий Object Pascal для настольных и мобильных приложений) и PascalABC.NET, ориентированный на обучение. Примеры в статье написаны под Free Pascal; в других реализациях синтаксис циклов такой же, различия касаются только расширений вроде break и continue.

Три цикла Pascal: синтаксис и как они работают

У любого цикла есть заголовок (условие или счётчик) и тело — оператор, который повторяется. Тело — ровно один оператор; чтобы повторять несколько, их заключают в begin ... end. Исключение — repeat ... until: сами ключевые слова ограничивают тело, begin ... end там не нужен.

Цикл for — известно число повторений

for счётчик := начало to конец do оператор;
for счётчик := начало downto конец do оператор;

Счётчик — переменная порядкового типа (integer, char, boolean, перечисление). С to он увеличивается на 1, с downto — уменьшается на 1; другого шага у for в Pascal нет. Границы вычисляются один раз перед первым проходом, поэтому изменение конец внутри тела ни на что не влияет. Если начало больше конец (для to), тело не выполнится ни разу. Изменять счётчик в теле запрещено: Free Pascal не скомпилирует такой код, а по стандарту значение счётчика после выхода из цикла не определено, поэтому использовать его дальше не стоит.

program SumFor;
var
  i, n, sum: integer;
begin
  n := 10;
  sum := 0;
  for i := 1 to n do
    sum := sum + i;
  writeln('sum = ', sum);      { sum = 55 }
  for i := 3 downto 1 do
    write(i, ' ');
  writeln('go');               { 3 2 1 go }
end.

Цикл while — условие проверяется до тела

while условие do оператор;

Условие — логическое выражение. Оно проверяется перед каждой итерацией; если с первого раза оно ложно, тело не выполнится ни разу. Тело обязано когда-нибудь сделать условие ложным, иначе программа зациклится.

program FirstPower;
var
  p: integer;
begin
  p := 1;
  while p <= 1000 do
    p := p * 2;
  writeln(p);                  { 1024 }
end.

Здесь число итераций заранее неизвестно — мы ищем первую степень двойки больше 1000, и for не подходит.

Цикл repeat-until — тело хотя бы раз, условие после

repeat
  операторы;
until условие;

Тело выполняется, затем проверяется условие. Важная деталь: условие после until — это условие выхода: цикл повторяется, пока оно ложно, и останавливается, когда стало истинным. Это обратная полярность по сравнению с do ... while в C, где цикл повторяется, пока условие истинно. Тем, кто переходит между языками, стоит держать это в голове.

program DigitCount;
var
  n, count: integer;
begin
  n := 0;
  count := 0;
  repeat
    n := n div 10;
    count := count + 1;
  until n = 0;
  writeln(count);              { 1 }
end.

Для n = 2026 программа выведет 4. Для n = 0 — тоже правильный ответ 1: у нуля одна цифра. Вариант с while n <> 0 do для нуля вернул бы 0, потому что тело не выполнилось бы ни разу. Так repeat подходит там, где первый проход нужен при любых входных данных: обработка первого элемента, повтор запроса ввода до корректного значения, меню до выбора «выход».

Как выбрать цикл по признаку задачи

Признак задачи важнее привычки. Три вопроса, которые стоит задать перед написанием заголовка:

  1. Число повторений известно до входа в цикл (размер массива, N из условия)? Берите for. Он короче и исключает ошибку «забыл увеличить счётчик».
  2. Число повторений зависит от того, что происходит в теле, и цикл может не понадобиться вовсе (данные уже отсортированы, список пуст)? Берите while.
  3. Тело обязательно должно выполниться хотя бы раз, а проверять есть смысл только после него (ввод с проверкой, первый шаг алгоритма)? Берите repeat ... until.

Любую задачу можно решить любым из трёх циклов, они взаимозаменяемы по выразительной силе. Выбор влияет на читаемость и на вероятность ошибки, а не на возможность решения.

Цикл Когда проверяется условие Минимум итераций Шаг Когда выбирать
for ... to / downto ... do до каждой итерации (сравнение счётчика с границей) 0 +1 или -1, менять нельзя число повторений известно заранее
while ... do до каждой итерации; продолжает, пока условие истинно 0 любой, задаётся в теле вручную условие зависит от тела, цикл может не выполниться
repeat ... until после каждой итерации; выходит, когда условие истинно 1 любой, задаётся в теле вручную первый проход нужен всегда

break и continue: досрочный выход

break немедленно завершает цикл, continue пропускает остаток текущей итерации и переходит к проверке условия. Обе конструкции — расширения, которых нет в стандарте ISO Pascal: они появились в Turbo Pascal 7 и поддерживаются Free Pascal и Delphi как процедуры модуля System. В программах под строгий стандартный Pascal их заменяют флагом в условии while.

program Powers;
var
  a: integer;
begin
  a := 1;
  while True do
  begin
    a := a * 2;
    if a = 64 then continue;   { 64 не выводим }
    if a = 256 then break;     { на 256 выходим }
    write(a, ' ');
  end;
  writeln;                     { 2 4 8 16 32 128 }
end.

Отдельно есть exit — выход не из цикла, а из всей процедуры или программы; путать их не стоит.

Три типичные ошибки и как их исправить

Бесконечный while: переменная не меняется

var
  i: integer;
begin
  i := 0;
  while i < 5 do
    writeln(i);
    i := i + 1;                { отступ обманывает: строка уже вне цикла }
end.
{ Результат: бесконечный вывод 0 0 0 ... - программу приходится прерывать }

Тело while — один оператор writeln(i), а i := i + 1 стоит после цикла и никогда не выполняется. Исправление — составной оператор:

  while i < 5 do
  begin
    writeln(i);
    i := i + 1;
  end;                         { 0 1 2 3 4 (каждое с новой строки) }

Изменение счётчика внутри for

var
  i: integer;
begin
  for i := 1 to 10 do
  begin
    if i mod 2 = 0 then
      i := i + 1;              { хотим перескочить чётные }
    writeln(i);
  end;
end.
{ Free Pascal: Error: Illegal assignment to for-loop variable "i" - код не компилируется.
  Turbo Pascal 7 соберёт программу, но по стандарту поведение такого цикла не определено }

Если нужен нестандартный шаг — это признак, что задача не для for. Исправление — while с ручным шагом или фильтр внутри тела:

  i := 1;
  while i <= 10 do
  begin
    writeln(i);
    i := i + 2;
  end;                         { 1 3 5 7 9 }

Ошибка на единицу (off-by-one)

var
  a: array[1..5] of integer;
  i: integer;
begin
  for i := 0 to 5 do           { индексы массива 1..5, а цикл стартует с 0 }
    a[i] := i * i;
  writeln(a[5]);
end.
{ С включённой проверкой диапазонов ({$R+}) - Runtime error 201 (range check error).
  Без проверки - запись мимо массива: программа может «случайно» вывести 25 или испортить соседние данные }

Исправление — брать границы из самого массива, а не переписывать вручную:

  for i := Low(a) to High(a) do
    a[i] := i * i;
  writeln(a[5]);               { 25 }

Тот же класс ошибок — while i < n вместо while i <= n при суммировании 1..n: для n = 10 получится 45 вместо 55. Проверяйте граничные значения (первый и последний элемент) отдельно, это ловит большинство off-by-one.

Выводы

  • Цикл в Pascal — это оператор, который выполняет фрагмент кода повторно: заданное число раз или пока истинно условие.
  • В языке три вида циклов: for со счётчиком, while ... do с проверкой условия до тела и repeat ... until с проверкой условия после тела.
  • У for шаг всегда +1 (to) или -1 (downto), границы вычисляются один раз до первого прохода, изменять счётчик в теле нельзя, а его значение после выхода из цикла не определено.
  • while может не выполниться ни разу, repeat ... until выполняется хотя бы раз; условие после until — это условие выхода, в отличие от do ... while в C, где цикл повторяется, пока условие истинно.
  • Цикл выбирают по признаку задачи: число повторений известно заранее — for, зависит от тела и цикл может не понадобиться — while, первый проход нужен при любых данных — repeat ... until.
  • break и continue — расширения Turbo Pascal, Free Pascal и Delphi, которых нет в стандарте ISO Pascal; три типичные ошибки — тело while без begin ... end, изменение счётчика внутри for и ошибка на единицу в границах.

Где применяется / связь с практикой

Циклы — основа любого императивного языка: обход массива, накопление суммы, поиск, обработка ввода. Три модели Pascal (счётчик, условие до, условие после) в том же виде есть в Python, C, Java и Go, меняется только запись, поэтому умение выбирать цикл по признаку задачи переносится на любой язык, который вы возьмёте следующим. Если после Pascal хотите перейти к языкам, на которых пишут в индустрии, посмотрите каталог курсов по программированию Otus — там есть направления для начинающих. Познакомиться с форматом занятий можно на бесплатных открытых уроках.

Смежные темы: Императивная разработка: характеристики и ее особенности, Delphi: описание языка и его применение, Программирование на C: описание языка, перспективы, основы разработки.

FAQ

Можно ли задать в for шаг 2 или другой? Нет, в Pascal у for шаг всегда 1 (to) или -1 (downto). Для другого шага используйте while или repeat с ручным изменением переменной либо пересчитывайте значение из счётчика внутри тела (2 * i).

Что будет со счётчиком после завершения for? По стандарту его значение не определено, и полагаться на него нельзя. Если после цикла нужно знать, на каком элементе остановились (например, при поиске), используйте while или отдельную переменную.

Нужен ли begin … end внутри repeat … until? Нет: repeat и until сами ограничивают тело, между ними можно писать несколько операторов. В for и while тело — один оператор, и составной блок обязателен.

OTUS Журнал
Скидка 15% 1-6 сентября на курсы (popup)