06. Control de Flujo
Ahhhh, control de flujo. Aquí es donde todo se une. Aunque este capítulo es más corto y más fácil que el capítulo sobre métodos, abrirá un mundo de posibilidades de programación. Después de este capítulo, podremos escribir programas realmente interactivos; antes hicimos programas que dicen cosas diferentes dependiendo de lo que escribas, pero después de este capítulo también harán cosas diferentes. Sin embargo, tenemos que poder comparar objetos en nuestro programa. Necesitamos...
Métodos de Comparación
Vamos a ser rápidos por aquí para llegar a las ramificaciones, que es donde las cosas interesantes comienzan a suceder. Para ver si un objeto es mayor o menor que otro, usamos los métodos > y <, así:
puts 1 > 2
puts 1 < 2
false
true
Todo en orden. Del mismo modo, podemos descubrir si un objeto es mayor o igual que (o menor o igual que) otro con los métodos >= y <=.
puts 5 >= 5
puts 5 <= 4
true
false
Por último, podemos descubrir si dos objetos son iguales o no usando == (que significa "¿son iguales estos objetos?") y != (que significa "¿son diferentes estos objetos?"). Es importante no confundir = con ==. = sirve para decirle a una variable que apunte a un objeto (asignación), y == es para hacer la pregunta: "¿Son estos dos objetos iguales?".
puts 1 == 1
puts 2 != 1
true
true
Y, por supuesto, también podemos comparar strings. Cuando se comparan strings, se tiene en cuenta su ordenamiento lexicográfico, que, en pocas palabras, significa su orden en el diccionario. perro viene antes de gato en el diccionario, así que:
puts 'perro' < 'gato'
true
Hay un inconveniente: los ordenadores suelen ordenar las letras mayúsculas antes que las minúsculas, como si vinieran antes (así es como guardan las letras en las fuentes, por ejemplo: todas las letras mayúsculas primero, seguidas de las minúsculas). Esto significa que el ordenador pensará que 'Zoológico' viene antes de 'abeja', así que si quieres descubrir qué palabra vendría primero en un diccionario de verdad, usa downcase (o upcase o capitalize) en ambas palabras antes de intentar compararlas.
Una última observación antes de Ramificaciones: los métodos de comparación no nos están dando las strings 'true' y 'false'; nos están dando los objetos especiales true y false (claro, true.to_s nos da 'true', que es como puts imprimió 'true'). true y false se usan todo el tiempo en...
Ramificaciones (Branching)
"Ramificación" es un concepto simple pero poderoso. De hecho, es tan simple que apuesto a que ni siquiera tengo que explicarlo; déjame mostrártelo:
puts 'Hola, ¿cuál es tu nombre?'
nombre = gets.chomp
puts 'Hola, ' + nombre + '.'
if nombre == 'Chris'
puts '¡Qué nombre tan bonito!'
end
Hola, ¿cuál es tu nombre?
Chris
Hola, Chris.
¡Qué nombre tan bonito!
Pero si ponemos un nombre diferente...
Hola, ¿cuál es tu nombre?
Chewbacca
Hola, Chewbacca.
Y eso es ramificar. Si lo que viene después de if (si) es true, ejecutamos el código entre if y end. Si lo que viene después de if es false, no ejecutamos nada. Claro y simple.
He indentado el código entre if y end porque creo que hace que sea más fácil seguir las ramificaciones. Casi todos los programadores hacen esto, independientemente del lenguaje en el que estén trabajando. Puede que no parezca muy útil en este pequeño ejemplo, pero cuando las cosas se ponen más complejas, marca una gran diferencia.
A menudo nos gustaría que un programa hiciera una cosa si una expresión es true, y otra si es false. Para eso sirve else (si no / de lo contrario):
puts 'Soy un adivino. Dime tu nombre:'
nombre = gets.chomp
if nombre == 'Chris'
puts 'Veo cosas maravillosas en tu futuro.'
else
puts 'Tu futuro es... ¡Oh, Dios! ¡Mira la hora!'
puts '¡Tengo que irme, lo siento mucho!'
end
Soy un adivino. Dime tu nombre:
Chris
Veo cosas maravillosas en tu futuro.
Ahora intentemos con un nombre diferente...
Soy un adivino. Dime tu nombre:
Ringo
Tu futuro es... ¡Oh, Dios! ¡Mira la hora!
¡Tengo que irme, lo siento mucho!
Ramificar es como llegar a una bifurcación en el código: ¿tomamos el camino para las personas con el nombre == 'Chris' o, else (si no), tomamos el otro camino?
Y como las ramas de un árbol, puedes tener ramificaciones que contienen sus propias ramificaciones:
puts 'Hola, y bienvenido a la clase de Español.'
puts 'Mi nombre es Sra. Gabbard. ¿Y tu nombre es...?'
nombre = gets.chomp
if nombre == nombre.capitalize
puts 'Por favor, toma asiento, ' + nombre + '.'
else
puts nombre + '? ¿Quieres decir ' + nombre.capitalize + ', verdad?'
puts '¿¿Ni siquiera sabes escribir tu nombre??'
respuesta = gets.chomp
if respuesta.downcase == 'si'
puts '¡Hmmph! ¡Bueno, siéntate!'
else
puts '¡¡FUERA!!'
end
end
Hola, y bienvenido a la clase de Español.
Mi nombre es Sra. Gabbard. ¿Y tu nombre es...?
chris
chris? ¿Quieres decir Chris, verdad?
¿¿Ni siquiera sabes escribir tu nombre??
si
¡Hmmph! ¡Bueno, siéntate!
Está bien, lo pondré en mayúscula...
Hola, y bienvenido a la clase de Español.
Mi nombre es Sra. Gabbard. ¿Y tu nombre es...?
Chris
Por favor, toma asiento, Chris.
A veces puede ser confuso averiguar dónde poner los ifs, elses y ends. Lo que hago es escribir el end al mismo tiempo que escribo el if. Así que mientras escribía el programa anterior, primero se veía así:
puts 'Hola, y bienvenido a la clase de Español.'
puts 'Mi nombre es Sra. Gabbard. ¿Y tu nombre es...?'
nombre = gets.chomp
if nombre == nombre.capitalize
else
end
Luego lo rellené con comentarios, cosas en el código que el ordenador ignorará:
puts 'Hola, y bienvenido a la clase de Español.'
puts 'Mi nombre es Sra. Gabbard. ¿Y tu nombre es...?'
nombre = gets.chomp
if nombre == nombre.capitalize
# Ella es amable.
else
# Ella se enoja.
end
Cualquier cosa después de un # se considera un comentario (a menos, claro, que estés dentro de una string). Después de rellenar con comentarios, los reemplacé con código funcional. A algunas personas les gusta dejar los comentarios; personalmente, creo que el código bien escrito habla por sí mismo. Solía escribir más comentarios, pero a medida que me vuelvo más "fluido" en Ruby, los uso cada vez menos. De hecho, me distraen gran parte del tiempo. Es una elección personal; encontrarás tu propio estilo (generalmente tu estilo evolucionará). Así que mi siguiente paso se veía así:
puts 'Hola, y bienvenido a la clase de Español.'
puts 'Mi nombre es Sra. Gabbard. ¿Y tu nombre es...?'
nombre = gets.chomp
if nombre == nombre.capitalize
puts 'Por favor, toma asiento, ' + nombre + '.'
else
puts nombre + '? ¿Quieres decir ' + nombre.capitalize + ', verdad?'
puts '¿¿Ni siquiera sabes escribir tu nombre??'
respuesta = gets.chomp
if respuesta.downcase == 'si'
else
end
end
Una vez más, escribí if, else y end al mismo tiempo. Realmente me ayuda a saber "dónde estoy" en el código. También hace que el trabajo parezca más fácil porque puedo concentrarme en una parte pequeña, como rellenar el código entre if y else. Otra ventaja de hacerlo de esta manera es que el ordenador puede entender el programa en cualquier etapa. Cualquiera de las versiones inacabadas del programa que te mostré funcionaría. No estaban terminadas, pero eran programas funcionales. De esta manera pude probarlo mientras escribía, lo que me ayudó a ver cómo progresaba y qué necesitaba mejorarse. Cuando pasó todas las pruebas, ¡supe que había terminado!
Estos consejos te ayudarán a escribir programas que se ramifican, pero también ayudan con el otro tipo principal de control de flujo:
Bucles (Looping)
A menudo querrás que el ordenador haga lo mismo una y otra vez; después de todo, se supone que los ordenadores son buenos en eso.
Cuando le dices a tu ordenador que siga repitiendo algo, también necesitas decirle cuándo parar. Los ordenadores nunca se aburren, así que si no le dices que pare, no lo hará. Nos aseguramos de que esto no suceda diciéndole al ordenador que repita ciertas partes de un programa while (mientras) una cierta condición sea verdadera. Esto funciona de manera muy similar a cómo funciona if:
comando = ''
while comando != 'adios'
puts comando
comando = gets.chomp
end
puts '¡Vuelve pronto!'
¿Hola?
¿Hola?
¡Hola!
¡Hola!
Encantado de conocerte.
Encantado de conocerte.
Oh... ¡qué adorable!
Oh... ¡qué adorable!
adios
¡Vuelve pronto!
Y eso es un bucle. (Puede que hayas notado la línea en blanco al principio de la salida; viene del primer puts, antes del primer gets. ¿Cómo modificarías el programa para deshacerte de esa primera línea? ¡Pruébalo! ¿Funcionó exactamente como el programa anterior, aparte de esa primera línea en blanco?)
Los bucles te permiten hacer todo tipo de cosas interesantes, como estoy seguro de que puedes imaginar. Sin embargo, también pueden causar problemas si cometes un error. ¿Qué pasa si tu ordenador se queda atascado en un bucle infinito? Si crees que esto puede haber sucedido, simplemente mantén presionada la tecla Ctrl y presiona C.
Sin embargo, antes de comenzar a jugar con bucles, aprendamos algunas cositas para facilitarnos la vida.
Un Poco de Lógica
Echemos un vistazo a nuestro primer programa de ramificación de nuevo. ¿Qué pasaría si mi esposa llegara a casa, viera el programa, intentara usarlo y no le dijera qué nombre tan bonito tiene? No me gustaría herir sus sentimientos (o dormir en el sofá), así que reescribámoslo:
puts 'Hola, ¿cuál es tu nombre?'
nombre = gets.chomp
puts 'Hola, ' + nombre + '.'
if nombre == 'Chris'
puts '¡Qué nombre tan bonito!'
else
if nombre == 'Katy'
puts '¡Qué nombre tan bonito!'
end
end
Hola, ¿cuál es tu nombre?
Katy
Hola, Katy.
¡Qué nombre tan bonito!
Bueno, funciona... pero no es un programa muy bonito. ¿Por qué no? La mejor regla que he aprendido sobre programación fue la regla DRY: Don't Repeat Yourself (No Te Repitas). Podría escribir un libro pequeño sobre por qué esa es una regla tan buena. En nuestro caso, repetimos la línea ¡Qué nombre tan bonito!. ¿Por qué es esto un problema? Bueno, ¿qué pasaría si cometiera un error tipográfico cuando lo reescribí? ¿Qué pasaría si quisiera cambiar bonito a hermoso en ambas líneas? Soy perezoso, ¿recuerdas? Básicamente, si quiero que mi programa haga lo mismo cuando recibe Chris o Katy, entonces realmente debería hacer la misma cosa:
puts 'Hola, ¿cuál es tu nombre?'
nombre = gets.chomp
puts 'Hola, ' + nombre + '.'
if (nombre == 'Chris' or nombre == 'Katy')
puts '¡Qué nombre tan bonito!'
end
Hola, ¿cuál es tu nombre?
Katy
Hola, Katy.
¡Qué nombre tan bonito!
Mucho mejor. Para que funcionara, usé or (o). Los otros operadores lógicos son and (y) y not (no). Siempre es bueno usar paréntesis al trabajar con ellos. Veamos cómo funcionan:
soyChris = true
soyMorado = false
amoLaComida = true
comoPiedras = false
puts (soyChris and amoLaComida)
puts (amoLaComida and comoPiedras)
puts (soyMorado and amoLaComida)
puts (soyMorado and comoPiedras)
puts
puts (soyChris or amoLaComida)
puts (amoLaComida or comoPiedras)
puts (soyMorado or amoLaComida)
puts (soyMorado or comoPiedras)
puts
puts (not soyMorado)
puts (not soyChris)
true
false
false
false
true
true
true
false
true
false
El único que podría engañarte es or. En español, a menudo usamos "o" para significar "uno o el otro, pero no ambos". Por ejemplo, tu mamá podría decir: "De postre, puedes comer pastel o helado". ¡Ella no quería decir que podrías comer ambos! Un ordenador, por otro lado, usa or para significar "uno o el otro, o ambos". (Otra forma de decirlo es, "al menos uno de estos es verdadero".) Es por eso que los ordenadores son más divertidos que las mamás.
Algunas Cositas Para Probar
- "Un elefante se balanceaba..." Escribe un programa que imprima la letra de ese clásico de los viajes en coche, con un límite de 100 elefantes.
- Escribe un programa de la Abuela Sorda. Cualquier cosa que le digas a la abuela (cualquier cosa que escribas), ella debe responder con
¿¿EH?! ¡HABLA MÁS FUERTE, HIJO!, a menos que le grites (escribas todo en mayúsculas). Si gritas, ella puede oírte (o al menos eso cree) y te grita de vuelta,¡NO, NO DESDE 1938!Para hacer que tu programa sea realmente creíble, haz que la abuela grite un año diferente cada vez; tal vez cualquier año al azar entre 1930 y 1950. (Esta parte es opcional, y sería mucho más fácil si leyeras la sección sobre el generador de números aleatorios de Ruby al final del capítulo sobre métodos). No puedes dejar de hablar con la abuela hasta que gritesADIOS.- Pista: ¡No te olvides de
chomp! ¡'ADIOS'con un enter no es lo mismo que'ADIOS'sin él! - Pista 2: Trata de pensar en qué partes de tu programa deben suceder una y otra vez. Todas esas deberían estar en tu bucle
while.
- Pista: ¡No te olvides de
- Mejora tu programa de la Abuela Sorda: ¿Qué pasa si la abuela no quiere que te vayas? Cuando grites
ADIOS, ella podría fingir no oírte. Cambia tu programa anterior para que tengas que gritarADIOStres veces seguidas. Asegúrate de probar tu programa: si gritasADIOStres veces, pero no seguidas, deberías seguir hablando con la abuela. - Años Bisiestos. Escribe un programa que pida un año de inicio y un año final, y luego imprima con
putstodos los años bisiestos entre ellos (e incluyéndolos, si también son bisiestos). Los años bisiestos son años divisibles por cuatro (como 1984 y 2004). Sin embargo, los años divisibles por 100 no son bisiestos (como 1800 y 1900) a menos que sean divisibles por 400 (como 1600 y 2000, que de hecho fueron años bisiestos). (Sí, es bastante confuso, pero no tan confuso como tener diciembre en medio del invierno, que es lo que sucedería al final).
¡Cuando termines esos, tómate un descanso! Ya has aprendido mucho. Felicidades. ¿Estás sorprendido por la cantidad de cosas que puedes decirle al ordenador que haga? Unos cuantos capítulos más y podrás programar casi cualquier cosa. ¡De verdad! Mira todas las cosas que puedes hacer ahora que no podías hacer antes de aprender sobre bucles y ramificaciones.
Ahora aprendamos sobre un nuevo tipo de objeto, uno que mantiene un registro de listas de otros objetos: arrays (arreglos).