18

Al revisar, a veces me encuentro con este tipo de bucle:¿El último elemento en un ciclo merece un tratamiento separado?

i = begin 
while (i != end) {  
    // ... do stuff 
    if (i == end-1 (the one-but-last element)) { 
     ... do other stuff 
    } 
    increment i 
} 

Entonces hago la pregunta: ¿usted escribir 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 es mejor que la intención de escribir un bucle: se recorre porque hay algo común a ser hecho para cada uno de los elementos. Usando esta construcción, para algunos de los elementos haces algo diferente. Por lo tanto, llego a la conclusión, es necesario un bucle separado para aquellos 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 un question el SO sobre la forma de escribir el -clause if de una manera agradable ... y me dieron triste: algo no está aquí mismo

¿Estoy equivocado? Si es así, ¿qué hay de bueno en abarrotar el cuerpo del lazo con casos especiales, de los que eres consciente por adelantado, en el momento de la codificación?

+0

Todo esto es muy bueno. Puedo mejorar mi estilo de trabajo personal con. Thx –

+0

"se repite porque hay algo común que hacer para cada uno de los elementos". Depende de cuán general es ese "algo". Puede ser común que todos los elementos sean autos. Algunos son azules, otros son rojos. Si es azul, elija el color azul, si es rojo, luego elija el rojo para pintar. Entonces, en cierto punto, las decisiones en bucles pueden tener sentido, aunque. Pero en algún punto, será obvio que debes dividir el ciclo. –

+0

@peter_the_oak: Estoy de acuerdo en que toma decisiones basadas en los valores que encuentra en el ciclo, pero no en el índice. – xtofl

Respuesta

5

@xtofl,

Acepto su preocupación.

Millones de veces Me encontré con un problema similar.

Cualquiera de los desarrolladores agrega un manejo especial para el primer elemento o para el último elemento.

En la mayoría de los casos, vale la pena solo bucle de startIdx + 1 o para 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 poco común cosas deben manejarse fuera del lazo siempre que sea posible.

1

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.

1

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

Oops, por supuesto, no está bucle dos veces ... En cuyo caso dos bucles es preferible. 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).

+0

Nunca mencioné el bucle dos veces, insinué que dividir el bucle en dos partes. – xtofl

10

Sé que he visto esto cuando la gente trató de unirse a elementos de una matriz en una cadena separados por comas:

for(i=0;i<elements.size;i++) { 
    if (i>0) { 
    string += ',' 
    } 
    string += elements[i] 
} 

O tienes que si la cláusula de allí, o usted tiene que duplicar la cadena + = línea nuevamente al final.

La solución obvia en este caso es

string = elements.join(',') 

Sin embargo, el método de unión hace lo mismo bucle interno. Y no siempre hay un método para hacer lo que quieres.

5

Me di cuenta de que cuando pongo casos especiales en un bucle for, normalmente soy demasiado listo para mi propio bien.

2

Creo que tiene 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 declaraciones if.

Si hay muchos casos especiales, probablemente debería pensar en encontrar la manera de tratar con los dos conjuntos diferentes de elementos en construcciones separadas.

1

Otra cosa que me gusta ver es el for-case pattern:

for (i=0; i<5; i++) 
{ 
    switch(i) 
    { 
    case 0: 
     // something 
     break; 
    case 1: 
     // something else 
     break; 
    // etc... 
    } 
} 

que he visto esto en código real.

+0

Si los casos solo se ejecutan en orden y no tienen un código común, parecería una tontería. Sin embargo, si la mayoría de las iteraciones son manejadas por código común o predeterminado, y hay suficientes excepciones para favorecer el uso de una prueba 'switch' en lugar de' if', no veo nada de malo en usar 'switch' para manejar casos de oddball dispersos. (Por ejemplo, tener un bucle que cuenta de 1 a 255 y hace algo extraño para valores de índice de 7, 8, 9, 10, 12, 13 y 27 puede parecer asqueroso, pero a veces los programas tienen que modelar cosas que son repulsivas en la realidad mundo). – supercat

19

No creo que esta pregunta deba responderse por un principio (por ejemplo, "en un ciclo, trate cada elemento por igual"). En su lugar, se puede tener en cuenta dos factores para evaluar si una aplicación es bueno o malo:

  1. Runtime efectividad - que hace el código compilado correr rápido, o sería más rápido haciendo de manera diferente?
  2. Mantenibilidad 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, hágalo 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).

5

En el último fragmento que ha publicado, 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 
} 

No siendo 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. Por lo tanto, 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

0

La carcasa especial debe realizarse fuera del lazo si solo se realiza una vez.

Sin embargo, puede haber un índice u 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.

0

Está a punto 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.

3

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: que 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 medio que usted describe es simplemente desagradable . Imagínese si ese código crece y necesita ser refactorizado en diferentes métodos.

A menos que esté analizando XML <grin> loops debe mantenerse lo más simple y conciso posible.

2

prefiero simplemente, excluir el elemento del bucle y darle un tratamiento spearate fuera del bucle

Por ejemplo: Vamos a considerar 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 
} 
Cuestiones relacionadas