Я всегда знал, что goto
это что-то плохое, запертое в подвале где-то, что никогда нельзя увидеть навсегда, но сегодня я наткнулся на пример кода, который имеет смысл использовать goto
.
У меня есть IP-адрес, где мне нужно проверить, есть ли в списке IP-адресов, а затем продолжить с кодом, в противном случае выдается исключение.
<?php
$ip = '192.168.1.5';
$ips = [
'192.168.1.3',
'192.168.1.4',
'192.168.1.5',
];
foreach ($ips as $i) {
if ($ip === $i) {
goto allowed;
}
}
throw new Exception('Not allowed');
allowed:
...
Если я не использую, goto
то я должен использовать некоторые переменные, такие как
$allowed = false;
foreach ($ips as $i) {
if ($ip === $i) {
$allowed = true;
break;
}
}
if (!$allowed) {
throw new Exception('Not allowed');
}
Мой вопрос в том, что такого плохого в том, goto
когда он используется для таких очевидных и не соответствующих им дел?
Ответы:
Само GOTO - это не непосредственная проблема, это неявные конечные автоматы, которые люди, как правило, используют с его помощью. В вашем случае вам нужен код, который проверяет, есть ли IP-адрес в списке разрешенных адресов, следовательно,
поэтому ваш код хочет проверить условие. Алгоритм реализации этой проверки здесь не должен быть проблемой, в ментальном пространстве вашей основной программы проверка является атомарной. Так и должно быть.
Но если вы поместите код, который выполняет проверку, в вашу основную программу, вы потеряете это. Вы вводите изменяемое состояние либо явно:
где
$list_contains_ip
ваша единственная переменная состояния или неявно:Как видите, в конструкции GOTO есть необъявленная переменная состояния. Само по себе это не проблема, но эти переменные состояния похожи на камешки: нести с собой не сложно, но с сумкой, полной их, вы потеете. Ваш код не останется прежним: в следующем месяце вас попросят разграничить частные и публичные адреса. Через месяц ваш код должен будет поддерживать диапазоны IP-адресов. В следующем году кто-то попросит вас поддержать адреса IPv6. Вскоре ваш код будет выглядеть так:
И тот, кто должен отладить этот код, проклянет вас и ваших детей.
Дейкстра формулирует это так:
И именно поэтому GOTO считается вредным.
источник
$list_contains_ip = false;
утверждение кажется неуместнымЕсть несколько законных вариантов использования
GOTO
. Например, для обработки ошибок и очистки в C или для реализации некоторых форм конечных автоматов. Но это не один из этих случаев. Второй пример является более читабельным IMHO, но еще более читабельным было бы извлечь цикл в отдельную функцию и затем вернуться, когда вы найдете совпадение. Еще лучше (в псевдокоде я не знаю точного синтаксиса):Так что же такого плохого в этом
GOTO
? Структурированное программирование использует функции и управляющие структуры для организации кода, так что синтаксическая структура отражает логическую структуру. Если что-то выполняется только условно, оно появится в блоке условного оператора. Если что-то выполняется в цикле, оно появится в блоке цикла.GOTO
позволяет обойти синтаксическую структуру, произвольно перемещаясь, что делает код более сложным для отслеживания.Конечно, если у вас нет другого выбора, который вы используете
GOTO
, но если тот же эффект может быть достигнут с помощью функций и структур управления, это предпочтительнее.источник
it's easier to write good optimizing compilers for "structured" languages.
разработать? В качестве встречного примера люди из Rust представили MIR - промежуточное представление в компиляторе, которое специально заменяет циклы, продолжения, разрывы и т. Д. На gotos, потому что его проще проверять и оптимизировать.goto
с использованием крысиных флагов и условных выражений (как во втором примере OP), гораздо менее читабельно, чем просто использование agoto
.Как уже говорили другие, проблема не в
goto
самом себе; проблема в том, как люди используютgoto
, и как это может усложнить понимание и поддержку кода.Предположим следующий фрагмент кода:
Какое значение печатается для
i
? Когда это напечатано? Пока вы не учтете каждый экземплярgoto label
вашей функции, вы не сможете знать. Простое присутствие этой метки разрушает вашу способность отлаживать код простым осмотром. Для небольших функций с одной или двумя ветвями проблем не так много. Для немалых функций ...Еще в начале 90-х нам дали кучу кода C, который работал на трехмерном графическом дисплее и сказал, чтобы он работал быстрее. Это было всего около 5000 строк кода, но все это было в
main
, и автор использовал около 15 или около тогоgoto
разветвления в обоих направлениях. С самого начала это был плохой код, но его присутствиеgoto
сделало его намного хуже. Моему коллеге понадобилось около 2 недель, чтобы разобраться с потоком контроля. Более того, этоgoto
привело к тому, что код был так тесно связан с самим собой, что мы не смогли внести никаких изменений, не нарушив что-либо.Мы попытались скомпилировать с оптимизацией уровня 1, и компилятор съел всю доступную оперативную память, затем всю доступную подкачку, а затем запаниковал систему (что, вероятно, не имело ничего общего с
goto
самими s, но мне нравится выкидывать этот анекдот).В итоге мы предоставили заказчику два варианта - давайте переписать все с нуля или купим более быстрое оборудование.
Они купили более быстрое оборудование.
Правила Боде по использованию
goto
:if
orfor
или orwhile
);goto
вместо контрольной структурыЕсть случаи, когда правильный ответ
goto
- a , но они редки (выход из глубоко вложенного цикла - единственное место, где я бы его использовал).РЕДАКТИРОВАТЬ
Расширяя это последнее утверждение, вот один из немногих допустимых вариантов использования для
goto
. Предположим, у нас есть следующая функция:Теперь у нас есть проблема - что делать, если один из
malloc
вызовов не проходит в середине? В отличие от этого события, мы не хотим возвращать частично выделенный массив и не хотим просто выйти из функции с ошибкой; мы хотим очистить себя и освободить частично выделенную память. В языке, который выдает исключение при неправильном выделении ресурсов, это довольно просто - вы просто пишете обработчик исключений, чтобы освободить то, что уже было выделено.В C вы не имеете структурированной обработки исключений; Вы должны проверить возвращаемое значение каждого
malloc
звонка и принять соответствующие меры.Можем ли мы сделать это без использования
goto
? Конечно, мы можем - это просто требует немного дополнительной бухгалтерии (и на практике это путь, который я бы выбрал). Но, если вы ищете место , где использованиеgoto
не сразу признак плохой практики или дизайна, это один из немногих.источник
goto
даже в соответствии с этими правилами - я бы не использовал его вообще, если только это не единственная форма ветвления, доступная на языке, - но я вижу, что отладка не будет слишком большим кошмаромgoto
Это если они были написаны в соответствии с этими правилами.GOTO
, в том числе известные фортрановские (in) известные арифметические переходы, если я правильно помню.return
,break
,continue
Иthrow
/catch
все по существу GOTOS - они все передачи управления в другой части кода и все они могут быть реализованы с помощью GOTOS - на самом деле я сделал это один раз в школьном проекте, Паскаля инструктор говорил , насколько лучше Паскаль , чем основной из-за структуры ... так что я должен был быть противоположным ...Самая важная вещь в Software Engineering (я собираюсь использовать этот термин над Coding для обозначения ситуации, когда кто-то платит вам за создание кодовой базы вместе с другими инженерами, что требует постоянного улучшения и обслуживания), делает код читабельным. Получение этого, чтобы сделать что-то, почти вторично. Ваш код будет написан только один раз, но в большинстве случаев люди будут тратить дни и недели на его пересмотр / переучивание, улучшение и исправление - и каждый раз, когда им (или вам) придется начинать с нуля и пытаться запомнить / выяснить ваш код.
Большинство функций, которые были добавлены к языкам на протяжении многих лет, делают программное обеспечение более легким в обслуживании, а не простым в написании (хотя некоторые языки идут в этом направлении - они часто вызывают долгосрочные проблемы ...).
По сравнению с аналогичными инструкциями управления потоком GOTO может быть почти так же легко следовать в лучшем виде (одиночное goto, используемое в случае, как вы предлагаете), и кошмар, когда злоупотребляют - и очень легко злоупотреблять ...
Так что после нескольких лет кошмаров со спагетти мы просто сказали «Нет», как сообщество, мы не собираемся это принимать - слишком много людей портят это, если им дают немного свободы - это действительно единственная проблема с ними. Вы можете использовать их ... но даже если это идеальный случай, следующий парень предположит, что вы ужасный программист, потому что вы не понимаете историю сообщества.
Многие другие структуры были разработаны только для того, чтобы сделать ваш код более понятным: функции, объекты, область видимости, инкапсуляция, комментарии (!) ... а также более важные шаблоны / процессы, такие как «СУХОЙ» (предотвращающий дублирование) и «ЯГНИ» (Уменьшение чрезмерного обобщения / усложнения кода) - все это действительно только для того, чтобы СЛЕДУЮЩИЙ парень прочитал ваш код (кто, вероятно, будет вами - после того, как вы забыли большую часть того, что вы делали в первую очередь!)
источник
return
break
continue
throw
catch
GOTO
это инструмент. Это может быть использовано во благо или во зло.В старые добрые времена, с Фортраном и Бейсиком, это был почти единственный инструмент.
При взгляде на код тех дней, когда вы видите,
GOTO
вы должны выяснить, почему он там есть. Это может быть частью стандартной идиомы, которую вы можете быстро понять ... или это может быть частью какой-то кошмарной структуры управления, которой никогда не должно было быть. Вы не знаете, пока не посмотрите, и легко ошибиться.Люди хотели чего-то лучшего, и были изобретены более совершенные структуры управления. Они охватывали большинство случаев использования, и люди, которые были сожжены плохими людьми,
GOTO
хотели полностью запретить их.Как ни странно,
GOTO
не так уж и плохо, когда это редко. Когда вы видите один, вы знаете, что происходит что-то особенное, и легко найти соответствующий ярлык, так как это единственный ярлык поблизости.Перенесемся в сегодня. Вы преподаватель программирования. Вы можете сказать: «В большинстве случаев вам следует использовать расширенные новые конструкции, но в некоторых случаях простое
GOTO
может быть более читабельным». Студенты не поймут этого. Они собираются злоупотреблять,GOTO
чтобы сделать нечитаемый код.Вместо этого вы говорите «
GOTO
плохо.GOTO
Зло.GOTO
Провалить экзамен». Студенты будут понимать , что !источник
За исключением того
goto
, что все конструкции потока в PHP (и большинство языков) имеют иерархическую область видимости.Представьте, что какой-то код проверен косыми глазами:
Независимо от того, что управление конструкцией
foo
является (if
,while
и т.д.), есть только определенные разрешенные заказыa
,b
иc
.Вы могли бы иметь
a
-b
-c
илиa
-c
или дажеa
-b
-b
-b
-c
. Но вы никогда не могли иметьb
-c
илиa
-b
-a
-c
.... если у вас нет
goto
.goto
(в частности, в обратном направленииgoto
) может быть достаточно хлопотным, так что лучше просто оставить его в покое и использовать иерархические, блокированные конструкции потока.goto
Они имеют место, но в основном как микрооптимизация на языках низкого уровня. ИМО, в PHP нет подходящего места для этого.К вашему сведению, пример кода может быть написан даже лучше, чем любое из ваших предложений.
источник
На языках низкого уровня GOTO неизбежен. Но на высоком уровне этого следует избегать (в случае, если язык поддерживает это), потому что это делает программы более трудными для чтения .
Все сводится к тому, чтобы сделать код более сложным для чтения. Языки высокого уровня необходимы для того, чтобы облегчить чтение кода, чем языки низкого уровня, например, ассемблер или C.
GOTO не вызывает глобального потепления и не вызывает бедность в третьем мире. Это просто затрудняет чтение кода.
Большинство современных языков имеют управляющие структуры, которые делают GOTO ненужным. У некоторых как Java даже нет этого.
Фактически, термин « код спагетти» происходит от сложного, сложного для понимания причин кода неструктурированными ветвящимися структурами.
источник
goto
Можно сделать код Eaiser для чтения. Когда вы создаете дополнительные переменные и вложенные ветви по сравнению с одним переходом, код становится намного сложнее для чтения и понимания.goto
что они не являются примерами запутанного кода спагетти, а скорее довольно простыми вещами, часто эмулирующими шаблоны потока управления в языках, которые не у него нет синтаксиса: два наиболее распространенных примера - разрыв нескольких циклов и пример в OP, гдеgoto
используется для реализацииfor
-else
.goto
также отлично подходит для таких вещей, как повторная попытка исключения, откат, конечные автоматы.Ничего плохого в
goto
самих заявлениях. Неправильны некоторые люди, которые неуместно используют это утверждение.В дополнение к тому, что сказал JacquesB (обработка ошибок в C), вы используете
goto
для выхода из не вложенного цикла, что вы можете сделать с помощьюbreak
. В этом случае вам лучше использоватьbreak
.Но если бы у вас был сценарий с вложенным циклом, то использование
goto
было бы более элегантным / простым. Например:Приведенный выше пример не имеет смысла в вашей конкретной проблеме, потому что вам не нужен вложенный цикл. Но я надеюсь, что вы видите только часть этого вложенного цикла.
Бонус: если ваш список IP-адресов невелик, ваш метод подойдет . Но если список растет, знайте, что ваш подход имеет асимптотическую наихудшую сложность времени выполнения O (n) . По мере роста вашего списка вы можете захотеть использовать другой метод, который достигает O (log n) (например, древовидная структура) или O (1) (хеш-таблица без коллизий).
источник
break
иcontinue
с числом (для разрыва / продолжения такого количества циклов) или меткой (для разрыва / продолжения цикла с этой меткой), любой из которых обычно должен быть предпочтительнее использоватьgoto
для вложенных циклов.Правда. Не волнует
Правда. Не волнует
Правда. Не волнует
Правда. Тем не менее, я был тем дисциплинированным программистом. Я видел, что происходит с кодом с течением времени.
Goto
начинается нормально. Затем возникает желание повторно использовать кодовые наборы. Довольно скоро я нахожусь в точке останова, не имея ни малейшего понятия, что происходит, даже после просмотра состояния программы.Goto
затрудняет рассуждения о коде Мы работали очень трудно Созданиеwhile
,do while
,for
,for each
switch
,subroutines
,functions
, и больше всего потому , что делает этот материал сif
иgoto
трудно на мозг.Так что нет. Мы не хотим смотреть на
goto
. Конечно, он жив и здоров в двоичном коде, но нам не нужно видеть это в источнике. На самом деле,if
начинает выглядеть немного шатким.источник
Goto
позволяет вам абстрагировать проблему, сделав ее немного другой проблемой кода.If
позволяет вам выбрать эту абстракцию. Есть лучшие способы абстрагирования и лучшие способы выбора абстракции. Полиморфизм для одного.Языки ассемблера обычно имеют только условные / безусловные переходы (эквивалент GOTO. В более старых реализациях FORTRAN и BASIC не было операторов блока управления, кроме подсчитанной итерации (цикл DO), оставляя весь другой поток управления для IF и GOTO. Цикл DO в эти языки заканчивались числовым выражением, в результате чего код, написанный для этих языков, мог быть, и часто был трудным для следования и подвержен ошибкам.
Чтобы подчеркнуть суть, есть изобретательно выдуманная фраза « ПРИХОДИТЕ ОТ ».
Практически нет необходимости использовать GOTO в таких языках, как C, C ++, C #, PASCAL, Java и т. Д .; можно использовать альтернативные конструкции, которые почти наверняка будут такими же эффективными и гораздо более ремонтопригодными. Это правда, что один GOTO в исходном файле не будет проблемой. Проблема в том, что не требуется много усилий, чтобы сделать блок кода сложным для отслеживания и подверженным ошибкам в обслуживании. Вот почему общепринятая мудрость - избегать GOTO, когда это возможно.
Эта статья в Википедии об утверждении goto может быть полезна
источник