significado - ¿Las etiquetas de bucle Perl cuentan como un GOTO?
la instrucción goto k modifica de manera directa (11)
En general, es una buena práctica evitar los GOTOs. Teniendo esto en cuenta, he estado teniendo un debate con un compañero de trabajo sobre este tema.
Considere el siguiente código:
Line:
while( <> ) {
next Line if (insert logic);
}
¿El uso de una etiqueta de bucle cuenta como un goto?
Aquí está lo que perlsyn en perldoc tiene que decir:
Aquí es cómo un programador de C podría codificar un algoritmo particular en Perl:
for (my $i = 0; $i < @ary1; $i++) {
for (my $j = 0; $j < @ary2; $j++) {
if ($ary1[$i] > $ary2[$j]) {
last; # can''t go to outer :-(
}
$ary1[$i] += $ary2[$j];
}
# this is where that last takes me
}
Mientras que aquí es cómo un programador de Perl más cómodo con el idioma podría hacerlo:
OUTER: for my $wid (@ary1) {
INNER: for my $jet (@ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
Mi opinión sobre esto es que no, porque está diciendo explícitamente un circuito de cortocircuito y avance, pero mi compañero de trabajo no está de acuerdo, dice que solo es un GOTO elegante y que debe evitarse. Estoy buscando un argumento convincente o documentación que explique por qué esto es o no un GOTO. También aceptaré una explicación de por qué esto es o no se considera una buena práctica en perl.
¿A quién le importa si cuenta como goto mientras hace que el código sea más fácil de entender? El uso de goto a menudo puede ser MÁS legible que tener un montón de pruebas adicionales en if () y condiciones de bucle.
4.4.4. Control de bucle
Mencionamos que puede poner una ETIQUETA en un bucle para darle un nombre. La ETIQUETA del bucle identifica el bucle para los operadores de control de bucle siguiente, último y rehacer. La ETIQUETA nombra el bucle como un todo, no solo la parte superior del bucle. Por lo tanto, un operador de control de bucle que se refiere al bucle en realidad no "va" a la etiqueta del bucle en sí. En lo que respecta a la computadora, la etiqueta podría haber sido colocada al final del bucle. Pero a la gente le gustan las cosas etiquetadas en la parte superior, por alguna razón.
Programación perl
Creo que la distinción es algo confusa, pero esto es lo que dice goto perldoc sobre la afirmación de goto (mal visto):
El formulario goto-LABEL encuentra la declaración etiquetada con LABEL y reanuda la ejecución allí.
...
El autor de Perl nunca ha sentido la necesidad de utilizar esta forma de goto (en Perl, es decir, C es otro asunto). (La diferencia es que C no ofrece bucles con nombre combinados con el control de bucle. Perl lo hace, y esto reemplaza la mayoría de los usos estructurados de goto en otros idiomas).
El perlsyn perldoc , sin embargo, dice esto:
La instrucción while ejecuta el bloque siempre que la expresión sea verdadera. La instrucción until ejecuta el bloque siempre que la expresión sea falsa. La ETIQUETA es opcional, y si está presente, consiste en un identificador seguido de dos puntos. La ETIQUETA identifica el bucle para las instrucciones de control de bucle siguiente, última y rehacer. Si se omite la ETIQUETA, la declaración de control de bucle se refiere al bucle envolvente más interno. Esto puede incluir revisar dinámicamente la pila de llamadas en el tiempo de ejecución para encontrar la ETIQUETA. Tal comportamiento desesperado dispara una advertencia si usa las advertencias de uso pragma o la bandera -w.
El bit de comportamiento desesperado no me parece muy bueno, pero puedo estar malinterpretando su significado.
El libro Learning Perl (5ª edición, página 162) dice esto:
Cuando necesite trabajar con un bloque de bucle que no sea el más interno, use una etiqueta.
...
Observe que la etiqueta nombra todo el bloque; no está marcando un punto de destino en el código. [Esto no es goto después de todo.]
¿Eso ayuda a aclarar las cosas? Probablemente no... :-)
El peligro de las etiquetas GOTO es que crean un código de espagueti y hacen que la lógica sea ilegible. Ninguno de los dos sucederá en este caso. Hay mucha validez en el uso de las declaraciones de GOTO, gran parte de la defensa proviene de Donald Knuth [ article ].
Profundizando en las diferencias entre sus ejemplos de C y Perl ... Si considera lo que está sucediendo en el nivel de ensamblaje con sus programas de C, todo se compila a GOTO de todos modos. Y si ha realizado cualquier MIPS u otra programación de ensamblaje, entonces ha visto que la mayoría de esos lenguajes no tienen construcciones de bucle, solo ramas condicionales e incondicionales.
Al final todo se reduce a la legibilidad y comprensibilidad. Ambos de los cuales son ayudados en gran medida por ser consistentes. Si su compañía tiene una guía de estilo, sígala, de lo contrario, seguir la guía de estilo de Perl me parece una buena idea. De esa manera, cuando otros desarrolladores de Perl se unan a su equipo en el futuro, podrán comenzar a trabajar y sentirse cómodos con su código base.
En mi opinión, la comparación de su código es injusta. El objetivo es el código legible.
Para ser justos, debe comparar un bucle anidado idiomático de Perl con etiquetas contra uno sin ellos. El estilo C y la instrucción if bloqueada agregan ruido que hace imposible comparar los enfoques.
Etiquetas:
OUTER: for my $wid (@ary1) {
INNER: for my $jet (@ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
Sin etiquetas:
for my $wid (@ary1) {
for my $jet (@ary2) {
last if $wid > $jet;
$wid += $jet;
}
}
Prefiero la versión etiquetada porque es explícita sobre el efecto de la condición $wid > $jet
. Sin etiquetas, debe recordar que el last
funciona en el bucle interno y que cuando se realiza el bucle interno, pasamos al siguiente elemento del bucle externo. Esto no es exactamente una ciencia espacial, pero es una sobrecarga cognitiva real, demostrable. Usadas correctamente, las etiquetas hacen que el código sea más legible.
Actualizar:
stocherilac preguntó qué sucede si tiene código después del bucle anidado. Depende de si desea omitirlo en función del condicional interno o ejecutarlo siempre.
Si desea omitir el código en el bucle externo, el código etiquetado funciona como se desea.
Si desea asegurarse de que se ejecute cada vez, puede usar un bloque de continue
.
OUTER: for my $wid (@ary1) {
INNER: for my $jet (@ary2) {
next OUTER if $wid > $jet;
$wid += $jet;
}
}
continue {
# This code will execute when next OUTER is called.
}
Este tipo de salto es un uso disciplinado de una declaración tipo goto. Entonces, ciertamente es menos dañino que el uso indisciplinado de goto. (Como escribió kasperjj, "la intención de Dijkstras nunca fue que algo que se asemejara a goto sea considerado dañino" ).
En mi opinión, este tipo de salto de Perl es incluso mejor diseño que "break" y "continue" de C, porque deja en claro qué bucle rompemos o continuamos, y lo hace más sólido ante los cambios de código. (Además, también permite romper o continuar un bucle externo).
Hay expertos a quienes no les gusta romper / continuar y cosas por el estilo, pero en algún momento hay que hacer una compensación entre las reglas prácticas y la legibilidad, y un descanso / continuar bien elegido o incluso goto puede ser más legible que "políticamente correcto "código.
La intención de Dijkstras nunca fue que algo que se asemejara a goto fuera considerado perjudicial. Fue que la estructura del código donde se utilizan los gotos como construcción principal para casi cualquier tipo de cambio en el flujo del programa resultará en lo que hoy llamamos código de espagueti.
Debe leer el artículo original y tener en cuenta que fue escrito en 1968 cuando los saltos etiquetados eran las principales construcciones de control de flujo en casi todos los lenguajes de programación.
Los saltos de bucle etiquetados en Perl son GOTO tanto como la break
de C y continue
.
Yo respondería así, y no estoy seguro si esto es lo suficientemente diferente de lo que otros han dicho:
Debido a que solo puede moverse dentro del alcance actual, o hacia un alcance principal, son mucho menos peligrosos de lo que típicamente implica goto
, observe:
if (1) {
goto BAR;
die ''bar''
}
BAR:
Esto debería funcionar obviamente, pero esto no (no puede moverse en esta dirección).
if (0) {
BAR:
die ''bar''
}
goto BAR;
Muchos casos de uso de labels
difieren de goto en que son solo variantes más explícitas del control de flujo del núcleo. Hacer una declaración de que son categóricamente peores sería implicar que:
LOOP: while (1) {
next LOOP if /foo;
}
es algo peor que
while (1) {
next if /foo/;
}
lo cual es simplemente ilógico si excluyes el estilo. Pero, hablando de estilo, la última variante es mucho más fácil de leer, y le impide tener que buscar la etiqueta con el nombre correcto. El lector sabe más con next
(que está reiniciando el ciclo en el ámbito actual), y eso es mejor.
Veamos otro ejemplo.
while (1) {
while (1) {
last;
}
stuff;
}
-vs-
FOO: while (1) {
BAR: while (1) {
next FOO;
}
stuff;
}
En el último ejemplo aquí, a next FOO
, next FOO
, omite stuff
: es posible que desee esto, pero es una mala idea. Implica que el programador ha leído el alcance de un padre hasta su finalización, lo cual es una suposición que probablemente sea mejor evitar. En resumen, la label
no es tan mala como goto
y algunas veces pueden simplificar el código; pero, en la mayoría de los casos, se deben evitar. Normalmente reescribo los bucles sin label
cuando los encuentro en CPAN.
romper / último y continuar / siguiente SON gotos. No entiendo por qué alguien haría todo lo posible para evitar una palabra clave y utilizar una palabra clave diferente que haga lo mismo ...
gotos
son malos porque crean un código difícil de entender, en particular, lo que a menudo se llama "código de espagueti". ¿Qué es difícil de entender acerca de la next Line...
?
Puede llamarlo un "nombre" de bucle, y realmente es algo para ayudar a enfatizar los límites de los bucles. No estás saltando a un punto arbitrario en relación con el bucle; estás volviendo a la cima de un bucle.
Lamentablemente, si se trata de un grupo o estándar de la casa, puede que no haya nada para convencer al grupo de que no es un goto. Tenía un gerente que insistía absolutamente en que un operador ternario hacía las cosas difíciles de leer, y prefería usar if-blocks para todo. Tenía un argumento bastante bueno: se puede hacer cualquier cosa en las cláusulas de if-else, pero un ternario hizo explícito que estaba buscando un valor en particular. No se vende.