lunes, 19 de septiembre de 2011

Primera toma de contacto con Ruby


Hace mucho tiempo que quería tomar contacto con un lenguaje de programación que ha tenido mucho auge en Japón y en EEUU, y que en España está tímidamente haciéndose conocer. En los últimos años, Ruby se está haciendo un hueco importante entre los lenguajes de programación, especialmente para la Web, gracias a su framework Ruby On Rails (RoR).

Ruby es un lenguaje de programación orientado a objetos, reflexivo e interpretado, con una sintaxis inspirada en Python y en Perl, y con una implementación de software libre. El intérprete de este lenguaje está disponible para varias plataformas, tales como Windows, Linux o MacOS. Fue creado en 1995 por Yukihiro "Matz" Matsumoto. La principal característica de este lenguaje es que está pensado para la productividad.

Para empezar a realizar cosas con este lenguaje tenemos una "shell" o consola (como en Python), donde podemos probar antes de generar el código final del programa. En el caso de Ruby, la consola se llama irb (Interface Ruby shell).

Lo más sencillo a probar es una simple operación matemática:

irb(main):001:0> 2+2
=>4

O un literal o cadena de caracteres, escrito entre comillas dobles o simples:

irb(main):002:0> "Soy Rafael"
=>"Soy Rafael"

La diferencia con otros lenguajes de programación es que en Ruby todo es un objeto y, por tanto, se puede acceder directamente a sus métodos y propiedades. Por ejemplo, en el último ejemplo, hemos usado un literal, que es un objeto de tipo String. Por tanto, se puede acceder a los métodos de dicho objeto:

irb(main):003:0> "Soy Rafael".length
=>10
irb(main):004:0> "Soy Rafael".reverse
=>"leafaR yoS"

El método length retorna la longitud del literal, mientras que el método reverse retorna el literal invertido.

El operador "+" permite sumar dos números, pero en el caso de los literales, permite concatenar dos literales en 1:

irb(main):005:0> "Soy " + "Rafael"
=>"Soy Rafael"


El operador "*" permite multiplicar dos números, pero en el caso de los literales permite repetir dicho literal n veces:

irb(main):006:0> "Rafael"*3
=>"RafaelRafaelRafael"

Sin embargo, si se intenta realizar cualquier otro tipo de operación matemática, ésta dará un error, ya que un literal tiene distinta naturaleza de los números. Lo mismo ocurriría si se desea ejecutar un método de literal para un número. Por ejemplo, lo siguiente daría un error:

irb(main):007:0> "El resultado es: " + 2+2                                            
=> TypeError: can't convert Fixnum into String

Para poder llevar a cabo ésto será necesario que los valores sean del mismo tipo, y para ello, disponemos de métodos de conversión o "casting":

irb(main):008:0> "El resultado es: " + (2+2).to_s                                     
=> "El resultado es: 4"

El método to_s (to string), permite convertir un valor numérico a un literal. En el ejemplo se ha utilizado el paréntesis para englobar el resultado del número.

El siguiente ejemplo realiza la operación inversa, es decir, convierte un literal a un número para poder realizar la operación matemática:

irb(main):009:0> 20 + "4".to_i                                                        
=> "24"

El método to_i (to integer), convierte el valor a un número de tipo entero.

Los siguientes ejemplos convierten un número en literal, y después ejecutan un método específico para literales:

irb(main):010:0> 365.to_s.reverse
=> "563"
irb(main):011:0> 65535.to_s.length                                                    
=> 5

Ruby también trata arrays, que son colecciones de valores. Dichas colecciones se especifican encerrando entre corchetes ([ ]) los valores que lo conforman. El siguiente ejemplo crea un array con una serie de valores numéricos:

irb(main):012:0>  [20, 15, 45, 78, 27]                                                 
=> [20, 15, 45, 78, 27]

Los arrays también son objetos, y como tales, también tienen métodos que realizan acciones:

irb(main):013:0> [20, 15, 45, 27].max                                                 
=> 45                                                                   
irb(main):014:0> [20, 15, 45, 27].min                                                 
=> 15                                                                   
irb(main):015:0> [20, 15, 45, 27].length                                              
=> 4                                                                    
irb(main):016:0> [20, 15, 45, 27].reverse                                             
=> [27, 45, 15, 20]

El método max (maximum) retorna el valor más alto del array. El método min (minimum) retorna el valor más bajo del array. El método length retorna la longitud del array, es decir, el número de valores que contiene. El método reverse retorna los valores del array en orden inverso.

Para facilitar el uso de los valores, Ruby permite almacenar los valores se pueden almacenar variables, las cuales, serán automáticamente del tipo de los valores que contienen. Las variables almacenan los valores bajo un nombre fácil de recordar, y mediante el cual se refiere a dichos valores. Por ejemplo, si la lista anterior se trata de una lista de préstamos, la cual se va a utilizar para varias operaciones, lo mejor es almacenarlo en una variable que repetir la lista constantemente:

irb(main):017:0> prestamos = [20, 15, 45, 27]                                            
=> [20, 15, 45, 27] 
irb(main):018:0> print ticket[1]                                                      
20=> nil                                                 
irb(main):019:0> prestamos.sort!                                                         
=> [15, 20, 27, 45]
irb(main):020:0> prestamos.sort!.reverse                                                 
=> [45, 27, 20, 15]

En este ejemplo, se almacena la lista en la variable prestamos. Las siguientes operaciones se realizan sobre la variable. En primer lugar, se visualiza el valor que se encuentra en el array, en la posición 1 (las posiciones dentro del array comienzan en 0 y terminan en longitud-1, y se accede a la misma indicándolo entre corchetes). A continuación, el método sort! ordena la lista de menor a mayor. Por último, ordena la lista y, a continuación, se invierte el orden de la lista (ordenación de mayor a menor).

Un literal es un array de caracteres. Probemos lo siguiente:

irb(main):021:0> texto = "en un lugar de la Mancha, de cuyo nombre no quiero acordarme"                                                                       
=> "en un lugar de la Mancha, de cuyo nombre no quiero acordarme"       
irb(main):022:0> texto['lugar']='sitio'                                               
=> "sitio"                                                              
irb(main):023:0> print texto                                                          
en un sitio de la Mancha, de cuyo nombre no quiero acordarme=> nil

La variable texto contendrá un literal, que en sí mismo en su array de caracteres. La segunda sentencia, busca la subcadena 'lugar' dentro del array, y reemplaza su valor por 'sitio'. La última sentencia muestra el valor del literal con el resultado, en donde 'lugar' ha sido sustituido por 'sitio'.

El método include? nos permite consultar si una subcadena se encuentra dentro del texto, retornando true (verdadero) o false (falso) en caso afirmativo o negativo.

irb(main):024:0> texto.include? "lugar"                                               
=> false
irb(main):025:0> texto.include? "Mancha"                                              
=> true                                                                

Recordemos que, aunque sea un literal, seguimos tratando un array, y que algunos de los métodos que estamos probando pueden servirnos para otro tipo de arrays. Así, el método include? también puede aplicarse a nuestro array prestamos:

irb(main):026:0> ticket.include? 20                                                   
=> true                                                                 
irb(main):027:0> ticket.include? 80                                                   
=> false

En el caso de los literales, hay métodos específicos, como, por ejemplo, el poder convertir todos los caracteres del array a minúsculas (con el método downcase) o a mayúsculas (con el método upcase):

irb(main):028:0> texto.downcase                                                       
=> "en un sitio de la mancha, de cuyo nombre no quiero acordarme"       
irb(main):029:0> texto.upcase                                                         
=> "EN UN SITIO DE LA MANCHA, DE CUYO NOMBRE NO QUIERO ACORDARME"

Los arrays son un recurso valioso en la programación con Ruby, pero la complejidad de las necesidades reales con los datos nos van a exigir algo más funcional y avanzado para un tratamiento secuencial y lineal de colecciones de datos. Ruby provee un tipo de dato cuya estructura es un diccionario y funciona como una tabla hash. Esto es, se define una clave o valor único, y un valor, lo que facilita las búsquedas. Las siguientes líneas definen y añaden valores a un diccionario, el cual es una colección de libros, los cuales están catalogados por una valoración. En este caso, el título del libro es la clave (valor único), y la catalogación es el valor (que puede repetirse):

irb(main):030:0> libros = {}
=> {}
irb(main):031:0> libros["Don Quijote de la Mancha"] = :esplendido
=> :esplendido
irb(main):032:0> libros["El Alquimista"] = :esplendido
=> :esplendido
irb(main):033:0> libros["Millenium"] = :muy_bueno
=> :muy_bueno
irb(main):034:0> libros["El angel caido"] = :mediocre
=> :mediocre
irb(main):035:0> libros
=> {"Don Quijote de la Mancha"=>:esplendido, "El Alquimista"=>:esplendido, "Millenium"=>:muy_bueno, "El angel caido"=>:mediocre}

A continuación, se consulta el tamaño del diccionario y se consulta la valoración de un determinado libro:

irb(main):036:0> libros.length
=> 4
irb(main):037:0> libros["Millenium"]
=> :muy_bueno

Para consultar las claves del diccionario, se utiliza el método key:

irb(main):037:0> libros.keys
=> ["Don Quijote de la Mancha", "El Alquimista", "Millenium", "El angel caido"]

Para consultar los valores del diccionario se utiliza el método values:

irb(main):038:0> libros.values
=> [:esplendido, :esplendido, :muy_bueno, :mediocre]



Conclusiones
Esta pequeña toma de contacto al lenguaje Ruby nos permite echar un vistazo a los tipos de datos básicos que conforman el lenguaje, así como vislumbrar la potencia que tiene al ser tratado todo como objetos, con sus correspondientes métodos. Para conocer los métodos de cualquier objeto, basta con ejecutar el método methods:

irb(main):038:0> 5.methods                                                            
=> [:to_s, :-@, :+, :-, :*, :/, :div, :%, :modulo, :divmod, :fdiv, :**,:abs, :magnitude, :==, :===, :<=>, :>, :>=, :<, :<=, :~, :&, :|, :^, :[], :<<, :>>, :to_f, :size, :zero?, :odd?, :even?, :succ, :integer?, :upto, :downto, :times, :next, :pred, :chr, :ord, :to_i, :to_int, :floor, :ceil, :truncate, :round, :gcd, :lcm, :gcdlcm, :numerator, :denominator, :to_r, :rationalize, :singleton_method_added, :coerce, :i, :+@, :eql?, :quo, :remainder, :real?, :nonzero?, :step, :to_c, :real, :imaginary, :imag, :abs2, :arg, :angle, :phase, :rectangular, :rect, :polar, :conjugate, :conj, :between?, :nil?, :=~, :!~, :hash, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :inspect, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :require, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]

irb(main):039:0> "Soy Rafael".methods                                                 
=> [:<=>;, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], :[]=, :insert, :length, :size, :bytesize, :empty?, :=~, :match, :succ, :succ!, :next, :next!, :upto, :index, :rindex, :replace, :clear, :chr, :getbyte, :setbyte, :to_i, :to_f, :to_s, :to_str, :inspect, :dump, :upcase, :downcase, :capitalize, :swapcase, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :bytes, :chars, :codepoints, :reverse, :reverse!, :concat, :<<, :crypt, :intern, :to_sym, :ord, :include?, :start_with?, :end_with?, :scan, :ljust, :rjust, :center, :sub, :gsub, :chop,:chomp, :strip, :lstrip, :rstrip, :sub!, :gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr, :tr_s, :delete, :squeeze, :count, :tr!, :tr_s!, :delete!, :squeeze!, :each_line, :each_byte, :each_char, :each_codepoint, :sum, :slice, :slice!, :partition, :rpartition, :encoding, :force_encoding, :valid_encoding?, :ascii_only?, :unpack, :encode, :encode!, :to_r, :to_c, :>, :>=, :<, :<=, :between?, :nil?, :!~, :class, :singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variables, :instance_variable_get, :instance_variable_set, :instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send, :public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method, :public_method, :define_singleton_method, :__id__, :object_id, :to_enum, :enum_for, :require, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__]
    


Enlaces
Página oficial de Ruby: http://www.ruby-lang.org
Prueba Ruby desde el navegador: http://tryruby.org