Рейтинг:  3 / 5

Звезда активнаЗвезда активнаЗвезда активнаЗвезда не активнаЗвезда не активна
 

Иногда в программировании необходимо повторять один и тот же программный код несколько раз. И для этого были придуманы циклы. Всего в Паскале четыре оператора повторений, каждый из которых применяется в своих целях.

1) Счетный оператор FOR.

Большим преимуществом этого оператора является то, что он сделает строгое кол-во повторов. Этот цикл часто используют тогда, когда заранее известно сколько раз нужно повторить. Этот оператор имеет слудующий синтаксис:

for Params:=Start_Value to Finish_Value do Procedure;

где Params - имя перменой, по которой будет идти цикл

Start_Value - стартовое значение переменной Params

Finish_Value - до какого значения будет идти цикл

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

Во время вычисления цикла переменной Params сначала присваивается стартовое значение Start_Value, а после этого выполняется оператор Procedure, после выполнения которого значение переменной Params увеличится на 1. И этот цикл будет повторяться до тех пор, пока значение переменной Params не превысит значения Finish_Value.

Рассмотрим пример, подсчитывающий кол-во четных чисел среди введенных.

begin

  var kol_vo:integer:=0;

  var n:integer;

  write('Введите кол-во чисел ');

  readln(n);

  for var i:integer:=to do

  begin

    var x:integer;

    x:=readinteger();

    if (not odd (x)) then

      kol_vo+=1;

  end;

  writeln('Было введено '+kol_vo+' четных');

end.

Давайте разберем этот код. Вначале мы создаем цулочисленную переменную «kol_vo», которая будет вести учет четных чисел. Затем создаем целочисленную переменную «n», которая будет хранить в себе кол-во чисел, которое будет введено пользователем. Следующие две строки выводят на экран строку «Введите кол-во чисел» и считывают с клавиатуры это количество чисел в перменуую «n», которое введет пользователь. (!)(Внимание! В данном коде нет никаких защит от неверного ввода с клавиатуры. Я привел этот код, чтобы показать принцип работы цикла for и не расчитываю, что данный код будет использоваться в неправильных целях. Если вам потребуется, чтобы в ходе выполнения программы не могло возникнуть никаких ошибок, то доработайте его сами (но для этого Вам потребуется знание статьи Обработка ошибок в Pascal ABC.NET и знание этой статьи.). Доработать этот код не трудно, но это увеличит его обьем и уменьшит понимание его кода) Далее идет цикл, который будет идти по переменной i, которую мы тут же создаем (Внимание! В PascalABC.NET, как и во многих других языках программирования конструкция вида «for var i:integer:=1» будет работать. Кроме того это будет означать, что переменная «i» будет доступна только внутри цикла. А вне цикла эта переменная не существует. Если она Вам нужна после цикла, то используйте следующий код: «var i:integer; for i:=1». При использовании этого фрагмента переменная i будет доступна и вне цикла, но только в том фрагменте, который находится ниже строки «var i:integer;») и присваиваем ей начальное значение «1». (!)(Внимание со стартовым значением и конечным значением нужно быть очень осторожными. Прежде чем начать писать цикл For нужно все как следует просчитать. Если его запрограммировать неправильно, то в ходе цикла можно заработать исключение (чуть дальше начнем изучать массивы. И это относится к массивам) или же просто программа выполнить ненужное действие. Так вот мне нужно только n действий. И как раз, если цикл начнется с 1 и будет выполняться до тех пор, пока значение переменной i не будет равно n+1, то n+1-1=n. Что мне и нужно. n действий.). Программа сразуже проверит условие «i>n». Если это условие истино, то программа выйдет из цикла, иначе оно начнет выполнять «тело» цикла.Что происходит в теле цикла Вам должно быть понятно (создается переменная «х», считывается целочисленное значение с клавиатуры и присваивается это значение переменной «х». После это, если значение переменной «х» четно, тогда значение переменной «kol_vo» увеличивается на 1. После этого заканчивается тело цикла. После этого программа значение переменной «i» увеличивает на 1 и проверяет условие «i>n». Так программа будет повторять эти действие, пока условие «i>n» не будет истинным.). После этого программа выведет на экран сообщение о количестве введенных четных чисел.

Также обратите внимание, что проверка условия «i>n» происходит перед выполнением тела цикла!

Вообще в паскале есть один неудобный момент в цикле For: можно идти с шагом (это разница между вторым значением и первым значением) 1 или -1. И это в некоторых случаях неудобно. Например с помощью цикла For сделать программу, выводящую на экран только четные числа неочень просто. Вернее это просто, но для этого нужно ввести в тело цикла условный оператор, который немного снизит скорость работы (ну как немного. На достаточно больших значениях очень сильно снизит работоспособность. Я проверил это и снижение скорости работы составило 7 с. 4006884мс. с циклом 1000000000 ниже приведены два кода проверки).

var time:=System.DateTime.Now;

begin

  var n:=1000000000;

  var s:string;

  for var i:integer:=1 to n do

  begin

    if (not odd(i)) then

      s:=i.ToString();

  end;

  writeln(system.DateTime.Now-time);

  ///00:01:00.9897740

end.

var time:=System.DateTime.Now;

begin

  var n:=1000000000;

  var s:string;

  var i:integer:=1;

  while i<=n do

  begin

    s:=i.ToString();

    i+=2;

  end;

  writeln(system.DateTime.Now-time);

  ///00:00:53.5890856

end.

Для того, чтобы сделать шаг -1 нужно написать вместо «to» «downto». Тогда программа будет идти с шагом -1. Так программа с «обратным» шагом, аналогичная вышеприведенной программе, привидена ниже.

begin

  var kol_vo:integer:=0;

  var n:integer;

  write('Введите кол-во чисел ');

  readln(n);

  for var i:integer:=downto do

  begin

    var x:integer;

    x:=readinteger();

    if (not odd (x)) then

      kol_vo+=1;

  end;

  writeln('Было введено '+kol_vo+' четных');

end.

Эта программа обсолютно эквивалентна предыдущей, только подсчет идет в обратную сторону. (то есть программа проверяет не условие «i>n», а наоборот «i<n»)

2)цикл с предусловием while.

Иногда нужно выполнять одну и ту же программу, пока будет выполняться некоторое условие. Для этих целей цикл For не подходит, поскольку цикл For подходит только для тех целей, в которых сразу ясно сколько циклов нужно совершить, а в данном случае мы не знаем, сколько раз нужно совершить цикл.

Синтаксис у этого оператора следующий:

while Predicate do

     Action;

где Predicate - это некоторая логическая функция

Action - действие, которое будет выполняться при истинности Predicate.

А теперь рассмотрим принцип действия на примере со вводом данных с клавиатуры, пока пользователь не введет корректные данные.

var Log:boolean;

begin

  log:=false;

  var x:byte;

  write('Введите двузначное положительное число ');

  while not log do

  begin

    try

      read(x);

      if ((x<10or (x>99)) then

        raise new System.Exception();

      log:=1=1;

    except

      writeln('некорректные данные. Попробуйте еще раз:');

    end;

  end;

  writeln('Вы ввели '+x.ToString);

end.

Мы сначала создаем переменную, которая будет проверять корректность введенных данных. Если пользователь ввел данные корректно, то программа запишет в переменную «log» истинное значение и программа прекратит работу. Далее, после вывода сообщения о вводе, идет цикл, который будет выполняться до тех пор, пока переменная «log» будет иметь ложное значение. Цикл while перед тем, как выполнять тело цикла, проверит переменную «log» на ложность. Если это условие выполнено, тогда программа переходит к телу цикла, иначе программа пропускает весь цикл. Именно поэтому цикл while называют циклом с предусловием (поскольку перед выполнением тела цикла проверяет условие на истинность. И только при истиности этого выражения программа переходит к выполнению тела цикла). В самом теле цикла мы видим обработчик ошибок try - except. В блоке try программа считывает с клавиатуры значение переменной «x» (полскольку я написал программу для считывания только двузначного числа, то нет смысла использовать тип данных, расчитанный на большие данный. Следовательно для экономии памяти компьютера можно использовать тип данных «byte», который идеально подходит для данных целей). При ошибки считывания программа переходит к блоку except, после чего заново начинает выполнять цикл. Если все считано без проблем, то программа идет дальше. Дальше программа проверяет значение переменной «х» на двузначность. И, если число не двузначное, то программа вызывает исключение, из-за которого программа перейдет к выполнению блока except. А дальше Вы знаете. После проверки на корректность данных программа записывает в переменную «log» истинное значение, после чего программа завершает выполнение тела цикла и переходит к проверке условия «(not log)=true». При ложности этого условия (программа успешно считала данные с клавиатуры и данные оказались корректными).

3) цикл с постусловием.

В привиденном коде программы есть один недочет: если изначально в переменную «log» записать значение true, то программа не будет выполнять свою задачу. Она даже не сможит попытаться считать данные. Поэтому в некоторых случаях нужно писать программу так, чтобы тело цикла было выполнено хотя бы один раз. Поэтому в програмировании ввели цикл с постусловием, который вначале выполняет тело цикла, а потом проверяет условие.

repeat

     Action1;

     Action2;

     ...

     ActionN;

until Predicate;

где Action1 ... AcnionN - это набор действий

Predicate - это условие, при ложности которого выполняется еще раз тело цикла, а при истиности - выполнение тела цикла прекращается, но по любому набор действий Action1 ... AcnionN выполняется хотя бы один раз.

Для рассмотрения действия данного цикла, рассмотрим модифицированную предыдущую программу.

begin

  var x:byte;

  var log:boolean:=false;

  repeat

    try

      write('Введите целое положительное двузначное число ');

      read(x);

      if ((x<10or (x>99)) then

        raise new System.Exception();

      log:=true;

    except

      writeln('Ошибка ввода. Попробуйте еще раз');

    end;

  until log;

  writeln('Вы ввели ',x);

end.

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

Сейчас я немного расскажу об этих циклах, а потом перейдем к последнему циклу.

Иногда нужно преждевременно остановить выполнение цикла. Что касается цикла for, вопросов зачем не должно возникнуть. А что касается циклов repeat или while, то все-таки бывают случаи, когда это нужно сделать. Для этого сделали оператор остановки циклов «break». Этот оператор останавливает только один цикл, внутри которого он непосредственно находится. Этот оператор останавливает любой из этих трех циклов.

Также для них есть еще один оператор continue, который останавливает выполнение выполнение текущего цикла и переходит к выполнению следующего цикла.

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

 

цикл

реализация через цикл 1

реализация через цикл 2

for

через цикл while

var i:integer:=1;

var n:integer:=10; //до какого значения идти

var step:integer:=1; //шаг цикла

while i<=n do

begin

  Action;

  i+=step;

end;

через цикл repeat

var i:integer:=1;

var n:integer:=10; //до какого значения идти

var step:integer:=1; //шаг цикла

repeat

  Action;

  i+=step;

until i>n;

while

через цикл for

for var i:=1 to 100000000 do

  if Predicate then

  begin

    Action;

  end

  else

    break;

Внимание! Это будет работать не всегда (только тогда, когда кол-во итераций меньше 100000000)

через цикл repeat

if Predicate then

  repeat

    Action;

  until not Predicate;

repeat

через цикл for

for var i:=1 to 100000000 do

begin

  Action;

  if Predicate then

    break;

end;

Опять же будет работать не всегда.

через цикл while

Action;

while not Predicate do

begin

  Action;

end;

Теперь перейдем к последниму «псевдоциклу».

4. Скрытый цикл goto

Последний оператор является не совсем циклом. Он является циклом лиш при некоторых условиях. Этот оператор позволяет перейти, почти, к любому участку кода. «Почти» заключается в том, что должны быть выполнены следующие условия:

1) операторов goto не должно быть много

2)нельзя переходить внутрь цикла, подпрограммы, блоков try, except, finally, но можно переходить наружу.

3) нельзя сделать goto в другую программу, модуль, библиотеку

4) одна метка не может быть использована в нескольких участках кода.

Если эти условия выполнены, то можно использовать этот оператор.

Теперь рассмотрим синтаксис этого оператора. Начнем с того, что для этого оператора нам нужно обьявлять глобальные (в секции инициализации, а не по ходу выполнения пограммы) переменные специального типа данных «метка». Но такие переменные обьявляются немного не так, как все переменные в Паскале. Они обьявляются следующим форматом:

label Mylabel;

Здесь «Mylabel» - это имя метки, которую мы поставим в нужный нам фрагмент кода и в дальнейшем сможет туда перейти. «label» - это имя типа «метка». Таким способом обьявив переменную типа «метка» мы сможем переходить к выполнению кода, написанного за этой переменной.

Теперь поговорим о том, как поставить метку в фрагмент кода. Она ставится очень просто: нужно поставить имя метки, а после имени поставить двоеточие, после которого должна идти процедура, присваивание, оператор и прочие. (!) Внимание: после имени метки нельзя писать обьявление переменной.

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

goto mylabel;

Где «mylabel» - имя метки, куда нужно перейти.

Теперь рассмотрим пример с этим оператором ввода данных, пока не будут выполнены все условия:

label my;

begin

  var b:byte;

  var tmp:boolean:=false;

  my:try

    read(b);

    if (b<10or (b>99then

      raise new System.Exception();

    tmp:=false;

  except

    tmp:=true;

  end;

  if tmp then

    goto my;

end.

В данном коде нету явных циклов, но есть неявный цикл goto. Эта программа считывает с клавиатуры двузначное число. Теперь рассмотрим принцип действия данного кода.

Обьявляется метка «my», переменная «b», хранящая введенные данные, логическая переменная «tmp», хранящая данные о необходимости перейти на следующую итерацию. Далее я ставлю метку «my» на вход в оператор «try» (в случае перехода программа перейдет к выполнению блока try). Поскольку это просто метка перехода, то программа все равно будет выполнять то, что находится на метке. (да, то что находится на метке выполняется хотя бы один раз, как в операторе repeat). А под меткой блок try. В этом блоке мы видим считывание с клавиатуры значения для «b», и идет проверка на двузначность, после чего переменной «tmp» присваивается значение «false», говорящее о том, что еще раз не нужно выпонять цикл. При возникновении каких-либо ошибок в данном коде программа переходит к выполнению блока except, в котором записано только присвоение переменной «tmp» истинного значения, говорящее о том, что нужно повторить цикл (Вы, наверное, подумали, что можно было бы упростить: убрать переменную «tmp» вообще и в блоке except поставить оператор goto. Но нет. Так нельзя. Если Вы так сделаете, то компилятор выдаст ошибку: «Program1.pas(12) : Переход внутрь составного оператора на метку my невозможен». Раньше было сказано, что можно переходить из составного оператора наружу, но, что касается блоков try, except, это не действует, поэтому в данном случае нам нужна еще одна переменная, которая это контролирует). Поскольку в данном примере я использовал блок except, то программа посчитает, что все возникшие ошибки обработаны и можно выполнять работу программы далее. А дальше стоит условный оператор, который при необходимости переходит к метке «my».

В данном примере я перещел по метке «вверх». Но допускается и переход по метке «вниз». Так можно перейти, почти, в любой момент кода.

Да и что уж там таить, все циклы, о которых я рассказал, устроены на операторе goto. Вот приблизительная их риализация:

цикл For

цикл While

цикл repeat

label fo;

begin

  var i:integer:=1;

  var n:integer:=50;

  fo:if i<=n then

  begin

    Action;

    i+=1;

    if (i<=n) then

      goto fo;

  end;

end.

label whil;

begin

  whil:if Predicate then

  begin

    Action;

    goto whil;

  end;

end.

label repea;

begin

  repea:Action;

  if Predicate then

    goto repea;

end.

Также при программировании нужно достаточно хорошо, что твариться в программе в ходе работы. Это нужно, в часности, для циклов, поскольку программа с циклами может зациклится. И чтобы этого не было нужно на достаточном уровне нужно хорошо понимать, что будет твариться при запуске программы. Если у цикла нет конкретной цели остановки, то программа может зациклится и она никогда не прекратит свою работу. Вот некоторые примеры зацикливания:

while true do

  writeln('Pascal');

repeat

  writeln('Pascal');

until false;

var i:integer:=1;

while i<5 do

  writeln('Pascal');

var i:integer:=1;

repeat

  writeln('Pascal');

until i>5;

В следующем уроке мы рассмотрим некоторые типовые примеры.