GOTO як і раніше вважається шкідливим? [зачинено]


282

Всім відомо про Дейкстри Листи до редакції: перейти на заяву вважається шкідливим (також тут .html транскрипта і тут .pdf) , і там був грізний поштовх з того часу , щоб відмовитися від заяви Гото щоразу , коли це можливо. Хоча можна використовувати goto для створення нездійсненного, розповсюдженого коду, проте він залишається в сучасних мовах програмування . Навіть вдосконалену структуру управління продовженням у схемі можна охарактеризувати як досконалу гото.

Які обставини обумовлюють використання goto? Коли найкраще уникати?

Як відповідне запитання: C надає пару функцій, setjmp та longjmp, які забезпечують можливість переходу не тільки в поточний кадр стека, але і в будь-який з кадрів виклику. Чи слід вважати їх такими ж небезпечними, як гото? Більш небезпечний?


Сам Дейкстра пошкодував того титулу, за який не відповідав. Наприкінці EWD1308 (також тут .pdf) він написав:

Нарешті коротка історія для запису. У 1968 році повідомленнями ОСБ було опубліковано мій текст під назвою " Заява Goto вважається шкідливою ", на яку в наступні роки найчастіше посилаються, на жаль, часто автори, які бачили не більше, ніж його заголовок, який став наріжним каменем моєї слави, ставши шаблоном: ми побачимо всілякі статті під заголовком "X вважаються шкідливими" майже для будь-якого X, включаючи одну під назвою "Dijkstra вважається шкідливою". Але що сталося? Я подав документ під заголовком " Справа проти заяви goto", який, щоб пришвидшити його публікацію, редактор змінив на" лист до редактора ", і в процесі цього він дав йому нову назву власного винаходу! Редактором був Ніклаус Вірт.

Добре продуманий класичний документ на цю тему, який повинен відповідати темі Дійкстри, - це Структурне програмування з переходом на заяви Дональда Е. Кнута. Читання обох допомагає відновити контекст та не догматичне розуміння теми. У цій роботі повідомляється думка Дійкстри щодо цієї справи і є ще сильнішою:

Дональд Е. Кнут: Я вважаю, що, висловлюючи таку точку зору, я насправді різко не погоджуюся з ідеями Дейкстри, оскільки він нещодавно написав таке: "Будь ласка, не потрапляйте у пастку вважати, що я страшенно догматичний щодо [ перейдіть до висловлювання]. У мене незручно відчувати, що інші роблять із цього релігію, ніби концептуальні проблеми програмування можна вирішити єдиним фокусом, простою формою дисципліни кодування! "


1
Гото C # '- це не те саме, про яке говорив Дійкстра, саме з тих причин, про які розповідав Дайкстра. Це гото є і шкідливим, як і тоді, але й набагато менш необхідним, оскільки сучасні мови надають альтернативні структури управління. C # goto надзвичайно обмежений.
jalf

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

28
Якщо у вас є вкладений цикл на 4 глибині (не те, що це добре), для виходу з усіх потрібні встановлення тимчасових значень. Тут гото набагато зрозуміліше, і IDE повинен легко показати, де знаходиться гото. З цього приводу використання goto має бути рідким, і, на мою думку, рухайтеся лише до пропуску коду
simendsjo

7
Пропоную прочитати дев'ять тисяч і одну тему з тегами goto.
Матті Вірккунен

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

Відповіді:


184

Наступні твердження - це узагальнення; Хоча завжди можна посилатися на виняток, це, зазвичай, на мій досвід та скромну думку, не варто.

  1. Необмежене використання адрес пам’яті (або GOTO, або нераціональних покажчиків) надає занадто багато можливостей, щоб легко уникнути помилок.
  2. Чим більше способів дійти до певного "місця" в коді, тим менш впевнено можна говорити про те, яким є стан системи в цей момент. (Дивись нижче.)
  3. Структуроване програмування IMHO менше стосується "уникнення GOTO" та більше про те, щоб структура коду відповідала структурі даних. Наприклад, структура даних, що повторюється (наприклад, масив, послідовний файл тощо), природно обробляється повторною одиницею коду. Маючи вбудовані структури (наприклад, поки, до, до, для кожного тощо), програміст дозволяє уникнути загрози повторення одних і тих самих шаблонів кодів.
  4. Навіть якщо GOTO є детальною інформацією про реалізацію на низькому рівні (це не завжди так!), Це нижчий рівень, про який повинен думати програміст. Скільки програмістів балансують своїми особистими чековими книжками у сирому двійковому? Скільки програмістів турбуються про те, який сектор на диску містить певний запис, а не просто надавати ключ до двигуна бази даних (і скільки способів може піти не так, якщо ми справді написали всі наші програми з точки зору фізичних дискових секторів)?

Виноски до вищезазначених:

Щодо пункту 2, врахуйте наступний код:

a = b + 1
/* do something with a */

У пункті "зроби щось" у коді, ми можемо з високою впевненістю заявити, що aбільше, ніж b. (Так, я ігнорую можливість переповнення цілого цілого числа. Не давайте простий приклад.)

З іншого боку, якщо код читав так:

...
goto 10
...
a = b + 1
10: /* do something with a */
...
goto 10
...

Безліч способів потрапити до позначки 10 означає, що нам доводиться працювати набагато важче, щоб бути впевненими у відносинах між aі bв цей момент. (Насправді, у загальному випадку це невирішене!)

Щодо пункту 4, усе поняття «кудись піти» в коді - це лише метафора. Ніщо насправді не «збирається» ніде всередині процесора, крім електронів і фотонів (для відпрацьованого тепла). Іноді ми відмовляємось від метафори іншої, більш корисної, однієї. Я пригадую, що зустрічав (кілька десятиліть тому!) Мову де

if (some condition) {
  action-1
} else {
  action-2
}

було реалізовано на віртуальній машині шляхом компіляції дій-1 та дії-2 як позапрограмних параметри без параметри, а потім з використанням одного двоаргументального коду VM, який використовував булеве значення умови для виклику того чи іншого. Концепція полягала в тому, щоб просто "вибрати, на що звернутися зараз", а не "йти сюди чи йти туди". Знову лише зміна метафори.


2
Хороший момент. У мовах вищого рівня goto насправді навіть нічого не означає (розглянемо стрибки між методами на Java). Функція Haskell може складатися з одного виразу; спробуйте вискочити з цього з гото!
Механічний равлик

2
Постскрипт працює як ваш приклад точки 4.
luser droog

Smalltalk працює аналогічно прикладу пункту 4, якщо під "аналогічно" ви маєте на увазі "нічого подібного до процедурних мов". : P Немає контролю потоку в мові; всі рішення обробляються за допомогою поліморфізму ( trueі falseбувають різного типу), і кожна гілка if / else - це в основному лямбда.
cHao

Це дійсні пункти, але врешті-решт вони просто повторюють, наскільки поганим може бути "якщо неправомірно використати". Перерва, продовження, вихід, повернення, gosub, settimeout, глобальний, включення тощо - все це сучасні методи, які вимагають розумового відстеження потоку речей, і всі вони можуть бути неправильно використані для створення коду спагетті та пропускання навколо, щоб створити невизначеність змінних станів. Якщо бути справедливим, хоча я ніколи не бачив поганого використання Goto з перших рук, я також бачив лише один раз або два рази. Це говорить про твердження, що завжди можна використовувати кращі речі.
Beejor

gotoсучасною мовою програмування (Перейти) stackoverflow.com/a/11065563/3309046 .
nishanths

245

GOTO Комікс XKCD

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

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


41
GOTO може зробити "стрибок" з одного довільного місця на інше довільне місце. Велоцираптор стрибав сюди нізвідки!
rpattabi

29
я не вважаю жарт смішним взагалі, тому що всі знають, що ви також повинні зв’язатись, перш ніж ви зможете виконати.
Кінджал Діксіт

31
Ваш колега помиляється, і, очевидно, не читав статті Кнут.
Джим Балтер

27
Я не бачив кінця змушеним і іншим чином перекрученим кодом протягом багатьох років, що використовувався лише для того, щоб уникнути гото. @jimmcKeeth, Мультфільм xkcd, наведений вище, не встановлює, що Goto є слабким. Це висміює істерику навколо її використання.

4
Ви розумієте, що вихідний код бібліотеки Delphi містить оператори goto. І це відповідне використання гото.
Девід Геффернан

131

Іноді справедливо використовувати GOTO як альтернативу обробці винятків в межах однієї функції:

if (f() == false) goto err_cleanup;
if (g() == false) goto err_cleanup;
if (h() == false) goto err_cleanup;

return;

err_cleanup:
...

Здається, що COM-код потрапляє в цю схему досить часто.


29
Я погоджуюся, є законні випадки використання, коли goto може спростити код і зробити його більш читабельним / ремонтопридатним, але, здається, існує якась готофобія, яка плаває навколо ...
Поп Каталін

48
@Bob: важко перемістити код err_cleanup в підпрограму, якщо він очищає локальні змінні.
Нікі

4
Власне, я використовував це в COM / VB6 лише тому, що не мав альтернативи, не тому, що це була альтернатива. Наскільки я щасливий в наші дні з спробою / ловити / нарешті.
Rui Craveiro

10
@ user4891 Ідіоматичний спосіб C ++ не пробувати {} catch () {очищення; }, а скоріше, RAII, де ресурси, які потрібно очистити, робляться так у деструкторах. Кожен конструктор / деструктор управляє рівно одним ресурсом.
Девід Стоун

5
Є два способи написати це на C без готу; і обидва значно коротші. Або: якщо (f ()) якщо (g ()) якщо (h ()) повернути успіх; прибирати(); відмова повернення; або: якщо (f () && g () && h ()) повернути успіх; прибирати(); відмова повернення;
LHP

124

Я можу згадати лише використання goto один раз. У мене була серія з п'яти вкладених петель, і мені потрібно було мати можливість вирвати всю структуру зсередини на основі певних умов:

for{
  for{
    for{
      for{
        for{
          if(stuff){
            GOTO ENDOFLOOPS;
          }
        }
      }
    }
  }
}

ENDOFLOOPS:

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

Жоден велоциратор на мене не напав.


83
"Refactor - це у функцію та замінити goto на return :)", а різниця? насправді в чому різниця? також не повернутись? Повернення також гальмує структурований потік, як це робить goto, і в цьому випадку вони роблять це так само (навіть якщо goto можна використовувати для гірших речей)
Pop Catalin

38
Вклавши безліч петель, як правило, кодовий запах, все, що у нього є. Якщо ви не робите, наприклад, 5-мірне множення масиву, важко уявити ситуацію, коли деякі внутрішні петлі неможливо було б корисно витягти на більш дрібні функції. Як і всі правила, існує певна кількість винятків.
Дуг МакКлін

5
Заміна повернення працює лише в тому випадку, якщо ви використовуєте мову, яка підтримує повернення.
Лорен Печтел

31
@leppie: Покоління, яке повстало проти gotoта надавало нам структуроване програмування, також відхилило дострокове повернення, з тієї ж причини. Це зводиться до того, як читається код, наскільки чітко він виражав наміри програміста. Створення функції з іншою метою, ніж уникнення використання злоякісного ключового слова, призводить до поганої згуртованості: вилікування гірше від захворювання.
Грязь

21
@ButtleButkus: Чесно кажучи, це так само погано, якщо не гірше. Принаймні з символом a gotoможна чітко вказати ціль. З break 5;, (1) я повинен рахувати закриття циклу, щоб знайти місце призначення; і (2) якщо структура циклу колись змінюється, можливо, може знадобитися змінити це число, щоб зберегти місце призначення правильним. Якщо я збираюсь уникати goto, тоді виграш повинен полягати в тому, що не потрібно вручну відстежувати такі речі.
cHao

93

Ми вже мали цю дискусію, і я відстоюю свою точку зору .

Крім того, я ситий з людьми , що описують структури мови вищого рівня , як « gotoзамаскованим» , тому що вони явно не отримали точку на всіх . Наприклад:

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

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

Так само, сказати, що "GOTO - це інструмент, і як усі інструменти, якими можна користуватися та ними зловживати", це абсолютно не вдається. Жоден сучасний будівельний робітник не використовував би скелю і стверджував, що "це інструмент". Скелі були замінені молотками. gotoбуло замінено структурами управління. Якби будівельник опинився в природі без молотка, він, звичайно, використовував би скелю. Якщо програмісту доводиться використовувати нижчу мову програмування, яка не має функції X, ну, звичайно, вона може використовувати її gotoзамість цього. Але якщо вона використовує її деінде замість відповідної мовної функції, вона явно не зрозуміла належну мову та використовує її неправильно. Це насправді так просто.


82
Звичайно, правильне використання скелі не є молотом. Одним із його правильних застосувань є шліфувальний камінь або для заточення інших інструментів. Навіть низькоросла скеля при правильному використанні є хорошим інструментом. Ви просто повинні знайти правильне використання. Те саме стосується і гото.
Кіббі

10
Отже, яке правильне використання Goto? Для кожного можливого випадку є ще один інструмент, який краще підходить. І навіть ваш шліфувальний камінь насправді замінений високотехнологічними інструментами в наші дні, навіть якщо вони все ще виготовлені з скелі. Існує велика різниця між сировиною та інструментом.
Конрад Рудольф

8
@jalf: Goto, безумовно , існує в C #. Дивіться stackoverflow.com/questions/359436/…
Джон Скіт

51
Мені страшно, що багато людей схвалюють цю посаду. Ваш пост здавався ефективним лише тому, що ви ніколи не намагалися запитати, яку логіку ви насправді виконуєте, таким чином, ви не помітили вашої помилки. Дозвольте перефразовувати весь ваш пост: "Існує чудовий інструмент для гото в будь-якій ситуації, тому готос ніколи не слід використовувати". Це логічно двозначно, і як такий весь ваш пост, по суті, задає питання "Як ти знаєш, що в будь-якій ситуації є найкращий інструмент для гота?"
Кодування зі стилем

10
@ Кодування: Ні, ви повністю пропустили суть публікації. Це був удар у відповідь , а не ізольований, повний аргумент. Я просто вказав на помилковість у головному аргументі «за» goto. Ви маєте рацію в тому, що я не пропоную аргумент проти gotoсебе, - я не мав наміру - тому немає жодних питань.
Конрад Рудольф

91

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

Goto може бути приємним для державних машин. Оператор перемикання в циклі є (в порядку типового значення): (а) насправді не є представником потоку управління, (б) негарним, (в) потенційно неефективним, залежно від мови та компілятора. Таким чином, ви закінчуєте писати одну функцію на стан і виконувати такі дії, як "повернути NEXT_STATE;" які навіть схожі на гото.

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

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

setjmp / longjmp може бути приємним для впровадження винятків або поведінки, що нагадує виняток. Незважаючи на загальну оцінку, винятки, як правило, вважаються "дійсною" структурою управління.

setjmp / longjmp є "небезпечнішими", ніж goto, в тому сенсі, що їх важче правильно використовувати, не маючи на увазі зрозуміло.

Ніколи не було, і ніколи не буде жодної мови, на якій написати поганий код найменш складно. - Дональд Кнут.

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

Далі це будуть "покажчики, які вважаються шкідливими", потім "типи качок вважаються шкідливими". Тоді хто залишиться захищати вас, коли вони прийдуть забрати вашу небезпечну конструкцію програмування? Так?


14
Особисто це такий коментар, на який я би дав чек. Одне, що я хотів би зазначити читачам, це те, що езотеричний термін "державні машини" включає такі повсякденні речі, як лексичні аналізатори. Перевірте вихід лексу колись. Повне готос.
ТЕД

2
Ви можете використовувати оператор перемикання всередині циклу (або обробника подій), щоб зробити державні машини просто чудовими. Я робив багато державних машин, не використовуючи jmp або goto.
Скотт Вітлок

11
+1 Ці стрілки на державних машинах відображаються до "goto" ближче, ніж до будь-якої іншої керуючої структури. Звичайно, ви можете використовувати перемикач всередині циклу - так само, як ви можете використовувати купу готів замість часу для інших проблем, але зазвичай це ідея; в чому вся суть цієї дискусії.
Едмунд

2
Чи можу я процитувати вас за останнім абзацом?
Кріс Луц

2
І ось, у 2013 році ми вже потрапили (і начебто минуло) фазу "покажчиків, які вважаються шкідливими".
Isiah Meadows

68

У Linux: Використання goto У коді ядра на пастці Kernel відбувається дискусія з Лінусом Торвальдсом та "новим хлопцем" про використання GOTO в коді Linux. Там є дуже хороші моменти, і Лінус одягнений у цю звичну зарозумілість :)

Деякі уривки:

Лінус: "Ні, вам промили мозок люди з CS, які думали, що Ніклаус Вірт насправді знає, про що він говорить. Він цього не зробив. У нього немає химерної підказки".

-

Лінус: "Я думаю, що гото - це добре, і вони часто легше читати, ніж великі відступи".

-

Лінус: "Звичайно, на дурних мовах, як Pascal, де мітки не можуть бути описовими, goto's може бути поганим".


9
Це хороший момент, як? Вони обговорюють його використання в мові, яка не має нічого іншого. Коли ви програмуєте у складанні, всі гілки та стрибки - це готові . І C є, і був, "переносною мовою складання". Більше того, уривки, які ви цитуєте, нічого не говорять про те, чому він вважає, що гото - це добре.
jalf

5
Ого. Це прикро читати. Ви б могли подумати, що такий великий хлопець з ОС, як Лінус Торвальдс, знав би краще, ніж сказати це. Паскаль (старий шкільний паскаль, а не сучасна версія Object) - це те, про що писав Mac OS Classic протягом 68-ти періодів, і це була найдосконаліша операційна система свого часу.
Мейсон Уілер

6
@mason Classic Mac OS мав декілька бібліотек Pascal (врешті-решт, час виконання Pascal зайняв занадто багато пам’яті на початку Macs), але більшість основних кодів було написано в Assembler, зокрема графіки та підпрограми інтерфейсу користувача.
Джим Дові

30
Лінус лише стверджує (явно, як Рик ван Ріель у цій дискусії) про те, щоб перейти до статусу виходу, і він робить це виходячи зі складності, яку альтернативні конструкції С принесуть, якби їх замість цього використали.
Чарльз Стюарт

21
ІМХО Лінус прав у цьому питанні. Його сенс полягає в тому, що код ядра, написаний на C, який повинен реалізувати щось подібне до обробки винятків, найбільш чітко і просто записується за допомогою goto. Ідіома goto cleanup_and_exitє одним з небагатьох «хороших» використання Гото залишилося тепер у нас є for, whileі ifуправляти нашим потоком управління. Дивіться також: programmers.stackexchange.com/a/154980
steveha

50

В C gotoпрацює лише в межах поточної функції, яка має тенденцію до локалізації будь-яких потенційних помилок. setjmpі longjmpнабагато небезпечніші, будучи немісцевими, складними та залежними від впровадження. Однак на практиці вони занадто незрозумілі та нечасті, щоб викликати багато проблем.

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

3420 IF A > 2 THEN GOTO 1430

Тут Лінус описує відповідне використання goto: http://www.kernel.org/doc/Documentation/CodingStyle (глава 7).


7
Коли вперше з'явився BASIC, альтернативи GOTO nnnn та GOSUB mmmm як способів стрибнути навколо не було. Структуровані конструкції були додані пізніше.
Джонатан Леффлер

7
Вам не вистачає суті ... навіть тоді вам не довелося писати спагетті ... ваші GOTO завжди можна використовувати дисципліновано
JoelFan

Варто також зазначити, що поведінка setjmp/ longjmpбула визначена лише тоді, коли вони використовувались як засіб для стрибків на місце в межах, з інших місць, що знаходяться в тій же області. Після того, як контроль покине область, де setjmpвиконується, будь-яка спроба використання longjmpна створеній структурі setjmpпризведе до невизначеної поведінки.
supercat

9
Деякі версії BASIC дозволять вам це зробити GOTO A * 40 + B * 200 + 30. Не важко зрозуміти, як це було дуже зручно і дуже небезпечно.
Джон Ханна

1
@Hjulle він обчислить вираз, а потім перейде до рядка коду з цим числом (явні номери рядків були вимогою більшості попередніх діалектів). ZX Spectrum Basic був тим, хто це погодиться
Джон Ханна

48

Сьогодні важко зрозуміти велику справу щодо цього GOTOтвердження, оскільки "структуроване програмування" в основному виграло дебати, і сьогоднішні мови мають достатню структуру потоків контролю, щоб цього не було GOTO.

Підрахуйте кількість gotos у сучасній програмі C. Тепер додайте число break, continueі returnзаяв. Крім того, додайте кілька разів ви використовуєте if, else, while, switchабо case. Ось про те, скільки GOTOу вас було б програми, якби ви писали в FORTRAN або BASIC в 1968 році, коли Дайкстра написав свій лист.

Мовам програмування на той час бракувало контрольного потоку. Наприклад, в оригіналі Dartmouth BASIC:

  • IFзаяви не було ELSE. Якщо ви хотіли його, вам потрібно було написати:

    100 IF NOT condition THEN GOTO 200
    ...stuff to do if condition is true...
    190 GOTO 300
    200 REM else
    ...stuff to do if condition is false...
    300 REM end if
    
  • Навіть якщо вашій IFзаяві не потрібна була ELSE, вона все одно обмежувалася одним рядком, який зазвичай складався з GOTO.

  • Не було DO...LOOPзаяви. Для не FORциклів вам довелося закінчити цикл явним GOTOабо IF...GOTOназад до початку.

  • Не було SELECT CASE. Вам довелося користуватися ON...GOTO.

Таким чином, ви в кінцевому підсумку з великим з GOTOз у вашій програмі. І ви не могли залежати від обмеження GOTOs в межах однієї підпрограми (оскільки це GOSUB...RETURNбула така слабка концепція підпрограм), тож вони GOTOмогли переходити куди завгодно . Очевидно, це змусило керувати потоком управління важко.

Звідси походить антирух GOTO.


Ще слід зазначити, що кращим способом написання коду, якби в коді був якийсь код, який потрібно було б виконувати рідко, 420 if (rare_condition) then 3000// 430 and onward: rest of loop and other main-line code// 3000 [code for rare condition]// 3230 goto 430. Введення коду таким чином дозволяє уникнути будь-яких взятих гілок або стрибків у загальному випадку основного рядка, але це ускладнює виконання. Уникнення відгалужень у коді збірки може бути гіршим, якщо деякі гілки обмежені, наприклад, +/- 128 байтами, а іноді не мають додаткових пар (наприклад, "cjne" існує, але не є "ce").
supercat

Я колись написав якийсь код для 8x51, який мав перерву, яка працювала один раз на 128 циклів. Кожен додатковий цикл, витрачений у загальній справі ISR, зменшив би швидкість виконання основного коду більш ніж на 1% (я думаю, що приблизно 90 циклів із 128 зазвичай були доступні для основної лінії), і будь-яка інструкція з розгалуження зайняла б два цикли ( і у випадках розгалуження, і у випадках пропускання). Код мав два порівняння - одне, яке зазвичай було б рівним; інший, не рівний. В обох випадках код рідкісного випадку був відсутній понад 128 байт. Отож ...
supercat

cjne r0,expected_value,first_comp_springboard/.../ cjne r1,unexpected_value,second_comp_fallthrough// `ajmp second_comp_target` // first_comp_springboard: ajmp first_comp_target// second_comp_fallthrough: .... Не дуже приємна схема кодування, але коли підраховують окремі цикли, такі речі роблять. Звичайно, в 60-х роках такі рівні оптимізації були важливішими, ніж сьогодні, тим більше, що сучасні процесори часто вимагають дивних оптимізацій, а системи, що складаються вчасно, можуть застосувати код оптимізації для процесорів, які не існували, коли код, про який йдеться, був написаний.
supercat

34

Go to може надати певний режим очікування для "реального" оброблення винятків у певних випадках. Поміркуйте:

ptr = malloc(size);
if (!ptr) goto label_fail;
bytes_in = read(f_in,ptr,size);
if (bytes_in=<0) goto label_fail;
bytes_out = write(f_out,ptr,bytes_in);
if (bytes_out != bytes_in) goto label_fail;

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

success=false;
do {
    ptr = malloc(size);
    if (!ptr) break;
    bytes_in = read(f_in,ptr,size);
    if (count=<0) break;
    bytes_out = write(f_out,ptr,bytes_in);
    if (bytes_out != bytes_in) break;
    success = true;
} while (false);

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

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


1
Хоча я схильний погоджуватися з тим, що ви тут говорите, факт, що breakвисловлювання знаходяться в контрольній структурі, чітко дає зрозуміти, що вони роблять. На gotoприкладі людина, яка читає код, повинна переглянути код, щоб знайти етикетку, яка теоретично може бути фактично перед структурою управління. Мені не вистачає досвіду роботи зі старим стилем C, щоб переконатися, що один, безумовно, кращий за інший, але в будь-якому випадку є компроміси.
Вівіан Рівер

5
@DanielAllenLangdon: Той факт, що breaks знаходяться всередині циклу, дає зрозуміти, що саме вони виходять із циклу . Це не "саме те, що вони роблять", адже насправді циклу взагалі немає! Ніщо в ньому ніколи не має шансів повторитись, але це не ясно до кінця. Той факт, що у вас є "цикл", який ніколи не виконується більше одного разу, означає, що керуючі структури зловживають. З gotoприкладом може сказати програміст goto error_handler;. Це більш чітко і навіть менш важко дотримуватися. (Ctrl + F, "error_handler:" щоб знайти ціль. Спробуйте це зробити з "}".)
cHao

1
Я колись бачив код, подібний до вашого другого прикладу, в системі управління повітряним рухом - тому що "goto не в нашому лексиконі".
QED

30

Дональд Е. Кнут відповів на це питання у книзі "Грамотне програмування", 1992 CSLI. На с. 17 є нарис " Структурне програмування з goto Statements " (PDF). Я думаю, що стаття може бути опублікована і в інших книгах.

У статті описано пропозицію Дейкстри та описано обставини, коли це справедливо. Але він також дає ряд зустрічних прикладів (проблеми та алгоритми), які неможливо легко відтворити, використовуючи лише структуровані петлі.

Стаття містить повний опис проблеми, історію, приклади та зустрічні приклади.


25

Перейшов до корисного.

Я розпочав програмування в 1975 р. До програмістів епохи 1970-х років слова "переходили до шкідливих" сказали більш-менш, що нові мови програмування з сучасними структурами управління варто спробувати. Ми пробували нові мови. Ми швидко конвертували. Ми ніколи не поверталися.

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

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

Слогани, які ніхто не розуміє, не дуже освітлюють. Напевно, найкраще забути такі гасла. Такі гасла не допомагають.

Однак саме цей слоган "Гото вважається шкідливим" взяв на себе неживе життя.

Чи не можна зловживати Гото? Відповідь: впевнений, але так що? Практично кожен елемент програмування може зловживати. Приниженим, boolнаприклад, зловживають частіше, ніж хтось із нас хотів би повірити.

Навпаки, я не можу згадати, як зустрічався з одним, фактичним випадком зловживань з Гото з 1990 року.

Найбільша проблема Goto, мабуть, не технічна, а соціальна. Програмісти, які не знають дуже багато, часом здаються, що знецінення goto робить їх здоровими. Можливо, вам доведеться час від часу задовольняти таких програмістів. Таке життя.

Найгірше, що сьогодні стосується goto, - це те, що він недостатньо використовується.


24

Залучив Джей Баллу, додавши відповідь, я додаю 0,02 фунта. Якби Бруно Раншаерт цього ще не робив, я б згадав статтю "Структуроване програмування з заявами GOTO" Кнута.

Одне, про що я не бачив обговорювати, - це такий тип коду, який, хоча і не зовсім поширений, викладався у текстових книгах Фортран. Такі речі, як розширений діапазон циклу DO та відкритих коду підпрограм (пам’ятайте, це був би Fortran II, або Fortran IV, або Fortran 66, а не Fortran 77 чи 90). Принаймні є ймовірність, що синтаксичні деталі неточні, але поняття повинні бути досить точними. У кожному випадку фрагменти знаходяться в одній функції.

Зауважимо, що відмінна, але вийшла з друку (і не надрукована) книга " Елементи стилю програмування, другий Едн " від Kernighan & Plauger включає деякі приклади реального життя зловживань GOTO з програмування текстових книжок своєї епохи (кінець 70-х). Наведений нижче матеріал не з цієї книги.

Розширений діапазон для циклу DO

       do 10 i = 1,30
           ...blah...
           ...blah...
           if (k.gt.4) goto 37
91         ...blah...
           ...blah...
10     continue
       ...blah...
       return
37     ...some computation...
       goto 91

Однією з причин такої дурниці була хороша старомодна перфокарта. Ви можете помітити, що мітки (добре не в послідовності, тому що це був канонічний стиль!) Знаходяться у колонці 1 (насправді вони повинні були бути у колонках 1-5), а код - у стовпцях 7-72 ​​(колонка 6 була продовженням стовпчик маркера). Стовпцям 73-80 буде надано порядковий номер, і були машини, які сортували колоди перфокарт у порядку послідовності номерів. Якщо у вас була програма на послідовно встановлених картах і вам потрібно було додати кілька карток (рядків) в середину циклу, вам доведеться повторно заново все після цих додаткових рядків. Однак якщо ви замінили одну картку на GOTO, ви могли б уникнути повторної повторної переробки всіх карт - ви просто підклеїли нові картки в кінці розпорядку новими порядковими номерами. Вважайте це першою спробою "зелених обчислень"

О, ви також можете зауважити, що я обманюю і не кричу - Фортран IV був написаний у великому регістрі нормально.

Підпрограма з відкритим кодом

       ...blah...
       i = 1
       goto 76
123    ...blah...
       ...blah...
       i = 2
       goto 76
79     ...blah...
       ...blah...
       goto 54
       ...blah...
12     continue
       return
76     ...calculate something...
       ...blah...
       goto (123, 79) i
54     ...more calculation...
       goto 12

GOTO між мітками 76 та 54 - це версія обчисленого goto. Якщо змінна i має значення 1, перейдіть до першої мітки у списку (123); якщо воно має значення 2, перейдіть до другого тощо. Фрагмент від 76 до обчисленої готи є відкритою кодовою підпрограмою. Це був фрагмент коду, виконаний на зразок підпрограми, але записаний у тілі функції. (У Fortran також були функції оператора - які були вбудованими функціями, які вміщувались в один рядок.)

Були гірші конструкції, ніж обчислений goto - ви можете призначити мітки змінним, а потім використовувати призначений goto. Гуглінг, призначений goto, говорить мені, що його було видалено з Fortran 95. Крейдаю один за революційну структуровану програмування, яка, як можна сказати, розпочала публічно з листа або статті "Дійкстра", що вважається шкідливою ".

Не маючи певних знань про те, що було зроблено у Фортран (та іншими мовами, більшість з яких справедливо впала в сторону), нам, новачкам, важко зрозуміти масштаб проблеми, з якою вирішував Дайкстра. Хек, я не почав програмувати лише через десять років після опублікування цього листа (але у мене було нещастя програмувати у Fortran IV на деякий час).


2
Якщо ви хочете побачити приклад якогось коду, що використовує goto"в дикій природі", у питанні Wanted: Робочий алгоритм сортування Bose-Hibbard вказується деякий (Algol 60) код, опублікований у 1963 році. Оригінальний макет не порівнюється із сучасними стандартами кодування. Уточнений (відступний) код все ще досить непереборний. У gotoвідомості там роблять зробити (дуже) важко зрозуміти , що алгоритм є.
Джонатан Леффлер

1
Будучи занадто молодим, щоб не відчути нічого, близького до перфокарт, було читати читати про проблему повторного штампування. +1
Qix - MONICA ПОМИЛИЛИ

20

Немає таких речей, як GOTO вважався шкідливим .

GOTO - це інструмент, і як і всі інструменти, ним можна користуватися та зловживати .

Однак існує багато інструментів у світі програмування, які мають тенденцію до зловживань більше, ніж використання , і GOTO - один із них. З констатацією Delphi це зовсім інша.

Особисто я не використовую ні типового коду , але я мав незвичне використання як GOTO, так і З, що було гарантовано, і альтернативне рішення містило б більше коду.

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

Це як сказати дітям не бігати ножицями . Ножиці непогані, але використання їх, можливо, не найкращий спосіб зберегти здоров'я.


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

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

Навіть якщо gotoключове слово було вичищене з C #, концепція "стрибати сюди" все ще існує в IL. Нескінченний цикл легко побудувати без ключового слова goto. Якщо ця відсутність гарантії того, що викликаний код повернеться, на вашу думку, означає "не в змозі міркувати про код", то я б сказав, що ми ніколи не мали такої можливості.
Лассе В. Карлсен

Ах, ви проникливо знайшли джерело нашого спілкування. gotoУ C # не є реальним «Готи» в первісному значенні цього слова. Це набагато слабша версія, яка дозволяє лише стрибати в межах функції. Сенс, у якому я використовую "goto", дозволяє стрибати з будь-якого місця. Тож, хоча C # має ключове слово "goto", воно, мабуть, ніколи не було, справжнього goto. Так, справжній goto доступний на рівні IL, таким же чином, як і коли будь-яка мова складається до монтажу. Але програміст захищений від цього, не може використовувати його за звичайних обставин, тому він не рахується.
Джонатан Хартлі

Між іншим, думка, яку я тут описую, спочатку не є моєю, але є ядром оригінального документа Дійкстри з 1967 року, я думаю, перейменованого його редактором "Гото вважається шкідливим", який став такою часто цитованою мемою на 50 років саме тому, що було настільки революційним, проникливим і загальновизнаним.
Джонатан Хартлі


17

Оскільки я почав робити декілька речей у ядрі Linux, gotos мене не турбує так сильно, як колись. Я спочатку жахнувся, побачивши, що вони (хлопці з ядра) додали готос в мій код. З тих пір я звик до використання gotos, в деяких обмежених контекстах, і тепер періодично буду використовувати їх сам. Як правило, це гото, яке стрибає до кінця функції, щоб зробити якесь очищення та витягнути, а не дублювати ту саму очистку та рятування в декількох місцях функції. І, як правило, це не щось досить велике, щоб передати іншу функцію - наприклад, звільнення деяких локально (k) змінених змінних є типовим випадком.

Я написав код, який використовував setjmp / longjmp лише один раз. Це було в програмі секвенсорних барабанних програм MIDI. Відтворення відбулося в окремому процесі від усієї взаємодії з користувачем, і процес відтворення використовував спільну пам'ять з процесом користувальницького інтерфейсу, щоб отримати обмежену інформацію, необхідну для відтворення. Коли користувач хотів зупинити відтворення, процес відтворення просто зробив longjmp "назад до початку", щоб почати спочатку, а не якесь складне розкручування, де б це не було виконано, коли користувач хотів його зупинити. Це працювало чудово, було просто, і я ніколи не мав жодних проблем або помилок, пов’язаних з ним у тому випадку.

setjmp / longjmp мають своє місце - але це місце, яке ви, швидше за все, не відвідаєте, але раз у дуже довгий час.

Редагувати: Я просто подивився на код. Фактично siglongjmp () я використовував, а не longjmp (не те, що це велика справа, але я забув, що siglongjmp навіть існував.)


15

Якщо ви пишете VM в C, виходить, що використовуючи (gcc) обчислені готи, як це:

char run(char *pc) {
    void *opcodes[3] = {&&op_inc, &&op_lda_direct, &&op_hlt};
    #define NEXT_INSTR(stride) goto *(opcodes[*(pc += stride)])
    NEXT_INSTR(0);
    op_inc:
    ++acc;
    NEXT_INSTR(1);
    op_lda_direct:
    acc = ram[++pc];
    NEXT_INSTR(1);
    op_hlt:
    return acc;
}

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


Єдина проблема, що це не стандарт, чи не так?
Кальмарій

&&op_incзвичайно не компілюється, тому що (зліва) &очікує значення, але (праворуч) &дає оцінку.
fredoverflow

7
@FredO: Це спеціальний оператор GCC. Однак я б відкинув цей код за всіх, окрім найважливіших обставин, тому що я, безумовно, не можу зрозуміти, що відбувається з WTF.
Щеня

Хоча це один приклад (максимальна оптимізація для швидкості), який виправдовує як використання goto, так і криптовалютний код, його все одно слід коментувати, щоб пояснити необхідність оптимізації, приблизно як це працює, і найкращий рядок за рядком коментарі, які можна зробити. Отже, вона все-таки виходить з ладу, а тому залишає програмістів технічного обслуговування в темряві. Але мені подобається приклад ВМ. Дякую.
MicroservicesOnDDD

14

Тому що gotoможна використовувати для плутаного метапрограмування

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

Це низькорівневий в тому сенсі , що Гото примітивна операція , яка реалізує щось більш високе , як whileі foreachчи що - то.

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

Отже, є прозаїчна і зла сторона goto.

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

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

Нарешті, щоб дійсно висвітлити цю тему, слід зазначити, що фактично всі ранні мови, за винятком Algol, спочатку складали лише окремі заяви, що підлягають їх версіям if-then-else. Отже, єдиний спосіб зробити умовний блок - це gotoнавколо нього за допомогою зворотного умовного. Божевільний, я знаю, але я прочитав кілька старих специфікацій. Пам’ятайте, що перші комп’ютери були запрограмовані у двійковому машинному коді, тому я припускаю, що будь-який HLL був рятівним рятувальником; Я здогадуюсь, вони не були надто прискіпливими до того, які саме характеристики HLL вони отримали.

Сказавши все, що я вкладав по одній gotoу кожну програму, я написав "просто для роздратування пуристів" .


4
+1 за роздратування пуристів! :-)
Лумі

10

Заперечувати використання заявою GOTO програмістам - це як сказати столяру не використовувати молоток, оскільки це може пошкодити стіну, коли він забиває цвях. Справжній програміст знає, як і коли використовувати GOTO. Я слідував за деякими з цих так званих «Структурованих програм», я бачу такий код Horrid, щоб уникнути використання GOTO, щоб я міг знімати програміста. Гаразд, на захист іншої сторони я бачив і справжній код спагетті, і знову, і тих програмістів теж слід знімати.

Ось лише один невеликий приклад коду, який я знайшов.

  YORN = ''
  LOOP
  UNTIL YORN = 'Y' OR YORN = 'N' DO
     CRT 'Is this correct? (Y/N) : ':
     INPUT YORN
  REPEAT
  IF YORN = 'N' THEN
     CRT 'Aborted!'
     STOP
  END

----------------------- АБО ----------------------

10:  CRT 'Is this Correct (Y)es/(N)o ':

     INPUT YORN

     IF YORN='N' THEN
        CRT 'Aborted!'
        STOP
     ENDIF
     IF YORN<>'Y' THEN GOTO 10

3
DO CRT "Це правильно? (Y / N): ': INPUT YORN UNTIL YORN =' Y 'OR YORN =' N '; пр.
joel.neely

5
Справді, але що ще важливіше, справжній програміст знає, коли не використовувати goto- і знає, чому . Уникаючи конструкції мови табу, оскільки так сказали $ programs_guru, це саме визначення вантажного культового програмування.
Пісквор вийшов з будівлі

1
Це хороша аналогія. Щоб забити цвях, не пошкодивши підкладку, молотком не усунути. Швидше за все, ви використовуєте простий інструмент, відомий як кулачок для нігтів. Це металевий штифт із конічним кінцем, що має на кінчику порожнистий відступ, щоб надійно з'єднатись з головкою нігтя (ці інструменти бувають різного розміру для різних нігтів). Інший, тупий кінець цвяха пробивають молотком.
Каз


7

Оригінальний папір слід розглядати як "Безумовний GOTO вважається шкідливим". Зокрема, це виступало за форму програмування, заснованого на умовних ( if) та ітеративних ( while) конструкціях, а не на тесті і стрибках, загальних для раннього коду. gotoвсе ще корисний у деяких мовах чи обставинах, де не існує відповідної структури управління.


6

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

Наприклад, якщо ви захоплюєте ресурси та використовуєте семафори чи мутекси, вам потрібно захопити їх у порядку, і ви завжди повинні випускати їх у зворотному порядку.

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

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

-Адам


5

Одне сучасне використання GOTO - компілятор C # для створення державних машин для обчислювачів, визначених поверненням урожайності.

GOTO - це те, що слід використовувати компіляторам, а не програмістам.


5
Хто саме, на вашу думку, створює компілятори?
tloach

10
Компілятори, звичайно!
Seiti

3
Я думаю, що він означає "GOTO - це те, що слід використовувати лише за кодом, виданим компілятором".
Саймон Бучан

Це справа проти goto. Там, де ми можемо використовувати gotoв кодованій державою машині, зашифрованої вручну, щоб реалізувати нумератор, ми можемо просто скористатися yieldнатомість.
Джон Ханна

увімкніть багато рядків (для запобігання компіляції до if-else) за допомогою компіляції регістрів за замовчуванням, щоб переключитися з оператором goto.
М.казем Акгарі

5

Поки C і C ++ (серед інших винуватців) не помітять перерви та продовжать, goto продовжуватиме виконувати свою роль.


Так мічений перерва чи продовження відрізнятиметься від goto як?
Матвій Віт

3
Вони не допускають абсолютно довільних стрибків у контрольному потоці.
DrPizza

5

Якби сам GOTO був злий, компілятори були б злі, оскільки вони генерують JMP. Якби стрибки в блок коду, особливо за вказівником, були за своєю суттю злі, інструкція RETurn була б злою. Скоріше, зло є в потенції до зловживань.

Часом мені доводилося писати програми, які повинні були відслідковувати ряд об’єктів, де кожен об’єкт повинен був дотримуватися складної послідовності станів у відповідь на події, але вся справа, безумовно, була однопоточною. Типовою послідовністю станів, якщо вона представлена ​​в псевдо-коді, буде:

request something
wait for it to be done
while some condition
    request something
    wait for it
    if one response
        while another condition
            request something
            wait for it
            do something
        endwhile
        request one more thing
        wait for it
    else if some other response
        ... some other similar sequence ...
    ... etc, etc.
endwhile

Я впевнений, що це не нове, але спосіб, яким я займався в C (++), - це визначити деякі макроси:

#define WAIT(n) do{state=(n); enque(this); return; L##n:;}while(0)
#define DONE state = -1

#define DISPATCH0 if state < 0) return;
#define DISPATCH1 if(state==1) goto L1; DISPATCH0
#define DISPATCH2 if(state==2) goto L2; DISPATCH1
#define DISPATCH3 if(state==3) goto L3; DISPATCH2
#define DISPATCH4 if(state==4) goto L4; DISPATCH3
... as needed ...

Потім (якщо стан спочатку 0) структурована машина стану вище перетворюється на структурований код:

{
    DISPATCH4; // or as high a number as needed
    request something;
    WAIT(1); // each WAIT has a different number
    while (some condition){
        request something;
        WAIT(2);
        if (one response){
            while (another condition){
                request something;
                WAIT(3);
                do something;
            }
            request one more thing;
            WAIT(4);
        }
        else if (some other response){
            ... some other similar sequence ...
        }
        ... etc, etc.
    }
    DONE;
}

З урахуванням цього варіанту можуть бути CALL і RETURN, тому деякі державні машини можуть діяти як підпрограми інших державних машин.

Це незвично? Так. Чи потрібне певне навчання з боку обслуговуючого персоналу? Так. Чи окупається це навчання? Я думаю так. Чи можна це зробити без GOTO, які стрибають у блоки? Ні.


1
Уникнення мовної функції із-за страху - ознака пошкодження мозку. Це теж ніжніше, ніж пекло.
Векторний Горгот

4

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

Це не варто.


Приємно про спробувати ... Ловіть блоки в C # - це те, що вони дбають про очищення стека та інших виділених ресурсів (так званих розмотування стека) як виняток, що перетворюється на обробник винятків. Це робить спробу ... Ловіть набагато краще, ніж Goto, тому якщо у вас є Try ... Ловіть, використовуйте його.
Скотт Вітлок

У мене є краще рішення: оберніть фрагмент коду, з якого ви хочете висказати, у пункті "do {...} while (0);" петля. Таким чином, ви можете вискочити таким же чином, як і goto, без накладних витрат циклу try / catch (я не знаю про C #, але в C ++ спроба недорога, а улов - це дорожнеча, тому здається надмірно кидати виняток там, де достатньо простого стрибка).
Джим Дові

10
Джім, проблема в тому, що це не що інше, як тупо обхідний спосіб отримання гото.
Кодування зі стилем

4

Я насправді змусив використовувати гото, тому що я буквально не міг придумати кращого (швидшого) способу написання цього коду:

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

SomeObject someObject;    

if (someObject.IsComplex())    // this test is trivial
{
    // begin slow calculations here
    if (result of calculations)
    {
        // just discovered that I could use the fast calculation !
        goto Fast_Calculations;
    }
    // do the rest of the slow calculations here
    return;
}

if (someObject.IsmediumComplex())    // this test is slightly less trivial
{
    Fast_Calculations:
    // Do fast calculations
    return;
}

// object is simple, no calculations needed.

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

Гюго


Не-GOTO-способом було б використання функції fast_calculations, яка спричиняє певні накладні витрати. Можливо, це не помітно в більшості обставин, але, як ви сказали, це було критично важливим.
Кайл Кронін

1
Ну це навряд чи дивно. Весь код, який має абсолютно божевільні рекомендації щодо ефективності роботи, завжди порушує всі найкращі практики. Найкращі практики - це доступність та ремонтопридатність, а не витіснення ще 10 мілісекунд або заощадження на 5 байт більше оперативної пам’яті.
Джонатан

1
@JonathonWisnoski, законне використання goto також усуває божевільні кількості коду спагетті, що втручається у гніздо змінних щурів, щоб слідкувати, куди ми йдемо.
vonbrand

4

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

Я особисто ніколи не використовую це прямо, ніколи не потрібно.


4

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

Розглянемо випадок багато вкладених циклів, де використання "goto" замість купи if(breakVariable)розділів очевидно ефективніше. Рішення "Ввести петлі у функцію та використовувати повернення" часто є абсолютно необгрунтованим. У ймовірному випадку, що петлі використовують локальні змінні, тепер вам доведеться пройти їх через параметри функції, потенційно обробляючи навантаження, які виникають із-за цього.

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

Тепер простір коду, використання стека та час виконання у багатьох ситуаціях можуть бути недостатньо важливими для багатьох програмістів, але коли ви знаходитесь у вбудованому середовищі, у якому працюєте лише 2 КБ кодового простору, 50 байт додаткових інструкцій, щоб уникнути чітко визначеного "Goto" просто сміється, і це не така рідкісна ситуація, як вважають багато програмістів високого рівня.

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


3

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.