Я давно собирался написать статью по этому вопросу, да как-то все руки не доходят. Но, поскольку здесь
viewtopic.php?f=27&t=3157&start=40 в очередной раз разгорелся базар по этому поводу, то напишу кое-какие тезисы.
Итак, имеем обобщенный цикл вида
Код:
цикл
последовательность команд Х
что-нибудь типа exit или break по условию
последовательность команд Y
конец цикла
Середину с выходом можно гонять как слайдер вверх-вниз и получать граничные случаи: цикл
while и цикл
repeat..until. Это хорошо.
Плохо то, что формальный аппарат, описанный у Дейкстры-Грисса можно применять вроде бы только для циклов типа while.
В принципе,
Кнут приводит аксиоматизацию Дала для обобщенного цикла:
Цитата:
Пусть: {P}S{Q} {Q & B}T{P}
Тогда: {P} loop: S; while B : T; repeat; {Q & ~B } (стр. 49)
В ней нет ничего сложного, но для простого программиста польза от этой записи равна строго нулю.
Тем не менее, все не так плохо. Надо только ответить на 2 вопроса.
1. Что является телом обобщенного цикла?
2. В какой точке цикла нужно проверять инвариант цикла?
Ответить на эти вопросы проще, чем их поставить.
Если немного подумать, то легко понять, что телом цикла является кусок Y;X. Т.е., чтобы рассуждать о таком цикле, нужно просто мысленно подставить вехний кусок X под нижний Y. А вся конструкция эквивалентна последовательности
Код:
X;
пока условие цикл
Y;
X;
конец цикла
Здесь как раз и вылезает то самое дублирование кода.
Что касается инварианта цикла, которой традиционно проверяется "перед циклом", то уже становится ясно, что в данном случае инвариант нужно проверять
внутри цикла, в точке перед условием выхода.
Вот и все.
Все наработки для циклов
while остаются в силе (в том числе и весь бред про линейный поиск, для любителей).
В заключение надо отметить, что с эстетической точки зрения цикл с выходом из середины выглядит, конечно, отвратно. И вреден для неокрепших умов. Здесь - самое главное иметь четкую картину в голове. И укреплять ум(ы)
.