Использование функций без побочных эффектов для извлечения различных элементов (сундуков, зайцев, уток, яиц, игл, смертей Кощея) друг из друга
невозможно, так как структурные значения не могут быть результатами функций. Если выводить ссылку на значение, то это уже изменяет значение динамической переменной (побочный эффект).
Как например здесь:
i := 0;
WHILE (i < LEN(сундуки)) & ((ЗАЯЦ(сундуки[i]) = NIL) OR (УТКА(ЗАЯЦ(сундуки[i])) = NIL) OR (ЯЙЦО(УТКА(ЗАЯЦ(сундуки[i]))) = NIL)) DO INC(i) END
....
(* приск первого сундука с иглой *)
WHILE list.HasMoreElements() AND смерть = NIL DO
сундук := list.GetNext();
IF сундук.UnpackTo( заяц ) AND
заяц .UnpackTo( утка ) AND
утка .UnpackTo( яйцо ) AND
яйцо .UnpackTo( игла ) AND
игла .UnpackTo( смерть ) THEN
Log.String("капец Кащею");
END;
END;
PROCEDURE Store (obj: ANYPTR; VAR ptr: ANYPTR): BOOLEAN;
BEGIN
ptr := obj;
RETURN obj # NIL
END Store;
i := 0;
WHILE (i < сундуки.length) &
~( Store(ЗАЯЦ(сундуки[i]), заяц)) & Store(УТКА(заяц(Заяц)), утка)) &
Store(ЯЙЦО(утка(Утка)), яйцо)) & Store(ИГЛА(яйцо(Яйцо)), игла)) )
DO
INC(i)
END
Впрочем само условие задачи (автор) требует, чтобы сохранялись промежуточные значения элементов. Так что без побочных эффектов не обойтись, если пользоваться одним выражением проверки.
Если представлять значения элементов самими указателями (как у меня), то их разыменованные значения (хранящие структуру вложенности элементов) станут изменяемыми во внешних модулях, то есть интерфейс модуля "сундуков Кощея" будет недостаточно изолирован. К тому же ссылочные структуры засоряют память.
Использование собственно процедур для извлечения различных элементов друг из друга, не позволит выполнять проверку в одном выражении. Придеться использовать вложенные операторы или же последовательные операторы с лишними проверками: (это собственно к вопросу о циклах)
existSearchDeath := FALSE; (* смерть Кощея пока не найдена *)
WHILE
(i < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchDeath (* проверка, что смерть Кощея еще не найдена *)
DO
ds.сhests^[i].Hare(hare); (* извлечение зайца из сундука *)
IF hare # nullHare THEN (* проверка, что в сундуке есть заяц *)
hare.Duck(duck); (* извлечение утки из зайца *)
IF duck # nullDuck THEN (* проверка, что в зайце есть утка *)
duck.Egg(egg); (* извлечение яйца из утки *)
IF egg # nullEgg THEN (* проверка, что в утке есть яйцо *)
egg.Needle(needle); (* извлечение иглы из яйца *)
IF needle # nullNeedle THEN (* проверка, что в яйце есть игла *)
needle.Death(death); (* извлечение смерти Кощея из иглы *)
existSearchDeath := death # nullDeath; (* смерть Кощея найдена, если игла не пустая *)
END
END
END
END;
INC(i) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END
или
existSearchDeath := FALSE; (* смерть Кощея пока не найдена *)
WHILE
(i < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchDeath (* проверка, что смерть Кощея еще не найдена *)
DO
ds.сhests^[i].Hare(hare); (* извлечение зайца из сундука *)
b := hare # nullHare; (* заяц найден, если сундук не пустой *)
IF b THEN (* проверка, что в сундуке есть заяц *)
hare.Duck(duck); (* извлечение утки из зайца *)
b := duck # nullDuck; (* утка найдена, если зайц не пустой *)
END;
IF b THEN (* проверка, что в зайце есть утка *)
duck.Egg(egg); (* извлечение яйца из утки *)
b := egg # nullEgg; (* яйцо найдено, если утка не пустая *)
END;
IF b THEN (* проверка, что в утке есть яйцо *)
egg.Needle(needle); (* извлечение иглы из яйца *)
b := needle # nullNeedle; (* игла найдена, если яйцо не пустое *)
END;
IF b THEN (* проверка, что в яйце есть игла *)
needle.Death(death); (* извлечение смерти Кощея из иглы *)
existSearchDeath := death # nullDeath; (* смерть Кощея найдена, если игла не пустая *)
END;
INC(i) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END