language-agnostic goto

language agnostic - GOTO todavía se considera perjudicial?



language-agnostic (30)

Porque gotose puede usar para confundir metaprogramación.

Gotoes una expresión de control de alto nivel y de bajo nivel , y como resultado simplemente no tiene un patrón de diseño adecuado adecuado para la mayoría de los problemas.

Es de bajo nivel en el sentido de que un goto es una operación primitiva que implementa algo más alto como whileo foreacho algo.

Es de alto nivel en el sentido de que, cuando se usa de cierta manera, toma código que se ejecuta en una secuencia clara, de manera ininterrumpida, a excepción de los bucles estructurados, y lo convierte en piezas de lógica que son, con suficiente gotos, un agarre -Bolsa de lógica siendo reensamblada dinámicamente.

Entonces, hay un lado prosaico y un lado malogoto .

El lado prosaico es que un goto que apunta hacia arriba puede implementar un bucle perfectamente razonable y que un goto que apunta hacia abajo puede hacer un breako perfectamente razonable return. Por supuesto, un real while, breako returnsería mucho más legible, ya que el pobre humano no tendría que simular el efecto del gotopara obtener el panorama general. Entonces, una mala idea en general.

El lado malo implica una rutina que no usa goto por un tiempo, rompe o regresa, sino que lo usa para lo que se llama lógica de espagueti . En este caso, el desarrollador de goto-happy está construyendo piezas de código a partir de un laberinto de goto''s, y la única forma de entenderlo es simularlo mentalmente como un todo, una tarea terriblemente agotadora cuando hay muchos goto''s. Quiero decir, imagínese el problema de evaluar el código donde el elseno es precisamente un inverso del if, donde el ifs anidado podría permitir algunas cosas que fueron rechazadas por el exterior if, etc.

Finalmente, para cubrir realmente el tema, debemos tener en cuenta que, en esencia, todos los idiomas antiguos, excepto Algol, inicialmente solo hicieron declaraciones individuales sujetas a sus versiones de if-then-else. Por lo tanto, la única forma de hacer un bloqueo condicional era gotorodearlo utilizando un condicional inverso. Loco, lo sé, pero he leído algunas especificaciones antiguas. Recuerde que las primeras computadoras fueron programadas en código binario de máquina, así que supongo que cualquier tipo de HLL fue un salvavidas; Supongo que no fueron muy exigentes con las características de HLL que obtuvieron.

Habiendo dicho todo lo que solía incluir uno gotoen cada programa, escribí "solo para molestar a los puristas" .

Todos conocen las Cartas al editor de Dijkstra : diríjase a una declaración que se considera dañina (también here .html transcripción y here .pdf) y ha habido un empuje formidable desde ese momento para evitar la declaración goto siempre que sea posible. Si bien es posible utilizar goto para producir un código extenso e inexorable, sin embargo, permanece en los lenguajes de programación modernos . Incluso la estructura avanzada de control de continuation en Esquema puede describirse como un goto sofisticado.

¿Qué circunstancias justifican el uso de goto? ¿Cuándo es mejor evitar?

Como una pregunta de seguimiento: C proporciona un par de funciones, setjmp y longjmp, que brindan la capacidad de pasar no solo dentro del marco de pila actual sino también dentro de cualquiera de los marcos de llamada. ¿Deberían estos ser considerados tan peligrosos como goto? ¿Más peligroso?

El propio Dijkstra lamentó ese título, del cual no era responsable. Al final de EWD1308 (también aquí .pdf) escribió:

Finalmente un cuento para el disco. En 1968, las Comunicaciones de la ACM publicaron un texto mío con el título " La declaración de goto considerada dañina ", que en los últimos años se haría referencia con mayor frecuencia, sin embargo, lamentablemente, a menudo por autores que no habían visto más que eso título, que se convirtió en la piedra angular de mi fama al convertirse en una plantilla: veríamos todo tipo de artículos con el título "X considerado perjudicial" para casi cualquier X, incluido uno titulado "Dijkstra considerado perjudicial". Pero ¿qué había pasado? Presenté un documento con el título " Un caso contra la declaración de goto ", que, para acelerar su publicación, el editor se convirtió en una "carta al editor", y en el proceso le dio una nueva Título de su propia invención! El editor fue Niklaus Wirth.

Un artículo clásico bien pensado sobre este tema, que se empareja con el de Dijkstra, es la Programación estructurada con ir a Declaraciones , por Donald E. Knuth. La lectura ayuda tanto a restablecer el contexto como a una comprensión no dogmática del tema. En este documento, la opinión de Dijkstra sobre este caso se informa y es aún más firme:

Donald E. Knuth: Creo que al presentar tal punto de vista no estoy en desacuerdo con las ideas de Dijkstra, ya que recientemente escribió lo siguiente: "Por favor, no caiga en la trampa de creer que soy terriblemente dogmático con respecto a [la ir a la declaración]. Tengo la incómoda sensación de que otros están haciendo una religión a partir de eso, como si los problemas conceptuales de la programación pudieran resolverse con un solo truco, con una forma simple de codificación de la disciplina ".


A veces es válido usar GOTO como una alternativa al manejo de excepciones dentro de una sola función:

if (f() == false) goto err_cleanup; if (g() == false) goto err_cleanup; if (h() == false) goto err_cleanup; return; err_cleanup: ...

El código COM parece caer en este patrón con bastante frecuencia.


Atraído por Jay Ballou agregando una respuesta, agregaré mi £ 0.02. Si Bruno Ranschaert no lo hubiera hecho ya, habría mencionado el artículo de Knuth "Programación estructurada con declaraciones GOTO".

Una cosa que no he visto discutida es el tipo de código que, aunque no es exactamente común, se enseñó en los libros de texto de Fortran. Cosas como el rango extendido de un bucle de OD y subrutinas de código abierto (recuerde, esto sería Fortran II, Fortran IV o Fortran 66, no Fortran 77 o 90). Hay al menos una posibilidad de que los detalles sintácticos sean inexactos, pero los conceptos deberían ser lo suficientemente precisos. Los fragmentos en cada caso están dentro de una sola función.

Tenga en cuenta que el excelente pero fechado (y agotado) libro '' The Elements of Programming Style, 2nd Edn '' de Kernighan & Plauger incluye algunos ejemplos de abuso de GOTO en la vida real de los libros de texto de programación de su era (finales de los 70). Sin embargo, el material a continuación no es de ese libro.

Rango extendido para un bucle de OD

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

Una de las razones de esas tonterías era la buena tarjeta anticuada. Puede notar que las etiquetas (bastante fuera de secuencia porque era un estilo canónico!) Están en la columna 1 (en realidad, tenían que estar en las columnas 1-5) y el código en las columnas 7-72 ​​(la columna 6 era la continuación columna de marcador). A las columnas 73-80 se les daría un número de secuencia, y había máquinas que clasificarían los mazos de tarjetas perforadas en el orden de los números de secuencia. Si tenía su programa en tarjetas secuenciadas y necesitaba agregar algunas tarjetas (líneas) en medio de un bucle, tendría que volver a ejecutar todo después de esas líneas adicionales. Sin embargo, si reemplaza una tarjeta con el material GOTO, podría evitar volver a poner en secuencia todas las tarjetas; simplemente, al final de la rutina, metió las nuevas tarjetas con nuevos números de secuencia. Considere que es el primer intento de "computación ecológica": un ahorro de tarjetas perforadas (o, más específicamente, un ahorro de trabajo de reescritura) y un ahorro de errores de cambio de clave consecuentes).

Oh, también podrías notar que estoy engañando y no gritando: Fortran IV fue escrito en mayúsculas normalmente.

Subrutina de codigo abierto

...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

El GOTO entre las etiquetas 76 y 54 es una versión del goto calculado. Si la variable i tiene el valor 1, vaya a la primera etiqueta en la lista (123); si tiene el valor 2, vaya al segundo, y así sucesivamente. El fragmento del 76 al goto calculado es la subrutina de código abierto. Era un fragmento de código ejecutado más bien como una subrutina, pero escrito en el cuerpo de una función. (Fortran también tenía funciones de sentencias, que eran funciones integradas que se ajustaban en una sola línea).

Hubo construcciones peores que el goto calculado: se podrían asignar etiquetas a las variables y luego usar un goto asignado. Googlear asignado a goto me dice que se eliminó de Fortran 95. Anote uno por la revolución de programación estructurada que podría decirse que comenzó en público con la carta o el artículo de Dijkstra "GOTO Considered Harmful".

Sin un conocimiento de la clase de cosas que se hicieron en Fortran (y en otros idiomas, la mayoría de los cuales se han quedado del lado del camino), es difícil para nosotros los recién llegados comprender el alcance del problema con el que estaba tratando Dijkstra. Diablos, no empecé a programar hasta diez años después de que se publicara esa carta (pero tuve la desgracia de programar en Fortran IV por un tiempo).


Donald E. Knuth respondió esta pregunta en el libro "Programación literaria", 1992 CSLI. En P. 17 hay un ensayo " Programación estructurada con declaraciones goto " (PDF). Creo que el artículo podría haber sido publicado en otros libros también.

El artículo describe la sugerencia de Dijkstra y describe las circunstancias en que esto es válido. Pero también da una serie de ejemplos de contador (problemas y algoritmos) que no se pueden reproducir fácilmente utilizando solo bucles estructurados.

El artículo contiene una descripción completa del problema, la historia, los ejemplos y los ejemplos de contador.


En Linux: usando goto In Kernel Code en Kernel Trap, hay una discusión con Linus Torvalds y un "chico nuevo" sobre el uso de GOTOs en el código de Linux. Hay algunos puntos muy buenos allí y Linus vestido con esa arrogancia habitual :)

Algunos pasajes:

Linus: "No, te han lavado el cerebro personas de CS que pensaron que Niklaus Wirth realmente sabía de lo que estaba hablando. No lo hizo. No tiene ni una pista".

-

Linus: "Creo que los goto están bien, y con frecuencia son más legibles que grandes cantidades de sangrado".

-

Linus: "Por supuesto, en lenguajes estúpidos como Pascal, donde las etiquetas no pueden ser descriptivas, los goto pueden ser malos".


En C, goto solo funciona dentro del alcance de la función actual, que tiende a localizar cualquier error potencial. setjmp y longjmp son mucho más peligrosos, ya que no son locales, son complicados y dependen de la implementación. En la práctica, sin embargo, son demasiado oscuros y poco comunes como para causar muchos problemas.

Creo que el peligro de goto en C es muy exagerado. Recuerda que los argumentos originales de goto tuvieron lugar en tiempos de lenguajes como el antiguo BASIC, donde los principiantes escribían código de espagueti como este:

3420 IF A > 2 THEN GOTO 1430

Aquí Linus describe un uso apropiado de goto : http://www.kernel.org/doc/Documentation/CodingStyle (capítulo 7).


Go To puede proporcionar una especie de suplente para el manejo de excepciones "reales" en ciertos casos. Considerar:

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;

Obviamente, este código se simplificó para ocupar menos espacio, así que no se obsesione demasiado con los detalles. Pero considere una alternativa que he visto demasiadas veces en el código de producción por parte de los codificadores que van a ser absurdos para evitar usar 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);

Ahora funcionalmente este código hace exactamente lo mismo. De hecho, el código generado por el compilador es casi idéntico. Sin embargo, en el afán del programador por apaciguar a Nogoto (el temido dios de la reprensión académica), este programador rompió completamente el lenguaje subyacente que representa el bucle while, e hizo un número real en la legibilidad del código. Esto no es mejor.

Entonces, la moraleja de la historia es que, si te encuentras recurriendo a algo realmente estúpido para evitar usar goto, entonces no lo hagas.


Goto es extremadamente bajo en mi lista de cosas para incluir en un programa por el simple hecho de hacerlo. Eso no significa que sea inaceptable.

Goto puede ser bueno para las máquinas de estado. Una declaración de cambio en un bucle es (en orden de importancia típica): (a) no es realmente representativa del flujo de control, (b) fea, (c) potencialmente ineficiente según el idioma y el compilador. Así que terminas escribiendo una función por estado y haciendo cosas como "return NEXT_STATE;" que incluso parecen goto.

Por supuesto, es difícil codificar las máquinas de estado de una manera que las haga fáciles de entender. Sin embargo, ninguna de esas dificultades tiene que ver con el uso de goto, y ninguna de ellas puede reducirse mediante el uso de estructuras de control alternativas. A menos que su idioma tenga una construcción ''máquina de estado''. El mío no lo hace.

En esas raras ocasiones en que su algoritmo es realmente más comprensible en términos de una ruta a través de una secuencia de nodos (estados) conectados por un conjunto limitado de transiciones permisibles (gotos), en lugar de por un flujo de control más específico (bucles, condicionales, todo eso) ), entonces eso debería ser explícito en el código. Y deberías dibujar un bonito diagrama.

setjmp / longjmp puede ser bueno para implementar excepciones o un comportamiento similar a una excepción. Aunque no son elogiadas universalmente, las excepciones generalmente se consideran una estructura de control "válida".

setjmp / longjmp son ''más peligrosos'' que goto en el sentido de que son más difíciles de usar correctamente, no importa de manera comprensible.

Nunca ha habido, ni nunca habrá, ningún idioma en el que sea un poco difícil escribir un código incorrecto. - Donald Knuth.

Tomar goto de C no facilitaría la escritura de un buen código en C. De hecho, preferiría pasar por alto el hecho de que se supone que C es capaz de actuar como un lenguaje ensamblador glorificado.

A continuación, será "punteros considerados dañinos", luego "tipificación de pato considerado dañinos". Entonces, ¿quién quedará para defenderte cuando vengan a quitarte tu construcción de programación insegura? Eh?


Hoy en día, es difícil ver el gran problema de la declaración de GOTO porque la gente de "programación estructurada" ganó principalmente el debate y los lenguajes de hoy tienen estructuras de flujo de control suficientes para evitar el GOTO .

Cuenta el número de goto s en un programa de C moderno. Ahora agregue el número de declaraciones de break , continue y return . Además, agregue el número de veces que usa if , de lo else , while , switch o case . Eso es aproximadamente la cantidad de GOTO que habría tenido tu programa si estuvieras escribiendo en FORTRAN o BASIC en 1968 cuando Dijkstra escribió su carta.

Los lenguajes de programación en ese momento carecían de flujo de control. Por ejemplo, en el Dartmouth BASIC original:

  • IF declaraciones no tienen ELSE . Si querías uno, tenías que escribir:

    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

  • Incluso si su declaración IF no necesitaba un ELSE , todavía estaba limitado a una sola línea, que generalmente consistía en un GOTO .

  • No había ninguna instrucción DO...LOOP . Para los bucles no FOR , tuvo que finalizar el bucle con un GOTO o IF...GOTO explícitos IF...GOTO al principio.

  • No había ningún caso de selección. Tenías que usar ON...GOTO .

Entonces, terminaste con muchos GOTO en tu programa. Y no podía depender de la restricción de GOTO a una sola subrutina (porque GOSUB...RETURN era un concepto tan débil de subrutinas), por lo que estos GOTO podrían ir a cualquier parte . Obviamente, esto hizo que el flujo de control fuera difícil de seguir.

Aquí es de donde viene el movimiento anti- GOTO .


Las siguientes afirmaciones son generalizaciones; Si bien siempre es posible alegar una excepción, por lo general (en mi experiencia y humilde opinión) no vale la pena correr riesgos.

  1. El uso sin restricciones de las direcciones de memoria (ya sea GOTO o punteros sin procesar) brinda demasiadas oportunidades para cometer errores fácilmente evitables.
  2. Cuantas más formas haya de llegar a una "ubicación" particular en el código, menos confiado puede ser sobre cuál es el estado del sistema en ese punto. (Vea abajo.)
  3. La programación estructurada IMHO es menos sobre "evitar GOTOs" y más sobre hacer que la estructura del código coincida con la estructura de los datos. Por ejemplo, una estructura de datos repetida (por ejemplo, matriz, archivo secuencial, etc.) es procesada por una unidad de código repetida. Tener estructuras integradas (por ejemplo, mientras, para, hasta, para-cada, etc.) permite al programador evitar el tedio de repetir los mismos patrones de código cliché.
  4. Incluso si GOTO es un detalle de implementación de bajo nivel (¡no siempre es así!) Está por debajo del nivel en el que el programador debería estar pensando. ¿Cuántos programadores equilibran sus chequeras personales en binario en bruto? ¿Cuántos programadores se preocupan sobre qué sector del disco contiene un registro en particular, en lugar de simplemente proporcionar una clave para un motor de base de datos (y de cuántas maneras podrían salir mal las cosas si realmente escribimos todos nuestros programas en términos de sectores de discos físicos)?

Notas a pie de página de lo anterior:

Con respecto al punto 2, considere el siguiente código:

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

En el punto de "hacer algo" en el código, podemos afirmar con gran confianza que a es mayor que b . (Sí, estoy ignorando la posibilidad de un desbordamiento de enteros sin trampa. No vamos a atascar un ejemplo simple).

Por otro lado, si el código hubiera leído de esta manera:

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

La multiplicidad de formas de obtener la etiqueta 10 significa que tenemos que trabajar mucho más para estar seguros de las relaciones entre b en ese momento. (De hecho, en el caso general es indecidible!)

Con respecto al punto 4, toda la noción de "ir a algún lugar" en el código es solo una metáfora. Nada es realmente "ir" a ninguna parte dentro de la CPU, excepto los electrones y los fotones (para el calor residual). A veces renunciamos a una metáfora por otra más útil. Recuerdo haber encontrado (¡hace unas décadas!) Un lenguaje donde

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

se implementó en una máquina virtual al compilar action-1 y action-2 como rutinas sin parámetros fuera de línea, y luego se utilizó un único código de operación de VM de dos argumentos que usaba el valor booleano de la condición para invocar uno u otro. El concepto era simplemente "elegir qué invocar ahora" en lugar de "ir aquí o ir allí". De nuevo, sólo un cambio de metáfora.


Solo puedo recordar usar un goto una vez. Tuve una serie de cinco bucles contados anidados y necesitaba poder salir de toda la estructura desde el principio en base a ciertas condiciones:

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

Podría haber declarado fácilmente una variable de ruptura booleana y usarla como parte del condicional para cada ciclo, pero en este caso decidí que un GOTO era tan práctico como legible.

Ningún velociraptor me atacó.


Ya tuvimos esta discussion y mantengo mi punto de vista .

Además, estoy harto de que la gente describa las estructuras de lenguaje de nivel superior como " goto a disfrazar" porque claramente no tienen el punto en absoluto . Por ejemplo:

Incluso la estructura avanzada de control de continuación en Esquema puede describirse como un goto sofisticado.

Eso es un completo disparate. Cada estructura de control puede implementarse en términos de goto pero esta observación es completamente trivial e inútil. goto no se considera dañino por sus efectos positivos sino por sus consecuencias negativas, que se han eliminado mediante una programación estructurada.

Del mismo modo, decir "GOTO es una herramienta, y como todas las herramientas, se puede usar y abusar" está completamente fuera de lugar. Ningún trabajador de la construcción moderna usaría una roca y afirmaría que "es una herramienta". Las rocas han sido reemplazadas por martillos. goto ha sido reemplazado por estructuras de control. Si el obrero de la construcción quedara varado en la naturaleza sin un martillo, por supuesto que usaría una roca en su lugar. Si un programador tiene que usar un lenguaje de programación inferior que no tiene la característica X, bueno, por supuesto, puede que tenga que usar goto lugar. Pero si lo usa en cualquier otro lugar en lugar del lenguaje apropiado, claramente no ha entendido el idioma correctamente y lo usa de manera incorrecta. Es realmente tan simple como eso.


Un compañero mío dijo que la única razón para usar un GOTO es si se programó en una esquina tan lejos que es la única salida. En otras palabras, diseñe correctamente con anticipación y no necesitará usar un GOTO más adelante.

Pensé que este cómic ilustra maravillosamente "podría reestructurar el flujo del programa, o usar un pequeño ''GOTO'' en su lugar". Un GOTO es una salida débil cuando tienes un diseño débil. Los velociraptores se aprovechan de los débiles .


Goto considerado útil.

Comencé a programar en 1975. Para los programadores de la década de 1970, las palabras "goto considerado dañino" decían más o menos que los nuevos lenguajes de programación con estructuras de control modernas valían la pena. Probamos los nuevos idiomas. Nos convertimos rápidamente. Nunca volvimos.

Nunca regresamos, pero, si eres más joven, nunca has estado allí en primer lugar.

Ahora, un fondo en lenguajes de programación antiguos puede no ser muy útil excepto como un indicador de la edad del programador. No obstante, los programadores más jóvenes carecen de este trasfondo, por lo que ya no entienden el mensaje que el eslogan "goto considerado dañino" transmitió a su público objetivo en el momento en que se presentó.

Los eslóganes que uno no entiende no son muy esclarecedores. Probablemente es mejor olvidar tales consignas. Tales consignas no ayudan.

Este eslogan en particular, sin embargo, "Goto considerado dañino", ha adquirido una vida propia de no-muertos.

¿No se puede abusar de goto? Respuesta: claro, pero ¿y qué? Prácticamente todos los elementos de programación pueden ser abusados. El humilde, boolpor ejemplo, es abusado con más frecuencia de lo que a algunos de nosotros nos gustaría creer.

Por el contrario, no recuerdo haber conocido una sola instancia real de abuso de Goto desde 1990.

El mayor problema con goto probablemente no sea técnico sino social. Los programadores que no saben mucho a veces parecen sentir que despreciarlos hace que suenen inteligentes. Es posible que tengas que satisfacer a tales programadores de vez en cuando. Así es la vida.

Lo peor de goto hoy es que no se usa lo suficiente.


Hasta que C y C ++ (entre otros culpables) hayan marcado roturas y continúen, goto seguirá teniendo un rol.


Nunca lo fue, siempre y cuando pudieras pensar por ti mismo.


Puede usarlo para salir de un bucle profundamente anidado, pero la mayoría de las veces su código puede ser refaccionado para ser más limpio sin bucles profundamente anidados.


Realmente me vi obligado a usar un goto, porque literalmente no podía pensar en una forma mejor (más rápida) de escribir este código:

Tenía un objeto complejo, y necesitaba hacer alguna operación en él. Si el objeto estaba en un estado, entonces podría hacer una versión rápida de la operación, de lo contrario tuve que hacer una versión lenta de la operación. Lo que ocurría era que, en algunos casos, en medio de la operación lenta, era posible darse cuenta de que esto podría haberse hecho con la operación rápida.

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.

Esto estaba en una pieza crítica de velocidad de código de UI en tiempo real, así que honestamente creo que aquí se justificó un GOTO

Hugo


Si estás escribiendo una máquina virtual en C, resulta que usar gotos computados (gcc) como este:

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

Funciona mucho más rápido que el interruptor convencional dentro de un bucle.


Un uso moderno de GOTO es el compilador de C # para crear máquinas de estado para los enumeradores definidos por el rendimiento.

GOTO es algo que deben usar los compiladores y no los programadores.



Casi todas las situaciones donde se puede usar un goto, puedes hacer lo mismo usando otras construcciones. Goto es utilizado por el compilador de todos modos.

Personalmente nunca lo uso explícitamente, nunca lo necesito.


Desde que comencé a hacer algunas cosas en el núcleo de Linux, los gotos no me molestan tanto como antes. Al principio me horroricé al ver que ellos (chicos del núcleo) agregaron gotos en mi código. Desde entonces me he acostumbrado al uso de gotos, en algunos contextos limitados, y ahora los usaré de vez en cuando. Por lo general, es un goto que salta al final de una función para realizar algún tipo de limpieza y rescate, en lugar de duplicar esa misma limpieza y rescate en varios lugares de la función. Y, por lo general, no es algo lo suficientemente grande como para transferirlo a otra función; por ejemplo, liberar algunas variables localmente (k) mallocadas es un caso típico.

He escrito código que usó setjmp / longjmp solo una vez. Estaba en un programa de secuenciador de batería MIDI. La reproducción sucedió en un proceso separado de toda interacción del usuario, y el proceso de reproducción usó la memoria compartida con el proceso de la interfaz de usuario para obtener la información limitada que necesitaba para realizar la reproducción. Cuando el usuario deseaba detener la reproducción, el proceso de reproducción solo hacía un "retroceso al principio" para comenzar de nuevo, en lugar de un desdoblamiento complicado donde se ejecutaba cuando el usuario deseaba que se detuviera. Funcionó muy bien, fue simple, y nunca tuve ningún problema o error relacionado con eso en ese caso.

setjmp / longjmp tienen su lugar, pero ese lugar es uno que probablemente no visitarás, pero de vez en cuando.

Edit: Acabo de ver el código. En realidad fue siglongjmp () lo que usé, no longjmp (no es que sea un gran problema, pero había olvidado que incluso existía siglongjmp).


El documento original debe considerarse como "GOTO incondicional considerado nocivo". En particular, abogaba por una forma de programación basada en construcciones condicionales ( if) e iterativas ( while), en lugar de en el código de prueba y salto común al principio. gotosigue siendo útil en algunos idiomas o circunstancias, donde no existe una estructura de control adecuada.


Lo evito ya que un compañero de trabajo / gerente indudablemente cuestionará su uso ya sea en una revisión del código o cuando se tropiece con él. Si bien creo que tiene usos (el caso de manejo de errores, por ejemplo), se encontrará en conflicto con algún otro desarrollador que tendrá algún tipo de problema con él.

Que no vale la pena.


Negar el uso de la declaración GOTO a los programadores es como decirle a un carpintero que no use un martillo, ya que podría dañar la pared mientras está clavando un clavo. Un programador real sabe cómo y cuándo usar un GOTO. He seguido detrás de algunos de estos llamados ''Programas estructurados''. He visto un código tan horrible como para evitar usar un GOTO, que podría disparar al programador. Ok, en defensa del otro lado, también he visto un código de espagueti real y, de nuevo, a esos programadores también se les debe disparar.

Aquí hay un pequeño ejemplo de código que he encontrado.

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

-----------------------O----------------------

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


No hay cosas que GOTO considere dañinas .

GOTO es una herramienta, y como todas las herramientas, puede ser usada y abusada .

Hay, sin embargo, muchas herramientas en el mundo de la programación que tienen una tendencia a ser objeto de abuso más que el uso , y GOTO es una de ellas. La declaración WITH de Delphi es otra.

Personalmente, tampoco uso el código típico , pero he tenido el uso imprevisto de GOTO y WITH que estaba justificado, y una solución alternativa habría contenido más código.

La mejor solución sería que el compilador le advierta que la palabra clave está manchada y que tendrá que rellenar un par de directivas pragma alrededor de la declaración para deshacerse de las advertencias.

Es como decirle a tus hijos que no corran con tijeras . Las tijeras no son malas, pero su uso quizás no sea la mejor manera de mantener tu salud.


Si GOTO fuera malvado, los compiladores serían malvados, porque generan JMPs. Si saltar en un bloque de código, especialmente después de un puntero, fuera inherentemente malo, la instrucción RETurn sería mala. Más bien, el mal está en el potencial de abuso.

A veces he tenido que escribir aplicaciones que tenían que hacer un seguimiento de una serie de objetos en los que cada objeto tenía que seguir una intrincada secuencia de estados en respuesta a los eventos, pero todo era definitivamente de un solo hilo. Una secuencia típica de estados, si se representa en pseudocódigo sería:

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

Estoy seguro de que esto no es nuevo, pero la forma en que lo manejé en C (++) fue definir algunas macros:

#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 ...

Entonces (asumiendo que el estado es inicialmente 0) la máquina de estado estructurado anterior se convierte en el código estructurado:

{ 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; }

Con una variación en esto, puede haber CALL y RETURN, por lo que algunas máquinas de estado pueden actuar como subrutinas de otras máquinas de estado.

¿Es inusual? Sí. ¿Toma algún aprendizaje por parte del mantenedor? Sí.¿Ese aprendizaje vale la pena? Creo que sí. ¿Podría hacerse sin GOTOs que salten en bloques? No


Sobre el único lugar en el que estoy de acuerdo con que se podría usar Goto es cuando necesita lidiar con los errores, y cada punto en particular en el que ocurre un error requiere un manejo especial.

Por ejemplo, si está capturando recursos y utilizando semáforos o mutex, tiene que tomarlos en orden y siempre debe liberarlos de la manera opuesta.

Algunos códigos requieren un patrón muy extraño de captura de estos recursos, y no puede simplemente escribir una estructura de control fácil de mantener y entender para manejar correctamente tanto la captura como la liberación de estos recursos para evitar el interbloqueo.

Siempre es posible hacerlo correctamente sin goto, pero en este caso y en algunos otros, Goto es en realidad la mejor solución principalmente para la legibilidad y el mantenimiento.

-Adán


Una cosa que no he visto en ninguna de las respuestas aquí es que una solución ''goto'' es a menudo más eficiente que una de las soluciones de programación estructurada que se mencionan a menudo.

Considere el caso de muchos bucles anidados, donde el uso de ''goto'' en lugar de un grupo de if(breakVariable)secciones es obviamente más eficiente. La solución "Ponga sus bucles en una función y use el retorno" a menudo es totalmente irrazonable. En el caso probable de que los bucles utilicen variables locales, ahora tiene que pasarlos todos a través de los parámetros de la función, posiblemente manejando cargas de dolores de cabeza adicionales que surgen de eso.

Ahora considere el caso de limpieza, que he usado con bastante frecuencia, y es tan común que presumiblemente ha sido responsable de la estructura try {} catch {} no disponible en muchos idiomas. La cantidad de controles y variables adicionales que se requieren para lograr lo mismo son mucho peores que las instrucciones para realizar el salto, y nuevamente, la solución de función adicional no es una solución en absoluto. No puedes decirme que es más manejable o más legible.

Ahora, el espacio de código, el uso de la pila y el tiempo de ejecución pueden no ser lo suficientemente importantes en muchas situaciones para muchos programadores, pero cuando se encuentra en un entorno integrado con solo 2 KB de espacio de código para trabajar, 50 bytes de instrucciones adicionales para evitar uno claramente definido ''goto'' es simplemente ridículo, y esta no es una situación tan rara como muchos programadores de alto nivel creen.

La afirmación de que ''goto es perjudicial'' fue muy útil para avanzar hacia la programación estructurada, incluso si siempre fue una generalización excesiva. En este punto, todos hemos escuchado lo suficiente como para desconfiar de su uso (como deberíamos). Cuando obviamente es la herramienta adecuada para el trabajo, no debemos tenerle miedo.