¿Qué tiene de malo ir a goto cuando se usa para estos casos obvios y relevantes?

40

Siempre he sabido que gotoes 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, gotoentonces 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 gotocuando se usa para casos tan obvios e importantes?

php_nub_qq
fuente
Los comentarios no son para discusión extendida; Esta conversación se ha movido al chat .
maple_shaft

Respuestas:

120

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

if (!contains($ips, $ip)) throw new Exception('Not allowed');

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:

$list_contains_ip = undef;        # STATE: we don't know yet

foreach ($ips as $i) {
  if ($ip === $i) {
      $list_contains_ip = true;   # STATE: positive
      break;
  }
                                  # STATE: we still don't know yet, huh?                                                          
                                  # Well, then...
  $list_contains_ip = false;      # STATE: negative
}

if (!$list_contains_ip) {
  throw new Exception('Not allowed');
}

donde $list_contains_ipes su única variable de estado, o implícitamente:

                             # STATE: unknown
foreach ($ips as $i) {       # What are we checking here anyway?
  if ($ip === $i) {
    goto allowed;            # STATE: positive
  }
                             # STATE: unknown
}
                             # guess this means STATE: negative
throw new Exception('Not allowed');

allowed:                     # Guess we jumped over the trap door

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í:

if ($ip =~ /:/) goto IP_V6;
if ($ip =~ /\//) goto IP_RANGE;
if ($ip =~ /^10\./) goto IP_IS_PRIVATE;

foreach ($ips as $i) { ... }

IP_IS_PRIVATE:
   foreach ($ip_priv as $i) { ... }

IP_V6:
   foreach ($ipv6 as $i) { ... }

IP_RANGE:
   # i don't even want to know how you'd implement that

ALLOWED:
   # Wait, is this code even correct?
   # There seems to be a bug in here.

Y quien tenga que depurar ese código te maldecirá a ti y a tus hijos.

Dijkstra lo pone así:

El uso desenfrenado de la declaración go to tiene como consecuencia inmediata que resulta terriblemente difícil encontrar un conjunto significativo de coordenadas para describir el progreso del proceso.

Y es por eso que GOTO se considera dañino.

Wallenborn
fuente
22
Esa $list_contains_ip = false;declaración parece fuera de lugar
Bergi
1
Una bolsa llena de guijarros es fácil, tengo una bolsa conveniente para guardarlos. : p
whatsisname
13
@ Bergi: Tienes razón. Eso muestra lo fácil que es arruinar estas cosas.
wallenborn
66
@whatsisname Esa bolsa se llama función / clase / paquete para guardarlos. usar gotos es como hacer malabarismos con ellos y tirar la bolsa.
Falco
55
Me encanta la cita de Dijkstra, no la había escuchado así, pero es un buen punto. En lugar de tener un flujo prácticamente de arriba a abajo, de repente tienes algo que puede saltar por toda la página al azar. Dicho de manera sucinta es bastante impresionante.
Bill K
41

Hay 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):

if (!in_array($ip, $ips)) throw new Exception('Not allowed');

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

JacquesB
fuente
55
@jameslarge ¿Quieres 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.
8bittree
66
"si no tienes otra opción, usa GOTO" Esto sería extremadamente raro. Honestamente, no puedo pensar en un caso en el que no tenga otra opción, fuera de restricciones completamente absurdas que impiden refactorizar el código existente o tonterías similares.
jpmc26
8
Además, IMO, emular un gotonido de ratas con banderas y condicionales (como en el segundo ejemplo del OP) es mucho menos legible que las juts que usan a goto.
3
@ 8bittree: La programación estructurada está destinada al idioma de origen, no al idioma de destino del compilador. El lenguaje de destino para los compiladores nativos son las instrucciones de la CPU, que definitivamente no está "estructurada", en el sentido del lenguaje de programación.
Robert Harvey
44
@ 8bittree La respuesta a su pregunta sobre MIR se encuentra en el enlace que proporcionó: "Pero está bien tener una construcción de este tipo en MIR, porque sabemos que solo se usará de formas particulares, como para expresar un bucle o una interrupción ". En otras palabras, debido a que goto no está permitido en Rust, saben que está estructurado y que la versión intermedia con gotos también lo está. En última instancia, el código tiene que pasar a ir a comandos de máquina. Lo que obtengo de su enlace es que simplemente están agregando un paso incremental en la conversión de lenguaje de alto nivel a lenguaje de bajo nivel.
JimmyJames
17

Como otros han dicho, el problema no está en gotosí mismo; El problema es cómo usan las personas gotoy cómo puede dificultar la comprensión y el mantenimiento del código.

Suponga el siguiente fragmento de código:

       i = 4;
label: printf( "%d\n", i );

¿Para qué valor se imprime i? ¿Cuándo se imprime? Hasta que no tenga en cuenta cada instancia de goto labelsu 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 15 gotoramificaciones en ambas direcciones. Para empezar, este era un código malo , pero la presencia de esos gotos lo hizo mucho peor. A mi compañero de trabajo le tomó alrededor de 2 semanas descifrar el flujo de control. Aún mejor, esos gotos 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 gotos, 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:

  1. Ramificarse solo hacia adelante;
  2. No pasar por alto las estructuras de control (es decir, no se ramifican en el cuerpo de un ifo foro whiledeclaración);
  3. No utilizar gotoen 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:

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  for ( i = 0; i < N; i ++ )
  {
    arr[i] = malloc( sizeof *arr[i] * M );
    for ( j = 0; j < M; j++ )
    {
      arr[i][j] = malloc( sizeof *arr[i][j] * P );
      for ( k = 0; k < P; k++ )
        arr[i][j][k] = initial_value();
    }
  }
  return arr;
}

Ahora, tenemos un problema: ¿qué sucede si una de las mallocllamadas 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 mallocllamada y tomar las medidas apropiadas.

T ***myalloc( size_t N, size_t M, size_t P )
{
  size_t i, j, k;

  T ***arr = malloc( sizeof *arr * N );
  if ( arr )
  {
    for ( i = 0; i < N; i ++ )
    {
      if ( !(arr[i] = malloc( sizeof *arr[i] * M )) )
        goto cleanup_1;

      for ( j = 0; j < M; j++ )
      {
        if ( !(arr[i][j] = malloc( sizeof *arr[i][j] * P )) )
          goto cleanup_2;

        for ( k = 0; k < P; k++ )
          arr[i][j][k] = initial_value();
      }
    }
  }
  goto done;

  cleanup_2:
    // We failed while allocating arr[i][j]; clean up the previously allocated arr[i][j]
    while ( j-- )
      free( arr[i][j] );
    free( arr[i] );
    // fall through

  cleanup_1:
    // We failed while allocating arr[i]; free up all previously allocated arr[i][j]
    while ( i-- )
    {
      for ( j = 0; j < M; j++ )
        free( arr[i][j] );
      free( arr[i] );
    }

    free( arr );
    arr = NULL;

  done:
    return arr;
}

¿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 un gotono sea inmediatamente una señal de mala práctica o diseño, este es uno de los pocos.

John Bode
fuente
Me gustan estas reglas. Todavía no lo usaría gotoincluso 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 depurar goto's si fueron escritos de acuerdo con estas reglas.
Comodín
si necesita salir de un bucle profundamente anidado, simplemente necesita hacer que ese bucle sea otra función y regresar
bunyaCloven
66
Como alguien lo resumió una vez para mí: "No es un problema recurrente, es un problema derivado". Como especialista en optimización de software, estoy de acuerdo en que el flujo de control no estructurado puede lanzar compiladores (¡y humanos!) Para un bucle. Una vez pasé horas descifrando una máquina de estado simple (cuatro de cinco estados) en una función BLAS, que usaba varias formas GOTO, incluyendo los famosos y asignados arosméticos de Fortran, si mal no recuerdo.
njuffa
@njuffa "Como especialista en optimización de software, estoy de acuerdo en que el flujo de control no estructurado puede generar compiladores (¡y humanos!) para un ciclo". - ¿más detalles? Pensé que prácticamente todos los compiladores de optimización hoy en día usan SSA como IR, lo que significa que está usando un código similar a goto debajo.
Maciej Piechotka
@MaciejPiechotka No soy un ingeniero compilador. Sí, todos los compiladores modernos parecen usar SSA en su representación intermedia. Los compiladores prefieren construcciones de control de entrada única-salida única, ¿no están seguros de cómo el uso de SSA juega con eso? Al observar el código de máquina generado y el uso de los recursos del compilador, y las discusiones con los ingenieros del compilador, el flujo de control no estructurado interfiere con la optimización de las transformaciones del código, presumiblemente debido al problema del "origen". El uso de recursos (tiempo, memoria) puede tener un impacto negativo en el rendimiento del código si el compilador decide omitir una optimización como demasiado costosa.
njuffa
12

return, break, continueY throw/ catchson 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!)

Bill K
fuente
3
Estoy votando esto solo por la frase: "Lo más importante ... es hacer que el código sea legible; lograr que haga algo es casi secundario". Yo diría que solo un poco diferente; no es tanto hacer que el código sea legible como hacerlo simple . Pero de cualquier manera, es tan cierto que obligarlo a hacer algo es secundario.
Comodín
" , Y / son todos esencialmente los goto" en desacuerdo, el antiguo respeto de los principios de la programación estructurada (aunque se podría argumentar acerca de las excepciones), van a definitivamente no. A la luz de esta pregunta, este comentario no gana mucho. returnbreakcontinuethrowcatch
Eric
@eric Puede modelar cualquiera de las declaraciones con GOTO. Puede asegurarse de que todos los GOTO estén de acuerdo con los principios de programación estructurados: simplemente tienen una sintaxis diferente y deben, para duplicar exactamente la funcionalidad, incluir otras operaciones como "if". La ventaja de las versiones estructuradas es que están restringidas a admitir solo ciertos objetivos: usted SABE cuando ve un continuar aproximadamente donde se ubicará el objetivo. La razón por la que lo mencioné fue para señalar que el problema es la abusabilidad, no porque no se pueda usar correctamente.
Bill K
7

GOTOEs 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 GOTOtiene 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 GOTOquerían prohibirlos por completo.

Irónicamente, GOTOno 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 GOTOpuede ser más fácil de leer." Los estudiantes no van a entender eso. Van a abusar GOTOpara hacer código ilegible.

En cambio, dices " GOTOmalo. GOTOMalvado. No GOTOapruebas examen". ¡Los estudiantes entenderán eso !

Stig Hemmer
fuente
44
De hecho, esto es cierto para todas las cosas obsoletas. Globales Nombres de variables de una letra. Código auto modificable. Eval. Incluso, sospecho, entrada de usuario sin filtrar. Una vez que nos hemos condicionado para evitar cosas como la peste, entonces estamos en una posición adecuada para evaluar si cierto uso de ellos, bien señalizado, podría ser la solución correcta a un problema específico. Antes de eso, es mejor tratarlos simplemente como prohibidos.
Dewi Morgan el
4

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:

a;
foo {
    b;
}
c;

Independientemente de lo construcción de control fooes ( if, while, etc.), sólo hay ciertas órdenes permitidas para a, byc .

Podrías tener a- b- c, o a- c, o incluso a- b- b- b- c. Pero nunca podrías tener b- co a- b- a-c .

... a menos que tengas goto.

$a = 1;
first:
echo 'a';
if ($a === 1) {
    echo 'b';
    $a = 2;
    goto first;
}
echo 'c'; 

goto(en particular al revés goto) puede ser lo suficientemente problemático como para dejarlo solo, y utilizar construcciones jerárquicas de flujo bloqueado.

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

if(!in_array($ip, $ips, true)) {
    throw new Exception('Not allowed');
}
Paul Draper
fuente
2

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.

Tulains Córdova
fuente
66
gotopuede 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.
Matthew Whited el
@MatthewWhited Quizás si tiene un solo goto en un método de diez líneas. Pero la mayoría de los métodos no tienen diez líneas de largo y la mayoría de las veces cuando las personas usan GOTO no lo usan "solo esta vez".
Tulains Córdova
2
@MatthewWhited 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. ¿O el código spaguetti es más fácil de rojo ahora? Tal vez. El vinilo está regresando, ¿por qué no ir a GOTO?
Tulains Córdova
3
@Tulains Tengo que creer que existen cosas tan malas porque la gente sigue quejándose en ese momento, pero la mayoría de las veces que veo gotoaparecer 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 donde gotose usa para implementar for- else.
1
El código de espagueti no estructurado proviene de lenguajes donde no existen funciones / métodos. gotoTambién es ideal para cosas como reintentos de excepción, reversión, máquinas de estado.
Matthew Whited el
1

No hay nada malo con las gotodeclaraciones 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 gotopara salir de un ciclo no anidado, algo que puede hacer utilizando break. En este caso es mejor usarlo break.

Pero si tuviera un escenario de bucle anidado, el uso gotosería más elegante / simple. Por ejemplo:

$allowed = false;

foreach ($ips as $i) {
    foreach ($ips2 as $i2) {
        if ($ip === $i && $ip === $i2) {
            $allowed = true;
            goto out;
        }
    }
}

out:

if (!$allowed) {
    throw new Exception('Not allowed');
}

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

cavernícola
fuente
66
No estoy seguro acerca de PHP, pero muchos idiomas admitirán el uso breaky continuecon 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 usar gotopara bucles anidados.
8bittree
@ 8bittree buen punto. No sabía que la ruptura de PHP es tan elegante. Solo sé que la ruptura de C no es tan elegante. La ruptura de Perl (lo llaman el último ) también solía ser similar a la de C (es decir, no elegante como PHP), pero también se volvió elegante después de algún punto. Supongo que mi respuesta se está volviendo cada vez menos útil a medida que más idiomas están introduciendo versiones elegantes de break :)
hombre de las cavernas
3
una ruptura con un valor de profundidad no resolvería el problema sin la variable adicional. Una variable en sí misma que crea un nivel adicional de complejidad.
Matthew Whited el
Por supuesto, solo porque tenga bucles anidados no significa que necesite un goto, hay otras formas de manejar con elegancia esa situación.
NPSF3000
@ NPSF3000 ¿Podría ofrecer un ejemplo de un bucle anidado que pueda salir sin usar una instrucción goto , mientras que también utiliza un lenguaje que no tiene una versión elegante de break que especifique la profundidad?
hombre de las cavernas
0

¡Con goto puedo escribir código más rápido!

Cierto. No me importa

¡Goto existe en asamblea! Simplemente lo llaman jmp.

Cierto. No me importa

Goto resuelve problemas de manera más simple.

Cierto. No me importa

En manos de un código de desarrollador disciplinado que usa goto puede ser más fácil de leer.

Cierto. Sin embargo, he sido ese codificador disciplinado. He visto lo que sucede con el código con el tiempo. Gotocomienza 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. Gotohace que sea difícil razonar sobre el código. Hemos trabajado muy duro creación while, do while, for, for each switch, subroutines, functions, y más todo porque haciendo estas cosas con ify gotoes 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, ifestá empezando a verse un poco inestable.

naranja confitada
fuente
1
"Goto resuelve los problemas de manera más simple". / "Cierto. No me importa". - Odiaría ver tu código donde evitas deliberadamente soluciones simples a favor de lo que es más extensible. (¿Has oído hablar de YAGNI?)
user253751
1
@Wildcard if y goto son formas muy simples de resolver problemas que se resuelven mejor de otras maneras. Son frutas bajas. No se trata del idioma particular. Gotole permite abstraer un problema convirtiéndolo en otro problema de códigos. Ifte permite elegir esa abstracción. Hay mejores formas de abstraer y mejores formas de elegir la abstracción. Polimorfismo para uno.
candied_orange
2
Aunque estoy de acuerdo con usted, esta es una respuesta mal redactada. "Cierto, no me importa" no es un argumento convincente para nada. Creo que su mensaje principal es este: aunque goto es atractivo para las personas que prefieren usar herramientas eficientes y potentes, conduce a una cantidad sorprendentemente enorme de tiempo perdido más adelante, incluso cuando se tiene cuidado.
Artelius
1
@artelius El tema "verdadero, no me importa" no pretende ser convincente. Está destinado a ilustrar los muchos puntos que concederé para ir a Goto y aún así enfrentarlo. Por lo tanto, discutir esos puntos conmigo no tiene sentido. Ya que ese no es mi punto. Obtener el punto?
candied_orange
1
Simplemente creo que una buena respuesta no solo expresa tu opinión, sino que también la explica y permite al lector hacer su propio juicio. Tal como está, no está claro por qué los buenos puntos de goto no son suficientes para superar sus problemas. La mayoría de nosotros hemos pasado horas tirando de los pelos tratando de encontrar errores que no impliquen goto. No es la única construcción sobre la que es difícil razonar. Alguien que hace esta pregunta probablemente tenga mucha menos experiencia y necesite que las cosas se pongan en perspectiva.
Artelius
-1

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

Zenilogix
fuente