[x]. Любое выполнение тела цикла выполняет инструкцию
В этом примере цикл является простым итерированием на последовательности целых чисел в конечном интервале, известный в языках программирования, как "цикл For" или "цикл DO", завершение которого не трудно проверить. Для более изощренных циклов число требуемых итераций определить не так просто, выявление завершения становится сложной задачей, единственным универсальным способом является нахождение варианта.
Нам понадобится еще одно понятие, преобразующее только что набросанную схему в программный текст, описывающий цикл. Мы нуждаемся в простом способе определения того, что текущая итерация достигла цели (постусловия)
post = inv and exit
так что мы можем остановить цикл, - чьи промежуточные состояния по построению удовлетворяют
Синтаксис цикла
Синтаксис цикла непосредственно следует из предшествующих соображений, определяющих ингредиенты цикла. Он будет включать элементы, отмеченные как необходимые.
[x]. Инвариант цикла
[x]. Условие выхода
[x]. Вариант
[x]. Множество инструкций инициализации, которые всегда приводят к состоянию, в котором
[x]. Множество инструкций
[x]. Синтаксис цикла честно комбинирует эти ингредиенты:
from
init
invariant
inv
variant
var
until
exit
loop
body
end
Предложения invariant и variant являются возможными. Предложение from по синтаксису требуется, но инструкция
Эффект выполнения цикла можно описать так: вначале выполняется
В языках Pasal, C и других такой цикл называется "циклом while", в отличие от цикла типа "repeat ... until", в котором тело цикла выполняется, по меньшей мере, один раз. Здесь же тест является условием выхода, а не условием продолжения, и синтаксис цикла явно содержит фазу инициализации. Потому эквивалент записи нашего цикла на языке Pascal выглядит следующим образом:
init;
while not exit do body
С вариантами и инвариантами цикл для
from
i := t.lower; Result := t @ lower
invariant
-- Result является максимумом нарезки массива t в интервале [t.lower,i].
variant
t.lower - i
until
i = t.upper
loop
i := i + 1
Result := Result.max (t @ i)
End
Заметьте, инвариант цикла выражен неформально, в виде комментария. Последующее обсуждение в этой лекции объяснит это ограничение языка утверждений.
Вот еще один пример, ранее показанный без вариантов и инвариантов. Целью следующей функции является вычисление наибольшего общего делителя - НОД (gcd - greatest common divisor) двух положительных целых
gcd (a, b: INTEGER): INTEGER is
-- НОД a и b
require
a > 0; b > 0
local
x, y: INTEGER
do
from
x := a; y := b
until
x = y
loop
if x > y then x := x - y else y := y - x end
end
Result := x
ensure
-- Result является НОД a и b
end
Как узнать, что функция
x > 0; y > 0
-- Пара
Это и будет служить инвариантом цикла
if x > y then x := x - y else y := y - x end