PDA

Просмотр полной версии : Стереотипы: goto


Algol
25.12.2009, 00:46
Недавно один мой знакомый был повергнут в шок, увидев в моем коде оператор goto. Попытки объяснить что не все так плохо не были восприняты :)
Нынче в институтах учат, что применение goto - не кошерно, не тру и ваще отстой. Применение goto считается преступлением перед мировой общественностью.
Жаль только, что ВУЗы не учат тому, что каждому овощу - свое блюдо.
Ни в коей мере не преувеличивая важность goto, приведу все же два примера, в которых решение без goto было бы некрасиво и сложнее для восприятия:
C#
/// <summary>
/// Функция проверяет, что во всех матрицах есть хотя бы один нуль
/// </summary>
/// <param name="listOfMatrix"></param>
/// <returns></returns>
public bool HasZero(List<int[,]> listOfMatrix)
{
foreach (int[,] matrix in listOfMatrix)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if(matrix[i, j] == 0)
goto nextMatrix;//<-----

return false;
nextMatrix: ;
}

return true;
}
C#
/// <summary>
/// Создание tcp-сокета, с несколькими попытками подключения
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public TcpClient Connect(IPEndPoint host)
{
TcpClient client = new TcpClient();

int maxTryCount = 5;
tryAgain:
try
{
maxTryCount--;
client.Connect(host);
return client;
}
catch (SocketException)
{
if(maxTryCount>0)
goto tryAgain;//<-----
else
throw;
}
}

Retimiled
25.12.2009, 00:59
foreach (int[,] matrix in listOfMatrix)
{
for (int i = 0,nextMatrix=false; i < matrix.GetLength(0) && !nextMatrix; i++)
for (int j = 0; j < matrix.GetLength(1) && !nextMatrix; j++)
if(matrix[i, j] == 0)
nextMatrix=true;//<-----
return nextMatrix;
}



:)

desTiny
25.12.2009, 01:00
Несмотря на всю мою ненависть к яве, в ней для таких случаев предусмотрены

outer:
for(int i = 0; ....){
for(int j = 0; ....){
if(...) continue outer;
if(...) break outer;
}
}

но goto удобен, когда проще написать его, чем переконопачивать весь код

Algol
25.12.2009, 01:03
foreach (int[,] matrix in listOfMatrix)
{
for (int i = 0,nextMatrix=false; i < matrix.GetLength(0) && !nextMatrix; i++)
for (int j = 0; j < matrix.GetLength(1) && !nextMatrix; j++)
if(matrix[i, j] == 0)
nextMatrix=true;//<-----

return nextMatrix;

}



:)
Эээ.... ну ваще-то этот код не эквивалентен приведенному, и вообще не рабочий. Будьте внимательней )

ЗЫ
Да и кстати даже если бы он был рабочий, он явно менее понятный чем исходный :)

Algol
25.12.2009, 01:05
Несмотря на всю мою ненависть к яве, в ней для таких случаев предусмотрены

outer:
for(int i = 0; ....){
for(int j = 0; ....){
if(...) continue outer;
if(...) break outer;
}
}

но goto удобен, когда проще написать его, чем переконопачивать весь код
Ну от того что goto назвать continue, ведь суть не меняется, не так ли?

Retimiled
25.12.2009, 01:06
писал как ответ 8)) и он делает то же самое ! Написать екзешник ? :D

... по поводу смотрибельности мой меньше по кол-ву строк! И уж естественно он рабочий 8)))))))))

desTiny
25.12.2009, 01:08
Ну от того что goto назвать continue, ведь суть не меняется, не так ли?
суть не меняется, просто читабельность и логичность кода - немного повышается )
Конечно же, я не спорю - goto решает! )

Algol
25.12.2009, 01:15
писал как ответ 8)) и он делает то же самое ! Написать екзешник ? :D
Пиши :D


... по поводу смотрибельности мой меньше по кол-ву строк!
"Смотрибельность" не измеряется в количестве строк. Если бы это было так, то фильм "Аватар" (ок 4ГБ) был бы гораздо сложнее для восприятия чем докзательство теоремы Ферма (ок 40 КБ), что явно не так :)

Retimiled
25.12.2009, 01:16
не буду .... :p я и так знаю что рабочий


... главное не это ... как то разбирая старый fig-forth заметил что goto там вытворяла НЕЧТО.... дело в том что счетчики for лежали на стеке ... и чтоб выйти по goto форту приходилось такой чисткой и анализом заниматься что КАРАУЛ!

Algol
25.12.2009, 01:18
не буду .... :p я и так знаю что рабочий
Ну послушай, как код может быть рабочим, если у тебя внутри цикла стоит безусловный return? :D

Fata1ex
25.12.2009, 01:18
по-моему, довольно очевидная вещь
было у Криса в трюках

Retimiled
25.12.2009, 01:19
он стоит в цикле foreach ... как и у тебя .... присмотрись

Algol
25.12.2009, 01:22
он стоит в цикле foreach ... как и у тебя .... присмотрись
Да нет, не как у меня, присмотрись лучше ты :D

Algol
25.12.2009, 01:23
по-моему, довольно очевидная вещь
было у Криса в трюках
Не очень понятное замечание.
Кто такой Крис, и какая именно вещь очевидна ?

Retimiled
25.12.2009, 01:25
foreach (int[,] matrix in listOfMatrix)
{
for (int i = 0,nextMatrix=false; i < matrix.GetLength(0) && !nextMatrix; i++)
for (int j = 0; j < matrix.GetLength(1) && !nextMatrix; j++)
if(matrix[i, j] == 0)
nextMatrix=true;//<----- тут циклы заканчиваются
printf("Этот код не выполнится в циклах for только в foreach 8-Ь");
return nextMatrix;

}

Algol
25.12.2009, 01:26
... главное не это ... как то разбирая старый fig-forth заметил что goto там вытворяла НЕЧТО.... дело в том что счетчики for лежали на стеке ... и чтоб выйти по goto форту приходилось такой чисткой и анализом заниматься что КАРАУЛ!
К счастью, в C# таких проблем нет :)
В общем случае неуправляемых языков - конечно я согласен, что goto может привести к очень плохим последствиям.

Retimiled
25.12.2009, 01:27
ну согласен ... но постоянно в мозгу а КАК ТАМ НА СТЕКЕ 8)))))))))))))

.... стекофобия 8)))

Algol
25.12.2009, 01:28
....
Ты все же напиши экзешник :)
Трассировка нас рассудит :D

Retimiled
25.12.2009, 01:40
кстати return - это тот же goto только в фас!

Fata1ex
25.12.2009, 01:41
я имел в виду, что для большинства ясно, что иногда goto очень даже полезен
крис касперски. вспомнил его, так как пример уж больно похож :) а вообще хотелось бы побольше подобных заметок со всякими тонкостями

Algol
25.12.2009, 01:46
кстати return - это тот же goto только в фас!
Ну не скажи. Во-первых кроме return тут большая компания: continue, break, throw... А во-вторых для этих операторов не требуется метка, точка выхода для них - более очевидна, чем для goto. Поэтому они считаются более-менее "структурными".

ЗЫ
Да и еще yield, как же ж без него, родимого, структурного нашего....

Retimiled
25.12.2009, 01:48
:D да goto приближает нас к низкоуровневому программированию ... ведь никто не пытается сказать ничего против джампов в ассемблере

... и поскольку у Си С++ положение аморфное .... то GOTO живет там как засланец мышыных кодов!


давненько было помню как z0mbie в своей теории недектируемости вируса (делал попытки на Goto выстроить матрицу исполнения, которая по его мнению могла завести любую эвристику в ступор) ... делал он именно на GOTO ... :p поэтому GOTO живет и будет жить ...

НО Я ГОВОРЮ НЕ О ВЕЩАХ УПРОЩАЮЩИХ ПОНИМАНИЕ, а наоборот 8)))) , вообще вопрос риторический должен ли антивирус понимать код вируса!

nerezus
25.12.2009, 02:03
public TcpClient Connect(IPEndPoint host)
{
TcpClient client = new TcpClient();

int maxTryCount = 5;
tryAgain:
try
{
maxTryCount--;
client.Connect(host);
return client;
}
catch (SocketException)
{
if(maxTryCount>0)
goto tryAgain;//<-----
else
throw;
}
}

public TcpClient Connect(IPEndPoint host)
{
TcpClient client = new TcpClient();
for(int maxTryCount = 5; maxTryCount; maxTryCount--) {
try
{
client.Connect(host);
return client;
}
catch (SocketException) {}
}
throw;
}

_nic
25.12.2009, 02:05
Я иногда использую goto что бы неплодить ненужных вложенных циклов.

Huster
25.12.2009, 02:09
Нынче в институтах учат, что применение goto - не кошерно, не тру и ваще отстой. Применение goto считается преступлением перед мировой общественностью.Да, и в книгах об этом пишут, а ты не думал почему ? Может быть потому что многолетний опыт подсказывает что и правда лучше отказаться от goto ? Если бы было все так просто, то не создавали б другие циклы ( do..while, for, whlie )
Да, может быть именно в твоей ситуации goto и смотрится лучше, но в других случаях, как правило, это лишь усложняет программу, особенно если ее пишут "неопытные" программисты, которые скачут с goto по программе вдоль и поперек.
Поэтому, используй goto только тогда когда это НЕ ЗАПУТАЕТ код программы. Во :)

Retimiled
25.12.2009, 02:11
циклы могут выходить стандартными средствами не делая скачков и не делая ненужных проходов.... просто обычно люди сравнение в for(....;X<Y;....) делают одно а досточно описать флаг выхода и вы выйдите из любого количества циклов без лишних проходов.... for(....,ex=0;X<Y && ex==0;....)

for(....,ex=0;X<Y && ex==0;....)
for(....;K<L && ex==0;....)
for(....;M<N && ex==0;....)
....
for(....;F<G && ex==0;....)
if(BLABLA==TUTU)
ex=1;

Algol
25.12.2009, 02:17
public TcpClient Connect(IPEndPoint host)
{
TcpClient client = new TcpClient();
for(int maxTryCount = 5; maxTryCount; maxTryCount--) {
try
{
client.Connect(host);
return client;
}
catch (SocketException) {}
}
throw; }
throw чего ?

Ra$cal
25.12.2009, 02:38
Retimiled, вообще то глубокой вложенности нужно избегать - с помощью выделения кода в методы\функции, ибо глубоко вложенные циклы нечитабельное говно с любой стороны - что с goto, что с флагами, что с break/continue.

Retimiled
25.12.2009, 02:49
2 Ra$cal
да .... если не заботишься об быстродействии и не отсчитываешь такты на PUSHA POPA , то конечно нужно оформлять в виде функций а как ты растащишь вот такой код
for(....,ex=0;X<Y && ex<10;....)
for(....;K<L && ex<9;....)
for(....;M<N && ex<8;....)
....
for(....;F<G && ex<1;....)
if(BLABLA==TUTU)
ex=8; // то есть я могу уйти в рамках цикла на люой уровень вложенности 8))

Kaimi
25.12.2009, 02:50
В перле goto как будто медленнее работает, чем, например, redo.

nerezus
25.12.2009, 03:08
throw чего ? А хз ) Синтаксиса не помню уже ) Но там переписать не сложно без goto с меньшими затратами =)
Не дотнетчик я - импровизировал )

Algol
25.12.2009, 11:27
Но там переписать не сложно без goto с меньшими затратами =)
Ну не знаю, мне например в голову не приходит как там переписать без goto с меньшими затратами :)

Algol
25.12.2009, 11:32
Retimiled, вообще то глубокой вложенности нужно избегать - с помощью выделения кода в методы\функции, ибо глубоко вложенные циклы нечитабельное говно с любой стороны - что с goto, что с флагами, что с break/continue.
В целом да, так и есть. Но опять же есть специфические случаи, когда это очень геморно. В основном из-за того что нужно передавать кучу параметров в такую функцию. Да и семантика такой функции не всегда понятна (К примеру, при перемножении матриц нужно делать три вложенных цикла, если два из них вынести в функцию, то как назвать такую функцию? func1() ? :) ).

Qwazar
25.12.2009, 12:39
Я бы вынес проверку одной матрицы на наличие нуля в отдельную функцию и делал бы return true если бы встретился хотя бы один ноль. А общий цикл оформил бы примерно так (псевдокод):



public boolean checkMatrix(Matrix m) {
for(List list: m) {
if(!checkForZeroValue(list))
return false;
}
return true;
}

private boolean checkForZeroValue(List list) {
for(Item i: list)
if(встретился_ноль) return true;

return false;
}


Читабельность имхо выше, чем всякие разные метки искать. Ну а в целом, я иногда использую continue; но никогда не приходилось использовать goto.

Sn@k3
25.12.2009, 12:52
Недавно один мой знакомый был повергнут в шок, увидев в моем коде оператор goto. Попытки объяснить что не все так плохо не были восприняты :)
Нынче в институтах учат, что применение goto - не кошерно, не тру и ваще отстой. Применение goto считается преступлением перед мировой общественностью.
Жаль только, что ВУЗы не учат тому, что каждому овощу - свое блюдо.
Ни в коей мере не преувеличивая важность goto, приведу все же два примера, в которых решение без goto было бы некрасиво и сложнее для восприятия:
C#
/// <summary>
/// Функция проверяет, что во всех матрицах есть хотя бы один нуль
/// </summary>
/// <param name="listOfMatrix"></param>
/// <returns></returns>
public bool HasZero(List<int[,]> listOfMatrix)
{
foreach (int[,] matrix in listOfMatrix)
{
for (int i = 0; i < matrix.GetLength(0); i++)
for (int j = 0; j < matrix.GetLength(1); j++)
if(matrix[i, j] == 0)
goto nextMatrix;//<-----

return false;
nextMatrix: ;
}

return true;
}
C#
/// <summary>
/// Создание tcp-сокета, с несколькими попытками подключения
/// </summary>
/// <param name="host"></param>
/// <returns></returns>
public TcpClient Connect(IPEndPoint host)
{
TcpClient client = new TcpClient();

int maxTryCount = 5;
tryAgain:
try
{
maxTryCount--;
client.Connect(host);
return client;
}
catch (SocketException)
{
if(maxTryCount>0)
goto tryAgain;//<-----
else
throw;
}
}

помню-помню когда-то в школе использовал его) но это была только одна задача) а прошло уже много лет)
п.с. о вкусах не спорят) если тебе так удобнее почемубы и нет) в бою все средства...

Qwazar
25.12.2009, 12:58
помню-помню когда-то в школе использовал его) но это была только одна задача) а прошло уже много лет)
п.с. о вкусах не спорят) если тебе так удобнее почемубы и нет) в бою все средства... В данном случае о вкусах спорят, т.к.:
1) Этот код будет разбирать не только автор
2) Возможно даже через несколько лет.

Algol
25.12.2009, 14:36
А общий цикл оформил бы примерно так (псевдокод):
Псевдокод это конечно хоршо и кошерно, но реальные программы пишутся не на псевдокоде.
Я же привожу конкретные примеры, в котором метка - самое простое и наглядное решение.
Читабельность имхо выше, чем всякие разные метки искать. Ну а в целом, я иногда использую continue; но никогда не приходилось использовать goto.
Странно, у меня continue чуть ли не в каждом втором цикле.

Но в целом точка зрения ясна.

Qwazar
25.12.2009, 18:03
Псевдокод это конечно хоршо и кошерно, но реальные программы пишутся не на псевдокоде.
Я же привожу конкретные примеры, в котором метка - самое простое и наглядное решение. Моё решение более наглядно, не запутаешься во вложенных циклах, и не придётся смотреть по коду, где находится метка, на которую ты скачешь. Да и псевдокод очень даже приближен к реальности :)

.Slip
25.12.2009, 18:37
Недавно один мой знакомый был повергнут в шок, увидев в моем коде оператор goto.
Увидев первый сурс я тоже был повергнут в шок :(

Algol
25.12.2009, 23:06
Увидев первый сурс я тоже был повергнут в шок :(
Ээ... Ну это понятно, а что-то по обсуждаемой теме есть :confused:

cheater_man
25.12.2009, 23:07
Goto использовать можно!НО, как считают программисты после использоватья этого оператора код становится менее читабельным. От этого оператора уже отказались в 90-е года.Но всетаки он присутствует в языках высокого уровня. И наиболее часто используется в assembler, он там как jmp.
Вывод: этот оператор лучше всего не использовать если хочешь казаться современным :)

Retimiled
26.12.2009, 00:07
имидж ничто жажда ВСЕ!

cupper
26.12.2009, 00:58
бред мне в уши...
Загляните в ядро линукса, там этих goto пруд пруди, и все они юзаются только с одной благородной целью, и делают код намного удобней чем что либо другое

scrat
26.12.2009, 01:27
В примере с матрицами можно было бы заюзать класс матрицы, вместо int[,].
Объекты этого класса могли бы проверять сами себя и бросаться исключениями.
Конечно же, всё зависит от задачи.

бред мне в уши...
Загляните в ядро линукса, там этих goto пруд пруди, и все они юзаются только с одной благородной целью, и делают код намного удобней чем что либо другое

Ага, и этот код очень легко читать и понимать.

Retimiled
26.12.2009, 01:33
писалось в стиле Аля-Си.... а там скорее

int *mass;
mass =new int[n][k];

... типа чето делаю не относясь к задаче алгола

delete[] mass;

чем классная лабуда с конструкторами деструкторами и исключениями лучше... попробуйте обьяснить человеку пишущему на ассмблере но не знающеего Си чему соответствуют исключения и что это далеко не Goto ... боюсь он со смехом вам диззассемблирует! 8))
а по поводы простоты .... можно взять человека с улицы и обьяснить ему код с классами и в стиле Аля-Си, результат о том какой код он поймет предсказуем!

razb
26.12.2009, 02:18
бред мне в уши...
Загляните в ядро линукса, там этих goto пруд пруди, и все они юзаются только с одной благородной целью, и делают код намного удобней чем что либо другое
Если ты внимательно смотрел, то должен был заметить что в основном он используется для обработки ошибок, что вполне допустительно для С. В таких языках как С++, Java, C#, есть исключения которые избавляют программера использовать метки для обработки ошибок.

В случае кода Algol'a, можно было бы поступить более изящней, как написал nerezus и Qwazar. Случай же с глубокой вложенностью циклов не более чем классический и часто можно встретить в "goto холиварах", а вот на практике встречается не так часто ибо можно переписать без использования последнего.

nerezus
26.12.2009, 10:53
public TcpClient Connect(IPEndPoint host)
{
TcpClient client = new TcpClient();
int maxTryCount = 5;
while(true) {
try
{
client.Connect(host);
return client;
}
catch (SocketException) {
if(0 == --maxTryCount) {
throw;
}
}
}
}

Вот вроде рабочий вариант без goto. Не проверял на синтаксис, но работать должно )

Algol
26.12.2009, 11:30
Вот вроде рабочий вариант без goto. Не проверял на синтаксис, но работать должно )
Да, это рабочий вариант. Но проще ли он ?
Любой алгоритм можно реализовать без goto. Вопрос в том, будет ли он проще для восприятия.

Algol
26.12.2009, 11:43
В случае кода Algol'a, можно было бы поступить более изящней, как написал nerezus и Qwazar.
Почему-то никто не отреагировал на критику такого подхода. На всякий случай повторю:
1)Падение производительности
2)Необходимость передачи в функцию большого числа параметров.
3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой.

Конечно это крайние случаи. Но и я в первом постинге и пишу про карйние случаи. В 90% случаев конечно я тоже выношу участки кода в отдельные функции. Но остаются еще 10% где это не рационально. И там я применяю тот подход который сделает код быстрее и понятнее, а не разбиваю лоб об стену, но пишу "правильный" код.

nerezus
26.12.2009, 12:03
Но проще ли он ? Кода меньше.
А можно просто твой вариант заменить, исправив гото на цикл и break.

Вопрос в том, будет ли он проще для восприятия. как минимум - не сложнее.

1)Падение производительности На сколько процентов? Я думаю, что на 0% ;)
2)Необходимость передачи в функцию большого числа параметров. Не большего, чем у тебя ;)

3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой. Субъективно, причем большинство так думает именно о goto.

Ra$cal
26.12.2009, 12:28
1)Падение производительности
да, для сишарпа это конечно проблема =) когда я тестировал c++ vs c#, шарп выйграл по причине инлайнинга функций. пришлось задавать _forceinline. так что я бы не сказал, что здесь можно ощутить проблемы.

2)Необходимость передачи в функцию большого числа параметров.
до 6-7 параметров человек вполне переварит. на худой конец передавай объект класса.

3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой
Кто мешает здраво именовать функции, на худой конец комменты никто не отменял.

Qwazar
26.12.2009, 16:12
Почему-то никто не отреагировал на критику такого подхода. На всякий случай повторю:
1)Падение производительности
2)Необходимость передачи в функцию большого числа параметров.
3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой.


По пунктам:
1) Время работы программиста куда дороже времени работы системы, а удобство понимания кода сильно на это время влияет. Мало того, многие классики начиная с Фаулера, и заканчивая более современными авторами рекомендуют не оптимизировать код до того как его прогнали профайлером, т.к. при мелочной оптимизации может оказаться, что ты оптимизируешь отнюдь не критичный участок, а читабельность кода в целом падает (а значит и время дебага, добавления новой функциональности растёт в разы). Т.е. на первом этапе оптимизация должна быть алгоритмической, а после профайлера, можно задумываться над push/pop и т.д., да и то только на самых часто вызываемых/тормозящих участках.

2) Это не страшно, главное давать осмысленные названия. Ну и не перегружать тела функций. Да и более 5 параметров практически никогда не бывает. К тому же, всегда часть кода можно вынести в отдельный класс.

3) Названия функций должны говорить сами за себя. А не func1, func2, func3 :)

Algol
26.12.2009, 17:23
Касаемо падения производительности - спорно. Далеко не всегда возможен инлайн, и на вызов метода в любом случае тратится время.

А касаемо осмысленных названий, так в том то и дело , что это куски алгоритма и у них нет отдельной и понятной семантики.