Goto

Нередко в разговорах программистов всплывает тема оператора «goto». Например, сообщество крупно побурлило после появления его в ПХП. Многие считают, что оператор вредный, его появление в языке «понижает» язык в классе до Бейсика (хотя современные Бейсики — очень мощные языки) и он несёт только вред.

Грустно то, что обычно критики предоставляют аргументы на уровне вкусовщины, даже не пытаясь сформулировать аргументы, видимо считая, что уровень «плохо, потому что goto» достаточен.

Попробую возразить.

Во-первых, если считать, что присутствие упомянутого оператора ставит язык в один ряд с худшими Бейсиками, то туда же низбежно попадают Ассемблеры и Си — языки, программировать на которых никогда зазорно не было (в Ассемблере goto называется иначе, но суть абсолютно та же).

Во-вторых, во многих языках goto уже есть. Только у него разные названия для разных целей, обычно это break и continue. Они всегда были в ПХП, есть они и в Перле, ПХП, Пайтоне, Джаве и многих других языках.

С последним аргументам в спорах обычно не соглашаются, говорят, что goto как правило умеет больше, чем эта пара операторов. Тут надо отступить  и вспомнить чем вообще был плох обсуждаемый оператор.

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

Так вот, всего этого современный оператор просто не умеет. Современный goto — тёзка того старого, плохого, сейчас он умеет переходить на метку, но не на всякую, а с огромным количеством ограничений и его использование не более вредно или стыдно, чем использование break или continue.
15 мая 2014 15:31

hshhhhh (hshhhhh.name)
15 мая 2014, 16:50

Да нормальный оператор, но я так ни разу им и не воспользовался -- ни разу не понадобился настолько чтобы им пользоваться без ухудшения читаемости.

Денис Попов (besisland.name)
15 мая 2014, 19:11

Так правильно, break и continue — это частные случаи goto; break и continue — это очень плохо, и нужно стараться их никогда-никогда не использовать.

Евгений Степанищев (bolknote.ru)
15 мая 2014, 20:00, ответ предназначен Денис Попов (besisland.name):

Это утверждение лучше бы смотрелось в паре с аргументом.

SiMM (mr-simm.livejournal.com)
16 мая 2014, 09:06, ответ предназначен Денис Попов (besisland.name):

break и continue — это частные случаи goto; break и continue — это очень плохо, и нужно стараться их никогда-никогда не использовать
Что может привести к ещё большей запутанности кода, чем даже с goto.

SiMM (mr-simm.livejournal.com)
16 мая 2014, 09:07

Упс… понял, что могу быть понят двусмысленно – неиспользование break и continue может привести к ещё более нечитаемому коду, чем даже использование goto.

Иван (инкогнито)
16 мая 2014, 11:25

Денис Попов идеально подходит на роль аналитика, поведение которого описал Евгений.
"Это плохо, ужасно плохо", а что именно плохо -- загадка.

Ничего плохого в гото нет. Да, его использование может усложнить код.
Но в редких ситуациях именно он может секономить много времени и кода.

Svan (инкогнито)
16 мая 2014, 18:27

А ещё try-catch-throw — это вообще ужосѣ

PastorGL (инкогнито)
16 мая 2014, 21:21

ну ок.

goto — это jump в чистом виде.
break/continue — это первая производная от goto, jump с кучей проверок.
try/catch/throw/finally — опять всё то же самое goto, только уже с объектным душком и всякими рюшечками. но всё равно всё сводится к банальному jump.

да ведь, в конце концов, return это оно же самое. возврат на стек, регистры восстановить, а потом — jump. но только почему никто не говорит, что return это плохо, и его надо выпилить? чё народ так непоследовательно-то мыслит?

Vladimir Moskva (fulc.ru)
17 мая 2014, 03:31

Я что-то не могу представить себе программу, использующую goto, которая от его использования стала проще, и не являлась бы спагетти-кодом. Это в любом случае будут бесконтрольные переходы, запутывающие логику и вызывающие ошибки.

break и continue — скорее не goto с кучей проверок, а оператор, работающий единственно возможным способом. И их можно рассматривать не как goto, а как синтаксический сахар для if/else с дополнительными переменными.

Евгений Степанищев (bolknote.ru)
17 мая 2014, 10:55, ответ предназначен Vladimir Moskva (fulc.ru):

break и continue, к сожалению, работают только в циклах. Иногда надо сделать break из обычного if и цикла сверху нет.

Евгений Степанищев (bolknote.ru)
17 мая 2014, 15:48, ответ предназначен Денис Попов (besisland.name):

break и continue — это очень плохо, и нужно стараться их никогда-никогда не использовать.
Кстати, напишите мне поиск указанного элемента массива без break.

Денис Попов (besisland.name)
18 мая 2014, 12:57

Вместо continue — просто оборачиваем в if.

Вместо break — добавляем условие цикла:



Я ещё return забыл — это тоже разновидность goto.

А вот try…catch — это goto технически, но не по смыслу. Их смысл — не в переходе в другое место программы, а в аварийной передаче управления.

Евгений Степанищев (bolknote.ru)
18 мая 2014, 14:25, ответ предназначен Денис Попов (besisland.name):

Т.е. цикл каждую итерацию теперь проверяет ещё одно, совершенно ненужное условие? Плохое решение. Но, вы, смотрю, не специалист в программировании. is_null (функция) вместо === null (конструкция), count, вычисляемый в каждую итерацию цикла.

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

zg (zg.livejournal.com)
18 мая 2014, 17:22, ответ предназначен Евгений Степанищев (bolknote.ru):

Но, вы, смотрю, не специалист в программировании
зачем так? некрасиво же. то что в тестовом примере у человека count вычисляется каждую итерацию, не делает его автоматически неправым во всех остальных утверждениях. правым впрочем тоже.

Евгений Степанищев (bolknote.ru)
18 мая 2014, 19:50, ответ предназначен zg (zg.livejournal.com):

Да, пожалуй. Денис, прошу прощения.

Vladimir Moskva (fulc.ru)
20 мая 2014, 14:55, ответ предназначен Евгений Степанищев (bolknote.ru):

Т.е. цикл каждую итерацию теперь проверяет ещё одно, совершенно ненужное условие?
А с использованием break он не будет проверять его каждый раз перед break? Можно в цикле просто получать элемент в переменную, а в условии проверять, она это или нет, будет по одной проверке на цикл.

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

Евгений Степанищев (bolknote.ru)
20 мая 2014, 15:55, ответ предназначен Vladimir Moskva (fulc.ru):

А с использованием break он не будет проверять его каждый раз перед break?
Да, тут я промахнулся.
скорее всего, будет преждевременной оптимизацией.
Не путай качество кода и преждевременную оптимизацию. То, что оптимизация не должна быть преждевременной не говорит о том, что код надо писать плохо.

http://gdriv.es/optiklab (инкогнито)
30 июля 2014, 16:04

Добрый день.
    Работал пару лет назад в компании, в которой очень сильно следили за качеством кода, поэтому были огромные своды правил, как с точки зрения аккуратности кода (4 пробела вместо табов, отсутствие лишних пробелов в инструкциях или наличие таковых для удобочитаемости, и т.д.), так и с технической точки зрения (не использовать goto, continue, break, и т.д.).
    Однако, всё время в компании ходят споры о том, почему это нельзя делать: никто доводов толковых не приводил. Я сам об этом читал в книгах, вроде МакКоннел "Совершенный код", и т.д.
   Везде один довод: "запутанность кода". Однако при правильном разбиении кода на методы и нормальной архитектуре, по-моему, break и continue приводит только к большей понятности кода.
    И вот, читая интереснейший цикл статей "Что каждый программист должен знать о памяти" я нарвался на доступное объяснение чем плох любой переход (JUMP) с точки зрения производительности (ссыль http://rus-linux.net/lib.php?name=/MyLDP/hard/memory/memory-6-3.html):
" Код имеет то преимущество, что между переходами он линеен. В такие периоды процессор может эффективно делать предварительную загрузку памяти. Переходы нарушают эту идеальную картину из-за того что
- цель перехода может быть определена не статически;
- даже если она определена статически, загрузка памяти может занять длительное время, если эта цель отсутствует во всех кэшах.
     Эти проблемы создают задержки при выполнении кода с возможным существенным снижением производительности."

Евгений Степанищев (bolknote.ru)
30 июля 2014, 19:49, ответ предназначен http://gdriv.es/optiklab

В случае интерпретируемых языков аргумент не принимается. Потому что при интерпретации происходит целая куча переходов, которые вы не видите. Кстати говоря, вызов любой функции/процедуры/метода — такой же переход.

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)