¿Por qué Go tiene una declaración "goto"?

110

Me sorprendió descubrir que Go tiene una declaración "goto" . Siempre me han enseñado que las declaraciones 'goto' son cosa del pasado y malvado, ya que obstruyen el flujo real de un programa, y ​​que las funciones o métodos son siempre una mejor manera de controlar el flujo.

Debo estar perdiendo algo. ¿Por qué Google lo incluyó?

daño
fuente
5
Hay momentos en los que realmente necesitas una declaración goto. Los Goto son malvados solo cuando se usan indiscriminadamente. Por ejemplo, si es muy difícil, si no imposible, escribir un analizador de máquina de estado finito sin instrucciones.
xbonez
5
No es específico de Go, pero para una buena discusión sobre por qué los idiomas retienen la declaración y para ver argumentos en contra de su uso, consulte esta publicación . Hay algunas buenas referencias vinculadas en la pregunta. Editar: aquí hay otro .
Cᴏʀʏ
3
Para evitar que el OP se convierta en grepping a través de las discusiones de SO proporcionadas, aquí está la discusión sobre LKML que resume bastante bien por qué gotoes útil en ciertos casos. Lea después de estudiar la respuesta de @ Kissaki.
kostix
1
Relacionado: programmers.stackexchange.com/q/566/33478 (y vea mi respuesta ).
Keith Thompson
Es útil implementar un patrón de continuación, en el que guarda la pila y luego vuelve a donde estaba cuando desea reanudar.
Justin Dennahower

Respuestas:

78

Cuando realmente verificamos el código fuente de la biblioteca estándar de Go, podemos ver dónde gotoestán realmente bien aplicados los correos electrónicos.

Por ejemplo, en el math/gamma.goarchivo, se usa la gotodeclaración :

  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)
}

En gotoeste caso, nos ahorra la introducción de otra variable (booleana) utilizada solo para el flujo de control, verificada al final. En este caso , la gotodeclaración hace que el código sea realmente mejor de leer y más fácil de seguir (muy al contrario del argumento gotoque mencionaste en tu contra ).

También tenga en cuenta que la gotodeclaración tiene un caso de uso muy específico. La especificación del lenguaje en goto establece que no puede saltar sobre las variables que entran en el alcance (siendo declaradas), y no puede saltar a otros bloques (de código).

Kissaki
fuente
69
En su ejemplo, ¿por qué no introducir una función small(x,z)para llamar? De esa forma, no tenemos que pensar en qué variables son accesibles en la small:etiqueta. Sospecho que la razón es que go todavía carece de ciertos tipos de soporte en línea en el compilador.
Thomas Ahle
5
@Jessta: Para eso tenemos visibilidad y alcance, ¿verdad?
Thomas Ahle
6
@ThomasAhle Go no permite gotoapuntar a una etiqueta después de que se hayan introducido nuevas variables. La ejecución de la instrucción "goto" no debe provocar que entren en el alcance variables que no estuvieran ya en el alcance en el punto de goto.
km6zla
4
@ ogc-nick Lo siento, no estaba claro, quise decir que las funciones se pueden declarar en el ámbito donde se necesitan, por lo que no son visibles para el código que no las necesita. No estaba hablando de goto y alcance.
Thomas Ahle
4
@MosheRevah El código referenciado no está optimizado para la legibilidad. Está optimizado para un rendimiento en bruto, utilizando un goto que abarca 22 líneas dentro de una sola función. (Y la propuesta de Thomas Ahle es aún más legible para mis ojos.)
joel.neely
30

Ir a es una buena idea cuando ninguna de las funciones de control integradas hace exactamente lo que desea y cuando puede expresar lo que desea con un goto. (Es una pena en estos casos en algunos lenguajes cuando no tienes un goto. Terminas abusando de alguna función de control, usando indicadores booleanos o usando otras soluciones peores que goto).

Si alguna otra función de control (usada de una manera razonablemente obvia) puede hacer lo que desea, debe usarla en lugar de ir a goto. Si no, ¡sé atrevido y usa goto!

Finalmente, vale la pena señalar que Go's goto tiene algunas restricciones diseñadas para evitar algunos errores oscuros. Consulte estas restricciones en la especificación.

Sonia
fuente
7

Las declaraciones de Goto han recibido mucho descrédito desde la era del código Spaghetti en los años 60 y 70. En aquel entonces, la metodología de desarrollo de software era muy pobre o nula. Sin embargo, los Goto no son malvados de forma nativa, sino que, por supuesto, pueden ser mal utilizados y abusados ​​por programadores perezosos o inexpertos. Muchos problemas con Gotos abusados ​​se pueden resolver con procesos de desarrollo, como revisiones de código de equipo.

gotoson saltos de la misma forma técnica que continue, breaky return. Se podría argumentar que estas son declaraciones son malas de la misma manera, pero no lo son.

La razón por la que el equipo de Go ha incluido Gotos probablemente se deba al hecho de que es una primitiva común de control de flujo. Además, con suerte, concluyeron que el alcance de Go excluye hacer que un lenguaje seguro para idiotas no sea posible de abusar.

Gustav
fuente
continue,, breaky returnson muy diferentes en una clave en particular: solo especifican "dejar el ámbito adjunto". No solo fomentan, sino que exigen explícitamente que el desarrollador considere la estructura de su código y confíe en primitivas de programación estructuradas (para bucles, funciones y declaraciones de conmutación). La única gracia salvadora de las gotodeclaraciones es que le permiten escribir ensamblado en una HLL cuando el optimizador del compilador no está a la altura de la tarea, pero esto tiene un costo de legibilidad y mantenibilidad.
Disparo parto
La razón de esto es tan sorprendente encontrar en marcha es que, en todos los demás casos en que los desarrolladores golang tenían una elección entre paradigma de la programación estructurada y "control de flujo excepcional" / manipulaciones indicador de instrucción como setjmp, longjmp, goto, y try / except / finallyeligieron a errar por el lado de precaución. goto, fwict, es el único consentimiento al flujo de control previo a la "programación estructurada".
Disparo parto