Просмотр полной версии : Стереотипы: goto
Недавно один мой знакомый был повергнут в шок, увидев в моем коде оператор 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;
}
:)
Несмотря на всю мою ненависть к яве, в ней для таких случаев предусмотрены
outer:
for(int i = 0; ....){
for(int j = 0; ....){
if(...) continue outer;
if(...) break outer;
}
}
но goto удобен, когда проще написать его, чем переконопачивать весь код
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;
}
:)
Эээ.... ну ваще-то этот код не эквивалентен приведенному, и вообще не рабочий. Будьте внимательней )
ЗЫ
Да и кстати даже если бы он был рабочий, он явно менее понятный чем исходный :)
Несмотря на всю мою ненависть к яве, в ней для таких случаев предусмотрены
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)))))))))
Ну от того что goto назвать continue, ведь суть не меняется, не так ли?
суть не меняется, просто читабельность и логичность кода - немного повышается )
Конечно же, я не спорю - goto решает! )
писал как ответ 8)) и он делает то же самое ! Написать екзешник ? :D
Пиши :D
... по поводу смотрибельности мой меньше по кол-ву строк!
"Смотрибельность" не измеряется в количестве строк. Если бы это было так, то фильм "Аватар" (ок 4ГБ) был бы гораздо сложнее для восприятия чем докзательство теоремы Ферма (ок 40 КБ), что явно не так :)
Retimiled
25.12.2009, 01:16
не буду .... :p я и так знаю что рабочий
... главное не это ... как то разбирая старый fig-forth заметил что goto там вытворяла НЕЧТО.... дело в том что счетчики for лежали на стеке ... и чтоб выйти по goto форту приходилось такой чисткой и анализом заниматься что КАРАУЛ!
не буду .... :p я и так знаю что рабочий
Ну послушай, как код может быть рабочим, если у тебя внутри цикла стоит безусловный return? :D
по-моему, довольно очевидная вещь
было у Криса в трюках
Retimiled
25.12.2009, 01:19
он стоит в цикле foreach ... как и у тебя .... присмотрись
он стоит в цикле foreach ... как и у тебя .... присмотрись
Да нет, не как у меня, присмотрись лучше ты :D
по-моему, довольно очевидная вещь
было у Криса в трюках
Не очень понятное замечание.
Кто такой Крис, и какая именно вещь очевидна ?
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;
}
... главное не это ... как то разбирая старый fig-forth заметил что goto там вытворяла НЕЧТО.... дело в том что счетчики for лежали на стеке ... и чтоб выйти по goto форту приходилось такой чисткой и анализом заниматься что КАРАУЛ!
К счастью, в C# таких проблем нет :)
В общем случае неуправляемых языков - конечно я согласен, что goto может привести к очень плохим последствиям.
Retimiled
25.12.2009, 01:27
ну согласен ... но постоянно в мозгу а КАК ТАМ НА СТЕКЕ 8)))))))))))))
.... стекофобия 8)))
....
Ты все же напиши экзешник :)
Трассировка нас рассудит :D
Retimiled
25.12.2009, 01:40
кстати return - это тот же goto только в фас!
я имел в виду, что для большинства ясно, что иногда goto очень даже полезен
крис касперски. вспомнил его, так как пример уж больно похож :) а вообще хотелось бы побольше подобных заметок со всякими тонкостями
кстати return - это тот же goto только в фас!
Ну не скажи. Во-первых кроме return тут большая компания: continue, break, throw... А во-вторых для этих операторов не требуется метка, точка выхода для них - более очевидна, чем для goto. Поэтому они считаются более-менее "структурными".
ЗЫ
Да и еще yield, как же ж без него, родимого, структурного нашего....
Retimiled
25.12.2009, 01:48
:D да goto приближает нас к низкоуровневому программированию ... ведь никто не пытается сказать ничего против джампов в ассемблере
... и поскольку у Си С++ положение аморфное .... то GOTO живет там как засланец мышыных кодов!
давненько было помню как z0mbie в своей теории недектируемости вируса (делал попытки на Goto выстроить матрицу исполнения, которая по его мнению могла завести любую эвристику в ступор) ... делал он именно на GOTO ... :p поэтому GOTO живет и будет жить ...
НО Я ГОВОРЮ НЕ О ВЕЩАХ УПРОЩАЮЩИХ ПОНИМАНИЕ, а наоборот 8)))) , вообще вопрос риторический должен ли антивирус понимать код вируса!
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;
}
Я иногда использую goto что бы неплодить ненужных вложенных циклов.
Нынче в институтах учат, что применение 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;
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 чего ?
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))
В перле goto как будто медленнее работает, чем, например, redo.
throw чего ? А хз ) Синтаксиса не помню уже ) Но там переписать не сложно без goto с меньшими затратами =)
Не дотнетчик я - импровизировал )
Но там переписать не сложно без goto с меньшими затратами =)
Ну не знаю, мне например в голову не приходит как там переписать без goto с меньшими затратами :)
Retimiled, вообще то глубокой вложенности нужно избегать - с помощью выделения кода в методы\функции, ибо глубоко вложенные циклы нечитабельное говно с любой стороны - что с goto, что с флагами, что с break/continue.
В целом да, так и есть. Но опять же есть специфические случаи, когда это очень геморно. В основном из-за того что нужно передавать кучу параметров в такую функцию. Да и семантика такой функции не всегда понятна (К примеру, при перемножении матриц нужно делать три вложенных цикла, если два из них вынести в функцию, то как назвать такую функцию? func1() ? :) ).
Я бы вынес проверку одной матрицы на наличие нуля в отдельную функцию и делал бы 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.
Недавно один мой знакомый был повергнут в шок, увидев в моем коде оператор 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;
}
}
помню-помню когда-то в школе использовал его) но это была только одна задача) а прошло уже много лет)
п.с. о вкусах не спорят) если тебе так удобнее почемубы и нет) в бою все средства...
помню-помню когда-то в школе использовал его) но это была только одна задача) а прошло уже много лет)
п.с. о вкусах не спорят) если тебе так удобнее почемубы и нет) в бою все средства... В данном случае о вкусах спорят, т.к.:
1) Этот код будет разбирать не только автор
2) Возможно даже через несколько лет.
А общий цикл оформил бы примерно так (псевдокод):
Псевдокод это конечно хоршо и кошерно, но реальные программы пишутся не на псевдокоде.
Я же привожу конкретные примеры, в котором метка - самое простое и наглядное решение.
Читабельность имхо выше, чем всякие разные метки искать. Ну а в целом, я иногда использую continue; но никогда не приходилось использовать goto.
Странно, у меня continue чуть ли не в каждом втором цикле.
Но в целом точка зрения ясна.
Псевдокод это конечно хоршо и кошерно, но реальные программы пишутся не на псевдокоде.
Я же привожу конкретные примеры, в котором метка - самое простое и наглядное решение. Моё решение более наглядно, не запутаешься во вложенных циклах, и не придётся смотреть по коду, где находится метка, на которую ты скачешь. Да и псевдокод очень даже приближен к реальности :)
Недавно один мой знакомый был повергнут в шок, увидев в моем коде оператор goto.
Увидев первый сурс я тоже был повергнут в шок :(
Увидев первый сурс я тоже был повергнут в шок :(
Ээ... Ну это понятно, а что-то по обсуждаемой теме есть :confused:
cheater_man
25.12.2009, 23:07
Goto использовать можно!НО, как считают программисты после использоватья этого оператора код становится менее читабельным. От этого оператора уже отказались в 90-е года.Но всетаки он присутствует в языках высокого уровня. И наиболее часто используется в assembler, он там как jmp.
Вывод: этот оператор лучше всего не использовать если хочешь казаться современным :)
Retimiled
26.12.2009, 00:07
имидж ничто жажда ВСЕ!
бред мне в уши...
Загляните в ядро линукса, там этих goto пруд пруди, и все они юзаются только с одной благородной целью, и делают код намного удобней чем что либо другое
В примере с матрицами можно было бы заюзать класс матрицы, вместо int[,].
Объекты этого класса могли бы проверять сами себя и бросаться исключениями.
Конечно же, всё зависит от задачи.
бред мне в уши...
Загляните в ядро линукса, там этих goto пруд пруди, и все они юзаются только с одной благородной целью, и делают код намного удобней чем что либо другое
Ага, и этот код очень легко читать и понимать.
Retimiled
26.12.2009, 01:33
писалось в стиле Аля-Си.... а там скорее
int *mass;
mass =new int[n][k];
... типа чето делаю не относясь к задаче алгола
delete[] mass;
чем классная лабуда с конструкторами деструкторами и исключениями лучше... попробуйте обьяснить человеку пишущему на ассмблере но не знающеего Си чему соответствуют исключения и что это далеко не Goto ... боюсь он со смехом вам диззассемблирует! 8))
а по поводы простоты .... можно взять человека с улицы и обьяснить ему код с классами и в стиле Аля-Си, результат о том какой код он поймет предсказуем!
бред мне в уши...
Загляните в ядро линукса, там этих goto пруд пруди, и все они юзаются только с одной благородной целью, и делают код намного удобней чем что либо другое
Если ты внимательно смотрел, то должен был заметить что в основном он используется для обработки ошибок, что вполне допустительно для С. В таких языках как С++, Java, C#, есть исключения которые избавляют программера использовать метки для обработки ошибок.
В случае кода Algol'a, можно было бы поступить более изящней, как написал nerezus и Qwazar. Случай же с глубокой вложенностью циклов не более чем классический и часто можно встретить в "goto холиварах", а вот на практике встречается не так часто ибо можно переписать без использования последнего.
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. Не проверял на синтаксис, но работать должно )
Вот вроде рабочий вариант без goto. Не проверял на синтаксис, но работать должно )
Да, это рабочий вариант. Но проще ли он ?
Любой алгоритм можно реализовать без goto. Вопрос в том, будет ли он проще для восприятия.
В случае кода Algol'a, можно было бы поступить более изящней, как написал nerezus и Qwazar.
Почему-то никто не отреагировал на критику такого подхода. На всякий случай повторю:
1)Падение производительности
2)Необходимость передачи в функцию большого числа параметров.
3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой.
Конечно это крайние случаи. Но и я в первом постинге и пишу про карйние случаи. В 90% случаев конечно я тоже выношу участки кода в отдельные функции. Но остаются еще 10% где это не рационально. И там я применяю тот подход который сделает код быстрее и понятнее, а не разбиваю лоб об стену, но пишу "правильный" код.
Но проще ли он ? Кода меньше.
А можно просто твой вариант заменить, исправив гото на цикл и break.
Вопрос в том, будет ли он проще для восприятия. как минимум - не сложнее.
1)Падение производительности На сколько процентов? Я думаю, что на 0% ;)
2)Необходимость передачи в функцию большого числа параметров. Не большего, чем у тебя ;)
3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой. Субъективно, причем большинство так думает именно о goto.
1)Падение производительности
да, для сишарпа это конечно проблема =) когда я тестировал c++ vs c#, шарп выйграл по причине инлайнинга функций. пришлось задавать _forceinline. так что я бы не сказал, что здесь можно ощутить проблемы.
2)Необходимость передачи в функцию большого числа параметров.
до 6-7 параметров человек вполне переварит. на худой конец передавай объект класса.
3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой
Кто мешает здраво именовать функции, на худой конец комменты никто не отменял.
Почему-то никто не отреагировал на критику такого подхода. На всякий случай повторю:
1)Падение производительности
2)Необходимость передачи в функцию большого числа параметров.
3)Код начинает изобиловать кучей малопонятных функций, с неопределенной семантикой.
По пунктам:
1) Время работы программиста куда дороже времени работы системы, а удобство понимания кода сильно на это время влияет. Мало того, многие классики начиная с Фаулера, и заканчивая более современными авторами рекомендуют не оптимизировать код до того как его прогнали профайлером, т.к. при мелочной оптимизации может оказаться, что ты оптимизируешь отнюдь не критичный участок, а читабельность кода в целом падает (а значит и время дебага, добавления новой функциональности растёт в разы). Т.е. на первом этапе оптимизация должна быть алгоритмической, а после профайлера, можно задумываться над push/pop и т.д., да и то только на самых часто вызываемых/тормозящих участках.
2) Это не страшно, главное давать осмысленные названия. Ну и не перегружать тела функций. Да и более 5 параметров практически никогда не бывает. К тому же, всегда часть кода можно вынести в отдельный класс.
3) Названия функций должны говорить сами за себя. А не func1, func2, func3 :)
Касаемо падения производительности - спорно. Далеко не всегда возможен инлайн, и на вызов метода в любом случае тратится время.
А касаемо осмысленных названий, так в том то и дело , что это куски алгоритма и у них нет отдельной и понятной семантики.
vBulletin® v3.8.14, Copyright ©2000-2026, vBulletin Solutions, Inc. Перевод: zCarot