OberonCore

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

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




Начать новую тему Ответить на тему  [ Сообщений: 15 ] 
Автор Сообщение
 Заголовок сообщения: Выход из середины процедуры
СообщениеДобавлено: Среда, 29 Апрель, 2009 10:29 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Модератор: перенесено из viewtopic.php?f=7&t=1500

Не совсем про выход из середины цикла, но про выход из середины процедуры. Сегодня переписал вот такую процедурку. Было:
Код:
PROCEDURE (pg: Page) Load* (): BOOLEAN;
BEGIN
   IF pg.base.bmh # NIL THEN RETURN TRUE;
   ELSIF ~pg.Load^ () THEN RETURN FALSE;
   ELSIF pg.base.bmh = NIL THEN AllocNew (pg);
   END; (* if *)
   RETURN TRUE;
END Load;
Стало:
Код:
PROCEDURE (pg: Page) Load* (): BOOLEAN;
VAR res: BOOLEAN;
BEGIN
   res := (pg.base.bmh # NIL) OR pg.Load^ ();
   IF res & (pg.base.bmh = NIL) THEN
      AllocNew (pg);
   END; (* if *)
   RETURN res
END Load;
Алгоритмически второй вариант от первого отличается только тем, что в случае (pg.base.bmh # NIL) данная проверка выполнится два раза, а в первом - один раз. Вот ради такой экономии "на спичках" была принесена в жертву понятность. Впрочем, не исключаю, что первый вариант самому автору читать было бы легче, чем второй, тут привычка к определённой манере конструированя алгоритма может оказывать сильное влияние на восприятие.
PS: Проверка "... # NIL" имеет здесь смысл "уже загружено".


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 12:16 

Зарегистрирован: Воскресенье, 04 Ноябрь, 2007 23:01
Сообщения: 151
Александр Ильин писал(а):
Было:.. Стало:..

Состояния Вы запрятали глубже, чем было:
    1.если загружено
    2.если не сработала загрузка
    3.если загрузка сработала, но чего-то не догрузилось..
Возможно, после Вас кто-нибудь, дотачивая код, случайно махнёт местами операнды в строке res := (pg.base.bmh # NIL) OR pg.Load^ ();? Математически - ничего такого, а результат :twisted:


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 13:05 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Рэйлвэй Каген писал(а):
Состояния Вы запрятали глубже, чем было:
Возможно. У меня есть один чёткий индикатор-инвариант: res = "картинка загружена". Это значение устанавливается в первой строке процедуры, и оно же является результатом функции Load. После успешной загрузки при необходимости выполняется одно дополнительное действие, условием необходимости которого является непроинициализированное значение pg.base.bmh.

Мне трудно понять, зачем делать THEN RETURN, если весь дальнейший код и так находится в ELSIF/ELSE, а значит не будет выполняться. От кого досрочно убегаем-то?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 13:37 
Модератор
Аватара пользователя

Зарегистрирован: Среда, 16 Ноябрь, 2005 00:53
Сообщения: 4625
Откуда: Россия, Орёл
Рэйлвэй Каген писал(а):
Возможно, после Вас кто-нибудь, дотачивая код, случайно махнёт местами операнды в строке res := (pg.base.bmh # NIL) OR pg.Load^ ();? Математически - ничего такого, а результат :twisted:
Никак не пойму, как это можно случайно взять и отредактировать выражение? (махнуть местами операнды).
Александр Ильин писал(а):
Это значение устанавливается в первой строке процедуры, и оно же является результатом функции Load.
Возможно стоит делающую процедуру сделать именно процедурой.
Код:
PROCEDURE (pg: Page) Load* (VAR res: BOOLEAN);
BEGIN
   res := pg.base.bmh # NIL; IF ~res THEN pg.Load^ (res) END;
   IF res & (pg.base.bmh = NIL) THEN
      AllocNew (pg);
   END; (* if *)
END Load;
Вроде не ошибся.. хотя, как я понимаю это дело, смущает такой момент:
Код:
   res := pg.base.bmh # NIL; IF ~res THEN pg.Load^ (res) END;
   (* res = загружено *)
   IF res & (pg.base.bmh = NIL) THEN  (* если загружено и (не загружено) то...*)
      AllocNew (pg);
   END; (* if *)


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 14:10 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Евгений Темиргалеев писал(а):
Возможно стоит делающую процедуру сделать именно процедурой.
Я как-то не определился для себя, в каком случае использовать функцию, а в каком процедуру. Если есть возвращаемый результат, то это как-то более однозначно: вот он, можно функцию использовать в выражении или в условии цикла. Значит, переменная заводится только в процедуре, а не в каждом клиенте. Кроме того, с VAR-параметрами не понятно, когда они in, когда out, а когда in-out. При чтении это вносит неоднозначность, в отличие от функции. В этой связи для простых результатов использую функции.

Сам не писал, но видел и такой вариант:
PROCEDURE (...; VAR res: INTEGER): BOOLEAN; (* Результат = (res = OK). *)
-> Можно использовать в ветвлении по IF, а подробно код возврата разбирать при необходимости и желании.

А у вас есть какие-то общие соображения на этот счёт? Было бы любопытно.
Евгений Темиргалеев писал(а):
IF res & (pg.base.bmh = NIL) THEN (* если загружено и (не загружено) то...*)
Там несколько более тёмный лес, я сам не сразу разобрался. Дело в том, что Load загружает картинку в pg.dib, а AllocNew инициализирует pg.base, используя pg.dib в качестве исходной.


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

Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 14:11 

Зарегистрирован: Воскресенье, 04 Ноябрь, 2007 23:01
Сообщения: 151
Александр Ильин писал(а):
..зачем делать THEN RETURN..
Тогда уж THEN res:=..; и в конце единственный RETURN res; , если цель - оставить только один выход.

Евгений Темиргалеев писал(а):
как это можно случайно
Это, чтобы притянуть за уши иллюстрацию математически корректных действий, приводящих к нарушению логики работы процедуры. На самом деле такое маловероятно. В сухом остатке останется лишь затруднённый рефакторинг второго варианта. Т.е. проблема не в правильности кода, а скорее, в эргономичности. Состояние оказывается закодировано логическим выражением - его надо прочитать и вычислить, что проигрывает первому варианту - там всё более, чем прозрачно(просто читаем).

Евгений Темиргалеев писал(а):
(* если загружено и (не загружено) то...*)
А вот это уже капкан для логики будущих поколений. Представляю такой текст в Дракон-схеме :roll:


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 14:21 

Зарегистрирован: Пятница, 24 Апрель, 2009 16:28
Сообщения: 563
Откуда: Москва
Александр Ильин писал(а):
Было:
Стало:

Что-то вы перемудрили при отказе от return. По-моему, все просто:
Код:
PROCEDURE (pg: Page) Load* (): BOOLEAN;
BEGIN
   res:= true;
   IF pg.base.bmh = NIL THEN
     res:= pg.Load^ ();
     IF res & (pg.base.bmh = NIL) THEN
       AllocNew (pg);
     END; (* if *)
   END; (* if *)
   RETURN res;
END Load;


Последний раз редактировалось Peter Almazov Среда, 29 Апрель, 2009 15:29, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 14:52 
Аватара пользователя

Зарегистрирован: Вторник, 19 Сентябрь, 2006 21:54
Сообщения: 2449
Откуда: Россия, Томск
Peter Almazov писал(а):
Что-то вы перемудрили при отказе от return. По-моему, все просто:
Вы правы, ваш вариант проще. Спасибо!
PS: Не злоупотребляйте цитированием, пожалуйста.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 16:24 
Модератор
Аватара пользователя

Зарегистрирован: Среда, 16 Ноябрь, 2005 00:53
Сообщения: 4625
Откуда: Россия, Орёл
Александр Ильин писал(а):
Я как-то не определился для себя, в каком случае использовать функцию, а в каком процедуру. Если есть возвращаемый результат, то это как-то более однозначно: вот он, можно функцию использовать в выражении или в условии цикла. Значит, переменная заводится только в процедуре, а не в каждом клиенте. Кроме того, с VAR-параметрами не понятно, когда они in, когда out, а когда in-out. При чтении это вносит неоднозначность, в отличие от функции. В этой связи для простых результатов использую функции.

А у вас есть какие-то общие соображения на этот счёт? Было бы любопытно.
Самые общие, пожалуй, базируются на принципе "чистоты функций".
1) Если цель алгоритма что-то вычислить, то это функция (если язык позволяет).
2) Если цель алгоритма что-то сделать, то процедура.
Наличие побочного результата в виде успешности действия игнорирую, т.к. действия привык видеть/записывать только как ProcedureCall. Требование отсутствия доп. переменной-результата в клиентах считаю оптимизацией. "Если есть возвращаемый результат, то" сделать функцию чтобы можно было "функцию использовать в выражении или в условии цикла" - тоже; плюс, имхо, нелогичность - выражение (мат. смысл - выч-е зн-я) вычисляется прежде всего для вып. дей-я.
Использую КП/ББ, а не Оберон-2. Там параметры результаты явно помечены OUT.


Последний раз редактировалось Евгений Темиргалеев Среда, 29 Апрель, 2009 18:21, всего редактировалось 1 раз.

Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 16:36 
Модератор
Аватара пользователя

Зарегистрирован: Среда, 16 Ноябрь, 2005 00:53
Сообщения: 4625
Откуда: Россия, Орёл
Peter Almazov писал(а):
Что-то вы перемудрили при отказе от return. По-моему, все просто:
Александр Ильин писал(а):
Вы правы, ваш вариант проще. Спасибо!
Кажется, имело место применение паттерна "res зависит от многих действий". :) Если их мало, как здесь, то вложенным IF-м проще. А когда много и получается лесенка из десятка или более вложенных IF-в, тогда забиваешь на оптимальность и пишешь на одном ур. десяток IF res &...


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 17:30 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 18:38
Сообщения: 1857
Александр Ильин писал(а):
Не совсем про выход из середины цикла, но про выход из середины процедуры. Сегодня переписал вот такую процедурку.


Проблема этой процедурки не в ретурнах (кстати, по мне так ваш вариант еще менее читабельный), а в том, что она оперирует несвязанными между собой сущностями - pg.base.bmh, pg.Load^(), AllocNew(pg). Без знания, как работает AllocNew, как работает Load и как они связаны с pg.base.bmh понять, что тут на самом деле творится - невозможно. Как не переписывай ретурны и ифы :)
Сделаю предположение (хотя еще раз подчеркну, что из кода это совершенно неочевидно), что AllocNew грузит bmp'ку в pg.base.bmh используя "что-то", полученное после вызова pg.Load^ (). Тогда я бы переписал этот код так:
Код:
    IF pg.base.bmh = NIL THEN
        hz := pg.Load^ () (* что делает Load в оригинальном варианте и каким он боком к остальному коду - вообще непонятно *)
        IF hz # NIL THEN
            pg.base.bmh := AllocNew(hz); (*теперь AllocNew _явно_ зависит от предыдущего вызова Load, а первое условие (pg.base.bmh = NIL) обретает смысл - теперь видно, что эта функция делает (грузит bmp в pg.base.bmh, если она еще не была загружена). *)
        END
    END
    RETURN pg.base.bmh # NIL


Александр Ильин писал(а):
PS: Проверка "... # NIL" имеет здесь смысл "уже загружено".


...и такого комментария уже не требуется ;)

P.S. Я ж говорю - циклы с break и прочие ретурны из середины - это фигня. Даже если отбросить архитектурные аспекты и сосредоточится исключительно на кодировании "в малом" - есть намного более интересные и важные аспекты, влияющие на сопровождение кода, чем следование/неследование принципам Дейкстры.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 17:51 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 18:38
Сообщения: 1857
Александр Ильин писал(а):
Я как-то не определился для себя, в каком случае использовать функцию, а в каком процедуру.


Э... А зачем определятся? В обероне же оставили только PROCEDURE? :) Хотя мне слово "функция" ближе. Функция имеет "аргументы" и "результат". Остальное (out-параметры) - от лукавого (от бедности языка :)


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 18:48 
Модератор
Аватара пользователя

Зарегистрирован: Понедельник, 14 Ноябрь, 2005 18:39
Сообщения: 9459
Откуда: Россия, Орёл
Разделение не по тому признаку, как описывать, а по тому, когда можно использовать в выражениях, а когда - нет.


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Среда, 29 Апрель, 2009 19:36 

Зарегистрирован: Суббота, 26 Ноябрь, 2005 18:38
Сообщения: 1857
Илья Ермаков писал(а):
Разделение не по тому признаку, как описывать, а по тому, когда можно использовать в выражениях, а когда - нет.


В смысле? Что-то возвращает - значит можно, ничего не возвращает - нельзя. Или есть какой-то смысл в том, чтобы запретить использовать функцию в выражении?


Вернуться к началу
 Профиль  
 
СообщениеДобавлено: Четверг, 30 Апрель, 2009 00:45 

Зарегистрирован: Понедельник, 30 Июль, 2007 10:53
Сообщения: 1538
Откуда: Беларусь, Минск
Ну и мои пять копеек:

Код:
PROCEDURE (pg: Page) Load* (): BOOLEAN;
   VAR ret : BOOLEAN;
BEGIN
   ret := FALSE;
   IF pg.base.bmh = NIL THEN
      IF pg.Load^ () THEN AllocNew (pg); ret := TRUE END
   ELSE
      ret := TRUE
   END;
   RETURN ret
END Load;


Код:
PROCEDURE (pg: Page) Load* (): BOOLEAN;
   VAR ret : BOOLEAN;
BEGIN (* ret, что логично, сейчас находится в состоянии неопределённости *)
   IF pg.base.bmh = NIL THEN
      IF  pg.Load^ ()  THEN  AllocNew ( pg ); ret := TRUE  ELSE  ret := FALSE  END
   ELSE
      ret := TRUE
   END;
   RETURN ret
END Load;


P.S. Здесь был похожий вариант, но я свой сделал ещё до того, как дочитал до него.


Вернуться к началу
 Профиль  
 
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 15 ] 

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


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

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


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

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