OberonCore https://forum.oberoncore.ru/ |
|
Убираю LOOP'ы https://forum.oberoncore.ru/viewtopic.php?f=82&t=2547 |
Страница 2 из 5 |
Автор: | Info21 [ Понедельник, 12 Апрель, 2010 22:39 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Александр Ильин писал(а): заниматься подобным ранее мне не доводилось, так как LOOP отродясь не использовал. Это Вам лекций читать не доводилось с контуперными гениями в аудитории (не буду в который раз ссылаться на свой пример).
|
Автор: | Александр Ильин [ Вторник, 13 Апрель, 2010 16:11 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Ещё один тривиальный экземпляр, в котором LOOP не нужен: Код: LOOP Это же самый обычный WHILE:IF Condition () THEN SomeCode (); (* несколько строк кода *) ELSE EXIT END; END; Код: WHILE Condition () DO Как и со всеми прочими примерами в данной теме, при записи через WHILE уменьшается и число строк, и уровень вложенности полезного кода (число отступов).
SomeCode (); (* несколько строк кода *) END; |
Автор: | Александр Ильин [ Вторник, 13 Апрель, 2010 21:58 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Не смог придумать более компактную альтернативу следующему LOOP'у, оставил как есть: Код: LOOP Можно вынести блоки кода в локальные процедуры, но при этом объём текста увеличится и надо будет придумывать им имена. При единственном EXIT'е код и так достаточно ясен.IF A THEN CodeBlock1; ELSIF B THEN CodeBlock2; ELSE EXIT END; END; UPD: Ба! Да это жеж цикл Дейкстры! |
Автор: | Александр Ильин [ Вторник, 13 Апрель, 2010 22:18 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
В некоторых случаях LOOP играет роль бесконечного цикла, с выходом через RETURN и без единого EXIT'а. В этом случае можно было написать WHILE TRUE DO вместо LOOP, но я пока что оставил LOOP. |
Автор: | Александр Ильин [ Вторник, 13 Апрель, 2010 23:23 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Вот ещё схемка: Код: LOOP Преобразуется элементарно в WHILE:IF Condition () THEN CodeBlock (); ELSE OneShotCode (); EXIT END; END; Код: WHILE Condition () DO Похожа на одну из ранее рассмотренных, поскольку эквивалентна следующей схеме:CodeBlock (); END; OneShotCode(); Код: LOOP
IF ~Condition () THEN OneShotCode (); EXIT END; CodeBlock (); END; |
Автор: | Александр Ильин [ Вторник, 13 Апрель, 2010 23:30 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Попался LOOP c двумя логическими RETURN'ами. Схема: Код: LOOP Сокращается следующим образом:IF A THEN RETURN FALSE ELSIF B THEN RETURN TRUE END; SomeCode (); END; Код: WHILE ~A & ~B DO UPD: Поправил опечатки в имени процедуры "SomeCode" - оба раза неправильно написал : ))
SomeCode (); END; RETURN ~A |
Автор: | Александр Ильин [ Среда, 14 Апрель, 2010 08:00 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Разновидность ранее встречавшейся схемы: Код: LOOP В отличие от ранее рассмотренного случая, здесь 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:IF A (x) THEN OneShotCode (); EXIT ELSIF ~Step (x) THEN EXIT END; END; Код: REPEAT Как правило, OneShotCode также зависит от x, что ужесточает требования к Step (x): (Step (x) = FALSE) также не должно влиять на результат выполнения OneShotCode. Если влияет, REPEAT необходим, иначе можно обойтись WHILE.found := A (x); UNTIL found OR ~Step (x); IF found THEN OneShotCode (); END; Также дополнительной переменной бывает полезно воспользоваться, если результат A (x) имеет смысл кэшировать из соображений эффективности или если повторное выполнение A (x) имеет нежелательные побочные эффекты. UPD: Мелкая литературная правка и дополнение. |
Автор: | Александр Ильин [ Среда, 14 Апрель, 2010 08:05 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Преобразование цикла Дейкстры: Код: 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 (); |
Автор: | Александр Ильин [ Среда, 14 Апрель, 2010 08:54 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Ещё один хитрый экземпляр: Код: LOOP Хитрость в том, что при B = TRUE работа цикла продолжается независимо от значения C.IF A THEN EXIT ELSIF B THEN SomeCode (); ELSIF C THEN EXIT END; Step (); END; Код: WHILE ~A & (B OR ~C) DO
IF B THEN SomeCode (); END; Step (); END; |
Автор: | Александр Ильин [ Среда, 14 Апрель, 2010 09:36 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Предыдущая схема извлечена из следующего кода: Код: 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; Дана строка s с записью числа по основанию base (2..10). В строке могут встречаться разделители групп разрядов tickMark, которые нужно проигнорировать (например: "1'000'000", tickMark = апостроф). maxdig - максимальный допустимый в записи числа символ = CHR (ORD ('0') + base - 1). Все используемые переменные проинициализированы ранее, здесь этот код не приводится. Требуется расшифровать число и поместить в переменную i типа INTEGER.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; Привожу данный пример постольку, поскольку преобразованный код считаю не только более лаконичным, но и гораздо более читабельным: Код: 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 (в коде после рассмотренного фрагмента она не использовалась, поэтому сохранять её значение за пределами цикла не было смысла). |
Автор: | Peter Almazov [ Среда, 14 Апрель, 2010 13:05 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Все эти варианты будут успешно глотать строки типа такой "'''''''''''''''1'1'1'1'1'1''''''0''''''''''''''0'1'''''''''''''''''". Хорошо ли это, непонятно. |
Автор: | Сергей Губанов [ Среда, 14 Апрель, 2010 21:43 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
А ещё бывает вот такая колбаса: Код: do Обратите внимание на while (false) - одноразовый такой цикл. { if (a) { A break; } if (b) { B break; } } while (false); Преобразуется в Код: if (a) Когда я пошёл возмущаться, тот парень показал книжку то ли "Рефакторинг", то ли "Совершенный код" (там это пропагандируется).{ A } else if (b) { B } Дальнейшее обсуждение прикреплено к существующей теме: viewtopic.php?p=45992#p45992 |
Автор: | Александр Ильин [ Понедельник, 19 Апрель, 2010 15:31 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Схема: Код: 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 Очевидно, что условие в ветке ELSIF всегда будет истинно, пока выполняется WHILE. Поэтому следующий шаг преобразования:IF A THEN CodeBlock; ELSIF B & (C OR D) THEN IF C THEN OneShotCode; EXIT END; END; Step; END; Код: WHILE A OR B & (C OR D) DO Выносим "тупиковую" ветку с EXIT за пределы цикла. При этом условие в WHILE получается такое:IF A THEN CodeBlock; ELSIF C THEN OneShotCode; EXIT END; Step; END; (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; |
Автор: | Александр Ильин [ Понедельник, 19 Апрель, 2010 16:06 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Следущая схема похожа на одну из ранее рассмотренных, но здесь в первой ветке 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; |
Автор: | Александр Ильин [ Понедельник, 19 Апрель, 2010 16:44 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
LOOP'ы из двух предыдущих постов были получены из одного: Код: CONST Before = 0; After = 1; Полагаю, это некая разновидность Loop unswitching - из одного LOOP'а делаем два: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; Код: CONST Before = 0; After = 1; Как и в случаях с циклами, которые исполняются один-два раза, я посчитал, что тащить state в WHILE неразумно.
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; |
Автор: | Info21 [ Понедельник, 19 Апрель, 2010 17:40 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Сумасшедший дом... Форма циклов зависит еще от устройства объемлющей программы. |
Автор: | Александр Ильин [ Понедельник, 19 Апрель, 2010 18:48 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Info21 писал(а): Сумасшедший дом... Tell me about it... : )Info21 писал(а): Форма циклов зависит еще от устройства объемлющей программы. Да, это так. Например, если используется пара процедур FindFirst/FindNext, то приходится писать:Код: IF FindFirst () THEN Однако, если для той же цели использовать пару Init/Next, то более естественным будет WHILE:REPEAT UNTIL ~FindNext (); END; Код: Init; В случае FindFirst имеем неграмотную декомпозицию, поскольку FindFirst выполняет одновременно и роль FindNext, но только для первого элемента последовательности. Встретить можно, например, в интерфейсе поиска файлов в WinApi: FindFirstFile, FindNextFile. Второй подход, ориентированный на WHILE, повсеместно используется в ББ и ОС Оберон. Например, в TextModels: Model.NewReader/Reader.Read.WHILE Next () DO END; Но не будем отдаляться от темы. В моём случае объемлющая программа ни при чём, все манипуляции идут с локальными данными. В предыдущем моём сообщении сам код занимался преобразованием строки в LONGREAL, а состояние state = Before означало "до десятичной точки", а After - после. Условие "C" - это, собственно, достижение разделителя целой и дробной части, а дополнительное условие "D" - это те же tickMark'и, игнорируемые в целой части, но являющиеся ошибкой в дробной. В общем, пока не начал переписывать, не разобрался. |
Автор: | Александр Ильин [ Понедельник, 19 Апрель, 2010 19:59 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Два выхода с разным результатом. Весьма занимательный пример. Код: LOOP Значение ok должно быть сохранено после завершения цикла, поэтому заводим новую переменную done:ok := Proc1 (); IF ~ok THEN EXIT END; SomeCode (); ok := Proc2 (); IF ok THEN EXIT END; END; Код: done := FALSE; Если приглядеться к ветке ELSE, то вот, что можно разглядеть:REPEAT ok := Proc1 (); IF ~ok THEN done := TRUE; ELSE SomeCode (); ok := Proc2 (); IF ok THEN done := TRUE; END; END; UNTIL done; Код: done := FALSE; А теперь, если приглядеться очень-очень внимательно, то увидим вот что:REPEAT ok := Proc1 (); IF ~ok THEN done := TRUE; ELSE SomeCode (); ok := Proc2 (); done := ok; END; UNTIL done; Код: done := FALSE; Получается, "скрипач не нужен"? Меняем done на ~ok:REPEAT ok := Proc1 (); IF ~ok THEN done := TRUE; ELSE SomeCode (); END; UNTIL done OR Proc2 (); ok := ~done; (* если здесь у нас ~done, значит Proc2 () вернула TRUE *) Код: ok := TRUE; В общем, если ~Proc1 (), то выходим с ok = FALSE, а если Proc2 (), то выходим с ok = TRUE. Всё как в первоначальном LOOP'е.REPEAT IF ~Proc1 () THEN ok := FALSE; ELSE SomeCode (); END; UNTIL ~ok OR Proc2 (); UPD: Для читабельности можно поменять местами две ветки IF'а: Код: ok := TRUE;
REPEAT IF Proc1 () THEN SomeCode (); ELSE ok := FALSE; END; UNTIL ~ok OR Proc2 (); |
Автор: | Peter Almazov [ Понедельник, 19 Апрель, 2010 20:36 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Александр Ильин писал(а): В предыдущем моём сообщении сам код занимался преобразованием строки в LONGREAL, а состояние state = Before означало "до десятичной точки", а After - после. Условие "C" - это, собственно, достижение разделителя целой и дробной части, а дополнительное условие "D" - это те же tickMark'и, игнорируемые в целой части, но являющиеся ошибкой в дробной. В общем, пока не начал переписывать, не разобрался. Видно, что первый автор в принципе неверно решал задачу. Ему следовало нарисовать синтаксическую диаграмму того, что может быть на входе. А затем механически переписать ее в код. Никаких экзотических LOOP'ов с EXIT'ами там не может возникнуть по определению.
|
Автор: | Info21 [ Понедельник, 19 Апрель, 2010 21:19 ] |
Заголовок сообщения: | Re: Убираю LOOP'ы |
Александр Ильин писал(а): ... В моём случае объемлющая программа ни при чём, все манипуляции идут с локальными данными. ... Одно другому не противоречит.Ну да ладно. Дальнейших успехов! |
Страница 2 из 5 | Часовой пояс: UTC + 3 часа |
Powered by phpBB® Forum Software © phpBB Group https://www.phpbb.com/ |