#11 Цикл While "с предусловием" - пример. Паскаль

Ранее мы уже рассмотрели цикл for (т.н. "цикл с известным числом повторений"), для которого число витков определялось правой и левой границей счетчика.

Далее мы рассмотрим цикл while, число повторов которого определяется иначе, а именно на основании проверки истинности логического выражения.

Когда используется

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

Цикл while - структура

Общий вид:

while <логическое выражение> do
  begin
    { группа операторов }
  end;

Например:


while (a<b) do // выполняй пока "а" меньше "b"
 begin
    a := a + 1; // например, увеличивай каждый раз на 1
    writeln('a -->' + a);
 end;

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

В качестве условия можно использовать более сложное выражение, лишь бы возвращаемый результат имело логический тип:

while (a<b and a<>5) do // выполняй пока "а" меньше "b" и "а" не равно 5
  begin
    a := a + 1; // например, увеличивай каждый раз на 1
    writeln('a -->' + a);
  end;

Условие выхода из while

Цикл типа while в Паскале выполняется до тех пока истинно логическое выражение в заголовке данного цикла.
То есть:

  1. Если логическое выражение истинно -- то делается следующий виток цикла.
  2. Если логическое выражение ложно -- то цикл завершается, и программа выполняется далее (по инструкциям, которые записаны в коде ниже тела цикла).

Бесконечный циклы и как с ними бороться

Как возникают бесконечные циклы

В for счетчик цикла (который обязательно есть в таком цикле) проходит заранее известное (и конечное) число значений, которое определяется в заголовке цикла for на основании правой и левой границ.

В цикле же while проверка логического условия приводит к тому, что есть возможность (в некотором смысле "опасность") получить зависание программы за счет бесконечного числа раз повторения цикла.
Эта ситуация может возникнуть при следующих условиях (они должны выполняться одновременно):

  1. Цикл while выполнился хотя бы один первый раз.
  2. Во всех последующих итерациях (повторах) тела цикла логическое выражение по-прежнему возвращает только true и никогда false.

Приведём пример бесконечного цикла (небольшая программа, которую можно запустить):

var a: integer;
begin
  a := 8;
  while (a < 100) do // пока a < 100
    write(a, ' '); // выводим очередное значение a

end.

В этой программе используется цикл, решение об очередном выполнении которого принимается на основании проверки условия:

a < 100 

Но фактически, мы видим, что ещё до начала цикла была выполнена операция присваивания:

a := 8;

-- а это значит, что на самом деле в цикле первый раз мы проводим сравнение (паскаль проводит):

8 < 100 // true

результат которого истинен, а цикл первый раз выполнится (первое условие для "угрозы бесконечности" у нас уже есть). Что же происходит далее?
В теле цикла значение переменной $a$ никак не меняется, а значит не меняется от витка к витку и значение логического выражения:

a < 100 

-- и потому цикл оказывается бесконечным.

Как избежать этой ситуации? Рассмотрим далее.

Как бороться с бесконечными циклами

Чтобы цикл с проверкой логического условие не был бесконечным необходимо:

  • изменять в теле цикла компоненты логического выражения (значения входящих в него переменных)

-- это не даёт гарантии защиты "от бесконечности", так как ваше логическое выражение или ваш алгоритм изменения переменных могут быть построены так, что несмотря на изменение значений переменных логическое выражение все равно каждый раз будет возвращать true, но без этого вы точно получите бесконечный цикл (если он начнётся, а выражение изменяться не будет).

Эту проблему непросто понять сходу, но по мере решения задач вы будете сталкиваться с "зацикливанием" и тогда уже сможете лучше разобраться в особенностях работы цикла while в Паскаль.

Главное: анализируйте ваш код на предмет того, что в каком-то витке условие должно вернуть false -- без этого любой while, который начал выполняться станет бесконечным.

Примеры кода (решения задач)

Далее рассмотрим примеры решения задач (код программ), в которых продемонстрируем различные ситуации, в которых удобно использовать цикла while.

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

Задача:

Пользователь вводит целые числа. Пока он не введёт число большее $17$, в ответ на каждое введённое число выводите сумму этого числа и числа $8$, если же введённое число больше $17$, то цикл необходимо завершить.

Решим эту задачу:

program Project1;
var a: integer;
begin
  a := 1; // любое значение, чтобы цикл мог начаться

  while (a <= 17) do // заголов цикла с провекой логического выражения
  begin
    writeln('vvedite celoe chislo:'); // приглащаем пользователя ввести число
    readln(a);   //считываем его из потока ввода
    writeln(a + 8); // выводим пользователю очередной ответ
  end;

  writeln('Poka!'); // показываем, что мы вышли из цикла
  readln(); // удерживаем консоль

end. 

Пример №2 -- замена for на while

Цикл while -- самая универсальная конструкция из всех циклов Паскаля. любой другой цикл можно заменить на него (хотя это не всегда рекомендуется делать). Рассмотрим код (запустите его):

var i: integer;

begin

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

  writeln(); // переносим строку

  {----------
    Далее сделаем то же самое,
     но у же с использованием цикла while}

  i:=1; // начальное значение счетчика

  while i<=10 do
  begin
    write(i, ' ');
    i:=i+1; // изменяем значение счетчика
  end;

  readln();

end.  

-- в этой задаче мы выводим числа от $1$ до $10$ два раза, делая одно и тоже разными циклами.
По сути в while в примере выше мы тоже используем переменную счетчик, но уже сами управляем её изменением в теле цикла, в то время как в for этого не требуется.

Для циклов с известным числом повторений рекомендуется использовать именно for, так как по его заголовку сразу видно, сколько раз выполнится тело (по крайней мере, это видно намного лучше чем, скажем, в цикле while).

Видео-пояснения

Для данного урока есть такие видео-пояснения:

Задачи для самостоятельного решения

  1. Решите с помощью цикла while:
    Пользователь вводит целое число N, большее 8.
    Выведите на экран последовательность четных числел от 8 до N,
    Например:
    1. для N=21:
      8 10 12 14 16 18 20
    2. Напр. для N=22:
      8 10 12 14 16 18 20 22
  2. Пользователь вводит целые числа. Пока он не введёт число большее $15$, в ответ на каждое введённое число выводите сумму этого числа и числа $7$, если же введённое число больше $15$, то цикл необходимо завершить.
  3. Пользователь вводит целые числа. Пока он не введёт число большее $15$, в ответ на каждое введённое число выводите сумму этого числа и предыдущего введённого им числа, если же введённое число больше $15$, то цикл необходимо завершить, не выводя в ответ сумму.

    Первое введенное пользователем число можно сложить с единицей.

    Подсказка: если не получается решить данную задачу самостоятельно (сначала постарайтесь сами хотя бы минут 20-ть), то посмотрите:

    Ниже мы приведём ещё пару задач на эту же тему.

  4. Решите с помощью цикла while:
    Выведите на экран, все нечетные числа, делящиеся на 3 нацело, лежащие в диапазоне от 35 до 117.
  5. Решите с помощью цикла while:
    Выведите на экран, все четные числа от 35 до 117 и нечетные числа, лежащие в диапазоне от 45 до 99.
    Указание: сначала можно решить задачу двумя циклами, но потом перепишите с использованием одного цикла, в теле которого составьте логическое выражение, описывающее подходящие числа (используйте логические операции).
  6. Модификация одной из пред. задач: Пользователь вводит целые числа. Пока он не введёт число большее $15$, в ответ на каждое введённое число выводите сумму этого числа и двух предыдущих введённых им чисел (предыдущего и пред-предыдущего), если же введённое число больше $15$, то цикл необходимо завершить.

    В первом витке цикла все "предыдущие" числа можно считать единицами.

  7. Модификация одной из пред. задач: Пользователь вводит целые числа. Пока он не введёт число большее $15$.
    Если очередное введенное число является чётным, то в ответ водите сумму этого числа и пред-предыдущего введённого пользоватлем числа, иначе просто запрашивать новое число. Если же введённое число больше $15$, то цикл необходимо завершить.

    В первом витке цикла все "предыдущие" числа можно считать единицами.

  8. Пользователь вводит целые числа, в ответ выводите символ #. Если введенное пользователем число равно 22, то завершите цикл не выводя ничего в ответ. Решите эту задачу 2-мя способами:
    • С использованием if()
    • С использованием if() и break;
  9. Пользователь вводит целые числа в цикле (разные: четные, нечетные, положительные, отрицательные в любом порядке и количестве). Считайте и выведите на экран значения первых трех отрицательных нечетных чисел. После чего завершите цикл.

    Подсказка: если не получается решить или закрепления см. этот видео-разбор решения.

  10. Пользователь вводит последовательно три произвольных целых числа. Заведите три переменных и сохраните эти числа в эти переменные. Пользовательский ввод обрабатывайте в цикле.

    В решении:

    • в одном витке цикла должен быть только 1 readln()
    • не использовать "обмен" (т.е. не присваивать одной переменной значение другой)

    Подсказка: Если не получается решить:

  11. Пользователь вводит целые числа в цикле (разные: четные, нечетные, положительные, отрицательные в любом порядке и количестве). Сохраните в переменные первые три отрицательных нечетных числа, сложите их, выведите сумму на экран и завершите цикл.

    Подсказка: если не получается решить, или для закрепления см. разборы решений:

  12. Вывести на экран все числа от 4 до 100 с шагом 4, т.е.:

    4 8 12 16.......100

    (при более оптимальном решении проверка через mod не потребуется)

  13. Получите первую и последнюю цифры числа 15 с помощью оперций div и mod (решается без цикла)
  14. Пользователь вводит целое 5-тизначное число, выведите все его цифры в обратном порядке, например для 14564 получим:
    46541

    (используйте опеции div и mod, подразумевается решение через цикл)

    Подсказка: см. разбор, если не получается решить или для самопроверки: https://youtu.be/tBW6vHuiOP4

  15. У вас есть число z=5. Пользователь вводит числа в цикле, если очередное введенное число больше или равно 15, то увеличивайте число z на 1 в ответ на каждое введенное число, а если меньше 15, то уменьшайте на 1. Цикл и программа должны завершиться, когда число z станет отрицательным.
  16. У вас есть число z=5. Пользователь вводит числа в цикле, если хотя бы одно введенное число число было равно 77, то увеличивайте число z на 1 в ответ на каждое введенное число, до тех пор пока пользователь не введет число 55, если такое произойдет то в ответ на это и любое последующее число, уменьшайте z на 1.

    Цикл и программа должны завершиться, когда число z станет отрицательным.
    (в начале цикла, если первое введенное число не равно ни 55, ни 77, считаем, что мы должны увеличивать z на 1 пока не встретим 55 или 77, которые укажут на конкретные действия).

    Пример работы в консоли:

    --Polzovatel: 
    12
    Otvet: 6
    --Polzovatel: 
    12
    Otvet: 7
    --Polzovatel: 
    90
    Otvet: 8 
    --Polzovatel: 
    55
    Otvet: 7
    --Polzovatel: 
    100
    Otvet: 6
    --Polzovatel: 
    123644
    Otvet: 5
    --Polzovatel: 
    77
    Otvet: 6
    --Polzovatel: 
    1
    Otvet: 7

    Примечание: если не получается решить самостоятельно или для изучения альтернативного варианта, посмотрите разбор решения этой задачи.

  17. У вас есть число z=5. Пользователь вводит числа в цикле, если за последние 4 попытки ввода было хотя бы одно число больше или равно 15, то увеличивайте число z на 1 в ответ на каждое введенное число, а если такого числа не было, то уменьшайте на 1 в ответ на каждый ввод.

    Цикл и программа должны завершиться, когда число z станет отрицательным.

    Пример работы в консоли:

    --Polzovatel: 
    12
    Otvet: 4
    --Polzovatel: 
    16
    Otvet: 5
    --Polzovatel: 
    5
    Otvet: 6
    --Polzovatel: 
    10
    Otvet: 7
    --Polzovatel: 
    8
    Otvet: 8
    --Polzovatel: 
    9
    Otvet: 7
    --Polzovatel: 
    9
    Otvet: 6
    --Polzovatel: 
    20
    Otvet: 7

    Примечание: если не получается решить самостоятельно или для изучения альтернативного варианта, посмотрите разбор решения этой задачи.

  18. Дополнительная проверка знаний (решать не обязательно):

  19. Модификация предыдущего условия
    У вас есть число z=5. Пользователь вводит числа в цикле, если за последние 4 попытки ввода было хотя бы одно число больше или равно 15, то увеличивайте число z на 1 в ответ на каждое введенное число, а если такого числа не было, то уменьшайте на 1 в ответ на каждый ввод.

    Цикл и программа должны завершиться, когда число z станет отрицательным либо когда значение z увеличивалось 7 раз подряд.