Чи використовувати гото коли-небудь варто?


29

gotoмайже універсально не відлякує. Чи корисне використання цього твердження?



1
gotoце те, що в кінцевому рахунку робить процесор, але він не працює добре з тим, що людині потрібно зрозуміти та абстрагувати через відсутність позначки на цільовій меті. Порівняйте з Intercal COMEFROM.

2
@ ThorbjørnRavnAndersen, що ще означають люди, коли вони говорять про переходи стану в державній машині? Ви не бачите подібності? "Переходьте до заданого стану ", ось що це означає, і все інше було б не що інше, як зайве ускладнення такої дрібниці. А що ви маєте на увазі під "без позначки"? Мітка - така марка.
SK-логіка

@ SK-логіка, залежить від того, наскільки далеко ви дозволите готу прийти. Державні машини не вимагають готи для впровадження.

@ ThorbjørnRavnAndersen, звичайно goto, не потрібно. Це просто найраціональніший спосіб їх реалізації імперативними мовами, оскільки це найбільш близький до самої семантики державного переходу. А призначення може бути досить далеко від джерела, це не завадить читати. Мій улюблений приклад такого коду - реалізація гри «Пригоди» від DE Knuth.
SK-логіка

Відповіді:


50

Про це кілька разів обговорювалося в Stack Overflow, і Кріс Гіллум підсумував можливі можливості використанняgoto :

Чіткий вихід із функції

Часто у функції ви можете виділяти ресурси та потребувати виходу в декілька місць. Програмісти можуть спростити свій код, поставивши код очищення ресурсу в кінці функції, всі "точки виходу" функції перейдуть на ярлик очищення. Таким чином, вам не доведеться писати код очищення в кожній "точці виходу" функції.

Вихід із вкладених циклів

Якщо ви знаходитесь у вкладеному циклі і вам потрібно вийти з усіх циклів, goto може зробити це набагато чистішим і простішим, ніж заяви про розрив та if-check.

Поліпшення продуктивності низького рівня

Це справедливо лише в перфрі-критичному коді, але оператори goto виконують дуже швидко і можуть дати вам імпульс під час переміщення через функцію. Однак, це меч з двома кінцями, оскільки компілятор, як правило, не може оптимізувати код, що містить gotos.

Я б зауважив, як багато хто стверджує, що у всіх цих випадках використання gotoвикористовується як засіб вийти з кута, зашифрованого самим собою, і, як правило, є симптомом коду, який може бути відновлений.


28
Перша задача вирішується дуже акуратно finallyблоками в сучасних мовах, а друга вирішується міткою breaks. Якщо ви застрягли в C, однак, gotoце єдиний спосіб єдиного вирішення цих проблем.
Chinmay Kanchi

2
Дуже хороші бали. Мені подобається, як Java дозволяє
виривати

4
@Chinmay - нарешті блоки застосовуються лише до 1) сучасних мов, які їх мають, і b) ви можете терпіти накладні витрати (обробка винятків має накладні витрати). Тобто використання finallyє дійсним лише за тих умов. Є дуже рідкісні, але дійсні ситуації, коли гото - це шлях.
luis.espinal

21
Гаразд, люди ... в чому біса різниця між break 3або break myLabelі goto myLabel. О, це просто ключове слово. Якщо ви використовуєте break, continueабо інші подібні ключові слова ви Infact , використовуючи goto(якщо ви використовуєте for, while, foreach, do/loopви використовуєте умовну Гото.)
Метью Whited

4
Щодо низької продуктивності - поведінку сучасного процесора може бути важко передбачити (конвеєр, виконання поза замовленням), тому, мабуть, у 99% випадків цього не варто, а навіть може бути повільніше. @MatthewWhited: Оцінка масштабу. Якщо ви вводите область в довільній точці, не зрозуміло (людині), які конструктори слід викликати (проблема існує і в C).
Maciej Piechotka

42

Конструкти потоків управління вищого рівня, як правило, відповідають поняттям в проблемній області. Рішення if / else - рішення, засноване на певній умові. Петля каже виконувати якусь дію повторно. Навіть заява про перерву говорить, що "ми це робили неодноразово, але зараз нам потрібно зупинитися".

З іншого боку, оператор goto, як правило, відповідає концепції в запущеній програмі, а не в проблемній області. Він говорить про продовження виконання у визначений момент програми . Хтось, хто читає код, повинен зробити висновок, що це означає щодо проблемної області.

Звичайно, всі конструкції вищого рівня можна визначити через готос та прості умовні гілки. Це не означає, що вони просто маскуються в готи. Подумайте про них як про обмежені готи - і саме обмеження роблять їх корисними. Заява перерви реалізується як перехід до кінця циклу, що вкладається, але краще думати про те, що він працює на циклі в цілому.

За інших рівних кодів, структура яких відображає структуру доменної області, як правило, легше читати та підтримувати.

Немає випадків, коли заява goto абсолютно необхідна (для цього є теорема ), але є випадки, коли це може бути найменш поганим рішенням. Ці випадки відрізняються від мови до мови, залежно від того, які конструкції вищого рівня підтримує мова.

Наприклад, у С я вважаю, що існує три основні сценарії, коли гото доречний.

  1. Виривання з вкладеної петлі. Це було б непотрібно, якби мова мала мітку про перерву.
  2. Виправлення розтягування коду (як правило, функціонального органу) у разі помилки чи іншої несподіваної події. Це було б зайвим, якби мова мала винятки.
  3. Реалізація явної машини з кінцевим станом. У цьому випадку (і, я думаю, лише в цьому випадку) goto відповідає безпосередньо поняттю в проблемній області, переходячи з одного стану у вказаний інший стан, де поточний стан представлений тим, який блок коду виконується в даний час .

З іншого боку, явна машина кінцевого стану також може бути реалізована з оператором перемикання всередині циклу. Це має перевагу в тому, що кожен стан починається з одного і того ж місця в коді, що може бути корисним, наприклад, для налагодження.

Основне використання goto в досить сучасній мові (та, яка підтримує if / else та циклі) - це імітувати конструкцію керуючого потоку, якої немає у мові.


5
Це насправді найкраща відповідь поки що - досить дивно, на питання, якому виповнилося півтора року. :-)
Конрад Рудольф

1
+1 до "найкращої відповіді поки що". --- "Основне використання goto в досить сучасній мові (та, яка підтримує if / else і циклі) - це імітувати конструкцію керуючого потоку, якої немає у мові."
Дейв Допсон

У машині стану оператора switch, кожне записування до керуючого значення дійсно є goto. SSSM часто є хорошими структурами для використання, оскільки вони відокремлюють стан SM від конструкції виконання, але вони дійсно не усувають "gotos".
supercat

2
"Якщо всі інші рівні, код, структура якого відображає структуру доменної області, як правило, легше читати та підтримувати." Я знаю це повністю давно, але не вистачало слів, щоб настільки точно передати це таким чином, що інші програмісти можуть і насправді зрозуміють. Дякую за це.
Wildcard

"Теорема про структуровану програму" мене розвеселює, оскільки вона, як правило, наочно демонструє те, що використання структурних операторів програмування для емуляції goto набагато менш читабельно, ніж просто використання goto!

11

Звичайно, це залежить від мови програмування. Основна причина gotoсуперечливості полягає в тому, що її погані наслідки виникають, коли компілятор дозволяє занадто вільно використовувати її. Проблеми можуть виникнути, наприклад, якщо це дозволить вам використовувати gotoтаким чином, що тепер ви можете отримати доступ до неініціалізованої змінної або, що ще гірше, перейти до іншого методу і зіпсувати зі стеком викликів. Компілятор повинен нести відповідальність за те, щоб заборонити безглузді потоки управління.

Java намагалася "вирішити" цю проблему, gotoповністю відмовившись . Однак Java дозволяє використовувати returnвсередині finallyблоку і, таким чином, викликати випадкове проковтування винятку. Ця ж проблема все ще існує: компілятор не виконує своєї роботи. Видалення gotoз мови не виправило.

У C #, gotoє таким же безпечним , як break, continue, try/catch/finallyі return. Це не дозволяє використовувати неініціалізовані змінні, не дозволяє вистрибнути з остаточного блоку тощо. Компілятор поскаржиться. Це тому, що вона вирішує справжню проблему, яка, як я вже сказав, безглуздий контроль потоку. gotoне магічно скасовує аналіз визначеного призначення та інші розумні перевірки компілятора.


Ваша думка в тому, що C # 's gotoне є злом? Якщо так, то це стосується кожної сучасної мови, що використовується звичайно, з gotoконструкцією, а не тільки C # ...
lvella

9

Так. Коли ваші петлі вкладені на кілька рівнів глибоко, gotoце єдиний спосіб елегантного прориву з внутрішньої петлі. Інший варіант - встановити прапор і вийти з кожного циклу, якщо цей прапор задовольняє умові. Це справді некрасиво і досить схильне до помилок. У цих випадках gotoпросто краще.

Звичайно, мітка Java, що робить мітку, breakробить те саме, але не дозволяє переходити до довільної точки коду, яка акуратно вирішує проблему, не дозволяючи тим, що приносять gotoшкоду.


Комусь хочеться пояснити сутичку? Це була цілком коректна відповідь на питання ...
Chinmay Kanchi

4
Гото ніколи не є елегантною відповіддю. Коли ваші петлі вкладені декількома рівнями в глибину, ваша проблема полягає в тому, що вам не вдалося сконструювати код, щоб уникнути багатонаправлених циклів. Процедурні виклики НЕ ворога. (Прочитайте статті "Лямбда: Остаточний ...".) Використання goto, щоб вирватися з петель, вкладених на кілька рівнів глибоко, накладає пов'язку на відкритий множинний перелом: це може бути просто, але це не правильно відповідь.
Джон Р. Стром

6
І звичайно, ніколи не буває дивного випадку, коли викликаються глибоко вкладені петлі? Бути хорошим програмістом не лише в тому, щоб дотримуватися правил, а й у тому, щоб знати, коли їх порушувати.
Chinmay Kanchi

2
@ JohnR.Strohm Ніколи не кажи ніколи. Якщо ви використовуєте такі терміни , як «ніколи» в програмуванні, ви явно не розглядається кутовими випадками, оптимізація, контрсили ресурсів і т.д. У глибоко вкладених циклах, Гото дуже дійсно найчистіший, елегантний спосіб виходу з петлі. Не має значення, скільки ви відремонтували свій код.
Sujay Phadke

@ JohnR.Strohm: Друга найбільш упущена функція щодо переходу з VB.NET на C #: Doцикл. Чому? Тому що я можу змінити один цикл, який потребує глибокого перерви в Doцикл і набрати, Exit Doа його немає GoTo. Вкладені петлі трапляються постійно. Розбиття стеків трапляється деякий% часу.
Джошуа

7

Більша частина розчарувань надходить із свого роду "релігії", яка була створена навколо Бога Джикстра, що переконливо на початку 60-х років про те, що вона має беззаперечну владу:

  • стрибати куди завгодно, в який би блок коду не був
    • функція не виконується з самого початку
    • петлі не виконуються з початку
    • пропущена ініціалізація змінної
  • стрибати з будь-якого блоку коду без можливого очищення.

Це не має нічого спільного з gotoтвердженням сучасних мов, існування яких обумовлено лише підтримкою створення кодових структур, окрім мови, що надається.

Зокрема, перша головна точка вище вже дозволена, а друга очищена (якщо ви gotoвийшли з блоку, стек розкручується належним чином і всі належні деструктори викликаються)

Ви можете посилатися на цю відповідь, щоб мати уявлення про те, як навіть код, не використовуючи goto, може бути нечитабельним. Проблема полягає не в тому, щоб перейти до себе, а в поганому використанні.

Я можу написати всю програму без використання if, просто for. Звичайно, він не буде добре читабельним, виглядає незграбним і зайво складним.

Але проблема не в цьому for. Це я.

Такі речі , як break, continue, throw, bool needed=true; while(needed) {...}і т.д., відзначаючи більш маскараду goto , щоб піти подалі від скімітаров цих Djikstrarian фанатиків, що -50 років після винаходу сучасного laguages- все ще хочуть , щоб їх укладений. Вони забули, про що говорив Джикстра, вони пам’ятають лише заголовок його записки (GOTO вважається шкідливим, і це навіть не було його назвою onw: це було змінено редактором), і звинувачувати і хитатися, бити і звинувачувати кожен контур, маючи ці 4 лист, розміщений в послідовності.

Настав 2011 рік: пора зрозуміти, що gotoзауваження, що стосується GOTOтвердження Джикстра, було переконливим.


1
"Такі речі, як зламати, продовжувати, кидати, потрібен бул = істина; в той час, як (потрібно) {...} і т. Д., Відзначають більше, ніж маскарад", "я не згоден з цим. Я б хотів, щоб "такі речі, як перерва тощо, є обмеженими можливостями ", або щось подібне Основна проблема Goto полягає в тому, що він зазвичай занадто потужний. Наскільки поточний гото є менш потужним, ніж Діккстра, зі мною ваша відповідь чудова.
Мухаммед Алкароурі

2
@MuhammadAlkarouri: Я повністю згоден. Ви щойно знайшли краще формулювання, щоб висловити саме мою концепцію. Я можу сказати, що таке обмеження іноді не може бути застосоване, і необхідне обмеження не в мові. Тоді більш «потужна» річ, менш спеціалізована, - це те, що дозволяє вам обійтись. Наприклад, Java break nнедоступна в C ++, отже goto escape, навіть якщо escapeне потрібно, щоб просто виходити з n-ple циклу.
Еміліо Гаравалья

4

Незвичайний сюди-сюди, доки він локальний для функції, рідко суттєво шкодить читабельності. Це часто приносить користь, звертаючи увагу на те, що в цьому коді відбувається щось незвичне, що вимагає використання дещо незвичної структури управління.

Якщо (локальні) готи значно завдають шкоди читабельності, то зазвичай це знак того, що функція, що містить goto, стала надто складною.

Останнім гото, який я вклав у фрагмент коду С, було створення пари перемикаючих петель. Це не вписується у звичайне визначення "прийнятного" використання goto, але функція в результаті вийшла значно меншою та зрозумілішою. Щоб уникнути гото, потрібно було б особливо безладно порушити DRY.


1
Відповідь на це найбільш жалюгідно викладено у старому слогані "Картопляні чіпси" кладуть "Ставка, що ти не можеш з'їсти лише одну". У вашому прикладі, у вас є дві "замикаючі" (що б це не означало, і я гадаю, що це не дуже) петлі, і гото випустив вам дешеве. А як щодо хлопця з технічного обслуговування, який повинен змінити цей код після того, як вас вдарив автобус? Гото збирається полегшити чи важче його життя? Чи потрібно переосмислити ці дві петлі?
Джон Р. Стром

3

Я думаю, що вся ця проблема була випадком гавкання неправильного дерева.

GOTO як такий не здається мені проблематичним, але, швидше, це дуже часто симптом фактичного гріха: код спагетті.

Якщо GOTO спричиняє велике перетинання ліній контролю потоку, то це погано, період. Якщо він не перетинає жодних ліній контролю потоку, це нешкідливо. У сірій зоні між нами є такі речі, як виправлення циклу, все ще є деякі мови, які не додали конструкцій, які б охоплювали всі законно-сірі випадки.

Єдиний випадок, коли я фактично використовую його протягом багатьох років, це випадок циклу, де точка прийняття рішення знаходиться в середині циклу. Вам залишається або дублюваний код, прапор або GOTO. Я вважаю рішення GOTO найкращим із трьох. Тут немає перетину ліній регулювання потоку, це нешкідливо.


Випадок, на який ви натякаєтеся, іноді називають "циклом і половиною" або "N плюс одна половина циклу", і це "відомий випадок використання для gotos, що стрибає в цикл (якщо мова дозволяє це; оскільки інший випадок, стрибки поза петлею посередині, як правило, тривіально без goto).
Конрад Рудольф

(На жаль, більшість мов забороняє стрибати в петлю.)
Конрад Рудольф

@KonradRudolph: Якщо ви реалізуєте "цикл" за допомогою GOTO, немає іншої структури циклу, в яку можна вступати.
Лорен Печтел

1
Я думаю про те goto, що кричав КОНТРОЛЬНИЙ ПОТОК ТУТ НЕ ПРИЄДНАЄТЬСЯ НОРМАЛЬНИХ СТРУКТУРНО-ПРОГРАММУЮЧИХ ПАТТЕРНІВ . Оскільки потоки управління, які відповідають структурованим моделям програмування, легше міркувати, ніж ті, які цього не роблять, потрібно намагатися використовувати такі шаблони, коли це можливо; якщо код відповідає таким шаблонам, не слід кричати, що це не так. З іншого боку, у випадках, коли код дійсно не відповідає таким шаблонам, кричати про це може бути краще, ніж робити вигляд, що код підходить, коли він насправді цього не робить.
supercat

1

Так, goto може використовуватися для використання досвіду розробника: http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

Однак так само, як і будь-який потужний інструмент (покажчики, багаторазове успадкування тощо), його потрібно дисциплінувати, використовуючи його. У прикладі, наведеному у посиланні, використовується PHP, який обмежує використання конструкції goto на тій самій функції / методі та відключає можливість перейти в новий блок управління (наприклад, цикл, оператор переключення тощо).


1

Залежить від мови. Наприклад, він все ще широко використовується в програмуванні Cobol. Я також працював над пристроєм Barionet 50, мова програмування якого є раннім діалектом BASIC, що, звичайно, вимагає використання Goto.


0

Я б сказав, що ні. Якщо ви виявите потребу в використанні GOTO, я буду обділятися, що потрібно змінити код.


3
Можливо, але ви не надали жодних міркувань
Casebash

Дійкстра виклав міркування детально десятиліття тому.
Джон Р. Стром

2
Просто догма. Ніяких резонів. Примітка: Djikstra НЕ БІЛЬШЕ ВІДПОВІДНИЙ ЗАСТОСУВАННЯ: він посилався на ОСНОВНЕ або FORTRAN GOTO заяву початку 60-х. Вони не мають нічого спільного з справді анемічними (порівняно з силою тих) готів сьогодні.
Еміліо Гаравалья

0

gotoможе бути корисним при перенесенні застарілого коду асемблера до C. У першому випадку перетворення інструкції за інструкцією на C, використовуючи gotoяк заміну інструкції асемблера branch, може забезпечити дуже швидке перенесення.


-1

Я б заперечив "ні". Їх завжди слід замінити на інструмент, більш конкретний для проблеми, з якою використовується гото (якщо це не доступно на вашій мові). Наприклад, заяви про перерви та винятки вирішують раніше вирішені goto проблеми усунення циклу та обробку помилок.


Я б заперечував, що коли мови мають обидві ці особливості, gotoвони зазвичай відсутні у цих мовах.
Chinmay Kanchi

Але це тому, що вони їм не потрібні, або тому, що люди думають, що вони їм не потрібні?
Майкл К

Ваші погляди є неоднозначними. Існує багато випадків, коли gotoнайближча до семантики доменної проблематики річ, все інше було б брудним злом.
SK-логіка

@ SK-логіка: Я не думаю, що слово "фанатичне" означає те, що ви думаєте, що воно означає.
Кіт Томпсон

1
@Keith, "нетерпимо відданий своїй власній думці та забобонам" - ось що я послідовно спостерігаю тут, з цією долею з готуючими очима. "Потрібно завжди замінювати" - це принаймні слова фанатика. Нічого, близького до належної, обгрунтованої думки, а просто чиста фанатичність. Це "завжди" не залишає місця для альтернативної думки, бачите?
SK-логіка
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.