Aprende a programar con Ruby

Variables

En Ruby, todo lo que se manipula es un objeto, y el resultado de esas operaciones también son objetos. La única forma que tenemos de manipular los objetos, son los métodos:

5.times { puts "Ratón!\n" } #se hablará más tarde de bloques
"A los elefantes le gustan los cacahuetes".length

Si los objetos (como los strings, números,…) son los nombres, entonces los métodos son los verbos. Todo método necesita un objeto. Es fácil decir qué objeto recibe el método: el que está a la izquierda del punto. Algunas veces, puede que no sea obvio. Por ejemplo, cuando se usa puts y gets, ¿dónde están sus objetos? Nada más iniciarse el intérprete, estamos dentro de un objeto: el objeto main. Por tanto, al usar puts y gets, estamos mandando el mensaje al objeto main.

¿Cómo podemos saber dentro de qué objeto estamos? Usando la variable self.

puts self

Escribiendo métodos

Un bloque de instrucciones que define un método, empieza por la palabra def y acaba por la end. Los parámetros son la lista de variables que van entre paréntesis. Aunque en Ruby, dichos paréntesis son opcionales: puts, p y gets son muy usados, y por ello que el uso de paréntesis sea opcional. En Rails, se llama a los métodos sin paréntesis.

Un método devuelve el valor de su última línea. Por norma, es recomendable dejar una línea en blanco entre las definiciones de métodos:

#metodos.rb

# Definición de un método
def hello  
  puts 'Hola'  
 end  
 #uso del método
 hello  

 # Método con un argumento
def hello1(nombre)  
  puts 'Hola ' + nombre  
  return 'correcto'  
end  
puts(hello1('Pedro'))  

# Método con un argumento (sin paréntesis, no funciona en versiones nuevas) 
def hello2 nombre2
  puts 'Hola ' + nombre2  
  return 'correcto'  
end  
puts(hello2 'Juan')

Esto es lo que obtenemos

>ruby metodos.rb  
Hola 
Hola Pedro 
correcto  
Hola Juan  
correcto  
metodos.rb:18 warning: parenthesize argument(s) for future version
>Exit code: 0

Los métodos bang (!)

Los métodos que acaban con una ! son métodos que modifican al objeto. Por lo tanto, estos métodos son considerados como peligrosos, y existen métodos iguales, pero sin el !. Por su peligrosidad, el nombre "bang". Ejemplo:

a = "En una lugar de la mancha"

#método sin bang: el objeto no se modifica
b = a.upcase
puts b
puts a

#método con bang: el objeto se modifica
c = a.upcase!
puts c
puts a

Normalmente, por cada método con !, existe el mismo método sin !. Aquellos sin bang, nos dan el mismo resultado, pero sin modificar el objeto (en este caso el string). Las versiones con !, como se dijo, hacen la misma acción, pero en lugar de crear un nuevo objeto, transforman el objeto original.

Ejemplos de esto son: upcase / upcase!, chomp /chomp!,…En cada caso, si haces uso de la versión sin !, tienes un nuevo objeto. Si llamas el método con !, haces los cambios en el mismo objeto al que mandaste el mensaje.

Alias

alias nuevo_nombre nombre_original

alias crea un nuevo nombre que se refiere a un método existente. Cuando a un método se le pone un alias, el nuevo nombre se refiere al método original: si el método se cambia, el nuevo nombre seguirá invocando el original.

def viejo_metodo
   "viejo metodo"  
end  
alias nuevo_metodo viejo_metodo
def viejo_metodo
  "viejo metodo mejorado"  
end  
puts viejo_metodo  
puts nuevo_metodo

En el resultado, vemos como nuevo_metodo hace referencia al viejo_metodo sin modficar:

viejo metodo mejorado  
viejo metodo

Métodos perdidos

Cuando mandas un mensaje a un objeto, el objeto busca en su lista de métodos, y ejecuta el primer método con el mismo nombre del mensaje que encuentre. Si no encuetra dicho método, lanza una error NoMethodError.

Una forma de solucionar esto, es mediante el método method_missing: si definimos dicho método dentro de una clase, se ejecuta este método por defecto:

class Dummy  
  def method_missing(m, *args)  
    puts "No existe un metodo llamado #{m}" 
  end  
end

Dummy.new.cualquier_cosa obtenemos:

No existe un metodo llamado cualquier_cosa

Por lo tanto, method_missing es como una red de seguridad: te da una forma de manejar aquellos métodos que de otra forma darían un error en tu programa.

Argumentos

Valores por defecto

Ruby deja especificar los valores por defecto de los argumentos, que son usados si no se especifica un valor explícitamente. Se hace esto mediante el operador de asignación =:

#argumentos.rb

def mtd(arg1="Dibya", arg2="Shashank", arg3="Shashank")  
   "#{arg1}, #{arg2}, #{arg3}."  
end  
puts mtd  
puts mtd("ruby")

Hemos usado el operador interpolación #{ }: se calcula la expresión entre paréntesis, y el resultado se añade al string. Lo que obtenemos es:

>ruby argumentos.rb  
Dibya, Shashank, Shashank.  
ruby, Shashank, Shashank.  
>Exit code: 0

Número de argumentos variable

Ruby permite escribir funciones que acepten un número variable de argumentos. Por ejemplo:

def foo(*mi_string)
  mi_string.each do |palabras|
    puts palabras
  end
end

foo('hola', 'mundo')
foo()

El asterisco indica que el número de argumentos puede ser el que se quiera. En este ejemplo, el asterisco toma los argumentos y los asigna a un array (o vector de elementos) llamado mi_string. Haciendo uso de ese asterisco, incluso se pueden pasar cero argumentos; que es lo que pasa con foo().

No hay máximo número de argumentos que podamos pasar a un método.

Argumentos opcionales

Si se quieren incluir argumentos opcionales, tienen que venir después de los argumentos no opcionales:

def arg_opc(a,b,*x) # bien
def arg_opc(a,*x,b) # mal

Los argumentos se interpretan de izquierda a derecha, por eso es importante que los argumentos no opcionales vayan en primer lugar. Si los pusiésemos en último lugar, no sabríamos decir donde acaban los argumentos opcionales y donde empiezan los no opcionales.

=begin
Ejemplo de como los argumentos se
interpretan de izquierda a derecha
=end

def mtd(a=99, b=a+1)  
  [a,b]  
end  
puts mtd

Introduciendo datos (gets)

Lecciones atrás vimos el método puts que saca datos por la pantalla. ¿Cómo podemos introducir nuestros propios datos? Para esto gets (get=coger, s=string) y chomp son de ayuda. Veamos el siguiente ejemplo:

# gets y chomp  
puts "¿En qué ciudad te gustaría vivir?"  
STDOUT.flush  
ciudad = gets.chomp  
puts "La ciudad es " + ciudad

El ejemplo superior, al ser ejecutado en SciTe, clickea en la pantalla de output y pon el nombre de tu ciudad favorita. STDOUT es una constante global que almacena las salidas del programa. flush vacía cualquier dato almacenado, y por lo tanto, limpiará cualquier resultado anterior. chomp es un método para strings y gets almacena strings que provienen del teclado. El problema es que gets almacena lo escrito y el caráter \n (retorno de carro); chomp lo que hace es borrar el carácter: \n.

RAILS: los datos vienen de muchos sitios. En la típica aplicación de Rails, vienen de una base de datos. Como un desarrollador de Rails, puedes usar con frecuencia algunos de estos métodos, porque Rails recoge los datos que los usuarios escriben en los formularios Web.

Ejercicio

Escribe un programa que pregunte por la temperatura en grados Fahrenheit. El programa usará este dato, y hallará el equivalente en grados Celsius. El resultado final lo mostrará en pantalla con dos decimales. (Celsius (°C) = [ Fahrenheit (°F) - 32 ] / 1.8)

Nota: para formatear un resultado a dos decimales, hay dos opciones:

  1. Usar el método format. Por ejemplo:
x = 45.5678
puts format("%.2f", x)
  1. Otra forma es la función round:
puts (x*100).round/100.0