Posibles duplicados:
While vs. Do While ¿
Cuándo debo usar los bucles do-while en lugar de while?
He estado programando por un tiempo (2 años de trabajo + 4.5 años de grado + 1 año de preuniversitario), y nunca he usado un ciclo de hacer mientras no me veo obligado a hacerlo en el curso de Introducción a la programación. Tengo la sensación de que estoy haciendo mal la programación si nunca me encuentro con algo tan fundamental.
¿Podría ser que simplemente no me he encontrado con las circunstancias correctas?
¿Cuáles son algunos ejemplos en los que sería necesario utilizar un do-while en lugar de un while?
(Mi educación fue casi toda en C / C ++ y mi trabajo está en C #, por lo que si hay otro lenguaje en el que tiene absolutamente sentido porque los do-whiles funcionan de manera diferente, entonces estas preguntas realmente no se aplican).
Para aclarar ... sé la diferencia entre a while
y a do-while
. Mientras comprueba la condición de salida y luego realiza tareas. do-while
realiza tareas y luego verifica la condición de salida.
Respuestas:
Si siempre desea que el bucle se ejecute al menos una vez. No es común, pero lo uso de vez en cuando. Un caso en el que es posible que desee utilizarlo es intentar acceder a un recurso que podría requerir un reintento, por ejemplo
do { try to access resource... put up message box with retry option } while (user says retry);
fuente
do-while es mejor si el compilador no es competente en la optimización. do-while tiene un solo salto condicional, a diferencia de for y while que tienen un salto condicional y un salto incondicional. Para las CPU que están canalizadas y no hacen predicción de ramas, esto puede marcar una gran diferencia en el rendimiento de un ciclo cerrado.
Además, dado que la mayoría de los compiladores son lo suficientemente inteligentes como para realizar esta optimización, todos los bucles que se encuentran en el código descompilado serán usualmente do-while (si el descompilador se molesta siquiera en reconstruir los bucles a partir de gotos locales hacia atrás).
fuente
He usado esto en una función TryDeleteDirectory. Fue algo como esto
do { try { DisableReadOnly(directory); directory.Delete(true); } catch (Exception) { retryDeleteDirectoryCount++; } } while (Directory.Exists(fullPath) && retryDeleteDirectoryCount < 4);
fuente
while
?Do while es útil para cuando quieres ejecutar algo al menos una vez. En cuanto a un buen ejemplo para usar do while versus while, digamos que desea hacer lo siguiente: Una calculadora.
Puede abordar esto utilizando un bucle y verificando después de cada cálculo si la persona quiere salir del programa. Ahora probablemente pueda asumir que una vez que se abre el programa, la persona quiere hacer esto al menos una vez, por lo que podría hacer lo siguiente:
do { //do calculator logic here //prompt user for continue here } while(cont==true);//cont is short for continue
fuente
continue
es una palabra clave; D== true
no es necesario.cont == true
si cont es verdadero, devuelve verdadero. Eso es totalmente inútil.Esta es una especie de respuesta indirecta, pero esta pregunta me hizo pensar en la lógica detrás de ella, y pensé que valdría la pena compartirla.
Como han dicho todos los demás, usa un
do ... while
bucle cuando desea ejecutar el cuerpo al menos una vez. Pero, ¿bajo qué circunstancias le gustaría hacer eso?Bueno, la clase de situaciones más obvia en la que puedo pensar sería cuando el valor inicial ("sin cebar") de la condición de verificación es el mismo que cuando desea salir . Esto significa que debe ejecutar el cuerpo del bucle una vez para cebar la condición a un valor no existente y luego realizar la repetición real basada en esa condición. Con los programadores siendo tan vagos, alguien decidió envolver esto en una estructura de control.
Entonces, por ejemplo, leer caracteres desde un puerto serie con un tiempo de espera podría tomar la forma (en Python):
response_buffer = [] char_read = port.read(1) while char_read: response_buffer.append(char_read) char_read = port.read(1) # When there's nothing to read after 1s, there is no more data response = ''.join(response_buffer)
Tenga en cuenta la duplicación de código:
char_read = port.read(1)
. Si Python tuviera undo ... while
bucle, podría haber usado:do: char_read = port.read(1) response_buffer.append(char_read) while char_read
El beneficio adicional para los lenguajes que crean un nuevo alcance para los bucles:
char_read
no contamina el espacio de nombres de la función. Pero tenga en cuenta también que hay una mejor manera de hacer esto, y es utilizando elNone
valor de Python :response_buffer = [] char_read = None while char_read != '': char_read = port.read(1) response_buffer.append(char_read) response = ''.join(response_buffer)
Así que aquí está el quid de mi punto: en los lenguajes con tipos que aceptan valores NULL, la situación
initial_value == exit_value
surge con mucha menos frecuencia y es posible que esa sea la razón por la que no la encuentre. No digo que nunca suceda, porque todavía hay momentos en los que una función volveráNone
a significar una condición válida. Pero en mi opinión apresurada y brevemente considerada, esto sucedería mucho más si los lenguajes que utilizó no permitieran un valor que signifique: esta variable aún no se ha inicializado.Este no es un razonamiento perfecto: en realidad, ahora que los valores nulos son comunes, simplemente forman un elemento más del conjunto de valores válidos que puede tomar una variable. Pero prácticamente, los programadores tienen una forma de distinguir entre una variable que está en estado sensible, que puede incluir el estado de salida del bucle, y que está en un estado no inicializado.
fuente
None
, lawhile
versión correctamente no agrega nada al búfer de respuesta, pero sudo
versión siempre agrega algo.read()
no debería volver nuncaNone
. Si está utilizando una biblioteca donde lo hace, la premisa no se aplica (es decir, que el valor centinela no está en el conjunto de posibles valores de verificación).''
lugar deNone
. Algún tipo de valor que se evalúa como falso. Lo atribuyo a mi falta de familiaridad con laread
función de Python .read()
regresa''
, no se agregará nada a la cadena, está vacío.Los usé bastante cuando estaba en la escuela, pero no tanto desde entonces.
En teoría, son útiles cuando desea que el cuerpo del bucle se ejecute una vez antes de la verificación de la condición de salida. El problema es que para los pocos casos en los que no quiero la verificación primero, normalmente quiero la verificación de salida en el medio del cuerpo del bucle en lugar de al final. En ese caso, prefiero usar el conocido
for (;;)
con algunaif (condition) exit;
parte del cuerpo.De hecho, si estoy un poco tembloroso en la condición de salida del bucle, a veces me resulta útil comenzar a escribir el bucle
for (;;) {}
con una declaración de salida donde sea necesario, y luego, cuando termine, puedo ver si puede ser " limpiado "moviendo las inicializaciones, las condiciones de salida y / o el código de incrementofor
entre paréntesis.fuente
for(;;)
con "bucle infinito", mientraswhile(true)
que tengo que detenerme y pensar. Quizás sea porque estaba codificando C antes de que existiera untrue
. En el pasado, solo teníamosTRUE
una macro, y si algo tenía errores, tendría que convencerme de que la macro no se ha redefinido de0
alguna manera (¡sucedió!). Confor(;;)
no hay posibilidad de eso. Si insistes en el formulario while, casi prefiero verwhile (1)
. Por supuesto, algunos podrían preguntarse si no es la letra l ...codeify
texto, puede escribirlo directamente en el campo de comentarios usted mismo. He visto a algunas personas poner hipervínculos, pero eso es demasiado duro para mí.Una situación en la que siempre es necesario ejecutar un fragmento de código una vez y, según el resultado, posiblemente más veces. Lo mismo se puede producir con un
while
bucle regular .rc = get_something(); while (rc == wrong_stuff) { rc = get_something(); } do { rc = get_something(); } while (rc == wrong_stuff);
fuente
do while
es si desea ejecutar el bloque de código al menos una vez.while
por otro lado, no siempre se ejecutará según los criterios especificados.fuente
Es tan simple como eso:
condición previa vs condición posterior
Ahora que conoces el secreto ... úsalos sabiamente :)
fuente
Veo que esta pregunta ha sido respondida adecuadamente, pero me gustaría agregar este escenario de caso de uso muy específico. Puede comenzar a usar do ... while con más frecuencia.
do { ... } while (0)
se utiliza a menudo para #defines de varias líneas. Por ejemplo:
#define compute_values \ area = pi * r * r; \ volume = area * h
Esto funciona bien para:
-pero- hay una trampa para:
if (shape == circle) compute_values;
ya que esto se expande a:
if (shape == circle) area = pi *r * r; volume = area * h;
Si lo envuelve en un bucle do ... while (0), se expande correctamente a un solo bloque:
if (shape == circle) do { area = pi * r * r; volume = area * h; } while (0);
fuente
Hasta ahora, las respuestas resumen el uso generalizado para hacer mientras. Pero el OP pidió un ejemplo, así que aquí hay uno: Obtenga la entrada del usuario. Pero la entrada del usuario puede no ser válida, por lo que solicita la entrada, la valida, procede si es válida, de lo contrario, repita.
Con do-while, obtiene la entrada mientras que la entrada no es válida. Con un ciclo while regular, obtienes la entrada una vez, pero si no es válida, la obtienes una y otra vez hasta que sea válida. No es difícil ver que el primero es más corto, más elegante y más simple de mantener si el cuerpo del bucle se vuelve más complejo.
fuente
break
si la entrada es correcta.Lo he usado para un lector que lee la misma estructura varias veces.
using(IDataReader reader = connection.ExecuteReader()) { do { while(reader.Read()) { //Read record } } while(reader.NextResult()); }
fuente
He usado un
do while
cuando leo un valor centinela al comienzo de un archivo, pero aparte de eso, no creo que sea anormal que esta estructura no se use con demasiada frecuencia, losdo-while
s son realmente situacionales.-- file -- 5 Joe Bob Jake Sarah Sue -- code -- int MAX; int count = 0; do { MAX = a.readLine(); k[count] = a.readLine(); count++; } while(count <= MAX)
fuente
Esta es mi teoría de por qué la mayoría de la gente (incluyéndome a mí) prefiere bucles while () {} para hacer {} while (): Un bucle while () {} se puede adaptar fácilmente para que funcione como un bucle do.. while () mientras que lo contrario no es verdad. Un bucle while es en cierto modo "más general". También a los programadores les gustan los patrones fáciles de entender. Un ciclo while dice desde el principio cuál es su invariante y esto es algo bueno.
Esto es lo que quiero decir con lo "más general". Toma este bucle do.. while:
do { A; if (condition) INV=false; B; } while(INV);
Transformar esto en un bucle while es sencillo:
INV=true; while(INV) { A; if (condition) INV=false; B; }
Ahora, tomamos un modelo while loop:
while(INV) { A; if (condition) INV=false; B; }
Y transformar esto en un bucle do.. while, produce esta monstruosidad:
if (INV) { do { A; if (condition) INV=false; B; } while(INV) }
Ahora tenemos dos comprobaciones en los extremos opuestos y si el invariante cambia, debe actualizarlo en dos lugares. En cierto modo, do.. while es como los destornilladores especializados en la caja de herramientas que nunca usa, porque el destornillador estándar hace todo lo que necesita.
fuente
Estoy programando alrededor de 12 años y hace solo 3 meses me encontré con una situación en la que era realmente conveniente usar do-while ya que siempre era necesaria una iteración antes de verificar una condición. Así que supongo que su gran momento está por venir :).
fuente
do
while
había una mejor solución sin remordimientos?No puedo imaginar cómo has pasado tanto tiempo sin usar un
do...while
bucle.Hay uno en otro monitor en este momento y hay varios bucles de este tipo en ese programa. Son todos de la forma:
do { GetProspectiveResult(); } while (!ProspectIsGood());
fuente
Es una estructura bastante común en un servidor / consumidor:
DOWHILE (no shutdown requested) determine timeout wait for work(timeout) IF (there is work) REPEAT process UNTIL(wait for work(0 timeout) indicates no work) do what is supposed to be done at end of busy period. ENDIF ENDDO
el
REPEAT UNTIL(cond)
ser undo {...} while(!cond)
A veces, la espera para el trabajo (0) puede ser más barata en cuanto a CPU (incluso eliminar el cálculo del tiempo de espera podría ser una mejora con tasas de llegada muy altas). Además, hay muchos resultados de la teoría de las colas que hacen que el número servido en un período ocupado sea una estadística importante. (Ver, por ejemplo, Kleinrock - Vol 1.)
Similar:
DOWHILE (no shutdown requested) determine timeout wait for work(timeout) IF (there is work) set throttle REPEAT process UNTIL(--throttle<0 **OR** wait for work(0 timeout) indicates no work) ENDIF check for and do other (perhaps polled) work. ENDDO
donde
check for and do other work
puede ser exorbitantemente caro poner en el bucle principal o quizás un kernel que no admite unawaitany(waitcontrol*,n)
operación de tipo eficiente o quizás una situación en la que una cola priorizada podría privar al otro trabajo y se usa el acelerador como control de inanición.Este tipo de equilibrio puede parecer un truco, pero puede ser necesario. El uso ciego de grupos de subprocesos anularía por completo los beneficios de rendimiento del uso de un subproceso cuidador con una cola privada para una estructura de datos complicada de alta tasa de actualización, ya que el uso de un grupo de subprocesos en lugar de un subproceso cuidador requeriría una implementación segura para subprocesos.
Realmente no quiero entrar en un debate sobre el pseudocódigo (por ejemplo, si el apagado solicitado debe probarse en el HASTA) o los subprocesos del cuidador frente a los grupos de subprocesos; esto solo tiene la intención de dar una idea de un caso de uso particular de la estructura del flujo de control.
fuente
Esta es mi opinión personal, pero esta pregunta pide una respuesta basada en la experiencia:
He estado programando en C durante 38 años y nunca uso
do
/while
loops en código regular.El único uso convincente de esta construcción es en macros donde puede envolver múltiples declaraciones en una sola declaración a través de un
do { multiple statements } while (0)
He visto innumerables ejemplos de
do
/while
loops con detección de errores falsos o llamadas a funciones redundantes.Mi explicación para esta observación es que los programadores tienden a modelar los problemas incorrectamente cuando piensan en términos de bucles
do
/while
. O pasan por alto una condición final importante o pasan por alto el posible fallo de la condición inicial que mueven al final.Por estas razones, he llegado a creer que donde hay un
do
/while
loop, hay un error , y regularmente desafío a los programadores novatos a que me muestren undo
/while
loop donde no puedo detectar un error cercano.Este tipo de bucle se puede evitar fácilmente: utilice ay
for (;;) { ... }
agregue las pruebas de terminación necesarias donde sea apropiado. Es bastante común que sea necesario realizar más de una prueba de este tipo.Aquí hay un ejemplo clásico:
/* skip the line */ do { c = getc(fp); } while (c != '\n');
Esto fallará si el archivo no termina con una nueva línea. Un ejemplo trivial de tal archivo es el archivo vacío.
Una mejor versión es esta:
int c; // another classic bug is to define c as char. while ((c = getc(fp)) != EOF && c != '\n') continue;
Alternativamente, esta versión también oculta la
c
variable:for (;;) { int c = getc(fp); if (c == EOF || c == '\n') break; }
Intenta buscar
while (c != '\n');
en cualquier motor de búsqueda y encontrará errores como este (recuperado el 24 de junio de 2017):En ftp://ftp.dante.de/tex-archive/biblio/tib/src/streams.c , la función
getword(stream,p,ignore)
tiene undo
/while
y , efectivamente , al menos 2 errores:c
se define como unachar
ywhile (c!='\n') c=getc(stream);
Conclusión: evite
do
/while
loops y busque errores cuando vea uno.fuente
while() {}
bucles, porque son más seguros en términos de comprobaciones iniciales, por lo que tiende a hacer que se "pierda el cerebro", que sea perezoso para pensar en un algoritmo mejor, que ponga un montón de comprobaciones de errores innecesarias, etc. "Falta dedo{}while()
uso" es una señal de programador malo (descerebrado) o novato, produce un código más lento y de menor calidad. Así que primero me pregunto "¿Es estrictamente un caso while () {}?" Si no, solo intentaré escribir el bucle do while, a pesar de que podría requerir más pensamiento y precisión de datos de entradabreak
oreturn
declaraciones. Convertir eldo
/while
loop en unfor(;;)
loop sea más consistente en mi humilde opinión. Mi ostracismo hacia esta declaración es similar a mi negativa a usarlastrncpy()
incluso en los raros casos en los que sería la herramienta adecuada: no permita que los lectores ocasionales se imaginen que estas construcciones son inofensivas, porque en la mayoría de los casos son fuentes de errores difíciles de encontrar. .while
los bucles verifican la condición antes del bucle, losdo...while
bucles verifican la condición después del bucle. Esto es útil si desea basar la condición en los efectos secundarios del bucle en ejecución o, como dijeron otros carteles, si desea que el bucle se ejecute al menos una vez.Entiendo de dónde vienes, pero
do-while
es algo que la mayoría usa raramente, y yo nunca lo usé. No lo estás haciendo mal.No lo estás haciendo mal. Eso es como decir que alguien lo está haciendo mal porque nunca ha usado el
byte
primitivo. Simplemente no es tan de uso común.fuente
El escenario más común con el que me encuentro donde uso un
do
/while
loop es en un pequeño programa de consola que se ejecuta en base a alguna entrada y se repetirá tantas veces como desee el usuario. Obviamente, no tiene sentido que un programa de consola no se ejecute tiempos; pero más allá de la primera vez, depende del usuario, por lo tantodo
/ enwhile
lugar de solowhile
.Esto permite al usuario probar un montón de entradas diferentes si lo desea.
do { int input = GetInt("Enter any integer"); // Do something with input. } while (GetBool("Go again?"));
Sospecho que los desarrolladores de software usan
do
/while
cada vez menos en estos días, ahora que prácticamente todos los programas bajo el sol tienen una GUI de algún tipo. Tiene más sentido con las aplicaciones de consola, ya que es necesario actualizar continuamente la salida para proporcionar instrucciones o solicitar al usuario información nueva. Con una GUI, por el contrario, el texto que proporciona esa información al usuario puede simplemente sentarse en un formulario y nunca necesitar repetirse mediante programación.fuente
Utilizo bucles do-while todo el tiempo cuando leo archivos. Trabajo con muchos archivos de texto que incluyen comentarios en el encabezado:
# some comments # some more comments column1 column2 1.234 5.678 9.012 3.456 ... ...
Usaré un ciclo do-while para leer hasta la línea "column1 column2" para poder buscar la columna de interés. Aquí está el pseudocódigo:
do { line = read_line(); } while ( line[0] == '#'); /* parse line */
Luego haré un ciclo while para leer el resto del archivo.
fuente
if (line[0]=='#') continue;
justo después de laread_line
llamada en su bucle while principal ...Siendo un programador geezer, muchos de mis proyectos de programación escolar usaban interacciones impulsadas por menús de texto. Prácticamente todos usaron algo como la siguiente lógica para el procedimiento principal:
do display options get choice perform action appropriate to choice while choice is something other than exit
Desde los días escolares, he descubierto que uso el bucle while con más frecuencia.
fuente
Una de las aplicaciones que he visto está en Oracle cuando miramos conjuntos de resultados.
Una vez que tenga un conjunto de resultados, primero obtenga de él (hacer) y desde ese punto en adelante ... compruebe si la búsqueda devuelve un elemento o no (mientras se encuentra el elemento ...) .. Lo mismo podría aplicarse a cualquier otro " implementaciones similares a fetch.
fuente
Lo he usado en una función que devolvió la siguiente posición de carácter en una cadena utf-8:
char *next_utf8_character(const char *txt) { if (!txt || *txt == '\0') return txt; do { txt++; } while (((signed char) *txt) < 0 && (((unsigned char) *txt) & 0xc0) == 0xc0) return (char *)txt; }
Tenga en cuenta que esta función está escrita desde la mente y no probada. El caso es que, de todos modos, debe realizar el primer paso y debe hacerlo antes de poder evaluar la afección.
fuente
*(unsigned char *)txt-0x80U<0x40
.Cualquier tipo de entrada de consola funciona bien con do-while porque lo solicita la primera vez y lo vuelve a solicitar cada vez que falla la validación de entrada.
fuente
Aunque hay muchas respuestas aquí, es mi opinión. Todo se reduce a la optimización. Mostraré dos ejemplos donde uno es más rápido que el otro.
Caso 1:
while
string fileName = string.Empty, fullPath = string.Empty; while (string.IsNullOrEmpty(fileName) || File.Exists(fullPath)) { fileName = Guid.NewGuid().ToString() + fileExtension; fullPath = Path.Combine(uploadDirectory, fileName); }
Caso 2:
do while
string fileName = string.Empty, fullPath = string.Empty; do { fileName = Guid.NewGuid().ToString() + fileExtension; fullPath = Path.Combine(uploadDirectory, fileName); } while (File.Exists(fullPath));
Entonces, dos harán exactamente las mismas cosas. Pero hay una diferencia fundamental y es que el while requiere una declaración adicional para ingresar el while. Lo cual es feo porque digamos que
Guid
ya se han tomado todos los escenarios posibles de la clase, excepto una variante. Esto significa que tendré que recorrer los5,316,911,983,139,663,491,615,228,241,121,400,000
tiempos. Cada vez que llegue al final de mi estado de cuenta while, tendré que hacer lastring.IsNullOrEmpty(fileName)
verificación. Así que esto tomaría un poco, una pequeña fracción del trabajo de la CPU. Pero, ¿esta tarea tan pequeña multiplica las posibles combinaciones que tiene laGuid
clase y estamos hablando de horas, días, meses o tiempo extra?Por supuesto, este es un ejemplo extremo porque probablemente no lo vería en producción. Pero si pensamos en el algoritmo de YouTube, es muy posible que se encuentren con la generación de una identificación donde ya se han tomado algunas. Entonces se trata de grandes proyectos y optimización.
fuente
Me gusta entender estos dos como:
while
-> 'repetir hasta',do ... while
-> 'repetir si'.fuente
Me encontré con esto mientras investigaba el bucle adecuado para usar en una situación que tengo. Creo que esto satisfará completamente una situación común en la que un bucle do .. while es una mejor implementación que un bucle while (lenguaje C #, ya que indicó que es el principal para el trabajo).
Estoy generando una lista de cadenas basada en los resultados de una consulta SQL. El objeto devuelto por mi consulta es un SQLDataReader. Este objeto tiene una función llamada Read () que avanza el objeto a la siguiente fila de datos y devuelve verdadero si había otra fila. Devolverá falso si no hay otra fila.
Con esta información, quiero devolver cada fila a una lista y luego detenerme cuando no haya más datos para devolver. Un bucle Do ... While funciona mejor en esta situación, ya que garantiza que se agregará un elemento a la lista ANTES de verificar si hay otra fila. La razón por la que esto se debe hacer ANTES de verificar el while (condición) es que cuando verifica, también avanza. El uso de un bucle while en esta situación haría que se saltara la primera fila debido a la naturaleza de esa función en particular.
En breve:
Esto no funcionará en mi situación.
//This will skip the first row because Read() returns true after advancing. while (_read.NextResult()) { list.Add(_read.GetValue(0).ToString()); } return list;
Esta voluntad.
//This will make sure the currently read row is added before advancing. do { list.Add(_read.GetValue(0).ToString()); } while (_read.NextResult()); return list;
fuente
Read()
y debe ser un bucle while, ya que la primeraRead()
llamada accede a la primera fila. El externo llamaNextResult()
y debería ser un do-while, porque el primer conjunto de datos de resultados está activo antes de la primeraNextResult()
llamada. Los fragmentos de código no tienen mucho sentido, especialmente porque los comentarios no coinciden con el código y son incorrectos.Console.WriteLine("hoeveel keer moet je de zin schrijven?"); int aantal = Convert.ToInt32(Console.ReadLine()); int counter = 0; while ( counter <= aantal) { Console.WriteLine("Ik mag geen stiften gooien"); counter = counter + 1;
fuente