Siempre he sabido que goto
es algo malo, encerrado en un sótano en algún lugar que nunca se verá para siempre, pero hoy me encontré con un ejemplo de código que tiene mucho sentido goto
.
Tengo una IP donde necesito verificar si está dentro de una lista de IP y luego proceder con el código; de lo contrario, lanzar una excepción.
<?php
$ip = '192.168.1.5';
$ips = [
'192.168.1.3',
'192.168.1.4',
'192.168.1.5',
];
foreach ($ips as $i) {
if ($ip === $i) {
goto allowed;
}
}
throw new Exception('Not allowed');
allowed:
...
Si no uso, goto
entonces tengo que usar alguna variable como
$allowed = false;
foreach ($ips as $i) {
if ($ip === $i) {
$allowed = true;
break;
}
}
if (!$allowed) {
throw new Exception('Not allowed');
}
Mi pregunta es, ¿qué tiene de malo goto
cuando se usa para casos tan obvios e importantes?
Respuestas:
GOTO en sí no es un problema inmediato, son las máquinas de estado implícitas que las personas tienden a implementar con él. En su caso, desea un código que verifique si la dirección IP está en la lista de direcciones permitidas, por lo tanto
entonces su código quiere verificar una condición. El algoritmo para implementar esta verificación no debería ser una preocupación aquí, en el espacio mental de su programa principal, la verificación es atómica. Así es como debería ser.
Pero si coloca el código que hace la verificación en su programa principal, lo pierde. Introduce el estado mutable, ya sea explícitamente:
donde
$list_contains_ip
es su única variable de estado, o implícitamente:Como puede ver, hay una variable de estado no declarada en la construcción GOTO. Eso no es un problema per se, pero estas variables de estado son como piedras: cargar una no es difícil, cargar una bolsa llena de ellas te hará sudar. Su código no será el mismo: el próximo mes se le pedirá que distinga entre direcciones privadas y públicas. Un mes después, su código deberá admitir rangos de IP. El año que viene, alguien le pedirá que admita direcciones IPv6. En poco tiempo, su código se verá así:
Y quien tenga que depurar ese código te maldecirá a ti y a tus hijos.
Dijkstra lo pone así:
Y es por eso que GOTO se considera dañino.
fuente
$list_contains_ip = false;
declaración parece fuera de lugarHay algunos casos de uso legítimo para
GOTO
. Por ejemplo, para el manejo de errores y la limpieza en C o para implementar algunas formas de máquinas de estado. Pero este no es uno de estos casos. El segundo ejemplo es más legible en mi humilde opinión, pero aún más legible sería extraer el bucle a una función separada y luego regresar cuando encuentre una coincidencia. Aún mejor sería (en pseudocódigo, no sé la sintaxis exacta):Entonces, ¿qué tiene de malo
GOTO
? La programación estructurada utiliza funciones y estructuras de control para organizar el código de modo que la estructura sintáctica refleje la estructura lógica. Si algo solo se ejecuta condicionalmente, aparecerá en un bloque de declaración condicional. Si algo se ejecuta en un bucle, aparecerá en un bloque de bucle.GOTO
le permite eludir la estructura sintáctica saltando arbitrariamente, haciendo que el código sea mucho más difícil de seguir.Por supuesto, si no tiene otra opción que usar
GOTO
, pero si se puede lograr el mismo efecto con funciones y estructuras de control, es preferible.fuente
it's easier to write good optimizing compilers for "structured" languages.
elaborar? Como contraejemplo, la gente de Rust introdujo MIR , una representación intermedia en el compilador que reemplaza específicamente bucles, continúa, se rompe y similares con gotos, porque es más fácil de verificar y optimizar.goto
nido de ratas con banderas y condicionales (como en el segundo ejemplo del OP) es mucho menos legible que las juts que usan agoto
.Como otros han dicho, el problema no está en
goto
sí mismo; El problema es cómo usan las personasgoto
y cómo puede dificultar la comprensión y el mantenimiento del código.Suponga el siguiente fragmento de código:
¿Para qué valor se imprime
i
? ¿Cuándo se imprime? Hasta que no tenga en cuenta cada instancia degoto label
su función, no puede saberlo. La simple presencia de esa etiqueta destruye su capacidad de depurar código mediante una simple inspección. Para funciones pequeñas con una o dos ramas, no es un gran problema. Para funciones no pequeñas ...A principios de los años 90, nos dieron una pila de código C que manejaba una pantalla gráfica en 3D y nos dijo que hiciera que funcionara más rápido. Solo tenía alrededor de 5000 líneas de código, pero todo estaba dentro
main
, y el autor usó aproximadamente 15goto
ramificaciones en ambas direcciones. Para empezar, este era un código malo , pero la presencia de esosgoto
s lo hizo mucho peor. A mi compañero de trabajo le tomó alrededor de 2 semanas descifrar el flujo de control. Aún mejor, esosgoto
s resultaron en un código tan fuertemente acoplado consigo mismo que no pudimos hacer ningún cambio sin romper algo.Intentamos compilar con la optimización de nivel 1, y el compilador consumió toda la RAM disponible, luego todo el intercambio disponible, y luego entró en pánico en el sistema (que probablemente no tuvo nada que ver con los
goto
s, pero me gusta lanzar esa anécdota).Al final, le dimos al cliente dos opciones: reescribir todo desde cero o comprar hardware más rápido.
Compraron hardware más rápido.
Reglas de Bode para usar
goto
:if
ofor
owhile
declaración);goto
en lugar de una estructura de control.Hay casos en los que a
goto
es la respuesta correcta, pero son raros (salir de un bucle profundamente anidado es el único lugar donde lo usaría).EDITAR
Ampliando esa última declaración, este es uno de los pocos casos de uso válidos para
goto
. Supongamos que tenemos la siguiente función:Ahora, tenemos un problema: ¿qué sucede si una de las
malloc
llamadas falla a la mitad? No es probable que sea un evento como ese, no queremos devolver una matriz parcialmente asignada, ni queremos simplemente abandonar la función con un error; queremos limpiar después de nosotros mismos y desasignar cualquier memoria parcialmente asignada. En un lenguaje que arroja una excepción en una mala asignación, eso es bastante sencillo: simplemente escribe un controlador de excepciones para liberar lo que ya se ha asignado.En C, no tiene un manejo estructurado de excepciones; debe verificar el valor de retorno de cada
malloc
llamada y tomar las medidas apropiadas.¿Podemos hacer esto sin usar
goto
? Por supuesto que podemos, solo requiere un poco de contabilidad adicional (y, en la práctica, ese es el camino que tomaría). Pero, si está buscando lugares donde usar ungoto
no sea inmediatamente una señal de mala práctica o diseño, este es uno de los pocos.fuente
goto
incluso de acuerdo con estas reglas, no lo usaría en absoluto a menos que sea la única forma de ramificación disponible en el idioma, pero puedo ver que no sería una pesadilla demasiado grande para depurargoto
's si fueron escritos de acuerdo con estas reglas.GOTO
, incluyendo los famosos y asignados arosméticos de Fortran, si mal no recuerdo.return
,break
,continue
Ythrow
/catch
son esencialmente todos los goto - todos ellos de control de transferencia a otra pieza de código y todos podrían ser implementados con GOTOS - de hecho lo hice una vez en un proyecto escolar, un instructor de PASCAL estaba diciendo cuánto mejor era que Pascal básico debido a la estructura ... así que tuve que ser contrario ...Lo más importante sobre la Ingeniería de Software (voy a usar este término sobre Codificación para referirme a una situación en la que alguien le paga para crear una base de código junto con otros ingenieros que requiere mejoras y mantenimiento continuos) es hacer que el código sea legible. -hacer que haga algo es casi secundario. Su código se escribirá solo una vez, pero, en la mayoría de los casos, las personas pasarán días y semanas revisando / reaprendiendo, mejorando y reparándolo, y cada vez que ellos (o usted) tengan que comenzar desde cero e intentar recordar / descifrar tu codigo.
La mayoría de las características que se han agregado a los idiomas a lo largo de los años son para hacer que el software sea más fácil de mantener, no más fácil de escribir (aunque algunos idiomas van en esa dirección, a menudo causan problemas a largo plazo ...).
En comparación con declaraciones de control de flujo similares, los GOTO pueden ser casi tan fáciles de seguir en su mejor momento (un solo goto usado en un caso como usted sugiere), y una pesadilla cuando se abusa, y se abusa muy fácilmente ...
Entonces, después de lidiar con las pesadillas de los espaguetis durante unos años, simplemente dijimos "No", como comunidad no vamos a aceptar esto, demasiadas personas lo arruinan si se les da un poco de margen de maniobra, ese es realmente el único problema con ellos. Podrías usarlos ... pero incluso si es el caso perfecto, el próximo tipo asumirá que eres un programador terrible porque no entiendes la historia de la comunidad.
Se han desarrollado muchas otras estructuras solo para hacer que su código sea más comprensible: funciones, objetos, alcance, encapsulación, comentarios (!) ... así como los patrones / procesos más importantes como "DRY" (evitar la duplicación) y "YAGNI" (Reducción de la generalización excesiva / complicación del código): todo realmente solo importa para que el tipo SIGUIENTE lea su código (¡Quién probablemente será usted, después de que haya olvidado la mayor parte de lo que hizo en primer lugar!)
fuente
return
break
continue
throw
catch
GOTO
Es una herramienta. Se puede usar para bien o para mal.En los viejos tiempos, con FORTRAN y BASIC, era casi la única herramienta.
Cuando mira el código de esos días, cuando ve un
GOTO
tiene que averiguar por qué está allí. Puede ser parte de un idioma estándar que puedes entender rápidamente ... o puede ser parte de una estructura de control de pesadilla que nunca debería haber sido. No lo sabrá hasta que haya mirado, y es fácil equivocarse.La gente quería algo mejor, y se inventaron estructuras de control más avanzadas. Estos cubrieron la mayoría de los casos de uso, y las personas que fueron quemadas por malvados
GOTO
querían prohibirlos por completo.Irónicamente,
GOTO
no es tan malo cuando es raro. Cuando ves uno, sabes que está sucediendo algo especial y es fácil encontrar la etiqueta correspondiente, ya que es la única etiqueta cercana.Avance rápido hasta hoy. Eres profesor de programación de enseñanza. Usted podría decir "En la mayoría de los casos se debe utilizar las nuevas construcciones avanzadas, pero en algunos casos un simple
GOTO
puede ser más fácil de leer." Los estudiantes no van a entender eso. Van a abusarGOTO
para hacer código ilegible.En cambio, dices "
GOTO
malo.GOTO
Malvado. NoGOTO
apruebas examen". ¡Los estudiantes entenderán eso !fuente
Con la excepción de
goto
, todas las construcciones de flujo en PHP (y la mayoría de los lenguajes) tienen un alcance jerárquico.Imagine un código examinado a través de los ojos entrecerrados:
Independientemente de lo construcción de control
foo
es (if
,while
, etc.), sólo hay ciertas órdenes permitidas paraa
,b
yc
.Podrías tener
a
-b
-c
, oa
-c
, o inclusoa
-b
-b
-b
-c
. Pero nunca podrías tenerb
-c
oa
-b
-a
-c
.... a menos que tengas
goto
.goto
(en particular al revésgoto
) puede ser lo suficientemente problemático como para dejarlo solo, y utilizar construcciones jerárquicas de flujo bloqueado.goto
s tienen un lugar, pero principalmente como micro optimizaciones en lenguajes de bajo nivel. En mi opinión, no hay un buen lugar para eso en PHP.Para su información, el código de ejemplo se puede escribir incluso mejor que cualquiera de sus sugerencias.
fuente
En idiomas de bajo nivel, GOTO es inevitable. Pero en un nivel alto debe evitarse (en el caso de que el lenguaje lo admita) porque hace que los programas sean más difíciles de leer .
Todo se reduce a hacer que el código sea más difícil de leer. Se supone que los lenguajes de alto nivel hacen que el código sea más fácil de leer que los lenguajes de bajo nivel como, por ejemplo, ensamblador o C.
GOTO no causa calentamiento global ni causa pobreza en el tercer mundo. Simplemente hace que el código sea más difícil de leer.
La mayoría de los lenguajes modernos tienen estructuras de control que hacen que GOTO sea innecesario. Algunos como Java ni siquiera lo tienen.
De hecho, el término código spaguetti proviene de un código complicado y difícil de seguir causado por estructuras de ramificación no estructuradas.
fuente
goto
puede hacer que el código sea más fácil de leer. Cuando creó variables adicionales y ramas anidadas en comparación con un solo salto, el código es mucho más difícil de leer y comprender.goto
aparecer no son ejemplos de código de espagueti complicado, sino cosas bastante sencillas, a menudo emulando patrones de flujo de control en idiomas que no No tiene la sintaxis para ello: los dos ejemplos más comunes son romper múltiples bucles y el ejemplo en el OP dondegoto
se usa para implementarfor
-else
.goto
También es ideal para cosas como reintentos de excepción, reversión, máquinas de estado.No hay nada malo con las
goto
declaraciones en sí. Los errores están en algunas de las personas que usan inapropiadamente la declaración.Además de lo que dijo JacquesB (manejo de errores en C), está utilizando
goto
para salir de un ciclo no anidado, algo que puede hacer utilizandobreak
. En este caso es mejor usarlobreak
.Pero si tuviera un escenario de bucle anidado, el uso
goto
sería más elegante / simple. Por ejemplo:El ejemplo anterior no tiene sentido en su problema específico, porque no necesita un bucle anidado. Pero espero que solo veas la parte del bucle anidado.
Punto positivo: si su lista de IP es pequeña, su método está bien. Pero si la lista crece, sepa que su enfoque tiene una complejidad asintótica peor en tiempo de ejecución de O (n) . A medida que su lista crece, es posible que desee utilizar un método diferente que logre O (log n) (como una estructura de árbol) u O (1) (una tabla hash sin colisiones).
fuente
break
ycontinue
con un número (para romper / continuar tantas capas de bucles) o una etiqueta (para romper / continuar el bucle con esa etiqueta), cualquiera de los cuales generalmente debería ser preferible a usargoto
para bucles anidados.Cierto. No me importa
Cierto. No me importa
Cierto. No me importa
Cierto. Sin embargo, he sido ese codificador disciplinado. He visto lo que sucede con el código con el tiempo.
Goto
comienza bien. Luego surge la necesidad de reutilizar el código. Muy pronto me encuentro en un punto de quiebre sin tener ni idea de lo que está sucediendo incluso después de mirar el estado del programa.Goto
hace que sea difícil razonar sobre el código. Hemos trabajado muy duro creaciónwhile
,do while
,for
,for each
switch
,subroutines
,functions
, y más todo porque haciendo estas cosas conif
ygoto
es duro en el cerebro.Entonces no. No queremos mirar
goto
. Seguro que está vivo y bien en el binario, pero no necesitamos ver eso en la fuente. De hecho,if
está empezando a verse un poco inestable.fuente
Goto
le permite abstraer un problema convirtiéndolo en otro problema de códigos.If
te permite elegir esa abstracción. Hay mejores formas de abstraer y mejores formas de elegir la abstracción. Polimorfismo para uno.Los lenguajes de ensamblaje generalmente solo tienen saltos condicionales / incondicionales (el equivalente a GOTO. Las implementaciones anteriores de FORTRAN y BASIC no tenían sentencias de bloque de control más allá de una iteración contada (el bucle DO), dejando todo el resto del flujo de control a IFs y GOTO. estos idiomas se terminaron con una declaración numéricamente etiquetada. Como resultado, el código escrito para estos idiomas podría ser, y a menudo era, difícil de seguir y propenso a errores.
Para subrayar el punto, está la afirmación " VENIDO DE ", inventada de manera graciosa .
Prácticamente no hay necesidad de usar GOTO en lenguajes como C, C ++, C #, PASCAL, Java, etc .; se pueden usar construcciones alternativas que seguramente serán igual de eficientes y mucho más fáciles de mantener. Es cierto que un GOTO en un archivo fuente no será un problema. El problema es que no se necesitan muchos para hacer que una unidad de código sea difícil de seguir y propenso a errores de mantenimiento. Es por eso que la sabiduría aceptada es evitar GOTO siempre que sea posible.
Este artículo de wikipedia sobre la declaración goto podría ser útil
fuente