«Спор возможен там, где истина закрыта. В бесплодных спорах можно бесконечно обсуждать, что в комнате находится закрытой дверью. Но стоит дверь открыть, и ясно станет всем и спорить не о чем, коль каждый истину увидеть сможет»

Владимир Мегре




Статья посвящается Зацепину П.М., выдающемуся инженеру Алтайского государственного университета, под чьим чутким руководством многие студенты, включая автора статьи, постигали магию инженерного творчества.

Введение


Спор о возможности использования в программах оператора GOTO ведётся уже очень давно (официальным его началом признана статья Дейкстры «О вреде оператора GOTO», опубликованная в 1968 году [2]). Через три года мы будем праздновать 50-летний юбилей этого спора. Это хороший повод, чтобы наконец-то «расставить все точки над i» и прекратить спор.

Цитата в эпиграфе выбрана неслучайно. Она в точности отражает текущую ситуацию в споре про GOTO. В нашем случае «комната за закрытой дверью» – это понятная всем постановка задачи. Пока, к сожалению, такой постановки задачи озвучено не было, поэтому споры и не угасают. Противоборствующие стороны спорят хоть и о схожих, но всё-таки о разных вещах, поэтому и не могут найти компромисса.

Давайте займём в этом споре нейтральную сторону, и беспристрастно во всём разберёмся. Рассмотрим доводы «противников» и «защитников» оператора GOTO и решим, «кто из них прав, а кто виноват».

Почему ведутся споры


Как уже было отмечено выше, споры о возможности использования в программах оператора GOTO ведутся из-за отсутствия понятной всем постановки задачи. Грубо говоря, одна из сторон доказывает, что дерево плавает, а другая, что кирпич тонет. Естественно, что при такой постановке каждая из сторон уверена в своей правоте и будет вечно её отстаивать.

Противники GOTO уповают на правила хорошего тона. Именно здесь и спрятан ключ от «закрытой двери», т.к. существуют, по крайней мере, три правила хорошего тона: «хороший тон в структурировании», «хороший тон в быстродействии» и «хороший тон в компактности», но противники GOTO учитывают только один из них.

Защитники GOTO уповают на требования заказчика, где, среди прочих не редко встречаются пункты, связанные с быстродействием и компактностью программы. При такой постановке одним правилом хорошего тона не обойтись – приходится искать компромиссное решение. В результате такого решения в программе иногда и появляется оператор GOTO.

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

Озвученная точка зрения весьма поверхностна, т.к. не учитывает деталей спора. Чтобы сформулировать объективную постановку задачи, необходимо рассмотреть аргументы и контраргументы каждой из сторон. Этим мы сейчас и займёмся. Жирные буквы З – это аргументы защитников GOTO, а жирные буквы П – это аргументы противников GOTO.

Доводы «противников» GOTO


1. Использование GOTO – плохой тон.
З: Это неаргументированное заявление, поэтому спорить здесь нет смысла.

2. Самый плохой тон – возвращение с помощью метки назад.
З: Действительно, так использовать GOTO нельзя, так же как нельзя его использовать и для перехода в другой блок области видимости – можно или оставаться в текущем, или выходить из него. Если следовать двум этим правилам, то использовать GOTO можно.

3. GOTO – избыточный оператор. Его легко можно заменить циклами и условиями.
З: Если на то пошло, то из языка можно выкинуть практически все операторы.

С точки зрения структурного программирования из языка можно вообще выкинуть все операторы, оставив только while и оператор присваивания. [1] В таком случае программы будут хоть и объёмными, но понятными. Если бы на практике внимание уделялось только структуре программы, то такой шаг был бы обоснованным, но в реальных задачах есть ещё требования на быстродействие и компактность, а этого одним оператором добиться невозможно.

GOTO – признак не кривизны кода, а кривизны языков, в которых без него порой никак (C, C++, C#, Pascal, Java, etc) и кривизны профанации под названием «структурное программирование» с его т.н. «циклами с предусловиями», «циклами с постусловиями» и «ветвлениями», которые являются не элементарными конструкциями, а типовыми паттернами, в которые задача не всегда удобно ложится.

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

4. Вирт и Дейкстра говорят, что GOTO это плохо. [2, 3]
З: Авторитетные мнения достойны внимания, но то, что говорят авторитеты, не есть истина в последней инстанции. Недаром в учёной среде бытует фраза «Если уважаемый учёный говорит, что «это сделать возможно», то он скорее всего прав, а если говорит, что «это сделать невозможно», то скорее всего не прав».

Есть и такие авторитеты, которые высказываются в пользу GOTO, например, Дональд Кнут [4], Фредрик Брукс. [5] Но при решении задачи более целесообразно опираться не на мнение авторитетов, а на здравый смысл.

5. GOTO аннулирует многие возможности компилятора по оптимизации управляющих структур, из-за чего код становится медленней и объёмней. [2]
З: Эта проблема никоим образом не связана с GOTO, т.к. оптимизация производится на уровне машинных кодов. Да, GOTO вставляет в машинный код инструкцию перехода, которая препятствует оптимизации кода, но те же самые инструкции вставляют и условный оператор, и операторы цикла.

Доводы «защитников» GOTO


1. Группа взаимоисключающих условий.
Пример кода...
if(objectA.nValue == objectB.nValue)
{
	...
	goto END;
}
if(objectC.nValue == objectD.nValue)
{
	...
	goto END;
}
if(objectE.nValue == objectF.nValue)
{
	...
	goto END;
}
END: ...
if(objectA.nValue == objectB.nValue)
{
}
else if(objectC.nValue == objectD.nValue)
{
}
else if(objectE.nValue == objectF.nValue)
{
}
…


П: В данном случае GOTO структуру программы не портит, но в таком построении нет практической необходимости, т.к. то же самое можно организовать через if/else.

З: Заменить приведённый код на if/else можно только в том случае, если перед завершением не выполняется дополнительных операций.

П: Дополнительные операции можно вынести в отдельную функцию и вызывать её в каждой ветке.

З: Вынос дополнительных операций в отдельную функцию снизит быстродействие программы, а в некоторых случаях это недопустимо.

П: Отдельную функцию можно оформить в виде inline-функции, тогда на быстродействии это никак не скажется.

З: Но тогда программа будет занимать больше памяти. А это тоже в некоторых случаях может противоречить задаче.

В результате этого спора во многих языках были введены процедуры завершения и механизм структурной обработки исключений. Эти инструменты работают немного медленнее GOTO, но более наглядны, поэтому для большинства задач их вполне хватает. Но, опять же, существуют задачи, где критично и это «немного», – в них использование GOTO видится целесообразным.

2. Принцип вселенской причинности – если где-то есть GOTO, значит он там нужен.
Новый язык появляется не с бухты-барахты. Перед разработчиками языков программирования стоит непростая задача – удовлетворить все запросы программистов, и учесть общепринятые парадигмы. Абсурдно предполагать, что в языке будет реализована концепция, которая никому не нужна. Если в качестве примера рассмотреть Си, то вообще все вопросы отпадают, т.к. при анализе языка складывается такое ощущение, что за каждый новый введённый в язык оператор разработчики должны были заплатить из своего кармана по 5000 долларов… а оператор GOTO там есть.

3. Выход из множества циклов одновременно.
П: Классический аргумент. Против него не поспоришь.

4. Конечные автоматы (пример кода).
state_1:
	switch (signal)
	{
	case 1: goto state_5;
	case 2: goto state_10;
	case 3: goto state_8;
	}
state_2:
	switch (signal)
	{
	case 7: goto state_37;
	case 10: goto state_1;
	case 9: goto state_100;
	}
	// Достойных реализаций без GOTO не обнаружено.

5. Ещё один пример.
Пример кода...
for (int i=0;i<n1;++i)
{
	for(int j=0;j<n2;++j)
	{
		if(come_condition1) goto next1;

	for(int k=0;k<n3;++k)
	{
		if(come_condition2) goto next2;
 	}
	// some code
	}
	next2:
// some code}
next1:
// some code
}
inline void doSomeActivityInFor()
{
	for(int i=0;i<n1;++i)
	{
		for(int j=0;j<n2;++j)
		{
			if(come_condition1) return;

			if (some_condition3(i,j)
			{
				break;
			}
			// some code
		}
		// some code
}

inline bool some_condition3(i,j)
{
	for(int k=0;k<n3;++k)
	{
		if(come_condition2()) return true;
	}
	return false;
}


П: Приведённый код выполняется с той же скоростью и занимает столько же памяти, что и код с GOTO.

З: Данный пример лишний раз показывает, что нужно более внимательно подходить к разработке алгоритма. Использование GOTO в программах допустимо, но не нужно из одной крайности бросаться в другую.

Подведём итоги


Толпы религиозных фанатиков команды квалифицированных программистов часто отказываются от оператора GOTO, даже когда его использование целесообразно с точки зрения эффективности и наглядности программы.

Плохой тон в программировании? Если учитывать только «тон структурирования», то да. Но ведь ещё есть «тон быстродействия» и «тон компактности», поэтому нужно искать компромисс между ними. [6] Программисты «работающие в песочнице», как правило, решают задачи, в которых не приходится задумываться об экономии ресурсов, отсюда и вытекает недопонимание.

Игнорировать «тон структурирования» тоже нельзя, т.к. программу в любом случае придётся дорабатывать, и если в ней будет запутанная структура, то возникнут ненужные затраты. Как и в любых других практических решениях оптимальным является компромиссный вариант: использование GOTO разрешено, но со следующими оговорками:

  1. Переходить можно только вперёд.
  2. Заходить в блоки категорически нельзя (либо оставаться, либо выходить).

Скорее всего, байки про вредность оператора GOTO придуманы для начинающих программистов, чтобы они не пользовались им во время обучения. Студенты, изучая в вузах структурное программирование и конструкции языка, знают, что GOTO – это зло. Но когда возникает конкретная задача нужно исходить из неё, а не из шаблонов, которые предоставляет язык программирования. Редко бывает, что практической задаче соответствует конкретный шаблон. GOTO как раз и помогает подогнать существующие шаблоны под решаемую задачу.

Благодарности


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

Кто же эти люди? Вот они:
Благодарю Дмитрия Леонова за создание сайта bugtraq.ru и за то, что ему удалось сплотить большое количество высококлассных специалистов на своём форуме. Именно на этом форуме развернулась самая интересная дискуссия. Благодарю людей, принявших участие в дискуссии на этом форуме:

Благодарю OlegY, Heller, Zef за примеры кода, где использование GOTO оправдано.

Благодарю HandleX за философские мысли о нужности GOTO при решении практических, а не теоретических задач.

Благодарю amirul за озвучивание правил применения GOTO.

Благодарю AMMOmium за мысль о «байках-страшилках» для начинающих программистов.

Благодарю команду программистов с форума codenet.ru за показательный пример классического спора, а именно следующих лиц: nilbog, koderAlex, OlgaKr, kerdan, kosfiz3A3-968M, IL84, fanto, Sanila_san, nixus, green, newonder.

PS. Спасибо за внимание. Буду рад комментариям; вопросы и возражения также приветствуются.

Библиография


  1. Р. Лингер, Х. Миллс, Б. Уитт. Теория и практика структурного программирования. – М.: Мир, 1982.-406с.
  2. Edsger W. Dijkstra. Go To Statement Considered Harmful // Communications of the ACM 11, March 1968. 147-148.
  3. Niklaus Wirth. Good Ideas, through the Looking Glass // Computer, V. 39, No 1, January 2006.
  4. Donald E. Knuth. Structured Programming with go to Statements // Computing Surveys, V.6, No.4. December 1974.
  5. Фредерик Брукс. Мифический человеко-месяц или как создаются программные системы.: Пер. с англ. – М.: Символ-Плюс, 2001.-304с.
  6. Карев А.А. Код #кода.

Комментарии (49)


  1. romy4
    18.11.2015 14:11

    Единственным и самым «сильным» аргументов спорщиков против goto чаще всего в моей практике попадалось то, что это как раз «плохой тон» и goto портит программу, делая её запутанной. Без явного goto можно обойтись, но какой ценой?


    1. romy4
      18.11.2015 14:44
      -2

      приведу пример, в котором goto неоходим:

      // lines
      do
      {
       // many lines
         if ( some_cond )
             break;
       // many lines
         if ( other_cond )
             break;
      // lines
      }
      while (0);
      


      либо же
      // lines
       if ( some_cond )
             goto label_skip_some;
      // lines
       if ( other_cond )
             goto label_skip_some;
      // lines
      label_skip_some:
      


      1. drakmail
        18.11.2015 14:57

        А что мешает many lines выделить в отдельные методы, возвращающие some_cond и other_cond, и все эти проверки делать в одном месте?


        1. romy4
          18.11.2015 15:13

          if ( !some_cond )
              func1();
          // lines
          if ( !other_cond )
              func2();
          

          Такой вариант? Если да, то делать остальные проверки после some_cond — лишняя трата времени, когда some_cond должен пропустить часть кода до label_skip_some. Вариант вычисления other_cond до проверки some_cond — не в этом примере, иначе был бы другой код.

          Вынесение части кода в функции — такой вопрос тоже часто является аргументом спора. Но есть моменты, почему не стоит выносить: а) для lines нужно общее пространство видимости, когда используется один набор переменных; б) нет смысла выносить маленькие участки кода, которые используются только в одном месте, в отдельные функции — получается лишняя необходимость бегать по коду смотреть определение функций. в) вызов функций отнимает время — уже, наверное, не аргумент. Но иногда и стэк приходится экономить (когда на поток у тебя 64к стэка).

          Можно писать
          if ( some_cond )
          {
          // lines
               if ( other_cond )
               {
                    // lines
               }
          }
          

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


    1. jrip
      18.11.2015 14:46

      Вот бывает да сложный выбор, либо репутация среди быдлокодеров либо красивый код :)


  1. Beholder
    18.11.2015 14:17
    +9

    Кино ни о чём. Особенно если рассуждать в отрыве от какого-то конкретного языка. На практике в случае необходимости часто применяется досрочный return. Так же смотрите в Java пример реализации циклов с меткой (точнее, блоков с меткой), break и continue. Конечные автоматы редко пишутся вручную, но чаще есть переменная state и код вида state = new_state; break;

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


    1. sim-dev
      18.11.2015 14:41
      +1

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


  1. maxru
    18.11.2015 14:31

    По второму пункту «защитников».
    Объясните, зачем нужен goto в PHP (причём появился он сравнительно недавно).

    По третьему пункту, в том же PHP есть break N.


    1. jrip
      18.11.2015 14:43

      В PHP, как и в других языках много вещей, необходимости в которых нету.
      Впрочем есть мнение, что он там чтобы злобно тролить самоуверенных неофитов :)


      1. maxru
        18.11.2015 16:59

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


        1. jrip
          18.11.2015 17:07

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


          1. maxru
            18.11.2015 18:24
            +1

            учавствуют

            обнаружаться

            участвуют
            обнаружатся

            Извините, не могу равнодушно смотреть на учавкающих людей :)
            «И мы еще боремся за почетное звание дома высокой культуры быта» (с)

            вой то поднимется

            Жду, злорадно потирая ладошки.


            1. jrip
              18.11.2015 18:28

              >участвуют
              >обнаружатся

              какая такая культура) особенно в комментариях к такой статье)


              1. maxru
                18.11.2015 18:30

                goto rozental


                1. phoederr
                  18.11.2015 20:37
                  +1

                  Rosenthal
                  «И мы еще боремся за почетное звание дома высокой культуры быта» (с)


                  1. maxru
                    19.11.2015 12:49
                    -2

                    «Имена собственные пишутся так, как слышатся»

                    «И мы еще боремся за почетное звание дома высокой культуры быта» (с)


  1. NeoCode
    18.11.2015 14:33
    +4

    Выход из множества циклов одновременно решается введением в язык именованных блоков кода и операторов break (и continue заодно) с аргументом — именем блока. Имена блоков также выполняют роль «комментариев» и повышают читаемость программы. Поскольку я до сих пор не встречал практической реализации именованныз блоков, честно считаю это своим собственным изобретением:)
    В целом не имею ничего против goto, хотя и не использую в коде (просто не сталкивался с необходимостью). Не имею ничего против потому, что несмотря на все доводы «против», есть один большой довод «за»: языки возможностей лучше языков ограничений. Мало-ли для каких хакерских целей может понадобиться профессиональному программисту этот оператор? Это не нам решать. Хуже когда что-то нужно, а нету. А значит, пусть будет.


    1. m1el
      18.11.2015 14:53
      +3

      1. NeoCode
        18.11.2015 18:31

        Это очень близко к меткам.
        Мой вариант на примере rust (кстати впервые придумал такой синтаксис около 10 лет назад, тогда точно аналогов не было):

        for x in range(0u, 5) outerLoop {
            for y in range(0u, 5) innerLoop {
                println!("{},{}", x, y);
                if y == 3 {
                    break outerLoop;
                }
            }
        }


        1. m1el
          18.11.2015 18:39

          Perl 5 вышел в свет 20 лет назад :)


      1. token
        18.11.2015 20:50

        Если говорить про JavaScript, то там это работает только для циклов.


  1. Ivan22
    18.11.2015 14:41
    +4

    даже в асемблере есть гото


    1. jrip
      18.11.2015 14:44
      +3

      даже? :)


      1. Weageoo
        18.11.2015 14:48
        +1

        Енто юмор.


        1. jrip
          18.11.2015 16:57

          Так ктож спорит :)


    1. velvetcat
      18.11.2015 14:53

      Раскройте идею за «даже».



  1. AMDmi3
    18.11.2015 14:48
    +4

    Никак не могу ожидать от человека считающегося авторитетным ни участия в споре о goto, ни попытки его разрешить. Спора ведь нет. То что выглядит как спор — это процесс самообучения новых разработчиков, которые выросли из «goto нельзя» начальной школы, начали думать своей головой, задавать вопросы и анализировать информацию. А установка «goto нельзя» очень нужна, потому что в неумелых руках он опасен. В остальном — обычный инструмент. Теперь по содержанию:

    > оптимизация производится на уровне машинных кодов

    В общем случае неверно, оптимизация производится на разных уровнях, начать можно непосредственно с AST. Для примера, даже простой транслятор может перевести for в инструкцию loop вместо dec/inc + cmp + jxx. Для оптимизации произвольных переходов уже нужна сложная логика.

    > Отдельную функцию можно оформить в виде inline-функции… Но тогда программа будет занимать больше памяти

    Не будет, с чего бы ей занимать больше памяти?

    > Конечные автоматы (пример кода) // Достойных реализаций без GOTO не обнаружено

    Плохо искали. Посмотрите, например, ragel — он умеет несколько способов представления сгенерированных автоматов, в том числе с goto. И с ним, к слову, не всё однозначно: «In general, the goto FSM produces faster code but results in a larger binary and a more expensive host language compile.»

    > в которых без него порой никак (C, C++

    Мне правда интересно было бы увидеть пример где без goto действительно «никак» в C++. В C, например, на нём реализуются деструкторы, и лучшего решения действительно нет, но в C++ для этого есть встроенные средства.


    1. encyclopedist
      18.11.2015 17:22

      Согласен свами, кроме одного мифа: на самом деле, для современных процессоров инструкция loop медленнее чем inc + jmp. (SO)


      1. AMDmi3
        18.11.2015 17:31

        Хотел написать оговорку, но забыл. Да, loop неактуален, но это был просто пример.


    1. monah_tuk
      19.11.2015 15:47

      В C, например, на нём реализуются деструкторы, и лучшего решения действительно нет

      два чаю! Причём вариант очень хорош со всех сторон, со стороны наглядности и понятности — тоже.


  1. token
    18.11.2015 15:12
    +2

    Конечные автоматы реализуются циклом:

    var state = 'START';
    for (;;) switch (state) {
        case 'START':
             state = 'NEXT';
             break;
        case 'NEXT':
            state = 'STOP';
            break;
        case 'STOP':
            return 'RESULT';
    }
    


    Да, с GOTO конечно было бы прикольней, но и без него неплохо.


    1. Alexeyslav
      18.11.2015 15:45
      -4

      Вместо того чтобы сразу перейти на нужное состояние автомата будет гонять цикл, делать многочисленные проверки чтобы перейти к следующему состоянию. Причём чем дальше от начала состояние тем дольше будет вести путь к нему — неоправданный расход процессорного времени, хотя такая форма и выглядит красиво(сомнительно).
      В вашем случае, чтобы перейти в состояние STOP нужно сделать как минимум 3 проверки и два перехода внутри цикла, не считая перехода по команде break из предыдущего состояния на начало цикла. А если отдельных состояний больше сотни и автомат должен работать Realtime?
      О чём и говорится в статье… многие в этом споре берут только один аспект применения GOTO.


      1. token
        18.11.2015 15:51
        +4

        Циклом можно принебречь в данном случае, ибо в большей части современных компиляторов / виртуальных машин есть Loop Unrolling, не думаю что цикл будет медленнее чем GOTO (в случае оптимизации!). Не очень понял чем моя конструкция будет отличаться от того же самого но сделанного при помощи GOTO… если вы про лишние проверки на каждой итерации цикла, то switch он тем и отличается от if'ов, что в основе его работы лежит т. н. таблица переходов, которая также строится на этапе оптимизации в ходе компиляции. Поэтому реально сомневаюсь в том что производительность будет как то отличаться.


      1. AMDmi3
        18.11.2015 16:12
        +1

        switch совершенно не обязан работать за O(N) (https://en.wikipedia.org/wiki/Switch_statement#Compilation). Скорее всего он скомпилируется в таблицу и оверхед составит всего лишь пару инструкций на каждую смену состояния. Но да, с goto в данном случае будет быстрее и лучше читается.


    1. zikher
      18.11.2015 17:28
      +1

      конечные автоматы лучше реализуются с помощью подобного кода:

      Код
      typedef enum {STATE1, STATE2..} tStateNum;
      typedef void (*tProcessEvent)(void);
      typedef struct
      {
         tStateNum current_state, next_state;
         tProcessEvent process;
      }tStateDescriptor;
      tStateDescriptor States[]={...}
      ...
      tStateDescriptor currState = ...
      while (1)
      {
         currState->process();
         currState = GetStateByNum(States, curr_state->next_state);
      }
      

      Для полного ускорения данного решения можно убрать current_state и вместо GetStateByNum делать
      currState = &(States[curr_state->next_state]);
      


      1. token
        18.11.2015 19:40

        Да я и не говорил что loop & switch — это самая лучшая реализация. Таблицы перехода никто не отменял. Было бы интересно сравнить накладные расходы loop & switch против lookup и вызовы функций. Я помню как то пытался сделать такое, но почему то это оказалось медленнее. Правда это был JavaScript…


  1. DrReiz
    18.11.2015 15:16
    +5

    Не достаточно ограничений «переход только вперёд, нельзя заходить в блок».

    if (cond)
      goto x;
    
    T a = GetA();
    
    x:
      Display(a);
    
    

    Чему равно a при срабатывании cond? И к каким проблемам в runtime-е это приведет?


    1. creker
      18.11.2015 16:00

      Умный компилятор говорит, что так нельзя, ибо goto прыгает через инициализацию переменной. Ежели умудрится скомпилировать, то, по идее, что на стеке будет, то в «a» и попадет.


  1. RomeoGolf
    18.11.2015 16:46
    +1

    Сногсшибательно выбрана тема для первой публикации…


  1. Mrrl
    18.11.2015 17:02

    А может кто-нибудь объяснить, почему так категорически нельзя переходить назад? Почему опасно переходить вперёд вокруг описания новой переменной, понятно. Но «обнаружить мелкую проблему, что-нибудь исправить и повторить попытку» — чем плохо?


    1. RomeoGolf
      18.11.2015 17:38
      +1

      «Категорически нельзя» даже про деление на ноль не говорят :-)
      Безусловные переходы назад чреваты мертвыми циклами, довольно таки трудноотлавливаемыми. Это по опыту ковыряния в чужом ассемблерном коде.
      А обнаруживать мелкую проблему и исправлять надо на этапе написания, а не в процессе выполнения :-) Ежели она не вполне исправится, и снова пойдем назад (а гарантии, что исправится — никакой, это же проблема все-таки) — получим красивую картину «кот, ловящий свой хвост».


      1. Mrrl
        18.11.2015 18:09

        Что значит — на этапе написания? «Проблема» возникает на этапе выполнения. Например, обнаружили, что мотор не включился — что-то подёргали и снова пошли в точку программы, где надо выполнить все действия, связанные с его включением. Цикл «по включению мотора» ставить нехорошо, поскольку это может быть не единственной причиной возврата, кроме того, сама проверка может быть где-то внутри if{} (в случае, если мотор нужен не всегда).
        А безусловные переходы назад есть в любом цикле for с пропущенной второй частью.


        1. RomeoGolf
          18.11.2015 18:24

          Я там как бы смайлик поставил… Как бы потому, что понял, что имелась в виду проблема на этапе выполнения. Но шутки-шутками, а решать такие проблемы надо действительно на этапе разработки — чтобы для их решения не требовались безусловные переходы назад. В крайнем случае — условные, с защитой от зависания.
          По поводу последнего предложения… Ну, таки да, а еще есть while(true) и еще масса способов организовать мертвый цикл. Для человека, у которого веревка слишком длинная, это очередной способ прострелить ногу, такой же простой и удобный, как goto. Всего лишь забыть условие для break или ошибиться в нем. Но на ЯВУ можно без этого обойтись, более того, в вариантах с for и while компилятор чаще всего укажет ошибку, а в ассемблере без безусловных переходов никуда не денешься.
          Отладка чужого плохо откомментированного ассемблерного кода с безусловными переходами назад — офигенный дзен-тренажер.


          1. Mrrl
            18.11.2015 19:06

            А что такое безусловный переход и чем он отличается от условного? Скорее всего, этот goto будет в конце какого-то if-блока. Защита от зависания —

            if(++nattempts<5) goto _retry;
            throw new Exception("Failed to start motors in 5 attempts");
            

            — скорее всего, тоже будет присутствовать. Но от наличия «goto назад» это вряд ли спасёт.
            А в ассемблере? Все ли помнят, что цикл while надо записывать как «безусловный переход вперёд — там проверка условия и условный переход назад, в начало тела цикла»? Если нет, то в конце цикла наверняка будет безусловный переход на самое начало.


  1. thelongrunsmoke
    18.11.2015 17:05
    +1

    Если стоит цель экономии программной памяти, то goto применим. В остальном, большинство языков имеют более читабельные средства.
    Отлаживать класс в несколько тысяч строк, в котором кто-то плохо выбрал метку для goto, ещё то удовольствие.


  1. namespace
    18.11.2015 18:54

    Шел 2015 год, а люди все продолжали обсуждать goto. Серьезно?


  1. AEP
    18.11.2015 19:13
    +2

    Хотелось бы видеть в списке литературы вот это исследование фактической статистики использования goto в программах на C с открытым кодом: peerj.com/preprints/826v1.pdf


    1. thelongrunsmoke
      18.11.2015 20:03

      Интересное исследование. Согласно их данным, в C, goto используют для хендлинга ошибок (более 80%), это вполне понятно. А вот то, что в популярных проектах (например, OpenLDAP), goto убирали в процессе дебага всего паре процентов случаев — интригующе.