language-agnostic - example - for loop python
¿El último elemento en un ciclo merece un tratamiento separado? (13)
¿Cuál se comporta mejor?
Si el número de elementos es muy grande, siempre buclearía una vez, especialmente si va a realizar alguna operación en cada elemento. El costo de evaluar el condicional es probable que sea menor al bucle dos veces.
Vaya, por supuesto que no está bucleando dos veces ... En cuyo caso dos bucles son preferibles. Sin embargo, sostengo que la consideración principal debe ser el rendimiento. No es necesario incurrir en el condicional en el bucle (N veces) si puede dividir el trabajo mediante una simple manipulación de los límites del bucle (una vez).
Al revisar, a veces encuentro este tipo de bucle:
i = begin
while ( i != end ) {
// ... do stuff
if ( i == end-1 (the one-but-last element) ) {
... do other stuff
}
increment i
}
Luego hago la pregunta: ¿escribirías esto?
i = begin
mid = ( end - begin ) / 2 // (the middle element)
while ( i != end ) {
// ... do stuff
if ( i > mid ) {
... do other stuff
}
increment i
}
En mi opinión, esto supera la intención de escribir un bucle: se repite porque hay algo común que hacer para cada uno de los elementos. Usando esta construcción, para algunos de los elementos haces algo diferente. Entonces, concluyo, necesitas un ciclo separado para esos elementos:
i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid ) {
// ... do stuff
increment i
}
while ( i != end ) {
// ... do stuff
// ... do other stuff
increment i
}
Ahora incluso vi una pregunta sobre SO sobre cómo escribir la cláusula if
de una manera agradable ... Y me puse triste: algo no está bien aquí.
¿Me equivoco? Si es así, ¿qué hay de bueno en abarrotar el cuerpo del lazo con casos especiales, que conoces por adelantado, en el momento de la codificación?
@xtofl,
Estoy de acuerdo con tu preocupación
Millones de veces me encontré con un problema similar.
Cualquiera de los desarrolladores agrega manejo especial para el primero o último elemento.
En la mayoría de los casos, vale la pena simplemente hacer un bucle desde startIdx + 1 o hasta endIdx - 1 elemento o incluso dividir un bucle largo en múltiples bucles más cortos.
En casos muy raros, no es posible dividir el bucle.
En mi opinión, las cosas poco comunes deben manejarse fuera del circuito siempre que sea posible.
Creo que lo tienes completamente clavado. La mayoría de las personas cae en la trampa de incluir ramas condicionales en bucles, cuando podrían hacerlo afuera: lo cual es simplemente más rápido .
Por ejemplo:
if(items == null)
return null;
StringBuilder result = new StringBuilder();
if(items.Length != 0)
{
result.Append(items[0]); // Special case outside loop.
for(int i = 1; i < items.Length; i++) // Note: we start at element one.
{
result.Append(";");
result.Append(items[i]);
}
}
return result.ToString();
Y el caso del medio que describes es simplemente desagradable . Imagínese si ese código crece y necesita ser refactorizado en diferentes métodos.
A menos que esté analizando XML, los bucles de <grin> deben mantenerse lo más simples y concisos posible.
Creo que tienes razón acerca de que el ciclo está destinado a tratar con todos los elementos por igual. Desafortunadamente, a veces hay casos especiales, sin embargo, y estos deben ser tratados dentro de la construcción del bucle a través de las declaraciones if.
Si hay muchos casos especiales, probablemente deberías pensar en encontrar la manera de tratar con los dos conjuntos diferentes de elementos en construcciones separadas.
El caso especial debe realizarse fuera del ciclo si solo se realizará una vez.
Sin embargo, puede haber un índice o alguna otra variable que sea más fácil de mantener dentro del ciclo debido al alcance. También puede haber una razón contextual para mantener juntas todas las operaciones en la estructura de datos dentro de la estructura de control de bucle, aunque creo que es un argumento débil por sí mismo.
En el último fragmento que publicó, está repitiendo el código para // ... hacer las cosas.
Tiene sentido mantener 2 bucles cuando tiene un conjunto de operaciones completamente diferente en un conjunto diferente de índices.
i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid ) {
// ... do stuff
increment i
}
while ( i != end ) {
// ... do other stuff
increment i
}
Al no ser este el caso, aún desea mantener un solo bucle. Sin embargo, sigue siendo cierto que aún guarda (finaliza) / 2 número de comparaciones. Así que todo se reduce a si desea que su código se vea limpio o si desea guardar algunos ciclos de CPU. La llamada es tuya
Llegué a la conclusión de que cuando pongo casos especiales en un bucle for, normalmente soy demasiado listo para mi propio bien.
No creo que esta pregunta deba ser respondida por un principio (por ejemplo, "en un bucle, trate cada elemento por igual"). En cambio, puede ver dos factores para evaluar si una implementación es buena o mala:
- Efectividad del tiempo de ejecución: ¿el código compilado se ejecuta rápidamente, o sería más rápido hacerlo de manera diferente?
- Mantenimiento del código: ¿es fácil (para otro desarrollador) entender lo que está sucediendo aquí?
Si es más rápido y el código es más legible haciendo todo en un bucle, hazlo de esa manera. Si es más lento y menos legible, hágalo de otra manera.
Si es más rápido y menos legible, o más lento pero más legible, averigüe cuál de los factores es más importante en su caso específico y luego decida cómo hacer un bucle (o no bucle).
Otra cosa que odio ver es el patrón para el caso :
for (i=0; i<5; i++)
{
switch(i)
{
case 0:
// something
break;
case 1:
// something else
break;
// etc...
}
}
Lo he visto en código real.
Por supuesto, las cosas con envoltura especial en un bucle que se pueden sacar son tontas. Sin embargo, no duplicaría el do_stuff; Lo pondría en una función o en una macro, así que no copio ni pego el código.
Sé que he visto esto cuando las personas intentaron unir elementos de una matriz en una cadena separada por comas:
for(i=0;i<elements.size;i++) {
if (i>0) {
string += '',''
}
string += elements[i]
}
O tiene esa cláusula if allí, o tiene que duplicar la cadena + = línea nuevamente al final.
La solución obvia en este caso es
string = elements.join('','')
Pero el método de unión hace el mismo ciclo internamente. Y no siempre hay un método para hacer lo que quieres.
Solo se trata de usarlo según la necesidad y la conveniencia. Como tal, no se menciona el tratamiento equitativo de los elementos y, desde luego, no perjudica las características que proporciona el lenguaje.
Prefiero simplemente, excluir el elemento del ciclo y dar un tratamiento explícito fuera del ciclo
Por ejemplo: veamos el caso de EOF
i = begin
while ( i != end -1 ) {
// ... do stuff for element from begn to second last element
increment i
}
if(given_array(end -1) != ''''){
// do stuff for the EOF element in the array
}