2009-10-21 8 views
6

Descargo de responsabilidad: traté de buscar una pregunta similar, sin embargo, esto volvió sobre cada pregunta en C++ ... También agradecería a cualquiera que pueda sugerir un título mejor.Directriz: while vs for

Hay dos estructura de bucle eminente en C++: while y for.

  • propósito que disolver la relación de do ... while, es una especie de paralelo
  • Sé de std::for_each y BOOST_FOREACH, pero no cada bucle es una for each

Ahora, puedo ser un poco estrecho, pero siempre me pica a correcta código así:

int i = 0; 
while (i < 5) 
{ 
    // do stuff 
    ++i; // I'm kind and use prefix notation... though I usually witness postfix 
} 

y transformarla en:

for (int i = 0; i < 5; ++i) 
{ 
    // do stuff 
} 

Las ventajas de for en este ejemplo son múltiples, en mi opinión:

  1. Localidad: la variable i sólo vive en el ámbito del bucle
  2. Pack: el 'control' de bucle está empaquetado, por lo que solo mirando la declaración de bucle puedo determinar si está formado correctamente (y terminará ...), suponiendo, por supuesto, que la variable de bucle no está más m odified dentro del cuerpo
  3. Puede ser inline, aunque no siempre habría aconsejado (que hace que para los insectos difíciles)

tengo una tendencia, por tanto, no utilizar while, excepto tal vez para el while(true) idioma pero eso es no es algo que haya usado en mucho tiempo (juego de palabras). Incluso para las complicadas condiciones que tienden a pegarse a una construcción for, aunque en varias líneas:

// I am also a fan of typedefs 
for (const_iterator it = myVector.begin(), end = myVector.end(); 
    it != end && isValid(*it); 
    ++it) 
{ /* do stuff */ } 

Usted puede hacer esto con un while, por supuesto, pero entonces (1) y (2) no se puede verificar.

Me gustaría evitar las observaciones "subjetivas" (del tipo "Me gusta para/mientras mejor") y definitivamente me interesan las referencias a las normas de codificación/estándares de codificación existentes.

EDITAR:

que tienden a quedarse realmente a (1) y (2) la medida de lo posible, (1), ya que se recomienda localidad >>C++ Coding Standards: Item 18, y (2), ya que hace más fácil el mantenimiento si no tengo que escanear un bucle de cuerpo entero para buscar posibles alteraciones de la variable de control (que doy por hecho usando un for cuando la tercera expresión hace referencia a las variables de bucle).

Sin embargo, como gf mostrados abajo, mientras que sí tienen su uso:

while (obj.advance()) {} 

Tenga en cuenta que esto no es una diatriba contra while sino más bien un intento de encontrar el que uno de while o for uso dependiendo del caso a la mano (y por razones de sonido, no solo me gusta).

+2

Sí. Esto se debe a que el ciclo "para" C/C++ es realmente un ciclo while con azúcar sintáctica para inicialización, prueba e incremento. Históricamente (FORTRAN, Algol, Pascal, ...) el bucle for ha sido puramente para la indexación: 'para i: = 1 paso 2 hasta que N haga '. No es así en C, donde el compilador también ha saltado a través de aros de análisis de flujo de datos adicionales para descubrir que esa era su intención. – bendin

+0

no olvide que las variables de inicialización dentro de la estructura 'para' no cumplen con la codificación ansi. por lo que para (int i = 0; etc.) no es compatible, pero int i; for (i = 0; etc.) es. Esa es mi comprensión :) – theraven

+0

@theraven: ¿Podrías señalarme esto? Definitivamente estoy interesado si existe un riesgo de portabilidad ya que mi código generalmente se compila tanto en Unix como en Windows. –

Respuesta

7

No todos los bucles son para la iteración:

while(condition) // read e.g.: while condition holds 
{ 
} 

es aceptable, si bien esto se siente forzado:

for(;condition;) 
{ 
} 

A menudo se ve esto para cualquier fuente de entrada.

También podría tener iteración implícita:

while(obj.advance()) 
{ 
} 

Una vez más, se ve obligado por.

Además, al forzar for en lugar de while, la gente tiende a abusar de ella:

for(A a(0); foo.valid(); b+=x); // a and b don't relate to loop-control 
+1

¡Ah, de hecho, un ejemplo de cursor es perfecto para ilustrar el uso de while! –

+0

Hay tantos usos indebidos posibles en C++, que no lo mantendría en contra de ninguna construcción/guía. –

+2

"No todos los loops son para iteración" - ¡Sí, lo son!Esa es la definición misma de iteración. Iterar significa Repetir. Creo que lo que quieres decir es que no todos los loops tienen una variable de incremento o decremento de control. – paxdiablo

2

i no aumenta automáticamente en un ciclo while.

while (i < 5) { 

    // do something with X 

    if (X) { 
     i++; 
    } 

} 
+0

Exacto, me olvidé de citar un caso en el que, aunque es útil. –

+1

Defendería este punto como un caso en contra. Ha sucedido antes que me olvidé de aumentar una variable en algún momento ... algo que en un ciclo for es menos probable que lo haga – Toad

+0

En realidad, es útil: while (successfullLaunch <5) { if (Lanzamiento()) { i ++; } } – ty812

2

Una de las cosas más bellas en C++ es la parte de los algoritmos de STL. Al leer el código escrito correctamente usando STL, el programador sería reading high-level loops instead of low-level loops.

+1

Aprecio el sentimiento, pero como dije, no todo es un 'para cada uno'. Además, siempre que no tengamos lambdas, el uso de algoritmos STL a veces puede parecer incómodo, y puede perjudicar la legibilidad más que ayudarla. Tenga en cuenta que BOOST_FOREACH es realmente genial en este sentido. –

+1

Sí ... estoy de acuerdo. Necesitamos lambdas mucho :) – AraK

0

que tienden a utilizar for bucles cuando hay algún tipo de contar pasando y el bucle termina cuando termina el conteo. Obviamente tiene su estándar for(i=0; i < maxvalue; i++), pero también cosas como for(iterator.first(); !iterator.is_done(); iterator.next()).

Yo uso while bucles cuando no está claro cuántas veces el bucle puede iterar, es decir "bucle hasta que alguna condición que no puede ser pre-calculada se mantiene (o no se puede mantener)".

4

Funcionalmente, son lo mismo, por supuesto. La única razón para diferenciar es impartir algún significado a un mantenedor o a algún lector/revisor humano del código.

Creo que la expresión while es útil para comunicar al lector del código que una prueba no lineal está controlando el ciclo, mientras que una expresión idiomática generalmente implica algún tipo de secuencia. Mi cerebro también tipo de "espera" que los bucles solo estén controlados por la sección de expresión de conteo de los argumentos de enunciado for, y estoy sorprendido (y decepcionado) cuando encuentro a alguien jugando con la variable de índice dentro del bloque de ejecución.

Puede poner en su estándar de codificación que los bucles "for" deben usarse solo cuando se sigue la construcción de bucle for completa: el índice debe inicializarse en la sección de inicialización, el índice debe probarse en la prueba de bucle sección, y el valor del índice solo debe modificarse en la sección de expresión de conteo. Cualquier código que quiera alterar el índice en el bloque de ejecución debería usar un constructo while en su lugar. El razonamiento sería "puede confiar en que un bucle for" se ejecutará utilizando solo las condiciones que puede ver sin tener que buscar declaraciones ocultas que alteren el índice, pero no puede suponer que algo sea cierto en un bucle while."

Estoy seguro de que hay personas que discutirían y encontrarían muchos ejemplos de contador para demostrar usos válidos de declaraciones que no se ajustan a mi modelo anterior. Eso está bien, pero considere que su código puede ser" sorprendente " para un mantenedor que puede no tener su visión o brillantez. Y lo mejor es evitar sorpresas.

+0

estoy de acuerdo. la comunicación es todo. así que cuando dos códigos tienen una eficacia muy cerrada, elija uno legible y no brinde a los demás una gran sorpresa, a menos que sea el único mantenedor o el código requiera mucho tiempo y se proporcionará un aviso extremadamente explícito. – Test

+1

No son del todo iguales ... si tiene un incremento de iterador al final de un ciclo 'while', entonces' continue' se saltará. Mientras que con un bucle 'for',' continue' todavía ejecutará el incremento. Esta diferencia da como resultado más de unos pocos bucles infinitos para nuevos desarrolladores. – Tom

+1

Tom, es por eso que un bucle for debe seguir ciertas pautas. Cuando escribes "nuevos desarrolladores", realmente te refieres a "¡Sorpresa! ¡Te lo pillé!" y es por eso que deberían ser guiados por el camino de la menor sorpresa. Un estándar de codificación que explica cuándo usar cada uno ayudaría a un nuevo desarrollador a entender esa diferencia. –

1

No creo que los compiladores puedan optimizar significativamente mejor si elige expresar su ciclo de una manera u otra. todo se reduce a la legibilidad, y eso es algo subjetivo (aunque la mayoría de la gente probablemente esté de acuerdo con el factor de legibilidad de más ejemplos).

Como habrás notado, un for bucle es sólo una forma más compacta de decir

type counter = 0; 
while (counter != end_value) 
{ 
    // do stuff 
    ++counter; 
} 

Mientras que su sintaxis es lo suficientemente flexible para permitirle hacer otras cosas con él, trato de restringir el uso de mi for a ejemplos que no son mucho más complicadas que el anterior. OTOH, no usaría un bucle while para lo anterior.

0
// I am also a fan of typedefs 
for (const_iterator it = myVector.begin(), end = myVector.end(); 
    it != end && isValid(*it); 
    ++it) 
{ /* do stuff */ } 

Me parece que el código anterior es bastante menos legible que el siguiente código.

// I am also a fan of typedefs 
const_iterator it = myVector.begin(); 
end = myVector.end(); 
while(it != end && isValid(*it)) 
{ 
    /* do stuff */ 
    ++it} 

Personalmente, creo que la legibilidad supera este tipo de estándares de formato. Si otro programador no puede leer fácilmente su código, eso lleva a errores en el peor de los casos, y en el mejor de los casos da como resultado un tiempo perdido que le cuesta dinero a la empresa.

+0

Ah, entonces es interesante. Encuentro que su ejemplo realmente es menos sostenible porque uno tiene que ver el cuerpo del bucle para decidir si el control del bucle es realmente correcto o no. –

+0

Creo que es peligroso mirar un programa de una manera tan abstracta. Si no comprende el cuerpo del bucle, posiblemente no pueda saber si el control del bucle es correcto. Tal vez el incremento se encuentre en una declaración IF. O tal vez no hay incremento, pero es un efecto secundario de otra cosa en el cuerpo del bucle. Tienes que entender todo el algoritmo que el código está expresando. –

+0

Sí, debe comprender todo el cuerpo para comprender el programa, sin embargo, esto no le impide enfocarse en ciertos puntos durante las revisiones del código: terminación de bucle, comportamiento indefinido, etc.> es decir, el tipo de errores que realmente puede generar su administrador enojado :) –

0

En Ye Olde C, los bucles while y while no eran lo mismo.

La diferencia era que en los bucles for, el compilador era libre de asignar una variable a un registro de CPU y reclamar el registro después del bucle. Por lo tanto, un código como éste no tenía definido el comportamiento:

int i; 
for (i = 0; i < N; i++) { 
    if (f(i)) break; 
} 

printf("%d", i);   /* Non-defined behaviour, unexpected results ! */ 

No estoy 100% seguro, pero creo que esto se describe en K&R

Esto está bien:

int i = 0; 
while (i < N) { 
    if (f(i)) break; 
    i++; 
} 
printf("%d", i); 

De Por supuesto, esto depende del compilador. Además, con el tiempo, los compiladores dejaron de utilizar esa libertad, por lo que si ejecuta el primer código en un compilador moderno de C, debería obtener los resultados esperados.

+0

No puedo decir con certeza que esto sea definitivamente incorrecto, pero no lo creo. Sé que he escrito un código que verifica el valor de i después del ciclo para ver si pasó todo o si se salta en el medio. Si 'i' no está definido después del ciclo, esto nunca hubiera funcionado. –

+0

@Graeme, "indefinido" no significa que el valor no está definido (un int siempre tiene un valor). Significa "el comportamiento del compilador no está definido por el estándar, por lo que un compilador puede hacer lo que quiera". Un compilador podría dejar el valor de bucle en su último valor cuando estaba en el bucle, pero un compilador diferente podría dejar el valor de bucle en su último valor más otro incremento. ¡Y una nueva versión de tu viejo compilador podría establecerlo en cero o incluso en 0xDEADBEEF! El código puede funcionar en su caso, pero no se garantiza que sea portátil. El código que se basa en un comportamiento indefinido es "incorrecto". –

0

No sería tan rápido tirar bucles do-while. Son útiles si sabes que el cuerpo de tu bucle se ejecutará al menos una vez. Considere algún código que cree un hilo por núcleo de CPU. Con un bucle for puede ser que aparezca:

for (int i = 0; i < number_of_cores; ++i) 
    start_thread(i); 

Uentering el bucle, la primera cosa que se comprueba es la condición, en caso number_of_cores es 0, en cuyo caso el cuerpo del ciclo no debe funcionar nunca. Espera, sin embargo, este es un cheque totalmente redundante. El usuario siempre tendrá al menos un núcleo; de lo contrario, ¿cómo se está ejecutando el código actual? El compilador no puede eliminar la primera comparación redundante, por lo que sabe, number_of_cores podría ser 0. Pero el programador sabe mejor.Por lo tanto, la eliminación de la primera comparación:

int i = 0; 
do { 
    start_thread(i++); 
} while (i < number_of_cores); 

Ahora cada vez que este bucle es golpeado sólo hay una comparación en lugar de dos en una máquina de un solo núcleo (con un bucle for se cumple la condición para i = 0, falsa para i = 1, mientras que el do while es falso todo el tiempo). La primera comparación se omite, por lo que es más rápido. Con menos ramificaciones potenciales, hay menos potencial para errores imprevistos en las ramas, por lo que es más rápido. Como la condición while es ahora más predecible (siempre falsa en 1-core), el predictor de bifurcación puede hacer un mejor trabajo, que es más rápido.

Minit nitpick realmente, pero no es algo que se debe desechar: si sabes que el cuerpo siempre funcionará al menos una vez, es una pesimismo prematura usar un ciclo for.

+2

El código for loop es más fácil de leer, es obvio en la parte superior del ciclo lo que el loop está probando. Hacer el código más difícil de leer solo para guardar una comparación es una mala idea en mi humilde opinión. –

+0

Bueno, el punto es para si necesita una optimización extrema en lugar de legibilidad. Supongo que tienes razón en que la mayoría de las veces, un bucle for es perfectamente suficiente. – AshleysBrain

+0

No considero el constructo do/while en esta pregunta, porque no se puede imitar exactamente su comportamiento con a for de forma directa (aunque es factible).Realmente creo que abordan 2 problemas diferentes y no son intercambiables. –

Cuestiones relacionadas