Factoriales
El siguiente guión aparece en el capítulo //Ejemplos sencillos//.
|| 01 def fact(n)
02 if n == 0
03 1
04 else
05 n * fact(n-1)
06 end
07 end
08 print fact(ARGV[0].to_i), "\n"
||
Debido a que es la primera explicación de un código, vamos a ir línea por línea.
|| 01 def fact(n)
||
En la primera línea, def es la sentencia que define una función (o con mayor precisión, un //método//; trataremos con más detalle los métodos en un capítulo posterior). Aquí se indica que la función fact toma un único argumento, que se llama n.
|| 02 if n == 0
||
Con if comprobamos una condición. Si la condición es cierta, se evalúa la siguiente línea; si no independientemente de lo que siga se evalúa el else
|| 03 1
||
Si la condición es cierta el valor del if es 1.
|| 04 else
||
Si la condición no es cierta, se evalúa el código desde esta línea hasta el end.
|| 05 n * fact(n - 1)
||
Si no se satisface la condición el valor de if es el resultado de multiplicar fact(n-1) por n.
|| 06 end
||
El primer end cierra la sentencia if.
|| 07 end
||
El segundo end cierra la sentencia def.
|| 08 print fact(ARGV[0].to_i), "\n"
||
Llama a la función fact() con el argumento especificado en la línea de comandos, e imprime el resultado.
ARGV es un array que contiene los argumentos de la línea de comandos. Los miembros de ARGV son cadenas por lo que hay que convertirlos a números enteros con to_i. Ruby no convierte automáticamente las cadenas a números como hace Perl.
Hmmm... ¿Qué pasa si alimentamos este programa con un número negativo? ¿Se ve cuál es el problema? ¿Cómo se podría solucionar?
Cadenas
A continuación examinaremos el programa acertijo del capítulo sobre las cadenas
|| 01 words = ['foobar', 'baz', 'quux']
02 secret = words[rand(3)]
03
04 print "adivina? "
05 while guess = STDIN.gets
06 guess.chop!
07 if guess == secret
08 print "¡Ganas!\n"
09 break
10 else
11 print "Lo siento. Pierdes\n"
12 end
13 print "adivina? "
14 end
15 print "La palabra era ", secret, ".\n"
||
En este programa se utiliza una nueva estructura de control, while. El código entre el while y su correspondiente end se ejecutará repetidamente mientras la condición especificada se mantenga cierta.
rand(3) de la línea número 2 devuelve un número aleatorio dentro del rango de 0 a 2. Este número se utiliza para extraer uno de los elementos del array words.
En la línea 5 se lee una línea de la entrada estándar con el método STDIN.gets. Si aparece el fin del fichero (EOF - End Of File), gets devuelve nil. Por lo tanto el código asociado con el while se repetirá hasta encontrar un ^D (o ^Z bajo DOS), que representa el fin de fichero
En la línea 6 guess.chop! elimina el último carácter de guess; en este caso siempre el carácter de línea nueva.
En la línea 15 se imprime la palabra secreta. Se ha escrito como una sentencia print con tres argumentos (que se imprimen uno detrás del otro), pero hubiera tenido la misma efectividad el hacerlo con un único argumento escribiendo secret como #{secret} para resaltar que la variable se debe evaluar y no imprimir la palabra literal:
|| print "la palabra era #{secret}. \n"
||
Expresiones regulares
Por último examinaremos el programa del capítulo sobre expresiones regulares.
|| 01 st = "\033[7m"
02 en = "\033[m"
03
04 while TRUE
05 print "str> "
06 STDOUT.flush
07 str = gets
08 break if not str
09 str.chop!
10 print "pat> "
11 STDOUT.flush
12 re = gets
13 break if not re
14 re.chop!
15 str.gsub! re, "#{st}\\&#{en}"
16 print str, "\n"
17 end
18 print "\n"
||
En la línea 4, se ha fijado la condición del while a true con lo que se obtiene un bucle infinito. Sin embargo se han colocado sentencias break en las líneas octava y decimotercera para salir del bucle. Estos dos breaks ejemplifican también el uso de los modificadores if. Un "modificador if" ejecuta la sentencia del lado izquierdo si y sólo si se satisface la condición especificada.
Hay más cosas que decir sobre chop! (veanse las líneas 9 y 14). En Ruby se añade, por convención, //!// o //?// al final de ciertos nombre de métodos. El marca de exclamación (//!//, pronunciada como un "bang!" sonoro) recalca algo potencialmente peligroso, es decir, algo que puede modificar el valor de lo que toca. chop! afecta directamente a la cadena pero chop sin el signo de exclamación actúa sobre una copia. A continuación se muestra la diferencia.
|| ruby> s1 = "forth"
"forth"
ruby> s1.chop! # modifica s1
"fort"
ruby> s2 = s1.chop # sitúa en s2 una copia de la modificación
"for"
ruby> s1 # ... sin afectar a s1
"fort"
||
Posteriormente no encontraremos con nombres de métodos que finalizan con un signo de interrogación (//?//, pronunciada a veces como un "huh?" sonoro). Esto indica que el método es un "predicado", aquel que puede devolver o true o false.
La línea 15 requiere una especial atención. Primero, se observa que gsub! es otro de los denominados métodos destructivos. Modifica str al reemplazar toda coincidencia del patrón re (sub significa sustituir, la g inicial indica que la sustitución es global, es decir reemplaza todas las coincidencias que hay en la cadena no sólo la primera encontrada). Hasta el momento todo parece correcto pero, ¿Por qué reemplazamos las coincidencias del texto? st y en se definieron en las líneas 1 y 2 como secuencias ANSI que presentan el color del texto como invertido o normal respectivamente. En la línea 15 se encuentran encerradas entre #{} para asegurar que se interpreten por lo que son (y no se impriman los //nombres// de las variables). Entre ella se ve "\\&". Esto es un pequeño truco. Dado que la sentencia de reemplazo se encuentra entre comillas dobles, los dos backslashes se interpretarán como uno solo, que gsub! verá como "\&" que no es otra cosa que el código que representa la primera coincidencia del patrón. Por lo tanto la nueva cadena, al imprimirse, será igual que la antigua, excepto que las partes que coinciden con el patrón aparecen resaltadas en vídeo inverso.