Maximiliano Burgos
Posted on December 15, 2022
Bienvenido/a a otro capítulo del Curso de Kotlin! Podés consultar el curso completo desde este link que te dejo acá. Podés seguirme por LinkedIn o Twitter si querés estar al tanto de las próximas publicaciones.
Los bucles nos permiten iterar una serie procedimientos definidos. Dicho de una forma mas mundana, recorren una serie de lineas de código determinado por una X cantidad de veces. Los más conocidos en la mayoría de los lenguajes de programación son For y While. Hoy vamos a poner en práctica al segundo.
Iterando la Lotería
No tengo idea como puedo reutilizar un ejemplo tantas veces, pero aquí estamos, con otra aplicación interesante de conceptos a nuestra querida lotería.
En la clase anterior utilizamos la sentencia when para crear un sistema de puntuación dependiendo la posición de nuestro número ganador:
var score = 0
when (selectedNum?.toInt()) {
numbers[0] -> score += 5
numbers[1] -> score += 4
numbers[2] -> score += 3
numbers[3] -> score += 2
numbers[4] -> score += 1
else -> {
println("Has perdido! El numero era ${numbers[0]}")
return
}
}
println("Has ganado! Tu score final es $score")
El problema con esto es que si bien estamos sumarizando valores a la variable score, este programa se ejecuta una sola vez. Esto significa que no podemos hacer más que 5 puntos. Nuestra variable solo se modifica una vez, por lo cual no tiene mucha utilidad. Necesitamos iterar el comportamiento del when; armar un bucle que repita la operación y nos permita seguir jugando hasta cansarnos. Es el momento de usar la sentencia While.
Implementación de While
While, al igual que If, evalúa una expresión y acciona en base a la misma. La diferencia es que en el segundo caso, existirá un bucle, una ejecución repetitiva, hasta que la condición se deje de cumplir. Vamos a un ejemplo sencillo:
val isRunning = true
while (isRunning){
println("Estoy corriendo!")
}
Mientras la expresión evaluada en While sea verdadera (true), se imprimirá en pantalla la frase “Estoy corriendo!”. Tenemos un bucle: todo lo que esté dentro de While se ejecutará hasta que la expresión sea falsa (false). Si dejamos el código así, tenemos lo que llamamos un “bucle infinito”. Nuestro programa seguirá corriendo hasta que se quede sin memoria o se termine forzadamente.
Un desarrollador que nació ayer (o escribe mensajes motivacionales en LinkedIn) te va a decir algo como “¡oh no, esto está mal!”. La realidad es que usar un bucle infinito no es algo incorrecto: depende de tu propósito. Por ejemplo en un videojuego, es muy común encontrar algo asi:
var isRunningAnimation = true
val isJumping = false
while (isRunningAnimation){
println("Estoy corriendo!")
if(isJumping) {
println("Estoy saltando!")
isRunningAnimation = false
}
}
Mientras el jugador esta corriendo, se “reproduce” la animación de correr. Pero si presiona el botón de salto, la animación cambia y isRunningAnimation cambia a false, lo cual implica que en la siguiente iteración While evalúa que no se esta corriendo, y sale del bucle.
Esto es una explicación muy vaga porque en el desarrollo de videojuegos se utilizan máquinas de estado; pero la realidad es que existen los bucles infinitos para determinar estos mismos estados. Por lo cual, no existen las malas prácticas con los bucles siempre y cuando esten generando los resultados esperados.
Volviendo a nuestra lotería, ya te estas haciendo una idea de la utilidad que tiene While para nuestro ejemplo. Implementemos un “comando” para nuestra consola:
println("Elije un numero de los siguientes: ${numbers.contentToString()}")
println("Escribe exit para salir del juego.")
Esto por supuesto es plenamente visual, pero vamos a encapsularla dentro de while:
var score = 0
var isRunning = true
while (isRunning){
println("Elije un numero de los siguientes: ${numbers.contentToString()}")
println("Escribe exit para salir del juego.")
numbers.shuffle()
val selectedNum = readLine()
if(selectedNum == "exit"){
isRunning = false
}
when (selectedNum?.toInt()) {
numbers[0] -> score += 5
numbers[1] -> score += 4
numbers[2] -> score += 3
numbers[3] -> score += 2
numbers[4] -> score += 1
else -> println("Has perdido! El numero era ${numbers[0]}")
}
println("Has ganado! Tu score final es $score")
}
Vamos a dividir todo lo que esta ocurriendo en algunos pasos, para mayor entendimiento:
- Definimos la variable score, inicializada en cero.
- Definimos una variable booleana isRunning inicializada en true.
- Comprobamos que la loteria siga funcionando mientras isRunning sea true.
- Imprimimos las instrucciones en pantalla y mezclamos el array.
- Comprobamos el input de selectedNum, y si escribió “exit” salimos.
- Si escribió un número, tenemos el when de siempre pero quitamos el return porque queremos que la iteración se complete.
- Finalmente, si ganamos, mostramos el mensaje con la variable score acumulando los puntos, y vuelta a empezar.
Todo esto suena genial hasta que lo ejecutamos, ganamos, perdemos y queremos salir escribiendo “exit”:
Nos encontramos con un error en la linea 19, vamos a ver que pasó:
when (selectedNum?.toInt()) {
No podemos castear “exit” a un entero; pero tampoco es el comportamiento que esperábamos. Esto ocurrió porque luego de la validación, nuestra instrucción While continuó ejecutando el resto del bloque. El funcionamiento de nuestro bucle es correcto, pero no es lo que queríamos. Podemos solucionarlo de la siguiente manera:
if(selectedNum == "exit"){
isRunning = false
break
}
La sentencia break genera, como lo dice la palabra, un salto en el código. Nos permite saltar entre condicionales y bucles, dependiendo de donde estemos. En este caso, si el jugador escribió “exit”, comprobaremos el valor de selectedNum y saldremos del bucle. Pero ahora Intellij nos indica que isRunning ya no tiene utilidad.
Por supuesto, tiene razón: si incluimos un break en este caso, nuestra variable de estado dejó de tener mucho sentido. Por lo cual, vamos a eliminarla y decirle a while lo siguiente:
while (true){
Esto podría ser un bucle infinito, porque “true” siempre es verdadero. No hay una condición que pueda decir lo contrario. Sin embargo, al usar break estamos generando una salida determinada, pero sin una variable que cambie en el bucle. Este sería el código final:
val numbers = arrayOf(1, 4, 6, 9, 15, 30, 45, 60, 78, 90)
var score = 0
while (true){
println("Elije un numero de los siguientes: ${numbers.contentToString()}")
println("Escribe exit para salir del juego.")
numbers.shuffle()
val selectedNum = readLine()
if(selectedNum == "exit"){
break
}
when (selectedNum?.toInt()) {
numbers[0] -> score += 5
numbers[1] -> score += 4
numbers[2] -> score += 3
numbers[3] -> score += 2
numbers[4] -> score += 1
else -> println("Has perdido! El numero era ${numbers[0]}")
}
println("Has ganado! Tu score final es $score")
}
Y dado que tenemos solo una linea en el If, podríamos acotarlo de la siguiente manera:
if(selectedNum == "exit") break
Con esto tenemos nuestra lotería funcional e iterativa.
Conclusiones
Me gusta que te puedas llevar la idea de que el bucle While es mucho más poderoso de lo que realmente se piensa. También es importante remarcar el hecho de que un bucle infinito no siempre es malo, si la intención es justificada. En la próxima clase vamos a tratar con otro tipo de bucle un poco más complejo pero fundamental cuando se trata de listas y arreglos: For.
Posted on December 15, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.