Me encontré con esta pregunta hace un segundo, y estoy sacando parte del material de allí: ¿Hay un nombre para la construcción 'break n'?
Esto parece ser una forma innecesariamente compleja para que las personas tengan que instruir al programa para que salga de un bucle doble anidado:
for (i = 0; i < 10; i++) {
bool broken = false;
for (j = 10; j > 0; j--) {
if (j == i) {
broken = true;
break;
}
}
if (broken)
break;
}
Sé que a los libros de texto les gusta decir que las declaraciones de goto son el demonio, y no me gustan en absoluto, pero ¿justifica esto una excepción a la regla?
Estoy buscando respuestas que traten con n-nested para bucles.
NOTA: Ya sea que responda sí, no o en algún punto intermedio, las respuestas completamente cerradas no son bienvenidas. Especialmente si la respuesta es no, proporcione una razón buena y legítima (que de todos modos no está demasiado lejos de las regulaciones de Stack Exchange).
fuente
for (j = 10; j > 0 && !broken; j--)
entonces, no necesitaría labreak;
declaración. Si mueve la declaración debroken
a antes de que comience el ciclo externo, entonces si eso podría escribirse comofor (i = 0; i < 10 && !broken; i++)
entonces, creo que nobreak;
es necesario.goto
: goto (Referencia de C #) - "La instrucción goto también es útil para salir de bucles profundamente anidados".goto
se requiere realmente en un interruptor C # para pasar a otro caso después de ejecutar cualquier código dentro del primer caso. Sin embargo, ese es el único caso que he usado goto.Respuestas:
La aparente necesidad de una declaración ir surge de que usted elija expresiones condicionales pobres para los bucles .
Usted declara que desea que el ciclo externo continúe tanto tiempo
i < 10
y que el más interno continúe tanto tiempoj > 0
.Pero en realidad eso no es lo que quería , simplemente no le dijo a los bucles la condición real que quería que evaluaran, luego desea resolverlo usando break o goto.
Si le dice a los bucles sus verdaderas intenciones para comenzar , entonces no necesita descansos ni goto.
fuente
i
al final del bucle externo, por lo que hacer un cortocircuito en el incremento es importante para que el código se vea 100% equivalente. No digo que nada en su respuesta sea incorrecto, solo quería señalar que romper el ciclo de inmediato era un aspecto importante del código.i
al final del bucle externo en la pregunta de OP.goto
(estoy seguro de que hay mejores), incluso si generó una pregunta que parece usarlo como tal, sino para ilustrar la acción básica de losbreak(n)
idiomas que no No lo soportes.En este caso, podría refactorizar el código en una rutina separada y luego solo
return
desde el bucle interno. Eso negaría la necesidad de salir del bucle externo:fuente
i
después del final del bucle externo. Entonces, para reflejar realmente esei
es el valor importante, aquí elreturn true;
yreturn false;
ambos deberían serreturn i;
.No, no creo que
goto
sea necesario. Si lo escribes así:Tener
&&!broken
ambos bucles te permite salir de ambos cuandoi==j
.Para mí, este enfoque es preferible. Las condiciones del bucle son extremadamente claro:
i<10 && !broken
.Una versión de trabajo se puede ver aquí: http://rextester.com/KFMH48414
Código:
Salida:
fuente
broken
en su primer ejemplo en absoluto ? Solo usa break en el bucle interno. Además, si absolutamente debe usarbroken
, ¿por qué declararlo fuera de los bucles? Solo declaralo dentro deli
bucle. En cuanto alwhile
bucle, no lo uses. Sinceramente, creo que logra ser aún menos legible que la versión goto.broken
se usa una variable llamada porque no me gusta usar labreak
declaración (excepto en caso de cambio, pero eso es todo). Se declara fuera del bucle externo en caso de que desee romper TODOS los bucles cuandoi==j
. Tienes razón, en general,broken
solo necesita ser declarado antes del ciclo más externo que se romperá.i==2
al final del ciclo. La condición de éxito también debe provocar un cortocircuito en el incremento si debe ser 100% equivalente a abreak 2
.broken = (j == i)
el if y, si el lenguaje lo permite, poner la declaración rota dentro de la inicialización del bucle externo. Aunque es difícil decir estas cosas para este ejemplo específico, ya que todo el ciclo es un no-op (no devuelve nada y no tiene efectos secundarios) y si hace algo, posiblemente lo haga dentro del if (aunque todavía me gustabroken = (j ==i); if broken { something(); }
más personalmente)i
después del final del bucle externo. En otras palabras, el único pensamiento realmente importante es cuándo sale exactamente del bucle externo.La respuesta corta es "depende". Recomiendo elegir con respecto a la legibilidad y la comprensibilidad de su código. La forma de ir a Goto podría ser más legible que modificar la condición, o viceversa. También puede tener en cuenta el principio de menor sorpresa, la guía utilizada en la tienda / proyecto y la coherencia de la base del código. Por ejemplo, goto para dejar el interruptor o para el manejo de errores es una práctica común en la base del código del kernel de Linux. También tenga en cuenta que algunos programadores pueden tener problemas para leer su código (ya sea planteado con el mantra "goto is evil", o simplemente no lo aprendieron porque su maestro lo cree) y algunos de ellos podrían incluso "juzgarlo".
En términos generales, un goto que va más allá en la misma función a menudo es aceptable, especialmente si el salto no es demasiado largo. Su caso de uso de dejar un bucle anidado es uno de los ejemplos conocidos de goto "no tan malvado".
fuente
En su caso, goto es una mejora suficiente que parece tentador, pero no es tanto una mejora que vale la pena romper la regla contra el uso de ellos en su base de código.
Piense en su futuro revisor: tendrá que pensar: "¿Es esta persona un mal programador o de hecho es un caso en el que valió la pena el goto? Permítanme tomar 5 minutos para decidir esto por mí mismo o investigarlo en el Internet." ¿Por qué demonios le harías eso a tu futuro codificador cuando no puedes usar goto por completo?
En general, esta mentalidad se aplica a todo el código "malo" e incluso lo define: si funciona ahora, y es bueno en este caso, pero crea un esfuerzo para verificar que sea correcto, lo que en esta era un goto siempre hará, entonces no hazlo.
Dicho esto, sí, usted produjo uno de los casos un poco más espinosos. Puedes:
Es muy difícil para mí crear un caso en el que un doble bucle no sea tan cohesivo que de todos modos no debería ser su propia rutina.
Por cierto, la mejor discusión sobre esto que he visto es el Código completo de Steve McConnell . Si te gusta pensar críticamente en estos problemas, te recomiendo leer, incluso de principio a fin a pesar de su inmensidad.
fuente
TLD; DR: No en específico, sí en general
Para el ejemplo específico que usó, diría que no por las mismas razones que muchos otros han señalado. Puede refactorizar fácilmente el código en una forma equivalentemente buena que elimine la necesidad de
goto
.En general, la proscripción contra
goto
es una generalidad basada en la naturaleza entendidagoto
y el costo de usarla. Parte de la ingeniería de software es tomar decisiones de implementación. La justificación de esas decisiones se produce comparando el costo con los beneficios y cada vez que los beneficios superan el costo, la implementación está justificada. La controversia surge debido a diferentes valoraciones, tanto de costo como de beneficio.El punto es que siempre habrá excepciones donde a
goto
está justificado, pero solo para algunos debido a diferentes valoraciones. La pena es que muchos ingenieros simplemente excluyen las técnicas en ignorancia intencional porque lo leen en Internet en lugar de tomarse el tiempo para entender por qué.fuente
No creo que este caso justifique una excepción, ya que esto se puede escribir como:
fuente