do-while
. Tanto alumnos como maestros me han preguntado sobre sobre la manera más conveniente de darle la vuelta a esta limitación del lenguaje. En esta entrada del blog de EduPython responderé de manera extensiva a esta pregunta.Para empezar, conviene distinguir entre los dos tipos de ciclo que encontramos en muchos lenguajes de programación: el ciclo
while
y el ciclo do-while
. Existe un tercer tipo de ciclo, el ciclo for
, pero no es relevante para esta discusión.
El ciclo while
En la familia de lenguajes basados en C (por ejemplo C++, C#, Java, JavaScript, etc.), el ciclo while
tiene la siguiente sintaxis:// Ciclo while while (condición) { cuerpo }El siguiente diagrama de flujo muestra como se comporta este tipo de ciclo:
Ciclo while
|
Podemos ver que primero se evalúa la condición. Si ésta resulta falsa, la instrucción termina. Sin embargo, si resulta verdadera el cuerpo se ejecuta, y posteriormente se vuelve a evaluar la condición. Esto se repite efectivamente mientras la condición siga dando un valor de verdadero. Normalmente se supone que algo ocurrirá dentro del cuerpo del ciclo que hará que la condición sea eventualmente falsa1.
El ciclo do-while
La sintaxis de la instrucción do-while
en los lenguajes basados en C es la siguiente: // Ciclo do-while do { cuerpo } while (condición);Y éste es el diagrama de flujo respectivo:
Ciclo do-while |
while
vs. do-while
La diferencia central entre el ciclo while
y ciclo do-while
radica en el número mínimo de veces que se ejecuta el cuerpo respectivo. En el caso de la instrucción while
, el cuerpo se ejecuta un mínimo de cero veces, mientras que la instrucción do-while
el mínimo es una vez. A nivel sintáctico, la diferencia entre ambos
ciclos se enfatiza por la posición en la que debe ir la expresión condicional: en la instrucción while
va antes del cuerpo y en la instrucción do-while
va después de éste2.Cuando llego a impartir un curso de introducción a la programación me gusta presentarle a mis alumnos el siguiente ejemplo para ayudarles a entender la diferencia entre estos dos tipos de ciclos:
Hay un chavo tímido que está con una chava que le gusta y desea darle de besos (todos los que se puedan). Existen dos alternativas:En la alternativa 1, si la chava no quiere besos, entonces el chavo nunca la podrá besar. Sin embargo en la alternativa 2, si la chava no quiere besos, el chavo por lo menos le alcanza a robar uno3. Por tanto la alternativa 1 es un ejemplo del ciclo
- Alternativa 1: El chavo le pregunta a la chava si la puede besar. Si la chava dice que no, al chavo no le queda mas que aguantarse. Pero si la chava dice que sí, el chavo besa a la chava y luego le vuelve a preguntar si la puede besar otra vez, repitiendo este proceso hasta que la chava diga que no.
- Alternativa 2: El chavo besa a la chava. El chavo le pregunta a la chava si la puede besar otra vez. Si la chava dice que sí, el chavo besa nuevamente a la chava y luego le vuelve a preguntar si la puede besar una vez más, repitiendo este proceso hasta que la chava diga que no.
while
, mientras que la alternativa 2 es un ejemplo del ciclo do-while
.
Implementación del ciclo do-while
en Python
Un caso de uso típico del ciclo do-while
es cuando requerimos solicitar al usuario un cierto dato de entrada el cual debe ser validado. Si el dato capturado contiene algún error, entonces se debe solicitar nuevamente la entrada y se debe validar otra vez, repitiendo esto hasta que la entrada sea correcta. Por ejemplo, en pseudocódigo lo anterior se podría expresar así:- Hacer lo siguiente:
- Sea e una cadena solicitada al usuario.
- Sea r igual a verdadero si e contiene el nombre de una de las estaciones del año (primavera, verano, otoño o invierno), o falso en caso contrario.
- Si r es falso, imprimir un mensaje de error.
- Repetir lo anterior mientras r sea falso.
- Imprimir un mensaje de éxito.
while
, mas no con la instrucción do-while
. Dada esta restricción, podemos re-plantear el código de tal forma que tenga la siguiente estructura:Simulación de un ciclo do-while medianteun ciclo while y duplicación del cuerpo |
e = input('Introduce el nombre de una estación ' 'del año: ') r = e.lower() in ['primavera', 'verano', 'otoño', 'invierno'] if not r: print('"{0}" no es el nombre de una estación ' 'del año.'.format(e)) print('Favor de volverlo a intentar.') while not r: e = input('Introduce el nombre de una estación ' 'del año: ') r = e.lower() in ['primavera', 'verano', 'otoño', 'invierno'] if not r: print('"{0}" no es el nombre de una estación ' 'del año.'.format(e)) print('Favor de volverlo a intentar.') print("Muy bien.")Las ocho líneas que aparecen antes del
while
se repiten también en su cuerpo. Aunque la funcionalidad obtenida es exactamente la esperada, la solución no es recomendable precisamente debido a la duplicación de código. El programa es más largo de lo necesario y obliga a ocupar más tiempo al momento de leerlo e intentar comprenderlo. Pero peor aún: si se requiere hacer un cambio en cualquiera de esas líneas, las modificaciones se tienen que hacer en dos lugares distintos; resulta muy fácil alterar el código en un lugar y olvidarse que hay otra parte que también necesita el mismo cambio.Cuando se tienen instrucciones duplicadas éstas pueden ser eliminadas aplicando una refactorización de código conocida como extracción de método. Sin embargo en nuestro ejemplo hay una solución más sencilla: podemos garantizar que el cuerpo del ciclo
while
se ejecutará al menos una vez si nos aseguramos que la condición del ciclo es verdadera antes de la primera iteración. Modificando nuestro código tendríamos lo siguiente:r = False while not r: e = input('Introduce el nombre de una estación ' 'del año: ') r = e.lower() in ['primavera', 'verano', 'otoño', 'invierno'] if not r: print('"{0}" no es el nombre de una estación ' 'del año.'.format(e)) print('Favor de volverlo a intentar.') print("Muy bien.")En este caso pudimos remplazar las ocho líneas de código que estaban antes del
while
por una sola línea: r = False
. Con esa sola instrucción obligamos a que la condición del ciclo (not r
) sea verdadera la primera vez que se evalúa, y por lo tanto haciendo que se ejecute efectivamente el cuerpo al menos una vez. El valor de la variable r
se re-asigna dentro del cuerpo del ciclo, así que de ese nuevo valor dependerá que el cuerpo se vuelva a ejecutar, tal como ocurría desde la primera versión del programa.Otra alternativa en Python, que resulta una solución más general, consiste en escribir un ciclo “infinito” (usando un instrucción
while True:
) y añadir una instrucción if
con un break
al final del cuerpo. La condición de este if
es la misma que la condición del do-while
, pero en forma lógicamente negada, ya que la condición se usa en este caso para indicar cuando se debe terminar el ciclo en lugar de establecer el criterio de permanencia en éste. Esta forma resulta más conveniente que las otras dos planteadas anteriormente gracias a que es prácticamente una simple traducción del ciclo do-while
. Modificando una vez más el código de arriba obtendríamos lo siguiente:
while True: e = input('Introduce el nombre de una estación ' 'del año: ') r = e.lower() in ['primavera', 'verano', 'otoño', 'invierno'] if not r: print('"{0}" no es el nombre de una estación ' 'del año.'.format(e)) print('Favor de volverlo a intentar.') # Condición para romper el ciclo. if r: break print("Muy bien.")Cuando se ejecuta un
break
el ciclo en el que está contenido termina de manera inmediata, continuando el flujo de control del programa en la siguiente instrucción posterior al ciclo.
Ciclo loop-with-exit
La instrucción condicional que contiene el break
ni siquiera tiene que ser la última instrucción del cuerpo del while
. De hecho, a veces resulta útil que se encuentre entre dos porciones de código. A la estructura de control de flujo que cumple con la descripción anterior se le conoce como loop-with-exit
(ciclo con salida) y está soportada de manera directa en lenguajes de programación como Ada y versiones modernas de Fortran y Basic. Dicha estructura tiene la siguiente forma:Ciclo loop-with-exit
|
El siguiente ejemplo muestra la implementación de un ciclo
loop-with-exit
en Python. El programa solicita al usuario una cadena de entrada, la convierte a mayúsculas, imprime el resultado y repite todo este proceso. Sin embargo, si la cadena capturada es vacía el ciclo se termina.
while True: e = input('Introduce una cadena de caracteres, ' 'o vacío para terminar: ') # Condición para romper el ciclo. if e == '': break m = e.upper() print('Convertido a mayúsculas: {0}'.format(m)) print('Fin.')Para que el código resulte más claro, se recomienda que solo haya una instrucción
break
dentro del ciclo. Esta recomendación surge desde de los años sesenta y setenta, cuando autores como Edsger W. Dijkstra y C.A.R. Hoare iniciaron una campaña a favor de la programación estructurada. La idea fundamental consistía en evitar la escritura de lo que se conoce de forma despectiva como “código espagueti”. Desde entonces se estableció que por legibilidad los ciclos debían contar con un solo punto de entrada y un solo punto de salida.
La instrucción while-else
Ya entrados en el uso del while
y el break
de Python, quizás valga la pena mencionar una peculiaridad poco conocida del lenguaje: el while
puede tener un else
asociado, de forma similar a como ocurre con la instrucción if
. En este caso el cuerpo del else
se ejecuta solo si el while
concluye de manera “normal” (cuando su condición resulta falsa) en lugar de terminar por una instrucción break
anidada dentro del ciclo. Por ejemplo:
n = [4, 8, 15, 16, 23, 42] x = 16 i = 0 while i < len(n): if x == n[i]: print('Se encontró el {0} en el índice {1}.' .format(x, i)) break i += 1 else: print('No se encontró el {0}.'.format(x))Este programa produce la siguiente salida:
Se encontró el 16 en el índice 3.En este caso el código asociado al
else
no se ejecuta porque el ciclo termina debido a un break
. Sin embargo, si cambiamos la segunda línea del programa de arriba por x = 20
, la salida ahora sería:No se encontró el 20.Dado que ahora el ciclo termina gracias a que en algún momento la condición del
while
se hace falsa, el cuerpo del else
ahora sí se ejecuta.En general el uso del
while-else
no es común, y su uso no es muy recomendable debido a que es un ciclo con un punto de entrada pero con dos puntos de salida, haciéndolo en principio más difícil de entender.Conclusión
A pesar de que Python no cuenta con la instruccióndo-while
es muy fácil emularla con un while True:
y un if
con un break
al final del cuerpo del ciclo. Esta forma se puede generalizar en una instrucción conocida como loop-with-exit
, en donde el if
con el break
puede ir en cualquier parte del cuerpo del ciclo. Sin embargo es importante procurar que nuestro código sea fácil de entender, por lo que de preferencia los ciclos solo deben tener un punto de entrada y un punto de salida. Haciendo esto cumplimos con un principio fundamental de la programación estructurada.Notas:
1 Por lo menos así es cuando existe solamente un hilo de ejecución. Un hilo distinto pero concurrente al que está ejecutando el ciclo también puede hacer que la condición cambie.2 Existen algunas variaciones del ciclo
do-while
en otros lenguajes. Un ejemplo es la instrucción repeat-until
del lenguaje Pascal. La principal diferencia entre estas dos instrucciones es que para terminar un ciclo do-while
se requiere que su condición sea falsa, mientras que para terminar un ciclo repeat-until
su condición debe ser verdadera. En ambos casos el cuerpo del ciclo se ejecuta mínimo una vez.3 Este ejemplo es solo para fines ilustrativos. En ningún momento se está consintiendo a que los chavos besen a las chavas sin su permiso.
excelente....saludos desde venezuela...
ResponderBorrarMuchas gracias. Saludos desde México.
Borrargracias por la muy buena informacion saludos desde Colombia
ResponderBorrarHola. Me alegra que le haya sido de utilidad.
BorrarLa información fue realmente muy valiosa; muchísimas gracias, logré entender muchas cosas a cerca de los ciclos.
ResponderBorrarSaludos desde Colombia.
Gracias por su comentario. Me da gusto saber que esta información le está resultando útil a la gente. Saludos.
BorrarExcelente información , para personas como yo que se introducen al grandioso mundo del Python aunque mas enfocado a la POO, estas técnicas no están demás saludos desde Venezuela .
ResponderBorrarQué bueno que te gusto esta entrada del blog. Saludos desde México.
BorrarMuchas gracias señor. Saludos desde Perú
ResponderBorrarDe nada, saludos.
BorrarMuchas gracias por tu ayuda! Súper claro el post!
ResponderBorrarQué bueno que te haya servido. Saludos.
BorrarMe encantó la info, bien explicado todo 👌. Muchas gracias por el aporte :)
ResponderBorrarGracias, Pablo. Me da gusto saber que te gustó. Saludos.
BorrarExcelente información. Por fin logré besar a la chica que me gusta.
ResponderBorrarMuy bueno.
ResponderBorrarGracias, me encantan los Blogs, me parecen muy elegantes.
ResponderBorrarBuenísima la información, agrego su blog a marcadores para seguir aprendiendo.
Hola! Alguien sabe cuando se creo el Do-while?
ResponderBorrar