¿GOTO todavía se considera dañino?

De hecho, volví y volví a leer la carta original a Niklaus Wirth, el editor de CACM titulada Un caso contra la declaración IR A.

Esto es lo que pienso:

He estado programando durante casi 40 años.

Hay casos en los que GOTO (o JMP o su equivalente) es inevitable. Estás y, debido a alguna condición relativa al estado, debes descontinuar lo que estás haciendo y estar , como AHORA MISMO. Auge. Hecho.

Los argumentos de Dijkstra aún se mantienen: cuanto más complejo es el entorno de ejecución del programa, menos capaces somos de razonar sobre el estado interno y gestionar las transiciones en orden de ejecución sin arruinar realmente nada. Caso en cuestión: estoy en el medio de ejecutar una llamada a procedimiento, y de repente tengo un GOTO que me saca del procedimiento en otra parte del programa principal. ¿Cómo, exactamente, se limpia la pila? ¿O alguien sabe que había una pila que necesita ser limpiada, o un estado de registro previo a la llamada que necesita ser restaurado?

Respuesta corta, no posible. Cambió el contador del programa de X a Y, y hará exactamente lo que le dijo que hiciera. Porque eso es lo que hacen las CPU.

Aquí está la ventaja: incluso escribiendo código simple (ya sea SO, o sistemas de control sin SO, o código de prueba para verificar nuevas CPU), rara vez uso GOTO. Tal vez una docena de veces en 40 años, una vez que llegué al punto en que me consideraba competente.

Pero todas y cada una de esas veces, era un requisito absoluto. La única forma de cumplir con el rendimiento y la función que el programa fue diseñado para ofrecer.

En estos días, con lenguajes de programación funcionales avanzados y marcos orientados a objetos pesados ​​por el estado, me sorprendería que la mayoría de los programadores practicantes supieran qué demonios es GOTO y mucho menos por qué se considera “Malo”; simplemente no se enseña y no se aprende.

Pero para aquellos de ustedes tentados a usarlo, lea el periódico. (Solo dice que es una carta al editor, es realmente un documento). Y piensa mucho en lo que estás a punto de hacer.

Dijstra escribió su artículo en 1968. FORTRAN era el idioma dominante entre muchos idiomas en uso. Este y muchos otros lenguajes no tenían bucles, excepto los interactivos, donde un índice pasó de un valor inicial a un valor final. La instrucción condicional era una instrucción IF ( etiqueta IF de condición GOTO). Los GOTO eran necesarios. Se necesitaban diagramas de flujo para comprender todos los programas, excepto los más simples. Los subprogramas tenían interfaces complicadas. Las variables globales fueron comunes. Las clases y la programación orientada a objetos estaban lejos en el futuro.

Identificó uno de los problemas con la programación y los lenguajes en ese momento, los GOTO. En respuesta a su observación, se crearon nuevos lenguajes con la programación estructurada en mente (bloques, REPETIR, MIENTRAS, SI / MÁS, CASO, DESCANSO) y los lenguajes antiguos como FORTRAN se expandieron para incluir la programación estructurada.

Con la programación estructurada ya no necesitamos diagramas de flujo. La sangría es suficiente para ver visualmente el flujo. (No sangró en el antiguo FORTRAN; las instrucciones comenzaron en la columna 7.) Hay algunos casos en que los GOTO simplifican la programación, pero rara vez se usan para que no se vuelvan confusos.

Sí, si elimina las construcciones de programación estructurada y regresa a la programación preestructurada donde el flujo del programa fue determinado por GOTO, eso sería perjudicial.

El problema con GOTO es que hay muchas razones por las que podría usar GOTO, pero la declaración GOTO en sí misma no tiene contexto. Puede usar GOTO para la ejecución condicional. Puede usar GOTO para saltar al código compartido y luego saltar hacia atrás. Puede usar GOTO para repetir algún código.

El problema es que el programador humano que está leyendo el código no puede mirar el GOTO y decir cómo lo está usando. Tienes que poner comentarios o tiene que averiguar la intención de tu código.

Entonces, hemos resuelto el problema al proporcionar construcciones de orden superior que describen la intención. La ejecución condicional se denota con un bloque if … then..else . Las funciones se utilizan para mantener el código compartido. Los bucles se utilizan para el código repititve. Estas construcciones de orden superior proporcionan 2 cosas a) proporcionan un modelo alrededor del cual el desarrollador piensa yb) proporcionan una forma para que el autor del código exprese su intención al lector del código

Las construcciones de orden superior, sin embargo, agregan restricciones. Probablemente puedas implementar un mecanismo loco usando GOTOs. Por ejemplo, podría saltar a la mitad de un bucle usando GOTO. Eso es una locura, ¿verdad? Si, es una locura. Las restricciones son buenas la mayor parte del tiempo porque reducen la entropía en el código. De los millones de cosas posibles que puedes hacer con GOTO, solo puedes hacer 4 ahora. Mientras pueda implementar todo lo que necesita usando las construcciones de orden superior, no necesita usar GOTO

No, no hay nada inherentemente malo en GOTO. Es solo una herramienta fácil de abusar. Tenga en cuenta estas estructuras básicas:


Primero en verde, es la construcción “while … do”. Esto se considera una buena práctica de programación.

Segundo en amarillo, es la construcción “repetir … hasta”. Use esto con precaución. Muchos programadores evitarán esto por completo.

Finalmente en rojo, está el “código de espagueti”; la mezcla impía de while / do / repeat / hasta. Este es un bucle que tiene una condición de ruptura enterrada en el medio. Pueden ser particularmente desagradables de encontrar y depurar.

Las construcciones son importantes. Las declaraciones no son. En algunos idiomas, GOTO es la única forma de crear una construcción “while … do”. Sin embargo, si un lenguaje tiene declaraciones “while … do”, entonces no debería haber necesidad de usar un GOTO.

Regresar de una función antes de tiempo o lanzar una excepción también es una forma de hacer un código de espagueti. Si bien es comúnmente aceptado, le animo a que use estas técnicas con precaución. Evite poner tales declaraciones dentro de un bucle.

También debo agregar que usar variables arbitrarias de “bandera” para salir de un ciclo es igual de malo. Todavía puedes terminar con espagueti usando nada más que “mientras … hacer”.

Es especialmente útil en el código de subprocesos donde tiene muchos mutexes y desea salir de la función sin desbloquear por cada caso de falla y se presta bien para cualquier tipo de limpieza posterior a la función.

Es casi una necesidad en condiciones de bucle anidado peludo donde necesita continuar en el bucle externo desde un bucle interno para distinguir qué bucle continuar.

En C, la mayoría de los usos abusivos están prohibidos por el compilador en estos días.

Mientras que las otras respuestas describen por qué GOTO sigue siendo tan dañino como siempre, puedo pensar en otra forma de ver que no sería “considerado” dañino.

GOTO ya no se considera “dañino” porque se ha eliminado de los lenguajes de programación que la gente usa comúnmente. Por lo tanto, los programadores actuales ni siquiera saben qué es GOTO. Y, aquellos que lo consideran dañino y han respondido aquí, saben que es una reliquia de una época pasada.

GOTO es una herramienta eléctrica. Similar a mi respuesta anterior sobre los punteros, todas las herramientas eléctricas son peligrosas si se usan sin habilidades y no se usan correctamente.

Sin embargo, hoy en día, hay muy pocas circunstancias en las que ya es absolutamente necesario. La capacitación y las prácticas deben centrarse en los mecanismos que no requieren su uso.

Hay un buen uso de goto en aquellos idiomas que no admiten excepciones directamente, como por ejemplo, C. Si se usa correctamente, la instrucción “goto” permite saltar directamente a la parte de inicialización de una función, por lo que se asignan elementos (archivos , memoria, etc.) se pueden liberar antes de abandonar la función con un código de retorno de error.

El artículo ¿Es realmente “goto” malvado? informa un ejemplo completo de ir a mejorar la calidad del código.

Creo que GOTO solo tiene dos lugares en el código moderno:

  1. Si tiene un objetivo GOTO en la parte inferior de su función para manejar la limpieza antes de regresar de un error. Y los lenguajes como Python que manejan excepciones sin mucha penalización de rendimiento han eliminado prácticamente ese uso.
  2. Si va a escribir un bucle infinito “falso GOTO” como “while (verdadero)”, también podría convertirlo en un verdadero GOTO. Pero solo si es el único ciclo en su función / método y constituye casi todo el código en esa función / método. Y casi nunca lo he hecho porque prefiero tener un bucle sangrado.

Y dicho eso, no creo que haya usado un GOTO en veinticinco años, aunque he permitido que otros lo usen en revisiones de código.

bueno, goto es como cualquier otra pieza de código. Puede ser súper útil o puede usarse para ofuscarse. Veo goto (s) con frecuencia en el kernel de Linux para algo como el manejo de errores y el bloqueo y desbloqueo y el uso de if en lugar de goto hará que el código sea realmente grande y muy difícil de seguir en tales casos

Solo he usado un goto una vez en mis días posteriores a Fortran, y eso fue mientras estaba transcribiendo un código existente antes de refactorizarlo. Con los idiomas modernos, siempre hay una mejor manera.

A veces uso goto para salir de dos o más bucles. Se podría hacer con un bool y un descanso en cada ciclo, pero eso parece mucho más desordenado que un simple goto

Aquí está el núcleo (en mi opinión) del argumento de Dijkstra, de su carta original:

El uso desenfrenado de la declaración go to tiene una consecuencia inmediata de que se vuelve terriblemente difícil encontrar un conjunto significativo de coordenadas para describir el progreso del proceso. Por lo general, las personas también tienen en cuenta los valores de algunas variables bien elegidas, ¡pero esto está fuera de discusión porque es relativo al progreso que se debe entender el significado de estos valores!

Esto no ha cambiado y nunca cambiará. Cuando transfiere el control de aquí a allá de manera arbitraria, está haciendo un montón de suposiciones que pueden o no ser válidas, probablemente no están documentadas y es posible que ni siquiera hayan sido consideradas.

Dicho eso, Dijkstra dijo desenfrenado y yo dije arbitrariamente . Los explosivos también son dañinos, pero la gente todavía los usa. Como último recurso, entendido cuidadosamente y manejado con cuidado (al igual que una demolición controlada o un espectáculo de fuegos artificiales), son útiles.

No creo que GOTO fuera dañino per se, sino que dificultó mucho seguir el código. No creo que eso haya cambiado.

Dicho esto, creo que hay momentos, muy pocos, pero momentos en los que un GOTO podría ser razonable de usar. En Perl, que tiene una sobrecarga de llamadas de función razonablemente alta, he usado construcciones GOTO con bucles anidados dobles donde necesitaba romper, continuar o reiniciar el bucle externo desde dentro del bucle interno. Creo que este es un caso justificable si todo el código se ajusta fácilmente dentro de una sola ventana del editor, y el uso de GOTO simplifica el código o lo deja más claro. Por lo general, los casos en los que lo he usado fueron bucles muy ajustados en los que el rendimiento importaba, donde la adición de una variable de estado y las instrucciones adicionales de if else para gestionar la caída del bucle interno habrían aumentado significativamente la cantidad de código, lo que ofuscaría el verdadero propósito del código.