Página original: http://pine.fm/LearnToProgram/?Chapter=08
Como hemos visto, los ciclos y las iteraciones nos permiten hacer lo mismo (ejecutar el mismo código) una y otra vez. Sin embargo, algunas veces queremos hacer la misma cosas un numero determinado de veces, pero en distintos lugares del programa. Por ejemplo, digamos que estamos escribiendo un programa que sea un cuestionario para un estudiante de psicología. De los estudiantes de psicología que conozco y de los cuestionarios que me han proporcionado, sería algo parecido a esto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | puts 'Hola y gracias por tomarte el tiempo para' puts 'ayudarme con este expermiento. Mi experimento' puts 'es sobre como se sienten las personas con' puts 'la comida mexicana. Piensa en cada comida Mexicana' puts 'e intenta responder la pregunta honestamente,' puts 'con un "si" o un "no". Mi experimento' puts 'no tiene nada que ver con mojar la cama.' puts # Hacemos las siguientes preguntas pero ignoraremos las respuestas. buenaRespuesta = false while (not buenaRespuesta) puts '¿Te gusta comer tacos?' respuesta = gets.chomp.downcase if (respuesta == 'si' or respuesta == 'no') buenaRespuesta = true else puts 'Por favor, responde con un "si" o un "no".' end end buenaRespuesta = false while (not buenaRespuesta) puts '¿Te gusta comer burritos?' respuesta = gets.chomp.downcase if (respuesta == 'si' or respuesta == 'no') buenaRespuesta = true else puts 'Por favor, responde con un "si" o un "no".' end end # Si tomamos en cuenta *esta* respuesta buenaRespuesta = false while (not buenaRespuesta) puts '¿Mojas la cama?' respuesta = gets.chomp.downcase if (respuesta == 'si' or respuesta == 'no') buenaRespuesta = true if respuesta == 'si' mojalaCama = true else mojalaCama = false end else puts 'Por favor, responde con un "si" o un "no".' end end buenaRespuesta = false while (not buenaRespuesta) puts '¿Te gusta comer chimichanga?' respuesta = gets.chomp.downcase if (respuesta == 'si' or respuesta == 'no') buenaRespuesta = true else puts 'Por favor, responde con un "si" o un "no".' end end puts 'Solo unas cuantas preguntas mas...' buenaRespuesta = false while (not buenaRespuesta) puts '¿Te gusta comer sopapillas?' respuesta = gets.chomp.downcase if (respuesta == 'si' or respuesta == 'no') buenaRespuesta = true else puts 'Por favor, responde con un "si" o un "no".' end end # Haz mas preguntas de la comida Mexicana puts puts 'INFORMACION ADICIONAL:' puts 'Gracias por regalarnos tiempo para' puts 'este experimento. De hecho, el experimento' puts 'no tiene nada que ver con comida Mexicana' puts 'Es, en realidad un experimento acerca de' puts 'mojar la cama. La comida Mexicana solo era' puts 'una manera de que bajaras la guardia con la' puts 'esperanza de que respondieras honestamente.' puts 'Gracias por participar' puts puts mojalaCama |
Hola y gracias por tomarte el tiempo para ayudarme con este expermiento. Mi experimento es sobre como se sienten las personas con la comida mexicana. Piensa en cada comida Mexicana e intenta responder la pregunta honestamente, con un "si" o un "no". Mi experimento no tiene nada que ver con mojar la cama. ¿Te gusta comer tacos? si ¿Te gusta comer burritos? si ¿Mojas la cama? Nop Por favor, responde con un "si" o un "no". ¿Mojas la cama? NO ¿Te gusta comer chimichanga? si Solo unas cuantas preguntas mas... ¿Te gusta comer sopapillas? si INFORMACION ADICIONAL: Gracias por regalarnos tiempo para este experimento. De hecho, el experimento no tiene nada que ver con comida Mexicana Es, en realidad un experimento acerca de mojar la cama. La comida Mexicana solo era una manera de que bajaras la guardia con la esperanza de que respondieras honestamente. Gracias por participar false
Este fue un programa bastante largo, con muchas partes que se repiten. (Todas las secciones del código que preguntaban cosas de la comida mexicana eran iguales, y la de la pregunta de mojar la cama fue la única con algo ligeramente diferente.) Repetirse es algo malo. Y ni siquiera podríamos usar un ciclo o iteraciones porque en ocasiones teníamos cosas entre las preguntas. En situaciones como estas, lo mejor es escribir un método. Y aquí esta como:
1 2 3 | def diMuu puts 'muuuuuuu...' end |
Mmm… nuestro programa no dijo muuuuuuu…. ¿Porque? Porque no le dijimos que lo hiciera. Le dijimos como decir muuuuuuu…, pero no le dijimos que lo dijera. Intentemos de nuevo:
1 2 3 4 5 6 7 8 9 | def diMuu puts 'muuuuuuu...' end diMuu diMuu puts 'coin-coin' diMuu diMuu |
muuuuuuu... muuuuuuu... coin-coin muuuuuuu... muuuuuuu...
Mucho mejor. (En caso de que no hables Francés, ese era un pato Francés en medio del programa. En Francia, los patos dicen “coin-coin”.)
Así que hemos definido el método diMuu. (Los nombres de los Métodos, al igual que los nombres de las variables, empiezan con una letra minúscula. Hay algunas excepciones, como + ó ==.) ¿Que no los métodos deben de estar siempre asociados con objetos? Bueno, si, pero en este caso (como en puts y gets), el método esta asociado con el objeto representando todo el programa. En el siguiente capitulo veremos como agregar métodos a otros objetos. Pero primero…
Parámetros de los Métodos
Debes de haber notado que en algunos métodos (como gets, to_s, reverse…) puedes llamarlos en un objeto. Sin embargo, otros métodos (como +, -, puts…) toman el parámetro para decirle al objeto que hacer con el método. Por ejemplo, no diríamos 5+, ¿verdad? Le estas diciendo a 5 que sume, pero no le estas diciendo que sumar.
Para agregar un parámetro a diMuu (por ejemplo el numero de muuus), lo haríamos así:
1 2 3 4 5 6 7 | def diMuu numeroDeMuus puts 'muuuuuuu...'*numeroDeMuus end diMuu 3 puts 'oink-oink' diMuu # Este debe de dar un error por un parametro faltante. |
muuuuuuu...muuuuuuu...muuuuuuu... oink-oink dimu2.rb:7:in `diMuu': wrong number of arguments (0 for 1) (ArgumentError)
numeroDeMuus es una variable que apunta al parámetro que se paso. Lo diré de nuevo, es algo complejo: numerodeMuus es una variable que apunta al parámetro que se paso. Así que si escribo diMuu 3, entonces el parámetro es 3 y la variable numeroDeMuus apuntaría al 3.
Como puedes ver, ahora el parámetro es un requisito. Después de todo, ¿con que se va a multiplicar el ‘muuuuuuu…’ si no se le paso un parámetro? La computadora no tiene idea.
Si los objetos en Ruby son como sujetos en el Ingles, y los métodos son como verbos, entonces se podría pensar que los parámetros son como adverbios (como en diMuu, donde el parámetro nos dijo cuantas veces decir muu) u otras veces como objetos directos (como con puts, donde el parámetro es lo que se imprimirá en pantalla)
Variables Locales
En el siguiente programa, existen dos variables:
1 2 3 4 5 6 | def duplicaEsto num numVeces2 = num*2 puts num.to_s+' duplicado es '+numVeces2.to_s end duplicaEsto 44 |
44 duplicado es 88
Las variables son num y numVeces2. Ambas están dentro del método duplicaEsto. Esta (y todas las variables que hemos visto hasta ahora) son variables locales. Esto significa que se encuentran dentro del método y no pueden salir. Si lo intentas obtendrás un error:
1 2 3 4 5 6 7 | def duplicaEsto num numVeces2 = num*2 puts num.to_s+' duplicado es '+numVeces2.to_s end duplicaEsto 44 puts numVeces2.to_s |
44 duplicado es 88 duplicar.rb:7: undefined local variable or method `numVeces2' for main:Object (NameError)
Variable local no definida … De hecho, si definimos la variable local, solo que no es local en donde intentamos usarla, pero es local dentro del método.
Esto puede parecer un inconveniente, pero de hecho no es así. Por un lado significa que no tenemos acceso a las variables dentro de los métodos, pero también significa que ellos tampoco pueden tener acceso a tus variables, y por lo tanto, no pueden modificarlas:
1 2 3 4 5 6 7 8 | def pequenaPeste var var = nil puts 'JAJA! Arruine tu variable!' end var = 'Ni siquiera puedes tocar mi variable!!!' pequenaPeste var puts var |
JAJA! Arruine tu variable! Ni siquiera puedes tocar mi variable!!!
Tal vez no lo parezca, pero hay dos variables en este pequeño programa llamadas var: una dentro del método pequenaPeste y otro fuera de el. Cuando mandamos llamar pequenaPeste var, en realidad solo pasamos la cadena de un var a otro, para que ambas apuntaran a la misma cadena. Pero luego dentro de pequenaPeste apuntamos la variable local var a nil, pero eso no hizo nada a la variable var fuera del método.
Regresando Valores
Puedes haber notado que algunos métodos si te regresan algo cuando los mandas llamar. Por ejemplo, gets nos regresa una cadena (la cadena que escribiste), y el método + en 5+3, (que en realidad es 5.+(3)) nos devuelve 8. Los métodos aritméticos para los números nos regresan números, y los métodos aritméticos para cadenas, regresan cadenas.
Es importante entender la diferencia entre los métodos que nos regresan un valor cuando el método fue llamado, y cuando un programa nos regresa un resultado a la pantalla, como puts. Observa que 5+3 nos regresa 8, mas no nos imprime un 8 en pantalla.
¿Entonces que nos devuelve un puts? Nunca nos había importado, pero ahora demos una mirada:
1 2 | valorDevuelto = puts 'Este puts devuelve:' puts valorDevuelto |
Este puts devuelve: nil
El primer puts nos regreso nil. Y aunque no lo probamos, el segundo puts también, puts siempre regresa nil. Todos los métodos nos regresan algo, aunque sea un simple nil.
Toma un pequeño descanso y escribe un programa que nos diga lo que regresa diMuu.
Te sorprendió? Bueno, veamos como funciona: el valor que nos regresan los métodos es simplemente la ultima linea del método. En el caso de diMuu, significa que nos regresa puts ‘muuuuuuu…’*numeroDeMuus, o sea nil, ya que puts siempre nos devuelve un nil. Si quisiéramos que todos nuestros métodos nos regresaran la cadena ‘submarino amarillo’, solo necesitamos poner esa cadena al final del método:
1 2 3 4 5 6 7 | def diMuu numeroDeMuus puts 'muuuuuuu...'*numeroDeMuus 'submarino amarillo' end x = diMuu 2 puts x |
muuuuuuu...muuuuuuu... submarino amarillo
Intentemos el experimento psicológico otra vez, pero esta vez, escribiremos un método que haga las preguntas en nuestro lugar. Necesitará tomar la pregunta como parámetro, y regresar true cuando hayan respondido si y false cuando hayan respondido no. (Aun cuando la mayoría de las veces ignoremos la respuesta, sigue siendo una buena idea que nuestro método nos regrese una respuesta. Así también podemos usarlo para la pregunta de mojar la cama.) También voy a recortar el saludo y la información adicional para leerlo con mayor comodidad:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | def preguntar pregunta buenaRespuesta = false while (not buenaRespuesta) puts pregunta replica = gets.chomp.downcase if (replica == 'si' or replica == 'no') buenaRespuesta = true if replica == 'si' respuesta = true else respuesta = false end else puts 'Por favor, responde con un "si" o un "no".' end end respuesta # Este es el valor que se regresa (true o false) end puts 'Hola y gracias por tomarte...' puts preguntar '¿Te gusta comer tacos?' # Ignoramos el valor que regresa preguntar '¿Te gusta comer burritos?' mojalaCama = preguntar '¿Mojas la cama?' # Guardamos este valor preguntar '¿Te gusta comer chimichanga?' preguntar '¿Te gusta comer sopapillas?' preguntar '¿Te gusta comer tamales?' puts 'Solo unas cuantas preguntas mas...' preguntar '¿Te gusta tomar agua de horchata?' preguntar '¿Te gusta comer flautas?' puts puts 'INFORMACION ADICIONAL:' puts 'Gracias por...' puts puts mojalaCama |
Hola y gracias por tomarte... ¿Te gusta comer tacos? si ¿Te gusta comer burritos? si ¿Mojas la cama? nop Por favor, responde con un "si" o un "no". ¿Mojas la cama? NO ¿Te gusta comer chimichanga? si ¿Te gusta comer sopapillas? si ¿Te gusta comer tamales? si Solo unas cuantas preguntas mas... ¿Te gusta tomar agua de horchata? si ¿Te gusta comer flautas? si INFORMACION ADICIONAL: Gracias por... false
Nada mal, ¿verdad? Pudimos agregar mas preguntas (agregar preguntas ahora es sencillo), pero nuestro programa ahora es mas corto. Es un gran avance, el sueño de todo programador flojo.
Otro Gran Ejemplo
Creo que otro ejemplo de métodos sería muy útil. Llamaremos a este numeroEspanol. Tomará un numero, como 22, y regresara una versión en letras del numero (en este caso, nos regresaría la cadena ‘veintidos’). Por el momento, solo lo haremos trabajar con números enteros del 0 al 100.
(NOTA: Este método usa un nuevo truco para regresar algo de un método antes de llegar a la ultima linea, la palabra reservada return, e introduce también un nuevo estilo de ramificar: elsif. Que veremos como trabaja dentro del programa.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | def numeroEspanol numero # Solo queremos numeros del 0 al 100 if numero < 0 return 'Por favor, escribe un numero igual o mayor a cero.' end if numero > 100 return 'Por favor, escribe un numero igual o menor a 100.' end numCadena = '' #Esta es la cadena que regresaremos # "falta" es que parte del numero es la que nos falta por escribir. # "escribiendo" es la parte que estamos escribiendo en este momento falta = numero escribiendo = numero/100 # Cuantas centenas nos falta por escribir falta = falta - escribiendo*100 # Restar esas centenas if escribiendo > 0 return 'cien' end escribiendo = falta/10 # Cuantas decenas nos falta por escribir falta = falta - escribiendo*10 # Restar las decenas if escribiendo > 0 if escribiendo == 1 # Como no podemos escribir "diecidos" en vez de "doce", # tenemos que hacer algo especial if falta == 0 numCadena = numCadena + 'diez' elsif falta == 1 numCadena = numCadena + 'once' elsif falta == 2 numCadena = numCadena + 'doce' elsif falta == 3 numCadena = numCadena + 'trece' elsif falta == 4 numCadena = numCadena + 'catorce' elsif falta == 5 numCadena = numCadena + 'quince' elsif falta == 6 numCadena = numCadena + 'dieciseis' elsif falta == 7 numCadena = numCadena + 'diecisiete' elsif falta == 8 numCadena = numCadena + 'dieciocho' elsif falta == 9 numCadena = numCadena + 'diecinueve' end # Como ya nos ocupamos de las unidades # no queda mas por escribir falta = 0 elsif escribiendo == 2 numCadena = numCadena + 'veinte' elsif escribiendo == 3 numCadena = numCadena + 'treinta' elsif escribiendo == 4 numCadena = numCadena + 'cuarenta' elsif escribiendo == 5 numCadena = numCadena + 'cincuenta' elsif escribiendo == 6 numCadena = numCadena + 'sesenta' elsif escribiendo == 7 numCadena = numCadena + 'setenta' elsif escribiendo == 8 numCadena = numCadena + 'ochenta' elsif escribiendo == 9 numCadena = numCadena + 'noventa' end if falta > 0 numCadena = numCadena + ' y ' end end escribiendo = falta # Cuantas unidades nos faltan? falta = 0 # Las substraemos if escribiendo > 0 if escribiendo == 1 numCadena = numCadena + 'uno' elsif escribiendo == 2 numCadena = numCadena + 'dos' elsif escribiendo == 3 numCadena = numCadena + 'tres' elsif escribiendo == 4 numCadena = numCadena + 'cuatro' elsif escribiendo == 5 numCadena = numCadena + 'cinco' elsif escribiendo == 6 numCadena = numCadena + 'seis' elsif escribiendo == 7 numCadena = numCadena + 'siete' elsif escribiendo == 8 numCadena = numCadena + 'ocho' elsif escribiendo == 9 numCadena = numCadena + 'nueve' end end if numCadena == '' # La unica manera de que "numCadena" se encuentre vacía # es si el numero es 0. return 'cero' end # Si ya pudimos llegar tan lejos, entonces tuvimos un numero # entre 0 y 100, así que necesitamos regresar "numCadena". numCadena end puts numeroEspanol(0) puts numeroEspanol(9) puts numeroEspanol(10) puts numeroEspanol(11) puts numeroEspanol(17) puts numeroEspanol(32) puts numeroEspanol(88) puts numeroEspanol(99) puts numeroEspanol(100) |
cero nueve diez once diecisiete treinta y dos ochenta y ocho noventa y nueve cien
Bueno, este programa tiene ciertas cosas que no me gustan. Primero, se repite bastante. Segundo, no puede manejar números mayores a 100. Tercero, hay muchos casos especiales y muchos return. Usemos algunos arreglos e intentemos limpiar el código un poco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | def numeroEspanol numero if numero < 0 # Sin numeros negativos. return 'Por favor ingresa un numero que no sea negativo.' end if numero == 0 return 'cero' end # No mas casos especiales! No mas return! numCadena = '' # Esta es la cadena que regresaremos unidades = ['uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'] decenas = ['diez', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'] especiales = ['once', 'doce', 'trece', 'catorce', 'quince', 'dieciseis', 'diecisiete', 'dieciocho', 'diecinueve'] # "falta" es lo que nos falta por escribir # "escribiendo" es lo que estamos escribiendo al momento falta = numero escribiendo = falta/100 # Cuantas centenas nos falta por escribir falta = falta - escribiendo*100 # Restamos las centenas if escribiendo > 0 # Aqui viene un muy buen truco centenas = numeroEspanol escribiendo numCadena = numCadena + centenas + ' cientos' # Eso es llamado "recursividad". ¿Que fue lo que hicimos? # Le dijimos al metodo que se llame a si mismo, pero usando escribiendo en # lugar de "numero". Recuerda que "escribiendo" es (en este momento) el numero de # decenas que queremos escribir. Despues de agregar "centenas" a "numCadena" # le agregamos la cadena ' cientos'. Asi, por ejemplo, si en un principio llamamos # numeroEspanol con 1999 ("numero" = 1999), entonces en este punto "escribiendo" seria # 19, y "falta" sería 99. Lo que hariamos en este punto es hacer que numeroEspanol # escriba 'diecinueve' por nosotros, despues escribiremos ' centenas', # y entonces el resto de numeroEspanol escribe 'noventa y nueve'. if falta > 0 # Para no tener que escribir 'doscientos cincuenta y uno'... numCadena = numCadena + ' y ' end end escribiendo = falta/10 # Cuantas decenas nos falta por escribir? falta = falta - escribiendo*10 # Restamos las decenas. if escribiendo > 0 if ((escribiendo == 1) and (falta) > 0) # Como no podemos escribir "diecidos" en lugar de "doce" # tenemos que hacer una excepcion especial para este. numCadena = numCadena + especiales[falta-1] # El "-1" es porque especiales[3] es 'catorce', no 'trece'. # Como ya nos hicimos cargo de los numeros de las unidades # no queda nada mas por escribir. falta = 0 else numCadena = numCadena + decenas[escribiendo-1] # El "-1" es porque decenas[3] es 'cuarenta' y no 'treinta'. end if falta > 0 # Para no tener que escribir 'sesenta y cuatro' numCadena = numCadena + ' y ' end end escribiendo = falta # Cuantas unidades faltan por escribir falta = 0 # Se las restamos if escribiendo > 0 numCadena = numCadena + unidades[escribiendo-1] # El "-1" es porque unidades[3] es 'cuatro' y no 'tres' end # Ahora regresamos "numCadena" numCadena end puts numeroEspanol(0) puts numeroEspanol(9) puts numeroEspanol(10) puts numeroEspanol(11) puts numeroEspanol(17) puts numeroEspanol(22) puts numeroEspanol(32) puts numeroEspanol(88) puts numeroEspanol(99) puts numeroEspanol(100) puts numeroEspanol(101) puts numeroEspanol(234) puts numeroEspanol(3211) puts numeroEspanol(999999) puts numeroEspanol(1000000000000) |
cero nueve diez once diecisiete veinte y dos treinta y dos ochenta y ocho noventa y nueve uno cientos uno cientos y uno dos cientos y treinta y cuatro treinta y dos cientos y once noventa y nueve cientos y noventa y nueve cientos y noventa y nueve uno cientos cientos cientos cientos cientos cientos
Está mucho mejor. El programa es algo denso, que es la razón por la que tiene tantos comentarios. Funciona aun con números grandes… pero no funciona tan bien como nos gustaría. Por ejemplo nos dice ‘uno cientos y uno’, los miles nos los lee como ‘treinta y dos cientos y once’ (3211). Digamos que los lee pero no de manera elegante. Al igual que ‘un trillon’ para el ultimo numero hubiera sido lo idóneo, o ‘un millon de millon’. De hecho, todo eso se puede mejorar…
Unas cosas para intentar
- Agrega al programa numeroEspanol que lea los miles, para que regrese ‘mil’ en vez de ‘diez cientos’, etc.
- Agrega al programa numeroEspanol que también lea los millones, y después intenta agregar millones, trillones, etc.
- “Un elefante se columpiaba…” Usando el programa numeroEspanol y el programa de los elefantes, haz que nos aparezca la letra de la canción, pero ahora escrita con letra. Intenta hasta 9999 (No uses un numero demasiado larga, ya que se puede tardar un rato en terminar.)
¡Felicidades! En este punto ya puedes ser considerado un verdadero programador. Haz aprendido lo indispensable para escribir programas grandes desde cero. Si tienes alguna idea para hacer un programa, intentalo.
Por supuesto que escribir desde cero es un proceso lento. ¿Porque perder el tiempo escribiendo código que ya ha sido escrito? ¿Te gustaría que tu programa enviara un correo? ¿Te gustaría guardar y cargar archivos en tu computadora? ¿O que te parecería generar paginas web para un tutorial, donde los programas de ejemplo ejecutaran su código cada que se carga la página? Ruby tiene diferentes objetos que nos pueden ayudar a escribir programas mas rápido.
© 2003-2009 Chris Pine
