Почему в Go есть инструкция goto

110

Я был удивлен, обнаружив, что в Go есть инструкция goto . Меня всегда учили, что операторы goto ушли в прошлое и являются злом, поскольку они перекрывают реальный поток программы, и что функции или методы всегда являются лучшим способом управления потоком.

Я, должно быть, что-то упускаю. Почему Google включил это?

вред
источник
5
Бывают случаи, когда вам действительно нужен оператор goto. Гото злы только тогда, когда используются без разбора. Например, если очень сложно, если не невозможно, написать анализатор конечного автомата без операторов goto.
xbonez
5
Это не относится к Go, но для хорошего обсуждения того, почему языки сохраняют это выражение, и чтобы увидеть аргументы против его использования, посмотрите этот пост . В вопросе есть несколько хороших ссылок. Изменить: вот еще один .
Cᴏʀʏ
3
Чтобы уберечь OP от grepping через предоставленные обсуждения SO, вот обсуждение LKML, которое в значительной степени подводит итог, почему gotoэто полезно в определенных случаях. Прочтите после изучения ответа @Kissaki.
kostix
1
По теме: programmers.stackexchange.com/q/566/33478 (и посмотрите мой ответ ).
Кейт Томпсон
Это полезно для реализации шаблона продолжения, когда вы сохраняете из стека, а затем возвращаетесь туда, где вы были, когда хотите продолжить.
Джастин Деннахауэр

Ответы:

78

Когда мы действительно проверим исходный код стандартной библиотеки Go, мы увидим, где на gotoсамом деле хорошо применены s.

Например, в math/gamma.goфайле используется gotoинструкция :

  for x < 0 {
    if x > -1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }
  for x < 2 {
    if x < 1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }

  if x == 2 {
    return z
  }

  x = x - 2
  p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
  q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
  return z * p / q

small:
  if x == 0 {
    return Inf(1)
  }
  return z / ((1 + Euler*x) * x)
}

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

Также обратите внимание, что этот gotoоператор имеет очень специфический вариант использования. В спецификации языка goto указано, что он не может перепрыгивать через переменные, входящие в область видимости (объявляемые), и не может переходить в другие (кодовые) блоки.

Kissaki
источник
69
В вашем примере, почему бы small(x,z)вместо этого просто не ввести функцию для вызова? Таким образом, нам не нужно думать о том, какие переменные доступны в small:метке. Я подозреваю, что причина в том, что в go все еще отсутствует поддержка определенных типов встраивания в компиляторе.
Thomas Ahle 02
5
@Jessta: Вот для чего у нас есть видимость и возможности, верно?
Thomas Ahle
4
@ ogc-nick Извините, я не понял, я имел в виду, что функции могут быть объявлены в той области, где они необходимы, поэтому они не видны для кода, который в них не нуждается. Я не говорил о goto и scope.
Thomas Ahle
4
@MosheRevah Указанный код не оптимизирован для удобочитаемости. Он оптимизирован для чистой производительности, используя переход, который охватывает 22 строки в одной функции. (И предложение Томаса Але даже более читабельно для моего глаза.)
joel.neely
30

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

Если какая-то другая функция управления (используемая достаточно очевидным образом) может делать то, что вы хотите, вам следует использовать ее вместо goto. Если нет, проявите смелость и используйте goto!

Наконец, стоит отметить, что goto Go имеет некоторые ограничения, призванные избежать некоторых неясных ошибок. См. Эти ограничения в спецификации.

Соня
источник
7

Утверждения Goto получили много дискредитации со времен эры кода спагетти в 60-х и 70-х годах. В то время методология разработки программного обеспечения была очень плохой. Однако Goto изначально не является злом, но, конечно, может быть неправильно использован ленивыми или неквалифицированными программистами. Многие проблемы со злоупотреблениями Gotos можно решить с помощью процессов разработки, таких как проверка кода команды.

gotoявляются прыжками в той же технической манере continue, что breakи , и return. Кто-то может возразить, что эти утверждения являются злом, но это не так.

Команда Go включила Gotos, вероятно, из-за того, что это обычный примитив управления потоком. Вдобавок они, надеюсь, пришли к выводу, что Go исключает возможность злоупотребления безопасным для идиотов языком.

Густав
источник
continue,, breakи returnсильно различаются в одном ключе: они определяют только «выход за пределы области видимости». Они не только поощряют, но и явно требуют, чтобы разработчик рассматривал структуру своего кода и полагался на примитивы структурированного программирования (для циклов, функций и операторов переключения). Единственное, что позволяет сэкономить на gotoоператорах - это то, что они позволяют писать сборку в HLL, когда оптимизатор компилятора не справляется с этой задачей, но это происходит за счет удобства чтения и поддержки.
Парфянский выстрел
Причина это так удивительно , чтобы найти в дороге, что, в любом другом случае , когда разработчики golang был выбор между структурированным программированием парадигмой и «исключительным контролем потока» / указатель инструкции Манипуляции нравится setjmp, longjmp, gotoи try / except / finallyони решило подстраховаться на стороне осторожности. goto, fwict, единственное согласие с потоком управления предварительно "структурированного программирования".
Парфянский выстрел