Представим, что мы вообще не видели код С0, а пытаемся запрограммировать обработку подобласти в указанных границах A..B. Сразу опознаём
схему "полный проход"и пытаемся ее применить:
Код: "OBERON"
взять_первую_ситуацию;
WHILE ~конец_ситуаций (* т.е. "удалось взять очередную ситуацию" *) DO
(* Предусловие: имеем текущую ситуацию, подлежащую обработке *)
полезное_действие_в_текущей_ситуации;
переход_к_следующей_ситуации
END
В этой схеме есть некие ситуации\элементы\объекты\члены\единицы (то, из чего состоит последовательность) и переходы между ними. «Полезное_действие_в_текущей_ситуации» т. е. «обработка текущего элемента» для нас не так важны — это будет просто тело цикла FOR. Сосредоточимся на
элементах и
переходах. Элемент — это, конечно же, сами целые числа из отрезка
А..В, последовательно сменяющие друг друга в переменной
х. Переходы — это переход от текущего числа к следующему - у нас,видимо, это должно обеспечиваться инкрементом.
Итак «взять первую ситуацию» означает присвоить переменной первый элемент. Ладно, пусть будет
x:=A; И что дальше? WHILE «удалось взять очередной (т. е. первый) элемент»? Но ведь первый всегда можно взять, потому, что
А в границах типа, а значит присваивание всегда возможно. Хотя, если
А>B, то отрезок
А..В пустой и никакого первого элемента нет...
Ах, вот в чём дело! "Переход" в данной схеме — это всегда "
попытка перехода" (с последующим выяснением ее успешности). Не «взять_первую_ситуацию», а «
попытаться взять», не «переход_к_следующей», а «
попытка перейти». Среда исполнителя гарантирует безопасное выполнение любого перехода (по крайней мере 1 раз подряд). Это дает нам право на ошибку- право сначала попробовать сделать, а потом узнать надо ли было.А как иначе,возможно, это единственный способ узнать? Сначала шагнем, потом поглядим куда; сначала схватим, потом разберемся наше или чужое. И ничего страшного при этом не произойдет, нигде не взорвется, нужная информация не потеряется, по рукам загребущим никто не даст. Только где-то какой-то флажок зафиксирует (не)успешность, мы потом на него поглядим и решим, что дальше.
Это
обработка ( полезное_действие_в_текущей_ситуации) у нас стоит внутри цикла, а значит под охраной WHILE-условия, а переход может выскочить за границы (и даже
должны мы когда-то выскочить, а то цикл бесконечный будет). Поэтому «удалось взять очередной элемент» следует понимать как «удалось взять очередной элемент
для обработки ». Переходом мы предлагаем на обработку некого кандидата, а уж потом смотрим (условием в WHILE) подойдет он нам или нет. И поскольку подходящие элементы должны идти подряд, то первый же «забракованный», означает и прекращение всего прохода. Вот такое тонкое различие между тем, что положено в переменную и тем, что положено с этим «покладенным» делать.
Поэтому присваивание
x:=A; технически возможно всегда (переход возможен всегда), но это именно попытка отдать
А на обработку. Дальше нужно придумать условие «переход был успешным»(«не выскочили за границы», «кандидат подходит»). Мы можем выскочить лишь за правую границу, поэтому «в границах» эквивалентно
x<=B. «Полезное действие», как уже сказано, просто тело цикла FOR. А вот с переходом к следующему интересней.
INC(x) обеспечит, вроде бы, такой переход (выдаст «очередного кандидата»), но...Переход должен выполняться всегда, инкремент выполним не всегда. Если
х станет равно максимальному значению в данном типе, то инкремент не определен. И переполнение ли тут произойдет, или ТРАП роли не играет, все равно неприятности. Даже молчаливое ничегонеделание в качестве эффекта INC(MAX(T)) плохо именно своей молчаливостью. Потому что: 1)все равно вне рамок ЯП, т. е. зависит от реализации; 2)переход должен быть не только безопасным, но и «отзывчивым» - должен сообщать об успешности своего выполнения, а поскольку флажок переполнение в самом Обероне недоступен, такой проверки нет.
Конечно, если заранее известно, что
B<MAX(T), то проблем с инкрементом нет:переходы безопасны , проверка успешности есть. Применяем схему полного прохода и получим ... тот самый стандартный С0.
Получается, что схема полного прохода не всегда применима, потому что не всегда выполняются условия о свойствах переходов (взятии очередного элемента). Если это не учесть, то получим такой же дефект, как в цикле FOR.
Цитата:
«Правильно организованный интерфейс потокового чтения предоставляет функцию «считать очередной элемент» и признак, который показывает, было ли считывание успешным."
Вот только INC(x) у нас не совсем правильный, и дает сбой на границе типа.
Т.е. перед применением схемы полного прохода нужно не только найти в ней элементы и переходы, но и проверить свойства этих переходов
при всех значениях параметров, определяемых предусловием. Если эти свойства выполняются не всегда, то либо усилить предусловие (по крайней мере, оговорить в комментариях, а лучше написать что-то вроде
АSSERT (B<MAX(T)) ), либо сконструировать из имеющихся средств переходы именно безопасные и ...(вот не могу термин подобрать, как назвать наличие обратной связи, при помощи которой можно проверить был ли успешен этот переход?). Конструированием такого инкремента займемся в следующем сообщении (должно получиться что-то похожее на С1\С2).