¿Vale la pena usar goto?

29

gotoestá casi universalmente desanimado. ¿Vale la pena usar esta declaración?

Casebash
fuente
16
xkcd.com/292
TheLQ
1
gotoes lo que finalmente hace la CPU, pero no funciona bien con lo que los humanos necesitan comprender y abstraer debido a que no hay una marca en el extremo objetivo. Compare con Intercal COMEFROM.
2
@ ThorbjørnRavnAndersen, ¿qué más quieren decir los humanos cuando hablan de transiciones de estado en una máquina de estados? ¿No ves una similitud? "Ir a un estado dado ", eso es lo que significa y cualquier otra cosa no sería más que una complicación innecesaria de algo tan trivial. ¿Y qué quieres decir con "sin marca"? La etiqueta es tal marca.
SK-logic
@ SK-logic, depende de qué tan lejos permita que vengan los goto's. Las máquinas de estado no requieren goto para implementarse.
@ ThorbjørnRavnAndersen, por supuesto, gotono es obligatorio. Es la forma más racional de implementarlos en los lenguajes imperativos, ya que es lo más parecido a la semántica de la transición de estado. Y su destino puede estar bastante lejos de la fuente, no obstaculizará la legibilidad. Mi ejemplo favorito de dicho código es la implementación de DE Knuth del juego Adventure.
SK-logic

Respuestas:

50

Esto se ha discutido varias veces en Stack Overflow, y Chris Gillum resumió los posibles usos degoto :

Salir limpiamente de una función

A menudo, en una función, puede asignar recursos y debe salir en varios lugares. Los programadores pueden simplificar su código colocando el código de limpieza de recursos al final de la función, todos los "puntos de salida" de la función irían a la etiqueta de limpieza. De esta manera, no tiene que escribir código de limpieza en cada "punto de salida" de la función.

Salir de bucles anidados

Si está en un bucle anidado y necesita salir de todos los bucles, un goto puede hacer esto mucho más limpio y simple que las declaraciones de interrupción y las comprobaciones if.

Mejoras de rendimiento de bajo nivel.

Esto solo es válido en el código crítico, pero las instrucciones goto se ejecutan muy rápidamente y pueden darle un impulso cuando se mueve a través de una función. Sin embargo, esta es una espada de doble filo, porque un compilador generalmente no puede optimizar el código que contiene gotos.

Yo diría, como muchos otros argumentan, que en todos estos casos, el uso de gotose utiliza como un medio para salir de una esquina en el que uno se codificó, y generalmente es un síntoma de código que podría ser refactorizado.

Comunidad
fuente
28
El primer problema se resuelve muy bien mediante finallybloques en los idiomas modernos, y el segundo se resuelve con la etiqueta breaks. Sin embargo, si está atascado con C, gotoes prácticamente la única forma de resolver estos problemas con elegancia.
Chinmay Kanchi
2
Muy buenos puntos. Me gusta cómo Java permite romper múltiples niveles de bucles
Casebash
44
@Chinmay: finalmente, los bloques solo se aplican a 1) los idiomas modernos que los tienen yb) puede tolerar la sobrecarga (el manejo de excepciones tiene una sobrecarga). Es decir, el uso finallysolo es válido en esas condiciones. Hay situaciones muy raras pero válidas en las que un goto es el camino a seguir.
luis.espinal
21
Bien gente ... cuál es la diferencia entre break 3o break myLabely goto myLabel. Oh, eso es justo la palabra clave. Si está utilizando break, continueo alguna palabra clave similar, de hecho está utilizando una goto(Si está utilizando for, while, foreach, do/loop, está utilizando un goto condicional)
Matthew Whited
44
Acerca del rendimiento de bajo nivel: el comportamiento del procesador moderno puede ser difícil de predecir (canalización, ejecución fuera de orden), por lo que probablemente en el 99% de los casos no valga la pena o incluso podría ser más lento. @MatthewWhited: Alcance. Si ingresa el alcance en un punto arbitrario, no está claro (para los humanos) qué constructores deben llamarse (el problema también existe en C).
Maciej Piechotka
42

Las construcciones de flujo de control de nivel superior tienden a corresponder a conceptos en el dominio del problema. Un if / else es una decisión basada en alguna condición. Un bucle dice que realice alguna acción repetidamente. Incluso una declaración de ruptura dice "estábamos haciendo esto repetidamente, pero ahora tenemos que parar".

Una declaración goto, por otro lado, tiende a corresponder a un concepto en el programa en ejecución, no en el dominio del problema. Dice que continúe la ejecución en un punto específico del programa . Alguien que lee el código tiene que inferir lo que eso significa con respecto al dominio del problema.

Por supuesto, todas las construcciones de nivel superior se pueden definir en términos de gotos y ramas condicionales simples. Eso no quiere decir que sean simplemente disfraces disfrazados. Piense en ellos como objetos restringidos , y son las restricciones las que los hacen útiles. Una declaración de interrupción se implementa como un salto al final del ciclo de cierre, pero es mejor pensar que opera en el ciclo como un todo.

En igualdad de condiciones, el código cuya estructura refleja la del dominio del problema tiende a ser más fácil de leer y mantener.

No hay casos en los que sea absolutamente necesaria una declaración goto (hay un teorema para ese efecto), pero hay casos en los que puede ser la solución menos mala. Esos casos varían de un idioma a otro, dependiendo de las construcciones de nivel superior que admita el lenguaje.

En C, por ejemplo, creo que hay tres escenarios básicos donde un goto es apropiado.

  1. Salir de un bucle anidado. Esto sería innecesario si el idioma tuviera una declaración de rotura etiquetada.
  2. Rescate de un tramo de código (generalmente un cuerpo de función) en caso de error u otro evento inesperado. Esto sería innecesario si el idioma tuviera excepciones.
  3. Implementación de una máquina explícita de estados finitos. En este caso (y, creo, solo en este caso) un goto corresponde directamente a un concepto en el dominio del problema, pasando de un estado a otro estado específico, donde el estado actual está representado por qué bloque de código se está ejecutando actualmente .

Por otro lado, una máquina explícita de estados finitos también se puede implementar con una declaración de cambio dentro de un bucle. Esto tiene la ventaja de que cada estado comienza en el mismo lugar en el código, lo que puede ser útil para la depuración, por ejemplo.

El uso principal de un goto en un lenguaje razonablemente moderno (uno que admite if / else y bucles) es simular una construcción de flujo de control que falta en el lenguaje.

Keith Thompson
fuente
55
Esta es realmente la mejor respuesta hasta ahora, bastante sorprendente, para una pregunta que tiene un año y medio. :-)
Konrad Rudolph
1
+1 a "la mejor respuesta hasta ahora". --- "El uso principal de un goto en un lenguaje razonablemente moderno (uno que admite if / else y bucles) es simular una construcción de flujo de control que falta en el lenguaje".
Dave Dopson
En la máquina de estado de declaración de cambio, cada escritura en el valor de control es realmente un goto. Los SSSM a menudo son buenas estructuras para usar, ya que separan el estado SM de la construcción de ejecución, pero en realidad no eliminan los "gotos".
supercat
2
"En igualdad de condiciones, el código cuya estructura refleja la del dominio del problema tiende a ser más fácil de leer y mantener". Lo he sabido por completo durante mucho tiempo, pero me faltaron las palabras para comunicarlo con tanta precisión de una manera que otros programadores puedan y realmente entiendan. Gracias por esto.
Comodín
El "teorema del programa estructurado" me divierte, ya que tiende a demostrar claramente que usar declaraciones de programación estructurada para emular un goto es mucho menos legible que simplemente usar un goto.
11

Seguramente depende del lenguaje de programación. La razón principal gotoha sido controvertida debido a sus efectos nocivos que surgen cuando el compilador le permite usarlo demasiado liberalmente. Pueden surgir problemas, por ejemplo, si le permite usar gotode tal manera que ahora puede acceder a una variable no inicializada, o peor, para saltar a otro método y meterse con la pila de llamadas. Debería ser responsabilidad del compilador rechazar el flujo de control sin sentido.

Java ha intentado "resolver" este problema rechazando por gotocompleto. Sin embargo, Java le permite usar returndentro de un finallybloque y, por lo tanto, provocar que una excepción se trague inadvertidamente. El mismo problema sigue ahí: el compilador no está haciendo su trabajo. Eliminar gotodel idioma no lo ha solucionado.

En C #, gotoes tan seguro como break, continue, try/catch/finallyy return. No le permite usar variables no inicializadas, no le permite saltar de un bloque finalmente, etc. El compilador se quejará. Esto se debe a que resuelve el problema real , que es como dije flujo de control sin sentido. gotono cancela mágicamente el análisis de asignación definida y otras comprobaciones razonables del compilador.

Timwi
fuente
Su punto es que C # 's gotono es malo? Si es así, ese es el caso de todos los lenguajes modernos utilizados habitualmente con una gotoconstrucción, no solo C # ...
lvella
9

Sí. Cuando sus bucles están anidados en varios niveles de profundidad, gotoes la única forma de romper con elegancia un bucle interno. La otra opción es establecer una bandera y salir de cada ciclo si esa bandera cumple una condición. Esto es realmente feo y bastante propenso a errores. En estos casos, gotoes simplemente mejor.

Por supuesto, la breakdeclaración etiquetada de Java hace lo mismo, pero sin permitirle saltar a un punto arbitrario en el código, lo que resuelve el problema perfectamente sin permitir las cosas que hacen el gotomal.

Chinmay Kanchi
fuente
¿A alguien le importa explicar el voto negativo? Esta fue una respuesta perfectamente válida a la pregunta ...
Chinmay Kanchi
44
Goto nunca es una respuesta elegante. Cuando sus bucles están anidados en varios niveles de profundidad, su problema es que no pudo diseñar su código para evitar los bucles multinested. Las llamadas a procedimientos NO son el enemigo. (Lea los documentos "Lambda: The Ultimate ..."). Usar un goto para romper bucles anidados a varios niveles de profundidad es poner una curita en una fractura múltiple abierta: puede ser simple pero no es lo correcto responder.
John R. Strohm
66
Y, por supuesto, ¿nunca hay un caso extraño cuando se requieren bucles profundamente anidados? Ser un buen programador no se trata solo de seguir las reglas, sino también de saber cuándo romperlas.
Chinmay Kanchi
2
@ JohnR.Strohm Nunca digas nunca. Si usa términos como "nunca" en la programación, claramente no ha tratado con casos de esquina, optimización, restricciones de recursos, etc. En bucles profundamente anidados, goto es realmente la forma más limpia y elegante de salir de los bucles. No importa cuánto haya refactorizado su código.
Sujay Phadke
@ JohnR.Strohm: segunda característica más perdida al cambiar de VB.NET a C #: el Dobucle. ¿Por qué? Porque puedo cambiar el bucle que necesita una ruptura profunda en un Dobucle y escribir Exit Doy no hay GoTo. Los bucles anidados suceden todo el tiempo. Romper las pilas ocurre un% de las veces.
Joshua
7

La mayor parte del desánimo proviene de una especie de "religión" creada por Dios Djikstra que fue convincente a principios de los años 60 sobre su poder indiscriminado para:

  • saltar a cualquier parte del bloque de código
    • función no ejecutada desde el principio
    • bucles no ejecutados desde el principio
    • Inicialización variable omitida
  • salte de cualquier bloque de código sin ninguna posible limpieza.

Esto no tiene nada más que ver con la gotodeclaración de los lenguajes modernos, cuya existencia se debe simplemente a apoyar la creación de estructuras de código que no sean las proporcionadas por el lenguaje.

En particular, el primer punto principal anterior ya está permitido y el segundo se limpia (si está gotofuera de un bloque, la pila se desenrolla correctamente y se llaman todos los destructores adecuados)

Puede consultar esta respuesta para tener una idea de cómo incluso el código que no usa goto puede ser ilegible. El problema no es ir a sí mismo, sino su mal uso.

Puedo escribir un programa completo sin usar if, solo for. Por supuesto, no será fácil de leer, se verá torpe e innecesariamente complicado.

Pero el problema no es for. Soy yo.

Cosas como break, continue, throw, bool needed=true; while(needed) {...}, etc están observando más de mascarada goto de escapar lejos de las cimitarras de los fanáticos Djikstrarian, que -50 años después de la invención de laguages- moderna todavía quieren que sus prisioneros. Se olvidaron de lo que Djikstra estaba hablando, solo recuerdan el título de su nota (GOTO lo consideró dañino, y ni siquiera era su único título: fue cambiado por el editor) y culpan y golpean, golpean y culpan a cada constructor que tiene esos 4 carta colocada en secuencia.

Es 2011: es hora de comprender que gototiene que hacer frente a la GOTOdeclaración sobre la que Djikstra fue convincente.

Emilio Garavaglia
fuente
1
"Cosas como romper, continuar, lanzar, bool needed = true; mientras que (need) {...}, etc. están notando algo más que ir enmascarado" No estoy de acuerdo con eso. Preferiría "cosas como descanso, etc., están restringidas a ir a", o algo así. El principal problema con goto es que generalmente es demasiado poderoso. En la medida en que el goto actual sea menos poderoso que el de Dijkstra, tu respuesta está bien conmigo.
Muhammad Alkarouri
2
@MuhammadAlkarouri: Estoy totalmente de acuerdo. Acabas de encontrar una mejor redacción para expresar exactamente mi concepto. Mi punto es que esas restricciones a veces no pueden ser aplicables y el tipo de restricción que necesita no está en el idioma. Entonces, una cosa más "poderosa", menos especializada, es lo que le permite solucionarlo. Por ejemplo, Java break nno está disponible en C ++, por lo tanto goto escape, incluso si escapeno se requiere que salga del bucle n-ple.
Emilio Garavaglia
4

El goto extraño aquí o allá, siempre que sea local para una función, rara vez perjudica significativamente la legibilidad. A menudo lo beneficia al llamar la atención sobre el hecho de que hay algo inusual en este código que requiere el uso de una estructura de control poco común.

Si los gotos (locales) están perjudicando significativamente la legibilidad, entonces generalmente es una señal de que la función que contiene el goto se ha vuelto demasiado compleja.

El último goto que puse en un fragmento de código C fue construir un par de bucles entrelazados. No se ajusta a la definición normal de uso de goto "aceptable", pero como resultado la función terminó siendo significativamente más pequeña y clara. Para evitar el goto habría requerido una violación particularmente desordenada de DRY.

Blucz
fuente
1
La respuesta a esto se expresa de manera más concisa en el viejo lema "Lay's Potato Chips": "Apuesto a que no puedes comer solo uno". En su ejemplo, tiene dos bucles de "enclavamiento" (lo que sea que eso signifique, y apuesto a que no es bonito), y el goto le dio una salida barata. ¿Qué pasa con el tipo de mantenimiento, que tiene que modificar ese código después de que lo atropelló un autobús? ¿El goto le hará la vida más fácil o más difícil? ¿Es necesario repensar esos dos bucles?
John R. Strohm
3

Creo que todo este problema ha sido un caso de ladrar al árbol equivocado.

GOTO como tal no me parece problemático, sino que a menudo es un síntoma de un pecado real: el código de espagueti.

Si el GOTO causa un cruce importante de las líneas de control de flujo, entonces es malo, punto. Si no cruza las líneas de control de flujo, es inofensivo. En la zona gris en el medio tenemos cosas como rescates en bucle, todavía hay algunos idiomas que no han agregado construcciones que cubran todos los casos grises legítimos.

El único caso en el que me he encontrado usándolo en muchos años es el caso del ciclo donde el punto de decisión está en el medio del ciclo. Te quedan códigos duplicados, una bandera o un GOTO. La solución GOTO me parece la mejor de las tres. No hay cruce de líneas de control de flujo aquí, es inofensivo.

Loren Pechtel
fuente
El caso al que alude a veces se denomina "bucle y medio" o "N más un medio bucle" y es un caso de uso famoso para gotolos que saltan en un bucle (si el idioma lo permite; desde el otro caso, saltando fuera del bucle en el medio, generalmente es trivial sin goto).
Konrad Rudolph
(Por desgracia, la mayoría de los idiomas prohíben saltar en un bucle.)
Konrad Rudolph
@KonradRudolph: Si implementa el "bucle" con GOTO, no hay otra estructura de bucle para saltar.
Loren Pechtel
1
Pienso gotoen gritar EL FLUJO DE CONTROL AQUÍ NO SE ADAPTA A LOS PATRONES DE PROGRAMACIÓN ESTRUCTURADA NORMAL . Debido a que los flujos de control que se ajustan a patrones de programación estructurados son más fáciles de razonar que aquellos que no lo hacen, uno debería intentar usar dichos patrones cuando sea posible; Si el código se ajusta a dichos patrones, no se debe gritar que no es así. Por otro lado, en los casos en que el código realmente no se ajusta a dichos patrones, gritar sobre él puede ser mejor que pretender que el código se ajusta cuando realmente no lo hace.
supercat
1

Sí, el goto se puede utilizar para beneficiar la experiencia del desarrollador: http://adamjonrichardson.com/2012/02/06/long-live-the-goto-statement/

Sin embargo, al igual que con cualquier herramienta poderosa (punteros, herencia múltiple, etc.), uno debe ser disciplinado al usarla. El ejemplo proporcionado en el enlace usa PHP, que restringe el uso de la construcción goto a la misma función / método y deshabilita la capacidad de saltar a un nuevo bloque de control (por ejemplo, bucle, instrucción de cambio, etc.)

AdamJonR
fuente
1

Depende del idioma. Todavía se usa ampliamente en la programación de Cobol, por ejemplo. También he trabajado en un dispositivo Barionet 50, cuyo lenguaje de programación de firmware es un dialecto BASIC temprano que, por supuesto, requiere que uses Goto.

usuario16764
fuente
0

Yo diría que no. Si encuentra la necesidad de usar GOTO, apuesto a que es necesario rediseñar el código.

Walter
fuente
3
Tal vez, pero no has proporcionado ningún razonamiento
Casebash
Dijkstra proporcionó el razonamiento, en detalle, hace décadas.
John R. Strohm
2
Solo dogma. No hay resones. Nota: Djikstra NO ES MÁS UNA RESONENCIA VÁLIDA: se refería a la declaración BASIC o FORTRAN GOTO de principios de los años 60. No tienen nada que ver con los gots-s realmente anémicos (en comparación con el poder de esos) de hoy.
Emilio Garavaglia
0

gotopuede ser útil cuando se transfiere el código de ensamblador heredado a C. En primera instancia, una conversión de instrucción por instrucción a C, que se usa gotocomo reemplazo de la branchinstrucción del ensamblador , puede permitir una transferencia muy rápida.

Graham Borland
fuente
-1

Yo diría que no. Siempre deben reemplazarse con una herramienta más específica para el problema que se está utilizando para resolver el goto (a menos que no esté disponible en su idioma). Por ejemplo, las declaraciones de interrupción y las excepciones resuelven problemas previamente resueltos por goto de escape de bucle y manejo de errores.

Fishtoaster
fuente
Yo diría que cuando los idiomas tienen ambas características, gotogeneralmente están ausentes de esos idiomas.
Chinmay Kanchi
¿Pero es porque no los necesitan o porque la gente piensa que no los necesita?
Michael K
Tus puntos de vista son intolerantes. Hay muchos casos en gotolos que lo más parecido a la semántica del dominio es un problema, cualquier otra cosa sería un truco sucio.
SK-logic
@ SK-logic: no creo que la palabra "intolerante" signifique lo que crees que significa.
Keith Thompson
1
@Keith, "intolerantemente dedicado a sus propias opiniones y prejuicios", eso es lo que constantemente observo aquí, con este lote con los ojos vendados. " Siempre se debe reemplazar" son palabras de fanáticos, al menos. Nada parecido a una opinión adecuada y sólida, sino solo una intolerancia pura. Este "siempre" no deja espacio para ninguna opinión alternativa, ¿ves?
SK-logic