El lenguaje de programación Ruby
Javier Smaldone (http://www.smaldone.com.ar/)
Historia
El origen de este lenguaje es bastante atÃpico. Fue desarrollado por el japonés Yukihiro Matsumoto (más conocido como "matz") en el año 1995 y durante sus primeros años de vida su uso no se extendió más alla de la frontera de Japón, debido a la falta de documentación en otro idioma. A finales del año 2001, gracias a la aparición de un libro de O'Reilly en inglés[0], comenzó a ser conocido internacionalmente y en el 2005 alcanzó la fama a través de "Ruby on Rails", un framework para el desarrollo de aplicaciones web escrito en Ruby.
Un detalle curioso aparece si comparamos la difusión de Ruby con la de Python. En los últimos 10 años, Python (que data de fines de los '80) ha sido el lenguaje de scripting de mayor crecimiento en todo el mundo. El auge de Ruby está apenas empezando y en los últimos 2 años ha tenido un crecimiento mucho mayor a Python. Sin embargo en Japón, donde ambos lenguajes han sido conocidos desde su comienzo, Ruby siempre ha sido más usado que Python.
Muchos programadores se acercan a Ruby a través de "Ruby on Rails" (las aplicaciones desarrolladas con este framework son, básicamente, programas Ruby que usan las librerÃas de RoR). Sin embargo no debemos confundirnos: el hecho de que Rails esté desarrollado y permita desarrollar en Ruby, no significa que este lenguaje sirva exclusivamente para construir aplicaciones web. En realidad, son las virtudes de este último las que han posibilitado desarrollar un framework tan potente y simple como Rails.
CaracterÃsticas
Ruby es un lenguaje totalmente orientado a objetos que incorpora caracterÃsticas de SmallTalk, Lisp, Eiffel, Ada y Perl. En palabras de su creador: "QuerÃa un lenguaje de scripting que fuera más potente que Perl y más orientado a objetos que Python [1]".
En rigor, el lenguaje no incorpora ninguna caracterÃstica realmente innovadora (inclusive, "revitaliza" algunas técnicas presentes en Lisp en 1958 y en SmallTalk en 1980). Su principal virtud es la forma simple en que integran mecanismos como orientación a objetos, clausuras y macros, entre otros. El resultado es un lenguaje simple (de fácil lectura), pequeño (muy pocas palabras reservadas y construcciones) y extremadamente potente (con una expresividad muy cercana a la de los lenguajes funcionales).
Ruby, como todo lenguaje de scripting, es interpretado. Existen varias implementaciones del intérprete, además de la "oficial". Una facilidad adicional, tanto para el aprendizaje como para la depuración de programas, es la disponibilidad de "irb
", un intérprete interactivo (ejecutado en la lÃnea de comandos), que permite tanto "jugar" e investigar el lenguaje, como también depurar clases de un sistema complejo.
Presentación del lenguaje
En Ruby todos son objetos. La forma de invocar un método en un objeto, es mediante expresiones "objeto.metodo
". El uso de paréntesis en este tipo de invocaciones es totalmente opcional, de manera que "cuenta.depositar(20)
" puede escribirse también como "cuenta.depositar 20
". Otra caracterÃstica llamativa es que los nombres de métodos pueden incluir, por ejemplo, el caracter "=
", de manera que podemos definir un método "saldo=(valor)
" y luego escribir "cuenta.saldo = valor
" (nótese el espacio introducido antes de "=
", permitido gracias al "azúcar sintáctica" de Ruby).
Veamos algunos ejemplos:
class Cuenta
def initialize(saldo_inicial)
@saldo = saldo_inicial
end
def saldo
@saldo
end
def depositar(monto)
@saldo += monto
end
def extraer(monto)
if monto < @saldo then
@saldo -= monto
return true
else
return false
end
end
end
a = Cuenta.new 20
puts a.saldo
a.depositar 10
puts a.saldo
if a.extraer 15 then
puts "Extracción realizada"
else
puts "Extracción superior al saldo"
end
puts a.saldo
Hemos definido una clase "Cuenta
", que tiene los métodos "initialize
", "saldo
", "depositar
" y "extraer
". Luego hemos creado una instancia (objeto) "a
" y lo hemos utilizado.
La salida de este programa es:
20
30
Extracción realizada
15
Examinemos este ejemplo:
- Las clases se nombran con el primer caracter en mayúsculas ("
Cuenta
"). En caso de usar varias palabras,
se utiliza la notación "camello" (por ejemplo "CuentaCorriente
"). - Los métodos y variables se nombran en minúsculas.
- Las variables cuyo identificador comienza con "
@
" son "variables de instancia" o atributos (por ejemplo, "@saldo
"). - El método "
initialize
" de una clase, se invoca al crear una instancia de la misma. La creación de un objeto se realiza llamando al método "new
" de la clase en cuestión, y los parámetros que éste reciba, serán pasados a "initialize
" (por ejemplo, "Cuenta.new 20
").
Ruby también permite variables y métodos de clase. Por ejemplo, podrÃamos modificar nuestra clase "Cuenta
" de la siguiente forma:
class Cuenta
@@cuentas = 0
def self.cuentas
@@cuentas
end
def initialize(saldo_inicial)
@saldo = saldo_inicial
@@cuentas += 1
end
...
end
Hemos definido una variable de clase "@@cuentas
", que será compartida por todas sus instancias (objetos). Además, hemos definido un método de clase "cuentas
" (nótese la palabra "self
" en la declaración, que indica que el método pertenece a la clase y no a sus instancias). Luego, podemos invocarlo directamente en la clase "Cuenta
". Por ejemplo, si hiciéramos lo siguiente:
a = Cuenta.new 20
b = Cuenta.new 50
puts Cuenta.cuentas
ObtendrÃamos como salida "2
".
Existe una serie de convenciones que simplifican la comprensión de los programas escritos en Ruby. Por ejemplo:
- Si un método toma un valor y lo asigna a un atributo (comúnmente llamado "setter"), ese método deberÃa llamarse igual que el atributo, con el agregado del caracter "
=
". Esto, sumado a la posibilidad de omitir los paréntesis, hace que la invocación de ese método parezca una asignación (como vimos en los ejemplos anteriores). - El nombre de un método que devuelve un valor lógico, deberÃa terminar con el caracter "
?
". (PodrÃamos implementar un método "puede_extraer?
" en la clase "Cuenta
" y luego escribir "a.puede_extraer? 40
".) - El nombre de un método que modifica el objeto sobre el que es invocado, deberÃa finalizar con "
!
". (Un ejemplo de esto son los métodos "sort
" y "sort!
" de la clase "Array
". El primero devuelve el arreglo resultante de ordenar el arreglo original, en tanto que el segundo ordena a este último.)
Ruby y las "clausuras"
Las "clausuras" son mecanismos que nos permiten pasar bloques de código como parámetros en la llamada a una función. En dichos bloques, podemos hacer referencia a identificadores definidos en el contexto (alcance o "scope") en el que se realiza la llamada. Veamos un ejemplo.
La clase "Array
" provee un método "each
" que recibe como parámetro un bloque y lo ejecuta para cada elemento del arreglo. El siguiente código:
saludo = "Hola "
a = ["Gabriel", "Pablo", "Javier"]
a.each{|i| puts saludo + i}
Produce la siguiente salida:
Hola Gabriel
Hola Pablo
Hola Javier
La expresión "|i|
" establece la ligadura de el identificador "i
" con cada uno de los elementos del arreglo "a
". Luego, el bloque se ejecuta para cada uno de los elementos de este último. Nótese que, en el contexto de arreglo "a
" no existe el identificador "saludo
", sin embargo, el bloque es pasado como parámetro incluyendo las ligaduras al contexto en el que fue definido. Esta es la "magia" de las clausuras.
Otro ejemplo: supongamos que tenemos un arreglo "c
" de objetos de clase "Cuenta
" y deseamos calcular la suma de todos los saldos. Para ello, podemos hacer lo siguiente:
sumatoria = 0
c.each{|i| sumatoria += i.saldo}
puts sumatoria
Otro método de "Array
" que soporta bloques como parámetros es "select
", que devuelve sólo aquellos elementos para los cuales el bloque devuelve "true
". Veamos otro ejemplo:
d = c.select{|i| c.saldo > 1000}
Esto selecciona (filtra) de "c
" aquellas cuentas con un saldo mayor a 1000
y asigna el arreglo resultante a "d
". Nótese cómo el texto del programa y su lectura (aún en castellano) son bastante similares. (Más adelante hablaremos sobre "expresividad".)
Clases "opensource"
Cualquier lenguaje de implementación "abierta" nos ofrece la posibilidad de modificar su librerÃa estándar de funciones. En Ruby esto es trivialmente simple, ya que si ya hemos definido una clase "MiClase
", una declaración del tipo "class MiClase ... end
" añadirá las declaraciones introducidas a la definición ya existente.
Por ejemplo, si estamos acostumbrados a usar los métodos "head
" y "tail
" que devuelven, respectivamente, el primer elemento de una lista y el resto, los extrañaremos en la clase "Array
" de Ruby. Pero, afortunadamente, podemos escribir la siguiente definición:
class Array
def head
self[0]
end
def tail
slice(1..-1)
end
end
Con esto, hemos añadido los métodos "head
" y "tail
" a la clase "Array
" y, por lo tanto, pueden ser invocados en cualquier otra clase que la utilice. De esta forma es que las clases de Ruby son totalmente abiertas, ya que en cualquier lugar de nuestro sistema en donde se requiera, podemos extenderlas o modificarlas.
Expresividad
Por "expresividad" entendemos la posibilidad de expresar "claramente" mediante el código de un programa qué es lo que este realiza. Todos sabemos que algunos lenguajes, aunque quizás muy eficientes, son extremadamente "oscuros", y requieren de un buen rato estudiando cada lÃnea y cada construcción para averiguar exáctamente qué hace un programa y cómo lo hace (al extremo en que podrÃamos hablar de lenguajes de "sólo escritura").
Ruby, a través de sus construcciones, nos ofrece la posibilidad de programar de forma "declarativa", esto es, que el código de nuestros programas resuelva el problema y, a la vez, sirva de especificación del mismo.
Tomemos como ejemplo el algoritmo de ordenamiento "quicksort". En su forma más simple, este algoritmo podrÃa enunciarse como sigue:
Se toma el primer elemento de la lista a ordenar, se ubican a su izquierda los valores menores que éste, a su derecha los mayores, y luego se aplica recursivamente el mismo algoritmo en ambas partes.
De una manera aún más sintética, podrÃamos decir que hacer "quicksort" de una lista es aplicar "quicksort" a los menores que el primer elemento, luego agregar este y finalmente "quicksort" aplicado a los mayores.
Implementar "quicksort" en lenguajes como Java, C#, PHP o Pascal requerirÃa de más de 50 lÃneas de código y su comprensión, para alguien que no conociera el algoritmo de antemano, no serÃa trivial. En Ruby, utilizando los métodos definidos anteriormente en "Array
", podrÃamos escribir lo siguiente:
class Array
def qs
empty? ? [] : tail.select{|e| e<=head}.qs + [head] + tail.select{|e| e>head}.qs
end
end
El método "empty?
" devuelve "true
" si y sólo si el arreglo está vacÃo. El operador de asignación condicional "a ? b : c
" evalúa la expresión "a
", si ésta es verdadera devuelve el valor "b
" y en caso contrario, "c
".
Habiendo aclarado esto, la lectura de nuestro "quicksort" ("qs
") es muy simple: Si el arreglo es vacÃo, devuelve un arreglo vacÃo ("[]
"), y en caso contrario, devuelve el resultado de aplicar "qs
" a los elementos menores al primero ("head
"), seguido de éste elemento, seguido del resultado de aplicar "qs
" a los elementos mayores al primero.
Como podemos ver, la implementación de "quicksort" en Ruby es prácticamente su especificación. ¡Esto es expresividad!.
Programación divertida
20:11 ST: Talleres 2 -vs- Instituto 0
La expresividad de Ruby se combina con un principio llamado de "menor sorpresa". Esto significa que las cosas se llaman como uno esperarÃa que se llamen y funcionan de la manera en que uno esperarÃa que funcionen. Sumando ambos elementos, el resultado es un lenguaje en el que resulta "divertido programar".
La realidad nos muestra que muchas veces los programadores desperdiciamos la mayor parte del tiempo (y de nuestra energÃa), lidiando con particularidades de los lenguajes que utilizamos. Esto llega a ocasionar que muchos pierdan la pasión que en los primeros años sentÃan por esas largas horas (¿dÃas? ¿noches?) escribiendo código. Ruby, en cierta forma, fue creado para tratar de evitar esta situación. En palabras de matz:
"QuerÃa minimizar mi frustración mientras programaba, por lo tanto, querÃa minimizar mi esfuerzo al programar. Ese fue mi principal objetivo en el diseño de Ruby. QuerÃa divertirme programando. Luego de liberar Ruby, cuando muchas personas en todo el mundo lo conocieron, dijeron que se sentÃan igual que yo."[2]
Enlaces útiles
- El sitio oficial de Ruby: http://www.ruby-lang.org/es/
- Intérprete Ruby online (accesible con un navegador, con un excelente tutorial): http://tryruby.hobix.com/
- Ruby on Rails (la "killer app" de Ruby):http://www.rubyonrails.org/
Referencias
- [0] http://www.oreilly.com/catalog/ruby/
- [1] http://www.linuxdevcenter.com/pub/a/
- [2] http://www.artima.com/intv/rubyP.html
Licencia
© Copyright 2007 Javier Smaldone
Este artÃculo se distribuye bajo una licencia
"Atribución-NoComercial-CompartirDerivadasIgual 2.5 Argentina" de Creative Commons.