Обращение к процедуре несколькими уровнями выше
От: Khimik  
Дата: 08.11.18 07:40
Оценка:
Извиняюсь если тема не совсем для этого раздела (у меня вопросы скорее конкретно про Delphi), но в ней тоже можно найти “философское начало”.
Мне время от времени не хватает возможность в функции обратиться к результату функции уровнем выше. Нет ли в Delphi таких опций?
Я имею в виду, что моя процедура или функция имеет простые вложенные процедуры или функции, и в них надо обратиться к чему-то уровнем выше. Например, мне надо заменить Values[I] на GetValue(I), которая возвращает Values[I], но заодно что-нибудь проверяет (например, инициализирован ли массив Values) и в случае ошибки возвращает -1. У меня были случаи, когда основная функция как результат возвращает класс, и маленькая подфункция, аналогичная GetValue выше, должна обратиться к свойству этого класса. Тут надо какую-то переменную вроде result[1], result[2] и т.д.
И ещё похожий запрос – когда GetValue нужно вызвать выход (exit) из верхней функции. Ну например GetValue обнаружила глобальную ошибку, из-за которой надо делать большой exit (не из самой GetValue а из той процедуры что над ней). Есть ли в Delphi или других языках что-нибудь вроде exit[1], exit[2] и т.д.?
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re: Обращение к процедуре несколькими уровнями выше
От: GarryIV  
Дата: 08.11.18 07:49
Оценка: +4
Здравствуйте, Khimik, Вы писали:

K>Извиняюсь если тема не совсем для этого раздела (у меня вопросы скорее конкретно про Delphi), но в ней тоже можно найти “философское начало”.

K>Мне время от времени не хватает возможность в функции обратиться к результату функции уровнем выше. Нет ли в Delphi таких опций?
K>Я имею в виду, что моя процедура или функция имеет простые вложенные процедуры или функции, и в них надо обратиться к чему-то уровнем выше. Например, мне надо заменить Values[I] на GetValue(I), которая возвращает Values[I], но заодно что-нибудь проверяет (например, инициализирован ли массив Values) и в случае ошибки возвращает -1. У меня были случаи, когда основная функция как результат возвращает класс, и маленькая подфункция, аналогичная GetValue выше, должна обратиться к свойству этого класса. Тут надо какую-то переменную вроде result[1], result[2] и т.д.
K>И ещё похожий запрос – когда GetValue нужно вызвать выход (exit) из верхней функции. Ну например GetValue обнаружила глобальную ошибку, из-за которой надо делать большой exit (не из самой GetValue а из той процедуры что над ней). Есть ли в Delphi или других языках что-нибудь вроде exit[1], exit[2] и т.д.?

Больше ада, больше спагетти! Сделай все переменные глобальными, что уж там.
Привел бы код что-ли, а о твое описание адово как-то звучит.

ЗЫЖ Для выхода из функции при ошибке существуют исключения.
WBR, Igor Evgrafov
Re: Обращение к процедуре несколькими уровнями выше
От: K13 http://akvis.com
Дата: 08.11.18 10:26
Оценка:
Здравствуйте, Khimik, Вы писали:

K>И ещё похожий запрос – когда GetValue нужно вызвать выход (exit) из верхней функции. Ну например GetValue обнаружила глобальную ошибку, из-за которой надо делать большой exit (не из самой GetValue а из той процедуры что над ней). Есть ли в Delphi или других языках что-нибудь вроде exit[1], exit[2] и т.д.?


Какие гарантии, что GetValue не позвали напрямую, когда никаких "процедур над ней" не существует?

А так -- как уже сказали, кидать исключение. Где можно обработать эту ситуацию -- ловить и обрабатывать.
Re: Обращение к процедуре несколькими уровнями выше
От: Буравчик Россия  
Дата: 09.11.18 05:56
Оценка: +1
Здравствуйте, Khimik, Вы писали:

K>Мне время от времени не хватает возможность в функции обратиться к результату функции уровнем выше. Нет ли в Delphi таких опций?

K>Я имею в виду, что моя процедура или функция имеет простые вложенные процедуры или функции, и в них надо обратиться к чему-то уровнем выше. Например, мне надо заменить Values[I] на GetValue(I), которая возвращает Values[I], но заодно что-нибудь проверяет (например, инициализирован ли массив Values) и в случае ошибки возвращает -1. У меня были случаи, когда основная функция как результат возвращает класс, и маленькая подфункция, аналогичная GetValue выше, должна обратиться к свойству этого класса. Тут надо какую-то переменную вроде result[1], result[2] и т.д.

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

K>И ещё похожий запрос – когда GetValue нужно вызвать выход (exit) из верхней функции. Ну например GetValue обнаружила глобальную ошибку, из-за которой надо делать большой exit (не из самой GetValue а из той процедуры что над ней). Есть ли в Delphi или других языках что-нибудь вроде exit[1], exit[2] и т.д.?


Кидайте исключение при ошибке, обрабатывайте в нужной функции (методе). Иногда можно ввести глобальный флаг (или даже несколько), в одной функции этот флаг устанавливается, а другие должны его учитывать при своей работе.

В любом случае, нужно:
— стараться уменьшать вложенность вызовов методов (функций), искать баланс между сложностью одного метода и сложностью взаимодействия методов
— уменьшать глобальное состояние (стараться значения функций передавать через аргументы)
— стараться делать чистые функции, без побочных эффектов (side effects)
Best regards, Буравчик
Re[2]: Обращение к процедуре несколькими уровнями выше
От: Khimik  
Дата: 09.11.18 08:27
Оценка:
GIV>Привел бы код что-ли, а о твое описание адово как-то звучит.

Я уже забыл когда конкретно было нужно exit[1], ну вот близкий пример:

result:=false;
for i := 0 to count-1 do 
  begin
    if value[i]=-1 then exit;
    newvalue[i] := value[i];
  end;
result:=true;

И что бы могло потребоваться:

function GetValue(valindex:integer):integer;
  begin
    result := value[valueindex];
    if result=-1 then exit[1];
  end;

begin
result:=false;
for i := 0 to count-1 do 
  begin
    newvalue[i] := getvalue(i);
  end;
result:=true;



Пример довольно неказистый, но надеюсь понятный.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Отредактировано 09.11.2018 8:30 Khimik . Предыдущая версия .
Re[3]: Обращение к процедуре несколькими уровнями выше
От: GarryIV  
Дата: 09.11.18 11:35
Оценка:
Здравствуйте, Khimik, Вы писали:

K>
K>function GetValue(valindex:integer):integer;
K>  begin
K>    result := value[valueindex];
K>    if result=-1 then exit[1];
K>  end;

K>begin
K>result:=false;
K>for i := 0 to count-1 do 
K>  begin
K>    newvalue[i] := getvalue(i);
K>  end;
K>result:=true;
K>



K>Пример довольно неказистый, но надеюсь понятный.


Да понятно теперь, либо исключения либо проверка в вызывающей функции.
Твой exit[1] вселенское зло. С каким значением будет возврат хотя бы?
WBR, Igor Evgrafov
Re[4]: Обращение к процедуре несколькими уровнями выше
От: Khimik  
Дата: 09.11.18 14:50
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>Да понятно теперь, либо исключения либо проверка в вызывающей функции.

GIV>Твой exit[1] вселенское зло. С каким значением будет возврат хотя бы?

result[1] := false;
exit[1];
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[5]: Обращение к процедуре несколькими уровнями выше
От: GarryIV  
Дата: 09.11.18 18:33
Оценка:
Здравствуйте, Khimik, Вы писали:

GIV>>Да понятно теперь, либо исключения либо проверка в вызывающей функции.

GIV>>Твой exit[1] вселенское зло. С каким значением будет возврат хотя бы?

K>result[1] := false;

K>exit[1];

А теперь надо из другой фукции позвать твое чудо, а она внезапно void или String возвращает.
Лучше уж возвращать Optional.
WBR, Igor Evgrafov
Re[6]: Обращение к процедуре несколькими уровнями выше
От: Khimik  
Дата: 10.11.18 08:17
Оценка:
GIV>А теперь надо из другой фукции позвать твое чудо, а она внезапно void или String возвращает.
GIV>Лучше уж возвращать Optional.

Вот ещё пример,когда мне требуется такая возможность. Довольно часто у меня есть функция или процедура, в начале которой инициализируются классы и динамические массивы, а в конце перед выходом освобождаются. Так вот довольно часто в середине этой функции код обнаруживает, что надо поскорее из неё выйти с result := false; Только чтобы из неё выйти, нужно заодно освободить все эти временные классы и массивы. В результате во многих местах функции появляется такой одинаковый код:

  result := false;
  tmpclass.free;
  setlength(tmparray,0);
  exit;


Я подумываю о том, чтобы почаще использовать безусловные переходы: в конце такой функции приведённый код будет стоять под цифрой, и в середине кода переход будет через goto. А так хотелось бы иметь универсальную под-процедуру для выхода из функции:

  procedure ErrExit;
    begin
      tmpclass.free;
      setlength(tmparray,0);
      result[1] := false;
      exit[1];
    end;
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Отредактировано 10.11.2018 8:19 Khimik . Предыдущая версия . Еще …
Отредактировано 10.11.2018 8:18 Khimik . Предыдущая версия .
Re[3]: Обращение к процедуре несколькими уровнями выше
От: alexsmirnoff  
Дата: 10.11.18 08:29
Оценка:
Здравствуйте, Khimik, Вы писали:

GIV>>Привел бы код что-ли, а о твое описание адово как-то звучит.


K>Я уже забыл когда конкретно было нужно exit[1], ну вот близкий пример:


K>
K>result:=false;
K>for i := 0 to count-1 do 
K>  begin
K>    if value[i]=-1 then exit;
K>    newvalue[i] := value[i];
K>  end;
K>result:=true;
K>

K>И что бы могло потребоваться:

K>
K>function GetValue(valindex:integer):integer;
K>  begin
K>    result := value[valueindex];
K>    if result=-1 then exit[1];
K>  end;

K>begin
K>result:=false;
K>for i := 0 to count-1 do 
K>  begin
K>    newvalue[i] := getvalue(i);
K>  end;
K>result:=true;
K>



K>Пример довольно неказистый, но надеюсь понятный.



Я извиняюсь, а почему не хотите использовать while?
Re[7]: Обращение к процедуре несколькими уровнями выше
От: GarryIV  
Дата: 10.11.18 08:38
Оценка:
Здравствуйте, Khimik, Вы писали:

GIV>>А теперь надо из другой фукции позвать твое чудо, а она внезапно void или String возвращает.

GIV>>Лучше уж возвращать Optional.

K>Вот ещё пример,когда мне требуется такая возможность. Довольно часто у меня есть функция или процедура, в начале которой инициализируются классы и динамические массивы, а в конце перед выходом освобождаются. Так вот довольно часто в середине этой функции код обнаруживает, что надо поскорее из неё выйти с result := false;


Это рефакторится нормально. Я дельфи не знаю как там у вас делают. C .Net это Disposable, в Java AutoCloseable, в JS колбяки.
WBR, Igor Evgrafov
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.