loops - selectiva - estructuras de control en java
¿Estructuras de control alternativas útiles? (28)
A veces, cuando estoy programando, encuentro que una estructura de control particular me sería muy útil, pero no está directamente disponible en mi lenguaje de programación. Creo que mi deseo más común es algo así como "separarme" (no tengo idea de cómo llamarlo en realidad):
{
foo();
} split_while( condition ) {
bar();
}
La semántica de este código sería que foo()
siempre se ejecuta y luego se verifica la condición. Si es verdadero, entonces se ejecuta bar()
y volvemos al primer bloque (ejecutando foo()
nuevamente, etc.). Gracias a un comentario de reddit usuario zxqdms , aprendí que Donald E. Knuth escribe sobre esta estructura en su documento "Programación estructurada con go to
declaraciones" (ver página 279).
¿Qué estructuras de control alternativas crees que son una forma útil de organizar el cálculo?
Mi objetivo aquí es darme a mí mismo y a los demás nuevas formas de pensar sobre la estructuración del código, a fin de mejorar la fragmentación y el razonamiento.
Nota : No estoy preguntando cómo generalizar todas las posibles estructuras de control, ya sea usando jne
, if
/ goto
, macros Lisp, continuaciones, mónadas, combinators, quarks o cualquier otra cosa. Estoy preguntando qué especializaciones son útiles para describir el código.
¿Qué tal PL / I estilo "para" rangos de bucle? El equivalente de VB sería:
'' Counts 1, 2, ... 49, 50, 23, 999, 998, ..., 991, 990 For I = 1 to 50, 23, 999 to 990 Step -1
El uso más común que puedo ver sería ejecutar un bucle para una lista de índices y luego lanzar uno más. Por cierto, un uso de For-For también podría ser útil:
'' Bar1, Bar2, Bar3 are an IEnum(Wazoo); Boz is a Wazoo For Each Foo as Wazoo in Bar1, Bar2, Enumerable.One(Boz), Bar3
Esto ejecutaría el ciclo en todos los elementos en Bar1, todos los elementos en Bar2, Boz y Bar3. Linq probablemente permitiría esto sin demasiada dificultad, pero el soporte del lenguaje intrínseco podría ser un poco más eficiente.
¿Qué tal iterar con una ventana móvil (de n elementos en lugar de 1) a través de una lista? Esto se relaciona tangencialmente con la answer @ munificent, creo.
Algo como
#python
#sum of adjacent elements
for x,y in pairs(list):
print x + y
def pairs(l):
i=0
while i < len(l)-1:
yield (l[i],l[i+1])
i+=1
Es útil para ciertos tipos de cosas. No me malinterprete, esto es fácil de implementar como una función, pero creo que mucha gente intenta sacar provecho for
y while
bucles cuando hay herramientas más específicas / descriptivas para el trabajo.
A veces, necesito tener un bucle foreach con un índice. Podría escribirse así:
foreach (index i) (var item in list) {
// ...
}
(No soy particularmente aficionado a esta sintaxis, pero entiendes la idea)
Algo que reemplaza
bool found = false;
for (int i = 0; i < N; i++) {
if (hasProperty(A[i])) {
found = true;
DoSomething(A[i]);
break;
}
}
if (!found) {
...
}
me gusta
for (int i = 0; i < N; i++) {
if (hasProperty(A[i])) {
DoSomething(A[i]);
break;
}
} ifnotinterrupted {
...
}
Siempre siento que debe haber una mejor manera que introducir una bandera solo para ejecutar algo después de la última ejecución (regular) del cuerpo del bucle. ¡Uno podría verificar !(i < N)
, pero i
fuera del alcance después del ciclo.
Bucle con else:
while (condition) {
// ...
}
else {
// the else runs if the loop didn''t run
}
Con las macros (de estilo lisp), las llamadas de cola y las continuaciones, todo esto es curioso.
Con las macros, si las construcciones de flujo de control estándar no son suficientes para una aplicación dada, el programador puede escribir las suyas (y mucho más). Solo requeriría una macro simple para implementar los constructos que dio como ejemplo.
Con las llamadas finales, uno puede factorizar patrones de flujo de control complejos (como la implementación de una máquina de estado) en funciones.
Las continuas son una poderosa primitiva de flujo de control (try / catch es una versión restringida de ellas). En combinación con las llamadas de cola y las macros, los patrones de flujo de control complejo (retroceso, análisis sintáctico, etc.) se vuelven directos. Además, son útiles en la programación web ya que con ellos puedes invertir la inversión del control; puede tener una función que le pida al usuario alguna entrada, haga algún procesamiento, le pida al usuario más información, etc.
Para parafrasear el estándar Scheme, en lugar de acumular más características en su idioma, debe tratar de eliminar las limitaciones que hacen que las otras características parezcan necesarias.
Creo que debería mencionar CityScript (el lenguaje de scripting de CityDesk ) que tiene algunos constructos de bucle realmente extravagantes.
Desde el archivo de ayuda:
{$ forEach n var in (condition) sort-order $}
... text which appears for each item ....
{$ between $}
.. text which appears between each two items ....
{$ odd $}
.. text which appears for every other item, including the first ....
{$ even $}
.. text which appears for every other item, starting with the second ....
{$ else $}
.. text which appears if there are no items matching condition ....
{$ before $}
..text which appears before the loop, only if there are items matching condition
{$ after $}
..text which appears after the loop, only of there are items matching condition
{$ next $}
Esta es solo una idea general y sintaxis:
if (cond)
//do something
else (cond)
//do something
also (cond)
//do something
else
//do something
end
TAMBIÉN la condición siempre se evalúa. ELSE funciona como de costumbre.
Funciona también para casos. Probablemente es una buena forma de eliminar la declaración de interrupción:
case (exp)
also (const)
//do something
else (const)
//do something
also (const)
//do something
else
//do something
end
puede leerse como:
switch (exp)
case (const)
//do something
case (const)
//do something
break
case (const)
//do something
default
//do something
end
No sé si esto es útil o simple de leer, pero es un ejemplo.
Esto es similar a la respuesta de @Paul Keister .
(murmurando, murmurando) hace años, la aplicación en la que estaba trabajando tenía muchas variaciones del llamado proceso de control y ruptura, toda esa lógica que se usa para dividir las filas ordenadas de datos en grupos y subgrupos con encabezados y pies de página. Como la aplicación estaba escrita en LISP, habíamos capturado los modismos comunes en una macro llamada WITH-CONTROL-BREAKS. Si tuviera que transponer esa sintaxis en la forma ondulada siempre popular, podría verse algo como esto:
withControlBreaks (x, y, z : readSortedRecords()) {
first (x) : { emitHeader(x); subcount = 0; }
first (x, y) : { emitSubheader(x, y); zTotal = 0; }
all (x, y, z) : { emitDetail(x, y, z); ztotal += z; }
last (x, y) : { emitSubfooter(x, y, zTotal); ++subCount; }
last (x) : { emitFooter(x, subcount); }
}
En esta era moderna, con SQL generalizado, XQuery, LINQ, etc., esta necesidad no parece surgir tanto como antes. Pero de vez en cuando, desearía tener esa estructura de control a mano.
Esto es un poco chistoso, pero puedes obtener el comportamiento que deseas así:
#include <iostream>
#include <cstdlib>
int main (int argc, char *argv[])
{
int N = std::strtol(argv[1], 0, 10); // Danger!
int state = 0;
switch (state%2) // Similar to Duff''s device.
{
do {
case 1: std::cout << (2*state) << " B" << std::endl;
case 0: std::cout << (2*state+1) << " A" << std::endl; ++state;
} while (state <= N);
default: break;
}
return 0;
}
ps formatear esto fue un poco difícil y definitivamente no estoy contento con eso; sin embargo, emacs lo hace aún peor. ¿Alguien quiere probar vim?
La mayoría de los lenguajes tienen funciones incorporadas para cubrir los casos comunes, pero los bucles "fencepost" son siempre una tarea ardua: bucles donde se desea hacer algo en cada iteración y también hacer algo más entre iteraciones. Por ejemplo, unir cadenas con un separador:
string result = "";
for (int i = 0; i < items.Count; i++) {
result += items[i];
if (i < items.Count - 1) result += ", "; // This is gross.
// What if I can''t access items by index?
// I have off-by-one errors *every* time I do this.
}
Sé que los pliegues pueden cubrir este caso, pero a veces quieres algo imperativo. Sería genial si pudieras:
string result = "";
foreach (var item in items) {
result += item;
} between {
result += ", ";
}
Las protecciones de alcance de D''s son una estructura de control útil que no se ve muy a menudo.
Los generadores, en Python, son realmente novedosos si usted ha trabajado principalmente con lenguajes no funcionales. Más en general: continuaciones, correlpacidades, listas perezosas.
Los loops etiquetados son algo que a veces me extraño en los lenguajes convencionales. p.ej,
int i, j;
for outer ( i = 0; i < M; ++i )
for ( j = 0; j < N; ++j )
if ( l1[ i ] == l2[ j ] )
break outer;
Sí, normalmente puedo simular esto con un goto
, pero un equivalente para continue
requeriría mover el incremento al final del cuerpo del bucle después de la etiqueta, lo que perjudicaría la legibilidad. También puede hacer esto configurando un indicador en el bucle interno y verificándolo en cada iteración del bucle externo, pero siempre se ve torpe.
(Bonificación: a veces me gustaría tener un redo
para continue
y continue
. Volvería al inicio del ciclo sin evaluar el incremento).
Me gustaría ver una palabra clave para agrupar resultados. En lugar de esto:
int lastValue = 0;
foreach (var val in dataSource)
{
if (lastValue != val.CustomerID)
{
WriteFooter(lastValue);
WriteHeader(val);
lastValue = val.CustomerID;
}
WriteRow(val);
}
if (lastValue != 0)
{
WriteFooter(lastValue);
}
Qué tal algo como esto:
foreach(var val in dataSource)
groupon(val.CustomerID)
{
startgroup
{
WriteHeader(val);
}
endgroup
{
WriteFooter(val)
}
}
each
{
WriteRow(val);
}
Si tiene una plataforma, controles y / o formato de informes decentes, no necesitará escribir este código. Pero es sorprendente la frecuencia con la que me encuentro haciendo esto. La parte más molesta es el pie de página después de la última iteración: es difícil hacer esto en un ejemplo de la vida real sin duplicar el código.
Probablemente esto no cuente, pero en Python, estaba molesto de que no hubiera un ciclo do.
Para asegurarme de no obtener ningún voto positivo para esta respuesta, termino molesto en cualquier idioma en el que trabaje durante un período de tiempo que carece de los de Goto.
Propongo el operador "entonces". Devuelve el operando izquierdo en la primera iteración y el operando derecho en todas las otras iteraciones:
var result = "";
foreach (var item in items) {
result += "" then ", ";
result += item;
}
en la primera iteración, agrega "" al resultado en todos los demás que agrega ",", de modo que se obtiene una cadena que contiene cada elemento separado por comas.
Qué tal si
alternate {
statement 1,
statement 2,
[statement 3,...]
}
para recorrer las declaraciones disponibles en cada pase sucesivo.
Edición : ejemplos triviales
table_row_color = alternate(RED, GREEN, BLUE);
player_color = alternate(color_list); // cycles through list items
alternate(
led_on(),
led_off()
);
Edición 2 : en el tercer ejemplo anterior, la sintaxis es tal vez un poco confusa ya que parece una función. De hecho, solo se evalúa una declaración en cada pasada, no en ambas. Una mejor sintaxis podría ser algo así como
alternate {
led_on();
}
then {
led_off();
}
O algo por el estilo. Sin embargo, me gusta la idea de que el resultado que se llame se pueda usar si se desea (como en los ejemplos de color).
Si observa Haskell, aunque hay una sintaxis especial para varias estructuras de control, el flujo de control a menudo se captura por tipos. El tipo más común de tales tipos de control son Mónadas, Flechas y funtores aplicativos. Entonces, si desea un tipo especial de flujo de control, generalmente es una especie de función de orden superior y puede escribirla usted mismo o encontrar una en la base de datos de paquetes de Haskells (Hackage) que es bastante grande.
Tales funciones generalmente se encuentran en el espacio de nombres de Control, donde puede encontrar módulos para la ejecución en paralelo para el manejo de errores. Muchas de las estructuras de control que generalmente se encuentran en los lenguajes de procedimiento tienen una contraparte de función en Control.Monad, entre estas se encuentran los bucles y las sentencias if. If-else es una expresión en keyworded en haskell, si sin otro no tiene sentido en una expresión, sino sentido perfecto en una mónada, entonces las declaraciones if sin otro son capturadas por las funciones when
y unless
.
Otro caso común es hacer una operación de lista en un contexto más general. Los lenguajes funcionales son bastante aficionados al fold
, y las versiones especializadas como el map
y el filter
. Si tienes una mónada, entonces hay una extensión natural de fold
en ella. Esto se llama foldM
y, por lo tanto, también hay extensiones de cualquier versión especializada de fold que se pueda imaginar, como mapM
y filterM
.
También tenga en cuenta que muchas estructuras de control adquieren un nuevo significado en el contexto monádico, dependiendo de la mónada en particular: busque en mapM, filterM, whileM, secuencia, etc. en Haskell.
Una de las estructuras de control que no está disponible en muchos idiomas es la estructura de tipo caso por caso. Similar a una estructura tipo switch, le permite tener una lista ordenada de opciones posibles, pero coincide con la primera que sea verdadera (en lugar de la primera que coincida con la entrada). Un LISP de tal tipo (que lo tiene):
(cond
((evenp a) a) ;if a is even return a
((> a 7) (/ a 2)) ;else if a is bigger than 7 return a/2
((< a 5) (- a 1)) ;else if a is smaller than 5 return a-1
(t 17)) ;else return 17
O, para aquellos que prefieren un formato más parecido a C
cond
(a % 2 == 0):
a; break;
(a > 7):
a / 2; break;
(a < 5):
a - 1; break;
default:
17; break;
Básicamente es una representación más precisa de la construcción if/elseif/elseif/else
que un modificador, y puede ser extremadamente útil para expresar esa lógica de una manera clara y legible.
Uno que es bastante común es el ciclo infinito. Me gustaría escribirlo así:
forever {
// ...
}
si no:
unless (condition) {
// ...
}
mientras no:
until (condition) {
// ...
}
ignoring
- Para ignorar las excepciones que ocurren en un cierto bloque de código.
try {
foo()
} catch {
case ex: SomeException => /* ignore */
case ex: SomeOtherException => /* ignore */
}
Con una construcción de control ignorante, podría escribirla de manera más concisa y más legible como:
ignoring(classOf[SomeException], classOf[SomeOtherException]) {
foo()
}
[Scala proporciona esto (y muchas otras construcciones de control de manejo de excepciones) en su biblioteca estándar, en el paquete util.control
. ]
foo();
while(condition)
{
bar();
foo();
}
for int i := 0 [down]to UpperBound() [step 2]
Falta en cada idioma derivado de C.
Por favor considere antes de votar o escribir un comentario :
Esto no es redundante para for (int i = 0; i <= UpperBound(); i++)
, tiene una semántica diferente:
UpperBound()
se evalúa solo una vezEl caso
UpperBound() == MAX_INT
no produce un bucle infinito
if (cond)
//do something
else (cond)
//do something
else (cond)
//do something
first
//do something
then
//do something
else (cond)
//do something
else
//do something
end
FIRST y THEN bloquea si cualquiera de 3 condicionales se evalúa como verdadero. El primer bloque se ejecuta antes del bloque condicional y luego se ejecuta después de que se ejecutó el bloque condicional.
ELSE escritura condicional o final después de FIRST y THEN declaración son independientes de estos bloques.
Puede leer como:
if (cond)
first()
//do something
then()
else (cond)
first()
//do something
then()
else (cond)
first()
//do something
then()
else (cond)
//do something
else
//do something
end
function first()
//do something
return
function then()
//do something
return
Estas funciones son solo una forma de leer. No crearían alcance. Es más como un gosub / regreso de Basic.
Utilidad y legibilidad como materia de debate.
{
foo();
} split_while( condition ) {
bar();
}
Puedes lograr eso bastante fácilmente usando un while
regular:
while (true) {
foo();
if (!condition) break;
bar();
}
Lo hago con bastante frecuencia ahora que supero mi disgusto irracional por el break
.