#10 Цикл For в Паскаль. Цикл с известным числом повторений (цикл с параметром). Тело цикла.

В Паскале можно использовать три конструкции для организации циклов, они известны по именами:

  1. For
  2. While
  3. Repeat

В этом уроке -- мы познакомимся с первый из них -- циклом For.

Цикл For -- схема работы

Цикл For также называют циклом с известным числом повторений.
Он обладает следующей структурой (изобразим её на блок-схеме):
Пасклаь структрура и работа цикла  FOR блок схема

Как видим на схеме, в цикле for имеются:

  1. Заголовок цикла (шестиугольный блок на схеме выше) -- а котором описывается как именно будет изменяться счётчик цикла (на схеме выше это переменная $i$).

    Счетчик цикла -- это специальная переменная (типа integer), для которой на основании правой и левой границы цикл for определяет ряд значений, которые она "проходит" при выполнении цикла.
    В примере на схеме в качестве левой и правой границы указаны числа $1$ и $10$, то есть переменная $i$ должна будет "пробежать" по ряду значений:

    1, 2, 3, 4, 5, 6, 7, 8, 9, 10

    -- для каждого из этих значений тело цикла будет повторяться (в данном случае 10 раз). Правая и левая границы всегда должны обладать типом integer.

  2. Тело цикла -- набор программных действий для, которых заголовок цикла определяет число повторений.

Сразу же приведём пример кода программы, цикл в которой соответствует блок схеме на рисунке выше:

var i, a:integer;
begin

  for i:=1 to 10 do
  begin // начало тела цикла (у нас в нём будет 3 операции, как на блок-схеме выше)

    a := i;    // получаем очередное значение счётчика (первый раз оно будет равно 1, а последний раз = 10)
    a := a + 2;  // прибавляем к значению 2
    write(a, ' '); // выводим с пробелом после значения

  end;  // конец тела цикла

  readln();
end. 

-- здесь в теле программы тоже три операции (но уже конкретные) и тот же диапазон значений счетчика, что и на блок-схеме. Запустите программу и посмотрите на результат.

Далее рассмотрим примеры решения задач, чтобы лучше понять как работает цикл for.

Если правая граница счётчика меньше левой -- downto

Если правая граница для счётчика цикла меньше чем левая то необходимо использовать вместо конструкции:

for i:=<леваяГраница> to <праваяГраница> do

конструкцию:

for i:=<леваяГраница> downto <праваяГраница> do

Разбор практических примеров

Пример №1 - использование значений счетчика

Задача:

Вывести на экран все числа от 1 до 125.

Решение:

var i:integer;
begin

  for i:=1 to 125 do
    write(i, ' '); // выводим значения счетчика через пробел

  readln();
end.

-- так как нам нужно было повторять только действие вывода, то в теле цикла мы разместили одну операцию, а потому операторные скобки для тела цикла не нужны.

Пример №2 -- условный оператор внутри цикла

Вывести на экран все нечетные числа от 37 до 1025.

Здесь для решения в сравнении с решением предыдущей задачи просто изменим диапазон значений счетчика и добавим в тело цикла условный оператор с проверкой остатка от деления на $2$:

var i:integer;
begin

  for i:=37 to 1025 do  // i пройдёт значения от 1 до 1025
    if ((i mod 2) = 1) then  // если очередное значение счётчика делится на 2 с остатком
       write(i, ' ');

  readln();
end.

-- обратите внимание, что здесь тоже не используются операторные скобки для окружения тела цикла. Дело в том, что вложенным в непосредственно в блок цикл for является только один условный оператор if, а вызов стандартной процедуры write() вложен уже в if, а не непосредственно в for, а потому в данном случае считается, что в теле цикла for находится только одна операция (for) и, следовательно, операторные скобки не обязательны.

Пример №3 -- downto

Задача:
Выведите на экран все число от 133 до 57.

Решение (тут всё просто):

var i: integer;
begin
  for i:=133 downto 57 do
     write(i, ' ');

  readln();
end. 

Пример №4 -- downto и цикл в одной из веток условного оператора

Задача:

Пользователь вводит целое число, если оно больше $8$ выведите на экран все числа от этого числа до $5$ (в обратном порядке), иначе сообщите об ошибке.

Решение:

var i, a: integer;
begin
  writeln('vvedite celoe chislo');
  readln(a);
  if (a > 8) then
     for i:=a downto 5 do // цикл вложен в ветку then условного оператора.
         write(i, ' ')  // перед else ';' не ставится
  else
     writeln('oshibka! vashe chislo ne > 8.');

  readln();
end.

Пример №5 -- условный оператор с составным условием внутри цикла

Задача:

Выведите на экран, все четные числа, делящиеся на 7 нацело, лежащие в диапазоне от 28 до 117.

Решение:
В этой задаче нам необходимо перебрать все числа от 28 до 117 (будем делать это циклом), проверяя каждое число, на соответствие сразу двум условиям:

  1. является четным -- т.е. по сути делится на 2 нацело;
  2. делится нацело на 7;

Так оба условия для выводимого числа нужно проверять одновременно (они должны выполняться одновременно-- только тогда его можно выводить), то будем использовать логическое И.
Третье же условие принадлежности диапазону будет гарантировать сам цикл for -- так как мы будем перебирать только числа из этого диапазона, а потом в услвоном операторе проверять его не нужно, в качестве очередного числа используем счетчик цикла:

var i:integer;
begin
  for i:=28 to 117 do // цикл по диапазону [28..117]
    if ((i mod 2) = 0) AND ((i mod 7) = 0)  then
      write(i, ' ');

  readln();
end.

Пример №6 -- логические выражения, условия внутри цикла + анализ условия

Задача:

Выведите на экран, все четные числа от 35 до 117 и все числа, делящиеся на $5$ нацело, лежащие в диапазоне от 45 до 178.
Указание: сначала можно решить задачу двумя циклами, но потом перепишите с использованием одного цикла, в теле которого составьте логическое выражение, описывающее подходящие числа (используйте логические операции).

Решение двумя циклами:

Решить двумя циклами проще -- достаточно пройтись по двум числовым "отрезкам" (диапазонам) сначала циклом по диапазону [35..117], а потом по диапазону [45..178]. Внутри тел циклов надо проверять условия, которые написаны в задаче с помощью условного оператора.

Приведём решение:

var i:integer;
begin
  for i:=35 to 117 do // цикл по [35..117]
   if ((i mod 2) = 0) then // если четное
     write(i, ' ');

  writeln(); // перенос строки, чтобы было понятно где начался новый цикл

  for i:=45 to 178 do // цикл по [45..178]
    if ((i mod 5) = 0) then // если делится на 5
     write(i, ' ');

  readln();
end. 

-- заметьте, что в этом решении числа выводится не по порядку, что не очень хорошо. В следующем решении мы это исправим.

Решение одним циклом:

Чтобы решить задачу одним циклом (используя единственную конструкцию for), нам стоит заметить, что отрезки [35..117], а потом по диапазону [45..178] пересекаются -- так как:
$35 \lt 45 \lt 117 \lt 178$
-- это значит, что мы можем просто перебрать все числа от 35 до 178, и:

  1. сначала проверяя какому отрезку они принадлежат,
  2. а потом какое условие требуется для вывода чисел из этого отрезка,

-- вывести только то, что нам нужно.

Приведём код, решающий задачу одним циклом:

var i:integer;
begin


  for i:=35 to 178 do // цикл по диапазону [35..178]
   begin

    if ((i >= 35) AND (i <= 117) // если принадлежит отрезку [35..117]
       AND ((i mod 2) = 0)) then // и если четное
         write(i, ' ');

    if ((i >= 45) // если принадлжит отрезку [45..178]
      AND ((i mod 5) = 0)) then // если делится на 5
        write(i, ' ');
   end;

  readln();
end. 

-- здесь внутри тела цикла стоит два независимых условных оператора, числа при выводе упорядочены, но есть дублирование, так как например 50 и четное и делится на 5 и находится на пересечении диапазонов.
Чтобы избежать дублирование надо сделать так, чтобы для каждого очередного числа из значений $i$ положительно выполнялось максимум одно условие -- тогда очередное значение $i$ либо будет выведено один раз либо не будет выведено вообще.

Поэтому сделаем проверку второго условия, альтернативой на случай неуспеха первого -- для чего введём ветку ложности в первый условный оператор и вложим в неё второй (это потребует совсем незначительных правок):

var i:integer;
begin


  for i:=35 to 178 do // цикл по диапазону [35..178]
   begin

    if ((i >= 35) AND (i <= 117) // если принадлежит отрезку [35..117]
       AND ((i mod 2) = 0)) then // и если четное
         write(i, ' ')
    else       // если не из [35..117] или не четно
      if ((i >= 45) // если принадлежит отрезку [45..178]
        AND ((i mod 5) = 0)) then // и если делится на 5
          write(i, ' ');
   end;

  readln();
end.

-- заметьте, что во вложенном в else условии проверяется только левая граница диапазона [45..178] -- это связано с тем, что значения $i$ в заголовке цикла итак ограничены сверху числом 178.

Самостоятельная работа

Вопросы

  1. Зачем нужен цикл for?
  2. Что такое заголовок цикла?
  3. Что такое счетчик цикла?
  4. Что такое тело цикла?
  5. Переменной $i$ в цикле for левую границу установили в $5$, а правую в $12$ -- сколько раз выполнится цикл?

Задачи

  1. Выведите на экран, все четные числа от 35 до 64.
  2. Пользователь вводит целое число, если оно больше единицы, то выведите на экран все целые числа от этого числа до единицы (в обратном порядке), которые делятся на 5 без остатка. Иначе (если введённое пользователем число не больше единицы) сообщите об ошибке.
  3. Пользователь вводит целое число, если оно больше 100, то выведите на экран все числа от этого числа до $1$, иначе же все числа от $1$ до этого числа.

    Подсказка: в каждой ветке условного оператора тут должно быть по одному циклу.

  4. Выведите на экран, все нечетные числа, делящиеся на 3 нацело, лежащие в диапазоне от 35 до 117.
  5. Выведите на экран, все четные числа от 35 до 117 и нечетные числа, лежащие в диапазоне от 45 до 99.
    Указание: сначала можно решить задачу двумя циклами, но потом перепишите с использованием одного цикла, в теле которого составьте логическое выражение, описывающее подходящие числа (используйте логические операции).