Автор Тема: Выход из цикла или смерть Кощея  (Прочитано 55329 раз)

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Выход из цикла или смерть Кощея
« Ответ #30 : Январь 11, 2013, 09:09:34 am »
Этот пост адресован тем, кто считает наличие exit/break внутри цикла плохим тоном. В идеале, тем, кто может показать, чем это плохо, но где ж таких возьмешь :)
Чем плохо сказать-то можно. Представьте себе цикл на 300 строчек с глубоко вложенными if, внутри которых иногда есть break|continue|return (цикл писался и дописывался студентами года два). Хочется переписать его  разбив на процедуры, но из-за глубоко вложенных break|continue|return эта задачка не так то уж проста.

Я бы так написал:

Поиск первого сундука с иглой:
if (сундуки.Length > 0)
{
игла = null;
int i = 0;
do
{
заяц = ЗАЯЦ(сундуки[i++]);
if (заяц != null)
{
утка = УТКА(заяц);
if (утка != null)
{
яйцо = ЯЙЦО(утка);
if (яйцо != null)
{
игла = ИГЛА(яйцо);
}
}
}
}
while ((i < сундуки.Length) && (игла == null));
if (игла != null)
{
// нашли первый сундук с иглой
сундук = сундуки[i - 1];
//...
}
}
Поиск первого сундука без иглы:
if (сундуки.Length > 0)
{
игла = null;
int i = 0;
do
{
заяц = ЗАЯЦ(сундуки[i++]);
if (заяц != null)
{
утка = УТКА(заяц);
if (утка != null)
{
яйцо = ЯЙЦО(утка);
if (яйцо != null)
{
игла = ИГЛА(яйцо);
}
}
}
}
while ((i < сундуки.Length) && (игла != null));
if (игла == null)
{
// нашли первый сундук без иглы
сундук = сундуки[i - 1];
//...
}
}

Kemet

  • Hero Member
  • *****
  • Сообщений: 587
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #31 : Январь 11, 2013, 10:06:37 am »
добавим чуть-чуть "грязи" ))
TYPE
  Element = OBJECT( List.Element )
    data: Element;

    PROCEDURE[EXTENSIBLE] UnpackTo( VAR to: Element ): BOOLEAN =
    VAR
      res: BOOLEAN;
    BEGIN
      res := SELF.data # NIL;
     
      to  := SELF.data;
    END UnPack;
  END Element;
 
  Смерть  = OBJECT ( Element )
  END Смерть;

  Игла    = OBJECT ( Element )
    PROCEDURE UnpackTo( to: Смерть ): BOOLEAN = INHERITED;
  END Игла;

  Яйцо    = OBJECT ( Element )
    PROCEDURE UnpackTo( to: Игла ): BOOLEAN = INHERITED;
  END Яйцо;

  Утка    = OBJECT ( Element )
    PROCEDURE UnpackTo( to: Яйцо ): BOOLEAN = INHERITED;
  END Утка;

  Заяц    = OBJECT ( Element )
    PROCEDURE UnpackTo( to: Утка ): BOOLEAN = INHERITED;
  END Утка;

  Сундук  = OBJECT ( Element )
    PROCEDURE UnpackTo( to: Заяц ): BOOLEAN = INHERITED;
  END Сундук;


VAR
  сундук, заяц, утка, яйцо, игла, смерть : Element := NIL;
  list := List.Ref;
 
....
(* приск первого сундука с иглой *)
  WHILE list.HasMoreElements() AND смерть = NIL DO
    сундук := list.GetNext();
   
    IF  сундук.UnpackTo( заяц ) AND
        заяц  .UnpackTo( утка ) AND
        утка  .UnpackTo( яйцо ) AND
        яйцо  .UnpackTo( игла ) AND
        игла  .UnpackTo( смерт ) THEN
      Log.String("капец Кащею");
    END;
  END;

ddn

  • Jr. Member
  • **
  • Сообщений: 59
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #32 : Январь 11, 2013, 11:06:20 am »
Чтобы вложенные предметы были доступны по очереди, сделал их через вложенные ссылки.
У меня получилось так.

Сам алгоритм:
existSearchDeath := FALSE; (* смерть Кощея пока не найдена *)
WHILE
  (i < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchDeath (* проверка, что смерть Кощея еще не найдена *)
DO
IF
  (ds.сhests^[i].hare # NIL) (* проверка, что в сундуке есть заяц *)
& (ds.сhests^[i].hare^.duck # NIL) (* проверка, что в зайце есть утка *)
& (ds.сhests^[i].hare^.duck^.egg # NIL) (* проверка, что в утке есть яйцо *)
& (ds.сhests^[i].hare^.duck^.egg^.needle # NIL) (* проверка, что в яйце есть игла *)
THEN
existSearchDeath := TRUE (* смерть Кощея найдена *)
END; (* проверка наличия смерти Кощея в сундуке *)
INC(i) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END

Модуль поиска:
MODULE searchdeathscrag;


IMPORT
StdLog, ds := deathscrag;


VAR
i-: INTEGER;
(* номер первого еще не просмотренного сундука, если такой есть *)
(* (i >= 0) & (i <= n) *)
existSearchDeath-: BOOLEAN;
(* existSearchDeath = (следующая смерть Кощея найдена при последнем поиске) *)
(* existSearchDeath -> ((номер последнего найденного сундука со смертью Кощея) = i-1) *)
(* ~existSearchDeath -> (i = n) *)
(* (i = 0) -> ~existDeath *)
k: LONGINT;
(* номер версии набора сундуков, в котором идет поиск *)


PROCEDURE ClearSearch*;
BEGIN
i := 0; (* переход к первому сундуку *)
existSearchDeath := FALSE; (* смерть Кощея не найдена *)
k := ds.k (* сохранение версии набора сундуков *)
END ClearSearch; (* очистка поиска *)


PROCEDURE SearchDeath*(*0:0*);
BEGIN
IF k # ds.k THEN (* проверка версии набора сундуков *)
StdLog.Ln;
StdLog.String("дальнейший поиск невозможен, так как массив сундуков был изменен");
StdLog.Ln;
HALT(0)
END(*0:0*); (* прерывание поиска если номер версии набора сундуков устарел *)
existSearchDeath := FALSE; (* смерть Кощея пока не найдена *)
WHILE
  (i < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchDeath (* проверка, что смерть Кощея еще не найдена *)
DO
IF
  (ds.сhests^[i].hare # NIL) (* проверка, что в сундуке есть заяц *)
& (ds.сhests^[i].hare^.duck # NIL) (* проверка, что в зайце есть утка *)
& (ds.сhests^[i].hare^.duck^.egg # NIL) (* проверка, что в утке есть яйцо *)
& (ds.сhests^[i].hare^.duck^.egg^.needle # NIL) (* проверка, что в яйце есть игла *)
THEN
existSearchDeath := TRUE (* смерть Кощея найдена *)
END; (* проверка наличия смерти Кощея в сундуке *)
INC(i) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END
END(*0:0*) SearchDeath; (* поиск смерти Кощея *)
(*
existDeath :=
  (ds.chests^[i].hare # NIL) (* проверка, что в сундуке есть заяц *)
& (ds.chests^[i].hare^.duck # NIL) (* проверка, что в зайце есть утка *)
& (ds.chests^[i].hare^.duck^.egg # NIL) (* проверка, что в утке есть яйцо *)
& (ds.chests^[i].hare^.duck^.egg^.needle # NIL) (* проверка, что в яйце есть игла *)
(* проверка наличия смерти Кощея в сундуке *)
*)


BEGIN
ClearSearch (* очистка поиска *)
END searchdeathscrag.

И модуль Кощея:
MODULE deathscrag;


IMPORT
StdLog;


CONST
noExistDeath = TRUE;
(* разрешено не иметь ни одного сундука со смертью Кощея, если число сундуков не нуль *)
multyExistDeath = TRUE;
(* разрешено иметь несколько сундуков со смертью Кощея *)


TYPE TypeChests* =
POINTER TO
ARRAY OF
RECORD
hare*: POINTER TO
RECORD
duck*: POINTER TO
RECORD
egg*: POINTER TO
RECORD
needle*: POINTER TO
RECORD
END (* тип иглы *)
END (* тип яйца *)
END (* тип утки *)
END (* тип зайца *)
END; (* тип сундука *)
(* тип массива сундуков *)
(* тип набора сундуков *)


VAR
сhests-: TypeChests;
(* набор сундуков *)
n-: INTEGER;
(* число сундуков *)
(* (chests # NIL) -> (n = LEN(chests^)) *)
(* (chests = NIL) -> (n = 0) *)
k-: LONGINT;
(* номер версии набора сундуков *)


PROCEDURE IncK (*0:0*);
BEGIN
IF k < MAX(LONGINT) THEN
INC(k) (* переход к номеру новой версии набора сундуков *)
ELSE
StdLog.String("дальнейшие версии набора сундуков невозможны, перезагрузите модули");
HALT(0)
END(*0:0*)
END(*0:0*) IncK; (* установка нового номера версии набора сундуков *)


PROCEDURE ClearChests* (*0:0*);
BEGIN
n := 0; (* количество сундуков в новом пустом наборе *)
IncK(*0:0*); (* переход к номеру новой версии набора сундуков *)
сhests := NIL; (* создание пустого массива сундуков *)
END(*0:0*) ClearChests; (* установка пустого массива сундуков *)


(* forall j (0 <= j <= LEN(a))(0 <= a[j] <= 4) *)
(* noExistDeath OR (exist j (0 <= j <= LEN(a))(a[j] = 4)) *)
(* multyExistDeath OR
(forall j1, j2 (0 <= j1,j2 <= LEN(a))((a[j1] = 4) & (a[j2] = 4) -> (j1 = j2))) *)
PROCEDURE NewChests* (IN a: ARRAY OF BYTE; OUT b: BOOLEAN)(*0:0*);
VAR
bNo, bMulty: BOOLEAN;
j: INTEGER;
(* номер первого сундука с непрописанным содержимым, если такой есть *)
BEGIN
bNo := FALSE; (* сундука со смертью Кощея пока не обнаружено *)
bMulty := FALSE; (* несколько сундуков со смертью Кощея пока не обнаружено *)
b := TRUE; (* ошибок данных пока не обнаружено *)
n := LEN(a); (* новое количество сундуков в наборе; LEN(a) > 0 *)
NEW(сhests, n); (* создание нового набора сундуков *)
j := 0; (* переход к первому сундуку с непрописанным содержимым,
если такой есть *)
WHILE
  (j < n) (* проверка, что сундуки с непрописанным содержимым еще остались *)
& b (* проверка, что ошибок данных пока не обнаружено *)
DO
IF a[j] = 0 THEN
сhests^[j].hare := NIL (* сундук остается пустым *)
ELSE
NEW(сhests^[j].hare); (* помещение зайца в сундук *)
IF a[j] = 1 THEN
сhests^[j].hare^.duck := NIL (* зайц остается пустым *)
ELSE
NEW(сhests^[j].hare^.duck); (* помещение утки в зайца *)
IF a[j] = 2 THEN
сhests^[j].hare^.duck^.egg := NIL (* утка остается пустой *)
ELSE
NEW(сhests^[j].hare^.duck^.egg); (* помещение яйца в утку *)
IF a[j] = 3 THEN
сhests^[j].hare^.duck^.egg^.needle := NIL (* яйцо остается пустым *)
ELSIF a[j] = 4 THEN
NEW(сhests^[j].hare^.duck^.egg^.needle); (* помещение иглы в яйцо *)
IF ~bNo THEN (* проверка, что сундука со смертью Кощея еще не обнаружено *)
bNo := TRUE (* первый сундук со смертью Кощея обнаружен *)
ELSIF ~bMulty THEN (* проверка, что только один сундук со смертью Кощея
обнаружен *)
bMulty := TRUE; (* несколько сундуков со смертью Кощея обнаружено *)
b := multyExistDeath (* ошибка данных обнаружена, если не разрешено иметь
несколько сундуков со смертью Кощея *)
END
ELSE
b := FALSE (* ошибка данных обнаружена *)
END
END
END
END; (* установка содержимого j-го сундука,
либо прекращение установки при ошибке данных *)
INC(j) (* переход к следующему сундуку с непрописанным содержимым,
если такой есть *)
END;
b := b & (noExistDeath OR bNo); (* ошибка данных обнаружена, если не имеется ни одного
сундука со смертью Кощея и это не разрешено *)
IF ~b THEN
ClearChests(*0:0*) (* создание пустого массива сундуков при наличии ошибки данных *)
ELSE
IncK(*0:0*) (* переход к номеру новой версии набора сундуков *)
END(*0:0*)
END(*0:0*) NewChests; (* создание нового массива сундуков с новым содержимым *)
(* b -> (в массиве 'a' ошибок данных нет,
новый массив сундуков установлен согласно содержимому массива 'a') *)
(* ~b -> (в массиве 'a' есть ошибки данных, новый массив сундуков пустой) *)


BEGIN
k := -1; (* номер версии, предшествующий версии первого набора сундуков *)
ClearChests (* установка первого массива сундуков *)
END deathscrag.

Перевод:
смерть кощей сундук заяц утка яйцо игла
death scrag chest hare duck egg needle

Berserker

  • Sr. Member
  • ****
  • Сообщений: 254
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #33 : Январь 11, 2013, 11:26:48 am »
Сундуков много, операция разборки дорогая, но вот после цикла таких операций одну такую лишнюю позволить - ни-ни.

Цитировать
Использовать функцию, чтобы упрятать разборку, недопустимо, по причине снижения эффективности.
Какое снижение эффективности? При росте тяжести операции разборки, резко падают затраты на любые обёртки. В ряде языков на такой ужасный случай есть inline.

Чтобы попусту не ворчать:

i := 0;

WHILE
  (i < LEN(сундуки)) &
  (
    ~ЗАЯЦ(сундуки[i], заяц) OR
    ~УТКА(заяц, утка)       OR
    ~ЯЙЦО(утка, яйцо)
  )
DO
  INC(i);
END;

Мои функции обычно возвращают только BOOLEAN, а результат в виде OUT-параметров. Так что сложностей не возникает.
« Последнее редактирование: Январь 11, 2013, 11:31:26 am от Berserker »

Губанов Сергей Юрьевич

  • Hero Member
  • *****
  • Сообщений: 590
    • Просмотр профиля
    • Домашняя страница
Re: Выход из цикла или смерть Кощея
« Ответ #34 : Январь 11, 2013, 11:56:56 am »
  WHILE list.HasMoreElements() AND смерть = NIL DO
    сундук := list.GetNext();   
    IF  сундук.UnpackTo( заяц ) AND
        заяц  .UnpackTo( утка ) AND
        утка  .UnpackTo( яйцо ) AND
        яйцо  .UnpackTo( игла ) AND
        игла  .UnpackTo( смерт ) THEN
      Log.String("капец Кащею");
    END;
  END;
+1. Близко к идеальному. Смотришь и всё сразу понятно.

ddn

  • Jr. Member
  • **
  • Сообщений: 59
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #35 : Январь 11, 2013, 12:48:33 pm »
добавим чуть-чуть "грязи" ))
...
        игла  .UnpackTo( смерт ) THEN
...
А разве не в каждой игле лежит смерть? Что-то этот уровень вложенности я пропустил.

Если еще искать сундук без смерти Кощея, то будет такой алгоритм:
WHILE
  (j < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchNoDeath (* проверка, что сундук без смерти Кощея еще не найден *)
DO
IF
   (ds.сhests^[j].hare = NIL) (* проверка, что в сундуке есть заяц *)
OR (ds.сhests^[j].hare^.duck = NIL) (* проверка, что в зайце есть утка *)
OR (ds.сhests^[j].hare^.duck^.egg = NIL) (* проверка, что в утке есть яйцо *)
OR (ds.сhests^[j].hare^.duck^.egg^.needle = NIL) (* проверка, что в яйце есть игла *)
THEN
existSearchNoDeath := TRUE (* сундук без смерти Кощея найдена *)
END; (* проверка отсутствия смерти Кощея в сундуке *)
INC(j) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END
(*
existSearchNoDeath :=
   (ds.chests^[j].hare = NIL) (* проверка, что в сундуке есть заяц *)
OR (ds.chests^[j].hare^.duck = NIL) (* проверка, что в зайце есть утка *)
OR (ds.chests^[j].hare^.duck^.egg = NIL) (* проверка, что в утке есть яйцо *)
OR (ds.chests^[j].hare^.duck^.egg^.needle = NIL) (* проверка, что в яйце есть игла *)
(* проверка отсутствия смерти Кощея в сундуке *)
*)

А в мой модуль поиска searchdeathscrag нужно добавить:
VAR
j-: INTEGER;
(* номер первого еще не просмотренного сундука, если такой есть *)
(* (j >= 0) & (j <= n) *)
existSearchNoDeath-: BOOLEAN;
(* existSearchNoDeath = (следующий сундук без смерти Кощея найден при последнем поиске) *)
(* existSearchNoDeath -> ((номер последнего найденного сундука без смерти Кощея) = j-1) *)
(* ~existSearchNoDeath -> (j = n) *)
(* (j = 0) -> ~existDeath *)
l: LONGINT;
(* номер версии набора сундуков, в котором идет поиск сундука без смерти Кощея *)


PROCEDURE ClearSearchNoDeath*;
BEGIN
j := 0; (* переход к первому сундуку *)
existSearchNoDeath := FALSE; (* сундук без смерти Кощея не найден *)
l := ds.k (* сохранение версии набора сундуков *)
END ClearSearchNoDeath; (* очистка поиска *)


PROCEDURE SearchNoDeath*(*0:0*);
BEGIN
IF k # ds.k THEN (* проверка версии набора сундуков *)
StdLog.Ln;
StdLog.String("дальнейший поиск невозможен, так как массив сундуков был изменен");
StdLog.Ln;
HALT(0)
END(*0:0*); (* прерывание поиска если номер версии набора сундуков устарел *)
existSearchNoDeath := FALSE; (* сундук без смерти Кощея пока не найден *)
WHILE
  (j < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchNoDeath (* проверка, что сундук без смерти Кощея еще не найден *)
DO
IF
   (ds.сhests^[j].hare = NIL) (* проверка, что в сундуке есть заяц *)
OR (ds.сhests^[j].hare^.duck = NIL) (* проверка, что в зайце есть утка *)
OR (ds.сhests^[j].hare^.duck^.egg = NIL) (* проверка, что в утке есть яйцо *)
OR (ds.сhests^[j].hare^.duck^.egg^.needle = NIL) (* проверка, что в яйце есть игла *)
THEN
existSearchNoDeath := TRUE (* сундук без смерти Кощея найден *)
END; (* проверка отсутствия смерти Кощея в сундуке *)
INC(j) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END
END(*0:0*) SearchNoDeath; (* поиск сундука без смерти Кощея *)
(*
existSearchNoDeath :=
   (ds.chests^[j].hare = NIL) (* проверка, что в сундуке есть заяц *)
OR (ds.chests^[j].hare^.duck = NIL) (* проверка, что в зайце есть утка *)
OR (ds.chests^[j].hare^.duck^.egg = NIL) (* проверка, что в утке есть яйцо *)
OR (ds.chests^[j].hare^.duck^.egg^.needle = NIL) (* проверка, что в яйце есть игла *)
(* проверка отсутствия смерти Кощея в сундуке *)
*)

...

ClearSearchNoDeath (* очистка поиска сундука без смерти Кощея *)

Но мне следует переделать свой вариант через методы.

albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #36 : Январь 14, 2013, 01:43:41 pm »
Вот в MUMPSе можно придумать структуру данных (сундуки) с которой проще работать,
и весь код сведётся к :
s i=""
 f  s i=$o(sunduki(i))  q:i=""  q:$d(sunduki(i,"заяц","утка","яйцо","игла"))
в переводе на литературный будет примерно так:
Организуем цикл перебора по порядку всех индексов массива sunduki()  до достижении конца массива или
наличии записи о зайце в сундуке, утки в зайце, яйце в утке и игле в яйце.
Если же ищется недостача, то всё отличие будет в одной закорючке - апострофе (инверсия лог.значения)

q:'$d(sunduki(i,"заяц","утка","яйцо","игла"))
По выходу i будет индексом при котором условие выполнилось или
пустой строкой (""), если безрезультатно.
Тут следует сказать, что какие-либо специфические данные об сундуке, зайцах, утках и т.д тоже могут быть в этом же
массиве sunduki. Например :)
sunduki(i,"заяц","вес")
sunduki(i,"заяц","утка","жирность")
sunduki(i,"заяц","утка","яйцо","игла","быстродействие")

Peter Almazov

  • Sr. Member
  • ****
  • Сообщений: 482
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #37 : Январь 15, 2013, 02:38:09 am »
Суть проблемы в следующем.
Традиционный цикл while имеет одну точку выхода – логическое выражение в заголовке цикла. В данной задаче (совсем не такой уж редкой, кстати) вычисление условия выхода – многоступенчатый процесс, в котором надо закешировать промежуточные переменные. Поэтому точку выхода надо растянуть до области выхода. В цикле while на Си это можно сделать с некоторой натяжкой, но, это конечно, изврат.

Практическая реализация предлагается такая: добавить ключевое слово АndWhile.
По русски, кстати, тоже будет неплохо: Пока ...ИПока...

Сразу пример, чтобы было понятно.

Тело цикла (строка "i=i+1") будет выполняться пока истинна коньюнкция всех охран – выражений после while и andwhile. Соответственно, после выхода из цикла будет истинна коньюнкция отрицаний всех охран.

В принципе, слово andwhile годится и для цикла do{….} while. Некошерно, конечно начинать с and когда еще не было просто while, но ничего лучше мне не удалось придумать.

Внедрить это в язык проще пареной репы, т. к. andwhile – это замаскированный if-exit.
Но на практике такое внедрение приведет к грандиозному, эпическому провалу.
Во-первых, andwhile нельзя засовывать в скобочные конструкции (в if, например) внутри цикла. Но это еще можно проверить.


Во-вторых, в области выхода (от первого while до последнего andwhile) нельзя выполнять никаких действий, влияющих на инвариант цикла. Только вычисление условий выхода и кеширование переменных. Как показывает практика, объяснить это не то что "ополченцам", но и легиону программистов совершенно невозможно. А формализовать это и проверить нельзя (не вижу как).
Поэтому все будет переврано и сделано через жопу самыми невероятными способами.

Peter Almazov

  • Sr. Member
  • ****
  • Сообщений: 482
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #38 : Январь 15, 2013, 03:04:11 am »
Соответственно, после выхода из цикла будет истинна коньюнкция отрицаний всех охран.
Дизъюнкция, конечно же.

albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #39 : Январь 15, 2013, 04:17:49 am »

Kemet

  • Hero Member
  • *****
  • Сообщений: 587
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #40 : Январь 15, 2013, 04:47:28 am »
Я тоже думал над этим, иногда полезно, нужно ввести какой-то механизм типа "конвейер операций" или "конвейер условий"

Kemet

  • Hero Member
  • *****
  • Сообщений: 587
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #41 : Январь 15, 2013, 05:04:26 am »
ну или как-то так  ;) :
IF (( a
  AND := BEGIN ... RETURN b END
  AND := BEGIN ... RETURN c END )
  OR d
  OR := BEGIN ... RETURN e END )
THEN
  ...
END;

albobin

  • Full Member
  • ***
  • Сообщений: 198
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #42 : Январь 15, 2013, 05:31:08 am »
Скажу опять про тот же MUMPS.
нужная конструкция получается влёт
действие0
if условие1        действие1
if  if условие2    действие2
if  if условие3    действие3
....
if  if условиеN    действиеN

понять поведение можно по имитирующей конструкции с использованием вспомогательной переменной t

t:=true
действие0
if условие1       then  действие1    else  t:=false   end
if t and условие2  then  действие2    else  t:=false   end
if t and условие3  then  действие3    else  t:=false   end
...
if t and условиеN  then  действиеN    else  t:=false   end





Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #43 : Январь 15, 2013, 06:51:03 am »
На Обероне я бы сделал, наверное, так:

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

Единственное, чтобы передать переменную по VAR-параметру, она должна иметь тип ANYPTR. Отсюда потом каждый раз приведение типа.

Илья Ермаков

  • Sr. Member
  • ****
  • Сообщений: 493
    • Просмотр профиля
Re: Выход из цикла или смерть Кощея
« Ответ #44 : Январь 15, 2013, 06:59:13 am »
А теперь решение... на ДРАКОНе (да простит меня уважаемый топикстартер :) )

Про что я всегда и пытался говорить - такие случаи разруливаются на плоскости безболезненно.

Посмотрите на вертикальный блок, проверяющий все !null и между проверками добывающий очередной объект.
Это наглядное выражение проверки конъюнкции условий с промежуточными действиями.
Два исхода - когда конъюнкция истинна (мы обладаем иглой) и когда она ложна (и мы продолжаем цикл).

И у самого цикла два исхода - "нашли"-"не нашли".

Поставьте палец где-нибудь внизу цикла - и сразу увидите постусловие (просто пробежавшись назад по маршруту, приведшему в данную точку, и собрав все условия на пути (или их отрицания) в конъюнкцию). Понятная структура, понятные логические свойства (делаю разрез в любой точке и знаю предусловие для данной точки).

А теперь расходитесь в стороны, господа, и дайте Петру ружьё :)