2008-10-22 8 views
21

Cuando estaba tomando CS en la universidad (mediados de los 80), una de las ideas que se repetía constantemente era escribir loops que prueban en la parte superior (mientras ...) en lugar de en la parte inferior (hacer ... mientras) del bucle. Estas nociones a menudo se respaldaron con referencias a estudios que demostraron que los bucles que se probaron en la parte superior fueron estadísticamente mucho más probables de ser correctos que sus contrapartes de pruebas de fondo.¿Bucles de prueba en la parte superior o inferior? (mientras que vs. do while)

Como resultado, casi siempre escribo loops que prueban en la parte superior. No lo hago si introduce complejidad adicional en el código, pero ese caso parece raro. Noté que algunos programadores tienden a escribir casi exclusivamente bucles que se prueban en la parte inferior. Cuando veo construcciones como:

if (condition) 
{ 
    do 
    { 
     ... 
    } while (same condition); 
} 

o el inverso (if dentro del while), hace que me pregunte si realmente escribieron esa manera o si se agregaron la declaración if cuando se dieron cuenta del bucle no manejar el caso nulo.

He hecho algunas búsquedas en Google, pero no he podido encontrar ninguna literatura sobre este tema. ¿Cómo pueden ustedes (y chicas) escribir sus loops?

+0

"estadísticamente mucho más probable que sea correcto" Olvide las estadísticas. La prueba de una condición superior es simple. La prueba de una condición de fondo de ciclo es mucho más compleja. –

+0

Pregunta relacionada: http://stackoverflow.com/questions/224138/infinite-loops-top-or-bottom – CesarB

+4

Tuve un profesor de CS alrededor de 1997 proclamando que sus dos opciones en la programación eran C o Pascal, y que debe elegir Pascal porque no le permitió incrementar los contadores de bucle desde el interior del bucle. Él pudo haber tenido razón, pero ¿estaba equivocado? – MusiGenesis

Respuesta

1

Escribo el mío casi exclusivamente con pruebas en la parte superior. Es menos código, por lo que para mí, al menos, tiene menos posibilidades de arruinar algo (por ejemplo, copiar y pegar la condición hace que siempre haya dos lugares para actualizarlo)

73

Siempre sigo la regla de que si debe ejecutarse cero o más veces, pruebe al comienzo, si debe ejecutar una vez o más, pruebe al final. No veo ninguna razón lógica para usar el código que enumeró en su ejemplo. Solo agrega complejidad.

+0

¡Me ganaste! +1 – Nescio

+0

Como nunca he visto un ciclo de "una o más veces", creo que esto se puede simplificar de alguna manera. Quizás solo vivo en un mundo cerrado. –

+0

Esta es la única respuesta válida. – mafu

7

En aras de la legibilidad, parece sensato probar en la parte superior. El hecho de que sea un ciclo es importante; la persona que lee el código debe conocer las condiciones del bucle antes de tratar de comprender el cuerpo del bucle.

+0

No puedo entender cómo alguien puede ver mi respuesta como inútil. – mxcl

+0

Aunque no es tan completo como la respuesta de Brett McCann, el suyo realmente no merece un voto negativo. Estoy parcialmente de acuerdo con su problema de legibilidad. Hay otras cosas como rupturas, devoluciones, etc. que dan una cantidad igual de incertidumbre a un bucle que prueba en la parte inferior. –

+0

Diría que si ... hacer ... mientras que construir es un desagradable consejo para un programador. El do ... while constructo es bien conocido y cumple su propósito. Además, utilizando esto, el if..do ... mientras está probando la misma condición dos veces, que es un golpe de rendimiento innecesario. – None

2

En realidad, está destinado para otras cosas. En C, puede usar do - mientras que construir para lograr ambos escenarios (se ejecuta al menos una vez y se ejecuta en modo verdadero). Pero PASCAL tiene repetir - hasta y mientras que para cada escenario, y si mal no recuerdo, ADA tiene otro constructo que le permite salir en el medio, pero por supuesto que no es lo que está preguntando. Mi respuesta a su pregunta: Me gusta mi ciclo con las pruebas en la parte superior.

3

Los casos de uso son diferentes para los dos. Esta no es una pregunta de "mejores prácticas".

Si desea un bucle para ejecutar basa en la condición exclusiva de utilizar para o mientras

Si usted quiere hacer algo una vez, independientemente de la condición y luego seguir haciéndolo basa la condición evaluación. do..while

1

Realmente depende existen situaciones en las que desea probar en la parte superior, los demás cuando se quiere probar en la parte inferior, y aún otros cuando se quiere probar en el medio.

Sin embargo, el ejemplo dado parece absurdo.Si va a hacer una prueba en la parte superior, no use una declaración if y pruebe en la parte inferior, solo use una declaración while, para eso está hecha.

1

Primero debe pensar en la prueba como parte del código de bucle. Si la prueba lógicamente pertenece al inicio del procesamiento del ciclo, entonces es una prueba de nivel superior. Si la prueba lógicamente pertenece al final del ciclo (es decir, decide si el ciclo debe continuar ejecutándose), entonces es probable que sea una prueba de fondo del ciclo.

Tendrá que hacer algo elegante si la prueba lógicamente pertenece a ellos en el medio. :-)

2

Para cualquier persona que no puede pensar en una razón para tener una o más de un bucle tiempos:

try { 
    someOperation(); 
} catch (Exception e) { 
    do { 
     if (e instanceof ExceptionIHandleInAWierdWay) { 
      HandleWierdException((ExceptionIHandleInAWierdWay)e); 
     } 
    } while ((e = e.getInnerException())!= null); 
} 

Lo mismo podría ser utilizado para cualquier tipo de estructura jerárquica.

en el Nodo clase:

public Node findSelfOrParentWithText(string text) { 
    Node node = this; 
    do { 
     if(node.containsText(text)) { 
      break; 
     } 
    } while((node = node.getParent()) != null); 
    return node; 
} 
+0

nodo nodo = this; while (node! = Null) {if (node.containsTest (text) break; node = node.getParent(); } es mucho más legible para mí (por supuesto que no en un comentario como este). – Yishai

20

diferencia es que el bucle hacer ejecuta "hacer algo" una vez y luego se comprueba la condición para ver si se debe repetir el "hacer algo", mientras que el bucle while comprueba la condición antes de hacer cualquier cosa

+2

Personally I RARELY use do-while. Parece que siempre hay una forma de expresarlo como un ciclo while, u otro tipo de ciclo, así que a menudo olvido que existe. – devios1

+0

Nunca lo he usado y cuando modifiqué código de otro desarrollador no me pareció natural lo usó. – UnkwnTech

+0

Ya no puedo pensar en una sola instancia en la que haya usado un ciclo do while. –

12

La primera puede no ejecutarse si la condición es falsa. Otro se ejecutará al menos una vez, luego verifica la conidición.

4

La primera prueba la condición antes de realizarla, por lo que es posible que su código nunca ingrese el código debajo. El segundo realizará el código dentro antes de probar la condición.

4

El ciclo while verifica primero la "condición"; si es falso, nunca "hará algo". Pero el do ... while loop "hará algo" primero, luego verifica "condición".

3

A mientras que() cheques la condición antes de cada ejecución del cuerpo del bucle y un hacer ... mientras que() cheques la condición después de cada ejecución del cuerpo del bucle.

Por lo tanto, ** do ... while() ** s siempre ejecutará el cuerpo del bucle al menos una vez.

Funcionalmente, un tiempo() es equivalente a

startOfLoop: 
    if (!condition) 
     goto endOfLoop; 

    //loop body goes here 

    goto startOfLoop; 
endOfLoop: 

y una do ... while() es equivalente a

startOfLoop: 

    //loop body 

    //goes here 
    if (condition) 
     goto startOfLoop; 

Tenga en cuenta que la aplicación es probablemente más eficiente que esto. Sin embargo, un do ... while() implica una comparación menos que un while() por lo que es un poco más rápido. Utilice un do ... while() si:

  • que saben que la condición siempre será verdad la primera vez, o
  • que quiere el bucle para ejecutar una vez, incluso si la condición es falso para empezar.
0

En una clase típica de Estructuras discretas en ciencias de la computación, es una prueba fácil de que hay un mapeo de equivalencia entre los dos.

Estilísticamente, prefiero while (easy-expr) {} cuando easy-expr se conoce desde el principio y está listo para funcionar, y el bucle no tiene muchas sobrecargas/inicializaciones repetidas. Prefiero do {} while (algo-menos-fácil-expr); cuando hay más sobrecarga repetida y la condición puede no ser tan simple de configurar con anticipación. Si escribo un bucle infinito, siempre uso while (true) {}. No puedo explicar por qué, pero simplemente no me gusta escribir para (;;) {}.

3

Aquí está la traducción:

do { y; } while(x); 

Igual que

{ y; } while(x) { y; } 

Nota del juego extra de llaves son para el caso de que tenga definiciones de variables en y. El alcance de estos debe mantenerse local como en el caso do-loop. Entonces, un ciclo do-while solo ejecuta su cuerpo al menos una vez. Aparte de eso, los dos bucles son idénticos. Así que si aplicamos esta regla a su código

do { 
    // do something 
} while (condition is true); 

El correspondiente bucle while para su do-loop parece

{ 
    // do something 
} 
while (condition is true) { 
    // do something 
} 

Sí, se ve el tiempo correspondiente para su ciclo do difiere de su tiempo:)

+0

No son lo mismo si hay definiciones de variable en la parte "do something". –

+0

bien manchado , Robert, lo solucionaré. ¡Gracias! –

+0

Otra forma, quizás mejor, de mostrar la equivalencia es que "do {y} while (x);" es equivalente a "while (1) {y; if (! x) break ;} ". –

2

Ambas convenciones son correctas si se sabe cómo escribir el código correctamente :)

Por lo general, el uso de la segunda convención (do {} while ()) está destinado a evitar tener una declaración duplicada fuera del ciclo. Considere la siguiente (más simplificada) ejemplo:

a++; 
while (a < n) { 
    a++; 
} 

puede escribirse de forma más concisa usando

do { 
    a++; 
} while (a < n) 

Por supuesto, este ejemplo en particular se puede escribir de una manera aún más concisa como (asumiendo sintaxis C)

while (++a < n) {} 

Pero creo que puede ver el punto aquí.

1

Supongo que algunas personas prueban en la parte inferior porque puede salvar uno o unos pocos ciclos de máquina haciendo eso hace 30 años.

1

Para escribir el código que es correcto, uno básicamente necesita realizar una prueba mental, quizás informal, de corrección.

Para demostrar que un bucle es correcto, la forma estándar es elegir un bucle invariante y una prueba de inducción. Pero omita las palabras complicadas: lo que hace, informalmente, es descubrir algo que es cierto para cada iteración del ciclo, y que cuando termina el ciclo, lo que quería lograr ahora es verdadero. El bucle invariante es falso al final, para que el bucle termine.

Si las condiciones de bucle se asignan con bastante facilidad al invariante, y el invariante está en la parte superior del bucle, y se deduce que el invariante es verdadero en la siguiente iteración del bucle al trabajar con el código del bucle, entonces es fácil darse cuenta de que el ciclo es correcto.

Sin embargo, si el invariante está en la parte inferior del ciclo, entonces a menos que tenga una aserción justo antes del ciclo (una buena práctica) entonces se vuelve más difícil porque esencialmente debe inferir lo que debería ser invariante, y que cualquier código que se ejecute antes del bucle hace que el bucle invariante sea verdadero (dado que no hay precondición de bucle, el código se ejecutará en el bucle). Simplemente se vuelve más difícil de demostrar que es correcto, incluso si es una prueba informal en la cabeza.

1

Esto no es realmente una respuesta sino una reiteración de algo que uno de mis profesores dijo y me interesó en ese momento.

Los dos tipos de bucle while..do y do ... while son en realidad instancias de un tercer bucle más genérico, que tiene la prueba en algún lugar en el medio.

begin loop 
    <Code block A> 
    loop condition 
    <Code block B> 
end loop 

Código bloque A se ejecuta al menos una vez y B se ejecuta cero o más veces, pero no se ejecuta en la última iteración (no). un ciclo while es cuando el bloque de código a está vacío y un do ... mientras que el bloque de código b está vacío. Pero si está escribiendo un compilador, podría interesarle generalizar ambos casos en un ciclo como este.

0

Diría que es una mala práctica escribir if..do..while en bucles, por la sencilla razón de que esto aumenta el tamaño del código y causa duplicaciones de código. Las duplicaciones de código son propensas a errores y deben evitarse, ya que cualquier cambio en una parte debe realizarse en el duplicado también, lo que no siempre es el caso. Además, un código más grande significa un tiempo más difícil en la memoria caché de la CPU. Finalmente, maneja casos nulos y resuelve dolores de cabeza.

Solo cuando el primer bucle es fundamentalmente diferente, debe usarse uno ... mientras, por ejemplo, si el código que le hace pasar la condición de bucle (como la inicialización) se realiza en el bucle. De lo contrario, si está seguro de que ese bucle nunca caerá en la primera iteración, entonces sí, haga ... mientras sea apropiado.

0

De mi conocimiento limitado de la generación de código, creo que puede ser una buena idea escribir loops de prueba de fondo, ya que permiten al compilador optimizar mejor los bucles. Para los bucles de prueba del fondo, se garantiza que el bucle se ejecute al menos una vez. Esto significa que el código invariante de bucle "domina" el nodo de salida. Y así se puede mover con seguridad justo antes de que comience el ciclo.

54

Utilice los bucles while cuando desee probar una condición antes de la primera iteración del bucle.

Utilice los bucles do-while cuando desee probar una condición después de ejecutar la primera iteración del ciclo.

Por ejemplo, si usted se encuentra haciendo algo así como cualquiera de estos fragmentos:

func(); 
while (condition) { 
    func(); 
} 

//or: 

while (true){ 
    func(); 
    if (!condition) break; 
} 

Usted debe reescribir como:

do{ 
    func(); 
} while(condition); 
+4

Sólidamente hecho. (+1) –

+0

A menudo uso una variación de su segundo caso de prueba porque necesito hacer algo después de la verificación 'condición' – ntd

4

Yaa..its cierto .. hacer mientras se ejecutará al menos una vez. Esa es la única diferencia. Nada más que debatir en este

+5

¿Eh? (15 caracteres) –

6

Aquí hay un buen ejemplo del mundo real que encontré recientemente. Supongamos que tiene varias tareas de procesamiento (como procesar elementos en una matriz) y desea dividir el trabajo entre una secuencia por núcleo de CPU presente. ¡Debe haber al menos un núcleo para ejecutar el código actual!Por lo tanto se puede utilizar un do... while algo como:

do { 
    get_tasks_for_core(); 
    launch_thread(); 
} while (cores_remaining()); 

Es casi insignificante, pero podría valer la pena considerar el beneficio de rendimiento: igualmente podría ser escrito como un bucle estándar while, pero que siempre haría una comparación inicial innecesaria eso siempre evaluaría true - y en single-core, la condición do-while se bifurca de manera más predecible (siempre es falsa, en lugar de alternar verdadera/falsa para un estándar while).

2

Como señala Piemasons, la diferencia es si el ciclo se ejecuta una vez antes de hacer la prueba, o si la prueba se realiza primero para que el cuerpo del ciclo nunca se ejecute.

La pregunta clave es cuál tiene sentido para su aplicación.

para tomar dos ejemplos sencillos:

  1. decir que estás bucle a través de los elementos de una matriz. Si la matriz no tiene elementos, no desea procesar el número uno de cero. Entonces deberías usar WHILE.

  2. Quiere mostrar un mensaje, aceptar una respuesta y, si la respuesta no es válida, volver a preguntar hasta que obtenga una respuesta válida. Entonces siempre quieres preguntar una vez. No puede probar si la respuesta es válida hasta que obtenga una respuesta, por lo que debe pasar por el cuerpo del ciclo una vez antes de poder probar la condición. Deberías usar DO/WHILE.

17

Does evitando do/while realmente ayudan a hacer más legible el código?

Si tiene más sentido utilizar un bucle do/while, y luego hacerlo. Si necesita ejecutar el cuerpo de un bucle una vez antes de probar la condición, entonces un bucle do/while es probablemente la implementación más directa.

+0

@ sje397: Haga lo que resulte en el código menos complejo y más legible. Como con la mayoría de los estándares de codificación, es una medida totalmente subjetiva. En cuanto a que un ciclo do/while sea "poco común", creo que solo un principiante se confundiría con un ciclo do/while. –

+0

El problema es que las estadísticas de la otra pregunta (que muestra un código que para mí, * claramente * debería ser un ciclo do/while) parecen indicar que lo que creo que es más legible no es lo mismo que lo que otra gente piensa. – sje397

+0

@ sje397: a menudo hay múltiples soluciones aceptables para un problema. Personalmente prefiero el for-loop sugerido por Jon, pero no creo que un loop while o un loop do/while sea necesariamente inapropiado. –

4

Sí, al igual que usar para en lugar de while, o foreach en lugar de para mejorar la legibilidad. Dicho esto, algunas circunstancias necesitan hacerlo y estoy de acuerdo en que sería tonto forzar esas situaciones en un ciclo de tiempo.

4

Es más útil pensar en términos de uso común. La gran mayoría de los bucles while funcionan de manera bastante natural con while, incluso si se pueden hacer funcionar con do...while, por lo que básicamente debe usarlo cuando la diferencia no importe. De este modo, utilizaría do...while para los casos excepcionales en los que proporciona una mejora notable en la legibilidad.

3

Tiendo a preferir do-while loops, yo mismo. Si la condición será siempre verdadero al comienzo del ciclo, prefiero probarlo al final. En mi opinión, el objetivo de las condiciones de prueba (aparte de las afirmaciones) es que no se conoce el resultado de la prueba. Si veo un ciclo while con la prueba de condición en la parte superior, mi inclinación es considerar el caso en que el ciclo se ejecute cero veces. Si eso nunca puede suceder, ¿por qué no codificar de una manera que muestre claramente eso?

+0

+1. Me enseñaron a inicializar una variable para controlar la condición de ciclo a VERDADERO y usar un ciclo WHILE en los casos en que la condición siempre sería cierta en el primer ciclo y, en retrospectiva, no puedo pensar en ninguna otra manera más legible que simplemente usar un do-while. –

+0

Salir de la manera de usar 'while' en lugar de 'due-while' me parece francamente tonto. Mi punto era que uso 'do-while' en lugar de 'while' incluso cuando cualquiera de los dos funcionaría igual de bien. – supercat

0

En general, depende de cómo se estructura el código. Y como alguien ya respondió, algunos algoritmos necesitan al menos una iteración realizada.Entonces, para escapar del indicador de iteración adicional o al menos una bandera ocurrida en la interacción, empleas do/while.

1
while(someConditionMayBeFalse){ 

// this will never run... 

} 


// then the alternative 

do{ 

// this will run once even if the condition is false 

while(someConditionMayBeFalse); 

La diferencia es obvia y le permite tener un plazo de código y luego evaluar el resultado para ver si tiene que "hacer de nuevo" y el otro método de tiempo le permite tener un bloque de secuencia de comandos ignora si el condicional no se cumple.

Cuestiones relacionadas