Обратите внимание на присваивание N:=1 перед циклом. Без него значение N может быть любым, и условие может быть некорректным, не говоря уже о самом значении факториала. Значение N меняется внутри цикла. При этом гораздо безопаснее так писать тело цикла, чтобы оператор, влияющий на условие, был последним в теле. Это гарантирует от нежелательных переборов. Если, скажем, на рис. 6.1 поставить строку N:=N+1; перед вычислением значения Factorial, то результатом программы будет значение 11!. Исправить оплошность можно, заменив стартовое значение N на 0, а условие — на N<10. Но от этого программа вряд ли станет нагляднее. Поскольку циклу WHILE «все равно», что происходит в его теле, тело может содержать другие, вложенные, циклы.
6.5. Оператор цикла с постусловием (REPEAT...UNTIL)
Рассмотренный выше оператор цикла с предусловием решает, выполнять свое тело или нет, до первой итерации. Если это не соответствует логике алгоритма, то можно использовать цикл с постусловием, т.е. решающий, делать или нет очередную итерацию, лишь после завершения предыдущей. Это имеет принципиальное значение лишь на первом шаге, а далее циклы ведут себя идентично. Цикл с постусловием всегда будет выполнен хотя бы один раз.
- 100 -
Оформляется такой цикл с помощью служебных слов REPEAT и UNTIL (повторять до):
REPEAT
Оператор1;
Оператор2;
...
ОператорN
UNTIL Условие;
Первое из них объявляет цикл и открывает его тело, а второе — закрывает тело и содержит условие окончания цикла. Тело цикла может быть пустым или содержать один и более операторов. В последнем случае слова BEGIN и END не нужны: их роль играют слова REPEAT и UNTIL.
Условие — это логическое значение, переменная или выражение с логическим результатом. Но работает оно здесь совсем не так, как в цикле WHILE. Если в цикле WHILE подразумевается алгоритм «пока условие истинно, выполнять операторы тела цикла», то цикл REPEAT...UNTIL соответствует алгоритму «выполнять тело цикла, пока не станет истинным условие».
Иными словами, в цикле с REPEAT...UNTIL условием продолжения итераций будет невыполнение условия (его значение False). Хорошей иллюстрацией к вышесказанному может быть конструкция «вечного цикла»:
REPEAT UNTIL False;
Этот цикл пустой и никогда не прекращающийся. Он хорош только в случае, когда нужно заблокировать программу, и, возможно, весь компьютер. (Но если отбросить шутки, то можно и его пристроить в дело. Обычно так организуются программы с повторяющимися действиями: вначале программы ставится REPEAT, а в конце — UNTIL False. А прервать цикл можно специальными операторами: Exit, Halt. Это имеет смысл, если условий завершения программы много или они очень сложны.)
Если условие конца цикла более гибкое, чем константа False, то в теле цикла должны содержаться операторы, влияющие на само условие. О предварительной корректности условия, как в случае цикла WHILE, заботиться уже необязательно.
6.6. Оператор цикла с параметром (FOR...DO)
Операторы циклов с пред- и с постусловием, хотя и обладают значительной гибкостью, не слишком удобны для организации
- 101 -
«строгих» циклов, которые должны быть проделаны данное число раз. Цикл с параметром вводится именно для таких случаев. Синтаксис оформления циклов с параметром следующий:
FOR ПараметрЦикла:=МладшееЗнач TO СтаршееЗнач DO Оператор;
или
FOR ПараметрЦикла := СтаршееЗнач DOWNTO МладшееЗнач DO
Оператор;
Слова FOR...TO (DOWNTO)...DO можно перевести как «для параметра от...до...делать».
Оператор, представляющий собой тело цикла, может быть простым, составным или пустым. В последнем случае за словом DO сразу ставится точка с запятой. Параметр цикла, а также диапазон его изменения (от стартового до конечного значения включительно) может быть только целочисленного или перечислимого типа. Сам параметр должен быть описан совместно с прочими переменными. Шаг цикла FOR всегда постоянный и равен «интервалу» между двумя ближайшими значениями типа параметра цикла. Изменение параметра цикла может быть возрастающим, но может быть и убывающим. В первом случае МладшееЗначение должно быть не больше чем Старшее, а во втором — наоборот. Примеры оформления циклов с параметром приведены на рис. 6.2.
| VAR
| i : Integer; { описание параметров циклов}
| c : Char;
| b : Boolean;
| e : (elem1, elem2, elem3 ); {вводимый перечислимый тип}
| BEGIN
| FOR i:= -10 TO 10 DO Writeln(i);
| FOR i:= 10 DOWNTO -10 DO Writeln(i);
| FOR c:= 'a' TO 'r' DO Writeln(с);
| FOR b:=True DOWNTO False DO Writeln(b);
| FOR e:= elem1 TO elem3 DO Writeln(Ord(e));
| END.
Рис. 6.2
Если параметр возрастает, то между границами его значений ставится слово TO, если же убывает, то ставится слово DOWNTO. Соответственно с этим меняются местами старшее и младшее зна-
- 102 -
чения в заголовке цикла. На месте старших и младших значений могут стоять константы (как на рис. 6.2), а могут и переменные или выражения, совместимые по присваиванию с параметром цикла. Выполнение цикла начинается с присваивания параметру стартового значения. Затем следует проверка, не превосходит ли параметр конечное значение (случай с TO) или не является ли он меньше конечного значения (случай с DOWNTO). Если результат проверки утвердительный, то цикл считается завершенным и управление передается следующему за телом цикла оператору. В противном случае выполняется тело цикла, и после этого параметр меняет свое значение на следующее, согласно заголовку цикла. Далее снова производится проверка значения параметра цикла, т.е. алгоритм повторяется. Из этого следует, что будут проигнорированы циклы
FOR i := 5 ТО 4 DO ...;
FOR i : = 4 DOWNTO 5 DO ...;
а цикл
FOR i := N TO N DO ...;
выполнит операторы своего тела строго один раз.
Запрещается изменять параметр цикла и его старшее и младшее значения (если они заданы переменными или выражениями с ними) изнутри тела цикла. Кроме того, параметр цикла не может участвовать в построении диапазонов этого же цикла. Компилятор таких «незаконных» действий не замечает, но программа, содержащая цикл с заголовком типа
FOR i := i-5 TO i+5 DO ...
не заслуживает никакого доверия, даже если запускается. Если же необходимо досрочно завершить цикл FOR (для чего велик соблазн искусственно «подрастить» параметр цикла), то можно воспользоваться оператором перехода Goto (о нем см. следующий раздел). В техническом описании Турбо Паскаля отмечается, что после завершения цикла FOR значение его параметра не определено. При экспериментальной проверке этого факта скорее всего получится обратный результат: параметр будет иметь конечное значение своего диапазона. Тем не менее, не следует опираться на это в своих программах. Лучше переприсвоить значение параметра после окончания цикла — так будет корректнее. Исключение — выход из цикла переходом Goto. В этом случае значение переменной (параметра цикла) останется таким же, каким было на момент выполнения оператора Goto.