OberonCore

Библиотека  Wiki  Форум  BlackBox  Компоненты  Проекты
Текущее время: Вторник, 19 Март, 2024 11:21

Часовой пояс: UTC + 3 часа




Начать новую тему Ответить на тему  [ Сообщений: 96 ]  На страницу Пред.  1, 2, 3, 4, 5  След.
Автор Сообщение
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 12 Апрель, 2010 22:39 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8500
Откуда: Троицк, Москва
Александр Ильин писал(а):
заниматься подобным ранее мне не доводилось, так как LOOP отродясь не использовал.
Это Вам лекций читать не доводилось с контуперными гениями в аудитории (не буду в который раз ссылаться на свой пример).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Вторник, 13 Апрель, 2010 16:11 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Ещё один тривиальный экземпляр, в котором LOOP не нужен:
Код:
LOOP
   IF Condition () THEN
      SomeCode (); (* несколько строк кода *)
   ELSE
      EXIT
   END;
END;
Это же самый обычный WHILE:
Код:
WHILE Condition () DO
   SomeCode (); (* несколько строк кода *)
END;
Как и со всеми прочими примерами в данной теме, при записи через WHILE уменьшается и число строк, и уровень вложенности полезного кода (число отступов).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Вторник, 13 Апрель, 2010 21:58 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Не смог придумать более компактную альтернативу следующему LOOP'у, оставил как есть:
Код:
LOOP
   IF A THEN
      CodeBlock1;
   ELSIF B THEN
      CodeBlock2;
   ELSE
      EXIT
   END;
END;
Можно вынести блоки кода в локальные процедуры, но при этом объём текста увеличится и надо будет придумывать им имена. При единственном EXIT'е код и так достаточно ясен.

UPD: Ба! Да это жеж цикл Дейкстры!


Последний раз редактировалось Александр Ильин Среда, 14 Апрель, 2010 01:28, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Вторник, 13 Апрель, 2010 22:18 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
В некоторых случаях LOOP играет роль бесконечного цикла, с выходом через RETURN и без единого EXIT'а. В этом случае можно было написать WHILE TRUE DO вместо LOOP, но я пока что оставил LOOP.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Вторник, 13 Апрель, 2010 23:23 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Вот ещё схемка:
Код:
LOOP
   IF Condition () THEN
      CodeBlock ();
   ELSE
      OneShotCode ();
      EXIT
   END;
END;
Преобразуется элементарно в WHILE:
Код:
WHILE Condition () DO
   CodeBlock ();
END;
OneShotCode();
Похожа на одну из ранее рассмотренных, поскольку эквивалентна следующей схеме:
Код:
LOOP
   IF ~Condition () THEN
      OneShotCode ();
      EXIT
   END;
   CodeBlock ();
END;


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Вторник, 13 Апрель, 2010 23:30 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Попался LOOP c двумя логическими RETURN'ами. Схема:
Код:
LOOP
   IF A THEN
      RETURN FALSE
   ELSIF B THEN
      RETURN TRUE
   END;
   SomeCode ();
END;
Сокращается следующим образом:
Код:
WHILE ~A & ~B DO
   SomeCode ();
END;
RETURN ~A
UPD: Поправил опечатки в имени процедуры "SomeCode" - оба раза неправильно написал : ))


Последний раз редактировалось Александр Ильин Четверг, 15 Апрель, 2010 20:31, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Среда, 14 Апрель, 2010 08:00 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Разновидность ранее встречавшейся схемы:
Код:
LOOP
   IF A (x) THEN
      OneShotCode ();
      EXIT
   ELSIF ~Step (x) THEN
      EXIT
   END;
END;
В отличие от ранее рассмотренного случая, здесь A (x) и Step (x) не независимые условия: выполнение Step (x) может изменить x так, что изменится результат A (x). Если точнее, критичным является только влияние Step (x) на A (x) в случае, когда Step (x) возвращает FALSE. То есть, если (Step (x) = FALSE) не влияет на результат A (x), то взаимосвязью можно пренебречь и написать WHILE как в прошлом примере. Если же в реализации Step нет уверенности (например, может измениться в будущем), то лучше перестраховаться следующим образом. Вводим переменную 'found' и превращаем в REPEAT:
Код:
REPEAT
   found := A (x);
UNTIL found OR ~Step (x);
IF found THEN
   OneShotCode ();
END;
Как правило, OneShotCode также зависит от x, что ужесточает требования к Step (x): (Step (x) = FALSE) также не должно влиять на результат выполнения OneShotCode. Если влияет, REPEAT необходим, иначе можно обойтись WHILE.

Также дополнительной переменной бывает полезно воспользоваться, если результат A (x) имеет смысл кэшировать из соображений эффективности или если повторное выполнение A (x) имеет нежелательные побочные эффекты.

UPD: Мелкая литературная правка и дополнение.


Последний раз редактировалось Александр Ильин Четверг, 15 Апрель, 2010 20:35, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Среда, 14 Апрель, 2010 08:05 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Преобразование цикла Дейкстры:
Код:
LOOP
   IF a THEN A ();
   ELSIF b THEN B ();
   ...
   ELSIF z THEN Z ();
   ELSE
      OneShotCode ();
      EXIT
   END;
END;
Выносим одноразовый блок за пределы цикла.
Код:
LOOP
   IF a THEN A ();
   ELSIF b THEN B ();
   ...
   ELSIF z THEN Z ();
   ELSE
      EXIT
   END;
END;
OneShotCode ();


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Среда, 14 Апрель, 2010 08:54 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Ещё один хитрый экземпляр:
Код:
LOOP
   IF A THEN
      EXIT
   ELSIF B THEN
      SomeCode ();
   ELSIF C THEN
      EXIT
   END;
   Step ();
END;
Хитрость в том, что при B = TRUE работа цикла продолжается независимо от значения C.
Код:
WHILE ~A & (B OR ~C) DO
   IF B THEN
      SomeCode ();
   END;
   Step ();
END;


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Среда, 14 Апрель, 2010 09:36 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Предыдущая схема извлечена из следующего кода:
Код:
ok := FALSE;
LOOP
   ch := s [ix];
   IF ch = 0X THEN ok := TRUE; EXIT;
   ELSIF ('0' <= ch) & (ch <= maxdig) THEN dig := ORD (ch) - ORD ('0');
   ELSIF ch # Common.region.tickMark [0] THEN EXIT END;
   IF ch # Common.region.tickMark [0] THEN i := i * base + dig END;
   INC (ix);
END;
Для начала отформатируем блок, чтобы была видна структура:
Код:
ok := FALSE;
LOOP
   ch := s [ix];
   IF ch = 0X THEN
      ok := TRUE;
      EXIT;
   ELSIF ('0' <= ch) & (ch <= maxdig) THEN
      dig := ORD (ch) - ORD ('0');
   ELSIF ch # Common.region.tickMark [0] THEN
      EXIT
   END;
   IF ch # Common.region.tickMark [0] THEN
      i := i * base + dig;
   END;
   INC (ix);
END;
Дана строка s с записью числа по основанию base (2..10). В строке могут встречаться разделители групп разрядов tickMark, которые нужно проигнорировать (например: "1'000'000", tickMark = апостроф). maxdig - максимальный допустимый в записи числа символ = CHR (ORD ('0') + base - 1). Все используемые переменные проинициализированы ранее, здесь этот код не приводится. Требуется расшифровать число и поместить в переменную i типа INTEGER.

Привожу данный пример постольку, поскольку преобразованный код считаю не только более лаконичным, но и гораздо более читабельным:
Код:
ch := s [ix];
WHILE (ch # 0X) & (('0' <= ch) & (ch <= maxdig) OR (ch = Common.region.tickMark [0])) DO
   IF ch # Common.region.tickMark [0] THEN
      i := i * base + ORD (ch) - ORD ('0');
   END;
   INC (ix);
   ch := s [ix];
END;
ok := ch = 0X;
В заголовке цикла сразу видно:
1) строка заканчивается символом 0X,
2) перечень допустимых символов описан единым достаточно простым условием: интервал + один особый символ.
В теле цикла чётко видно:
1) что делать со всеми символами, кроме одного особого,
2) шаг итерации - берём следующий символ.
После цикла видно, что ok - это признак достижения конца строки, то есть успешная расшифровка. Не нужно выискивать присваивания ok в теле цикла (даже если бы они там были, конечное значение однозначно задано после цикла).
Первоначальный и конечный варианты оба состоят из 9 строк, но в первом случае краткость достигнута нарушениями стиля, препятствующими пониманию структуры, а во втором - нормальной структурой.
Избавились от вспомогательной переменной dig (в коде после рассмотренного фрагмента она не использовалась, поэтому сохранять её значение за пределами цикла не было смысла).


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Среда, 14 Апрель, 2010 13:05 

Зарегистрирован: Пятница, 24 Апрель, 2009 16:28
Сообщения: 563
Откуда: Москва
Все эти варианты будут успешно глотать строки типа такой "'''''''''''''''1'1'1'1'1'1''''''0''''''''''''''0'1'''''''''''''''''".
Хорошо ли это, непонятно.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Среда, 14 Апрель, 2010 21:43 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 18:55
Сообщения: 2272
Откуда: Россия, Нижний Новгород
А ещё бывает вот такая колбаса:
Код:
do
{
  if (a)
  {
     A
     break;
  }
  if (b)
  {
     B
     break;
  }
}
while (false);
Обратите внимание на while (false) - одноразовый такой цикл. :shock:

Преобразуется в
Код:
if (a)
{
   A
}
else if (b)
{
   B
}
Когда я пошёл возмущаться, тот парень показал книжку то ли "Рефакторинг", то ли "Совершенный код" (там это пропагандируется).

Дальнейшее обсуждение прикреплено к существующей теме: viewtopic.php?p=45992#p45992


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 15:31 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Схема:
Код:
LOOP
   IF A THEN
      CodeBlock;
   ELSIF B & (C OR D) THEN
      IF C THEN
         OneShotCode;
         EXIT
      END;
   ELSE
      EXIT
   END;
   Step;
END;
Наполовину преобразованный код выглядит так:
Код:
WHILE A OR B & (C OR D) DO
   IF A THEN
      CodeBlock;
   ELSIF B & (C OR D) THEN
      IF C THEN
         OneShotCode;
         EXIT
      END;
   END;
   Step;
END;
Очевидно, что условие в ветке ELSIF всегда будет истинно, пока выполняется WHILE. Поэтому следующий шаг преобразования:
Код:
WHILE A OR B & (C OR D) DO
   IF A THEN
      CodeBlock;
   ELSIF C THEN
      OneShotCode;
      EXIT
   END;
   Step;
END;
Выносим "тупиковую" ветку с EXIT за пределы цикла. При этом условие в WHILE получается такое:
(A OR B & (C OR D)) & ~(~A & C) =
(A OR B & (C OR D)) & (A OR ~C) =
A OR B & (C OR D) & ~C =
A OR B & D & ~C
Поменяем ~C и D местами, чтобы не менять порядок вычисления для тех случаев, когда это важно. Окончательный вариант кода:
Код:
WHILE A OR B & ~C & D DO
   IF A THEN
      CodeBlock;
   END;
   Step;
END;
IF ~A & B & C THEN
   OneShotCode;
END;


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 16:06 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Следущая схема похожа на одну из ранее рассмотренных, но здесь в первой ветке IF'а нет EXIT:
Код:
LOOP
   IF A THEN
      SomeCode;
   ELSIF B THEN
      RETURN FALSE
   ELSE
      EXIT
   END;
   Step;
END;
Преобразуется в:
Код:
WHILE A DO
   SomeCode;
   Step;
END;
IF B THEN
   RETURN FALSE
END;


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 16:44 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
LOOP'ы из двух предыдущих постов были получены из одного:
Код:
CONST Before = 0; After = 1;
BEGIN
   state := Before;
   LOOP
      IF A THEN
         SomeCode (state);
      ELSIF B & (C OR D) THEN
         IF state > Before THEN
            RETURN FALSE
         END;
         IF C THEN
            state := After
         END;
      ELSE
         EXIT
      END;
      Step;
   END;
Полагаю, это некая разновидность Loop unswitching - из одного LOOP'а делаем два:
Код:
CONST Before = 0; After = 1;
BEGIN
   state := Before;
   LOOP
      IF A THEN
         SomeCode (state);
      ELSIF B & (C OR D) THEN
         IF C THEN
            state := After;
            Step; (* ВАЖНО! *)
            EXIT
         END;
      ELSE
         EXIT
      END;
      Step;
   END;
   IF state = After THEN
      LOOP
         IF A THEN
            SomeCode (state);
         ELSIF B & (C OR D) THEN
            RETURN FALSE
         ELSE
            EXIT
         END;
         Step;
      END;
   END;
Как и в случаях с циклами, которые исполняются один-два раза, я посчитал, что тащить state в WHILE неразумно.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 17:40 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8500
Откуда: Троицк, Москва
Сумасшедший дом...

Форма циклов зависит еще от устройства объемлющей программы.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 18:48 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Info21 писал(а):
Сумасшедший дом...
Tell me about it... : )
Info21 писал(а):
Форма циклов зависит еще от устройства объемлющей программы.
Да, это так. Например, если используется пара процедур FindFirst/FindNext, то приходится писать:
Код:
IF FindFirst () THEN
   REPEAT
   UNTIL ~FindNext ();
END;
Однако, если для той же цели использовать пару Init/Next, то более естественным будет WHILE:
Код:
Init;
WHILE Next () DO
END;
В случае FindFirst имеем неграмотную декомпозицию, поскольку FindFirst выполняет одновременно и роль FindNext, но только для первого элемента последовательности. Встретить можно, например, в интерфейсе поиска файлов в WinApi: FindFirstFile, FindNextFile. Второй подход, ориентированный на WHILE, повсеместно используется в ББ и ОС Оберон. Например, в TextModels: Model.NewReader/Reader.Read.

Но не будем отдаляться от темы. В моём случае объемлющая программа ни при чём, все манипуляции идут с локальными данными. В предыдущем моём сообщении сам код занимался преобразованием строки в LONGREAL, а состояние state = Before означало "до десятичной точки", а After - после. Условие "C" - это, собственно, достижение разделителя целой и дробной части, а дополнительное условие "D" - это те же tickMark'и, игнорируемые в целой части, но являющиеся ошибкой в дробной. В общем, пока не начал переписывать, не разобрался.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 19:59 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Два выхода с разным результатом. Весьма занимательный пример.
Код:
LOOP
   ok := Proc1 ();
   IF ~ok THEN
      EXIT
   END;
   SomeCode ();
   ok := Proc2 ();
   IF ok THEN
      EXIT
   END;
END;
Значение ok должно быть сохранено после завершения цикла, поэтому заводим новую переменную done:
Код:
done := FALSE;
REPEAT
   ok := Proc1 ();
   IF ~ok THEN
      done := TRUE;
   ELSE
      SomeCode ();
      ok := Proc2 ();
      IF ok THEN
         done := TRUE;
      END;
   END;
UNTIL done;
Если приглядеться к ветке ELSE, то вот, что можно разглядеть:
Код:
done := FALSE;
REPEAT
   ok := Proc1 ();
   IF ~ok THEN
      done := TRUE;
   ELSE
      SomeCode ();
      ok := Proc2 ();
      done := ok;
   END;
UNTIL done;
А теперь, если приглядеться очень-очень внимательно, то увидим вот что:
Код:
done := FALSE;
REPEAT
   ok := Proc1 ();
   IF ~ok THEN
      done := TRUE;
   ELSE
      SomeCode ();
   END;
UNTIL done OR Proc2 ();
ok := ~done; (* если здесь у нас ~done, значит Proc2 () вернула TRUE *)
Получается, "скрипач не нужен"? Меняем done на ~ok:
Код:
ok := TRUE;
REPEAT
   IF ~Proc1 () THEN
      ok := FALSE;
   ELSE
      SomeCode ();
   END;
UNTIL ~ok OR Proc2 ();
В общем, если ~Proc1 (), то выходим с ok = FALSE, а если Proc2 (), то выходим с ok = TRUE. Всё как в первоначальном LOOP'е.

UPD: Для читабельности можно поменять местами две ветки IF'а:
Код:
ok := TRUE;
REPEAT
   IF Proc1 () THEN
      SomeCode ();
   ELSE
      ok := FALSE;
   END;
UNTIL ~ok OR Proc2 ();


Последний раз редактировалось Александр Ильин Вторник, 20 Апрель, 2010 14:04, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 20:36 

Зарегистрирован: Пятница, 24 Апрель, 2009 16:28
Сообщения: 563
Откуда: Москва
Александр Ильин писал(а):
В предыдущем моём сообщении сам код занимался преобразованием строки в LONGREAL, а состояние state = Before означало "до десятичной точки", а After - после. Условие "C" - это, собственно, достижение разделителя целой и дробной части, а дополнительное условие "D" - это те же tickMark'и, игнорируемые в целой части, но являющиеся ошибкой в дробной. В общем, пока не начал переписывать, не разобрался.
Видно, что первый автор в принципе неверно решал задачу. Ему следовало нарисовать синтаксическую диаграмму того, что может быть на входе. А затем механически переписать ее в код. Никаких экзотических LOOP'ов с EXIT'ами там не может возникнуть по определению.


Вернуться к началу
 Профиль  
 
 Заголовок сообщения: Re: Убираю LOOP'ы
СообщениеДобавлено: Понедельник, 19 Апрель, 2010 21:19 
Аватара пользователя

Зарегистрирован: Пятница, 25 Ноябрь, 2005 12:02
Сообщения: 8500
Откуда: Троицк, Москва
Александр Ильин писал(а):
... В моём случае объемлющая программа ни при чём, все манипуляции идут с локальными данными. ...
Одно другому не противоречит.
Ну да ладно.
Дальнейших успехов! :)


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 96 ]  На страницу Пред.  1, 2, 3, 4, 5  След.

Часовой пояс: UTC + 3 часа


Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Вся информация, размещаемая участниками на конференции (тексты сообщений, вложения и пр.) © 2005-2024, участники конференции «OberonCore», если специально не оговорено иное.
Администрация не несет ответственности за мнения, стиль и достоверность высказываний участников, равно как и за безопасность материалов, предоставляемых участниками во вложениях.
Без разрешения участников и ссылки на конференцию «OberonCore» любое воспроизведение и/или копирование высказываний полностью и/или по частям запрещено.
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB