Извиняюсь если тема не совсем для этого раздела (у меня вопросы скорее конкретно про 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: Обращение к процедуре несколькими уровнями выше
Здравствуйте, 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: Обращение к процедуре несколькими уровнями выше
Здравствуйте, Khimik, Вы писали:
K>И ещё похожий запрос – когда GetValue нужно вызвать выход (exit) из верхней функции. Ну например GetValue обнаружила глобальную ошибку, из-за которой надо делать большой exit (не из самой GetValue а из той процедуры что над ней). Есть ли в Delphi или других языках что-нибудь вроде exit[1], exit[2] и т.д.?
Какие гарантии, что GetValue не позвали напрямую, когда никаких "процедур над ней" не существует?
А так -- как уже сказали, кидать исключение. Где можно обработать эту ситуацию -- ловить и обрабатывать.
Re: Обращение к процедуре несколькими уровнями выше
Здравствуйте, 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]: Обращение к процедуре несколькими уровнями выше
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;
Пример довольно неказистый, но надеюсь понятный.
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
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]: Обращение к процедуре несколькими уровнями выше
Здравствуйте, GarryIV, Вы писали:
GIV>Да понятно теперь, либо исключения либо проверка в вызывающей функции. GIV>Твой exit[1] вселенское зло. С каким значением будет возврат хотя бы?
result[1] := false;
exit[1];
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Re[5]: Обращение к процедуре несколькими уровнями выше
Здравствуйте, Khimik, Вы писали:
GIV>>Да понятно теперь, либо исключения либо проверка в вызывающей функции. GIV>>Твой exit[1] вселенское зло. С каким значением будет возврат хотя бы?
K>result[1] := false; K>exit[1];
А теперь надо из другой фукции позвать твое чудо, а она внезапно void или String возвращает.
Лучше уж возвращать Optional.
WBR, Igor Evgrafov
Re[6]: Обращение к процедуре несколькими уровнями выше
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;
"Ты должен сделать добро из зла, потому что его больше не из чего сделать". АБ Стругацкие.
Здравствуйте, 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]: Обращение к процедуре несколькими уровнями выше
Здравствуйте, Khimik, Вы писали:
GIV>>А теперь надо из другой фукции позвать твое чудо, а она внезапно void или String возвращает. GIV>>Лучше уж возвращать Optional.
K>Вот ещё пример,когда мне требуется такая возможность. Довольно часто у меня есть функция или процедура, в начале которой инициализируются классы и динамические массивы, а в конце перед выходом освобождаются. Так вот довольно часто в середине этой функции код обнаруживает, что надо поскорее из неё выйти с result := false;
Это рефакторится нормально. Я дельфи не знаю как там у вас делают. C .Net это Disposable, в Java AutoCloseable, в JS колбяки.