From Fedora Project Wiki

< FWN‎ | Beats

Revision as of 14:52, 16 April 2011 by Pcalarco (talk | contribs)

LATAM Fedora!

LATAM Fedora is a regular column of Spanish language contributions around open source software. It is our first expansion into incorporating foreign language content into FWN.

This week's contribution is from Guillermo Gómez, a third installment on Ruby. Enjoy!

Ruby Capítulo 3 : Métodos

Como ya hemos venido desarrollando en nuestra previas entregas, los métodos sabemos se definen por medio de la palabra clave def. Los nombres de métodos deben comenzar con una letra minúscula. Los métodos que funcionan como consultas suelen ser llamados con el sufijo "?", tal como instance_of? Los métodos peligrosos o que modifican el objeto mismo suelen también llamarse con un el sufijo "!". Por ejemplo String tiene el método chop y chop!. El primero devuelve un string modificado, el segundo modifica el objeto mismo.

El cuerpo de un método contiene expresiones Ruby normales, excepto que no se puede definir un método de instancia, clase o modulo dentro de un método, si lo intenta obtendrá un error como el siguiente:

1 SyntaxError: compile error
2 (irb):2: class definition in method body
3     from (irb):7

El valor devuelto por los métodos es el valor de la última expresión ejecutada, o el resultado explícito de una expresión return.

1 >> def resultado
2 >> "Uno" 
3 >> end
4 >> p resultado
5 "Uno" 
6 
7 >> def resultado
8 >> "Uno" 
9 >> return true
10 >> end
11 => nil
12 >> p resultado
13 true

Parámetros

Ya que sabe definir un método con su nombre, ahora seguramente se encontrará con la necesidad de invocar el método pasando parámetros.

1 def miMetodo(arg1, arg2, arg3)
2   # su codigo
3 end
4 
5 def miOtroMetodo
6   # sin argumentos
7   # su codigo
8 end

Ruby le permite definir valores por omisión a los argumentos de lo métodos que son obviados durante la invocación.

1 def trampa(arg1="boba", arg2="tnt", arg3="30s")
2   "#{arg1}, #{arg2}, #{arg3}" 
3 end
1 ?> trampa
2 => "boba, tnt, 30s" 
3 >> trampa("lisa")
4 => "lisa, tnt, 30s" 
5 >> trampa("lisa","h2o")
6 => "lisa, h2o, 30s" 
7 >> trampa("lisa","h2o", "45h")
8 => "lisa, h2o, 45h" 

Si desea pasar una cantidad variable de argumentos, o desea capturar múltiples argumentos en un único parámetro, el colocar un asterisco (*) antes del nombre del último argumento hace justo eso.

1 def varargs(arg1, *resto)
2   "Recibi #{arg1} y #{resto.join(', ')}" 
3 end
1 >> varargs("uno")
2 => "Recibi uno + " 
3 >> varargs("uno","dos")
4 => "Recibi uno + dos" 
5 >> varargs("uno","dos","tres")
6 => "Recibi uno + dos, tres" 
7 >> varargs("uno","dos","tres", "cuatro")
8 => "Recibi uno + dos, tres, cuatro" 

El resto de argumentos se apilan en un Array y se pone a disposición del método bajo el nombre especificado.

Métodos con y sin bloques asociados

Una técnica poderosa con Ruby es la posibilidad de asociar bloques de código a los métodos, veamos un ejemplo simple para ilustrar la técnica.

1 >> def con_y_sin_bloque(p1)
2 >>   if block_given?
3 >>     yield(p1)
4 >>   else
5 ?>     p1
6 >>   end
7 >> end
8 => nil
9 >> con_y_sin_bloque(10) { |p1| (1..5).each { |m| p m + p1 } }
10 11
11 12
12 13
13 14
14 15
15 => 1..5
16 >> con_y_sin_bloque(10)
17 => 10

Para determinar la presencia del bloque hemos usado el método block_given? para condicionar el procesamiento en la ejecución del método. Luego yield permite ejecutar dicho bloque de código ruby con el argumento que deseemos, en este caso p1.

Si en la definición de método, el último parámetro es precedido por un ampersand "&", cualquier bloque asociado a la llamada es convertido a un objeto Proc y dicho objeto es asignado a dicho parámetro, veamos un ejemplo para visualizar su uso.

1 class CalculadoraDeVentas
2   def initialize(nombre, &bloque)
3     @nombre, @bloque = nombre, bloque
4   end
5 
6   def precio_de_venta(costo)
7     @bloque.call(costo)
8   end
9 end
1 >> cpd = CalculadoraDeVentas.new("Distribuidor1") { |costo| p "Precio de venta #{(1.5*costo).to_s} BsF." }
2 => #<CalculadoraDeVentas:0xb735ccc8 @nombre="Distribuidor1", @bloque=#<Proc:0xb735cd18@(irb):75>
3 >> cpm = CalculadoraDeVentas.new("Minorista1") { |costo| p "Precio de venta #{(1.8*costo).to_s} BsF." }
4 => #<CalculadoraDeVentas:0xb7347008 @nombre="Minorista1", @bloque=#<Proc:0xb7347058@(irb):77>
5 
6 >> cpd.precio_de_venta(100)
7 "Precio de venta 150.0 BsF." 
8 => nil
9 >> cpm.precio_de_venta(100)
10 "Precio de venta 180.0 BsF." 
11 => nil

En el ejemplo hemos introducido el uso del método call de Proc para invocar la ejecución correspondiente y con los argumentos necesarios. Tendremos más que decir acerca de Proc en futuras ediciones.

Por supuesto que esta funcionalidad puede implementarse de otras maneras, muchas otras maneras, el ejemplo lo que demuestra sutilmente es que también es posible reusar bloques de código por medio del uso de los bloques asociados a las llamadas de los métodos, es decir, puede por ejemplo compartir un bloque de código con más de una definición de método en su aplicación de hecho reduciendo y reusando el código por medio de funcionalidad común no representaba en clases o módulos. Esto igualmente puede hacerse en tiempo de ejecución, es decir, código que genera código y que lo pasa otro código para que lo ejecute. Si está confundido es porque está comenzando a experimentar el fuerte componente de dinamismo que tiene Ruby, no se asuste y abrace el poder que de esto se deriva.

Invocando los métodos

Usted puede invocar o "llamar" un método especificando un "receptor" (objeto), el nombre del método, y opcionalmente algunos parámetros y un bloque asociado. Para el caso de los métodos de clase y módulo, el receptor sería el nombre de clase o módulo.

1 File.size("archivo_grande.mpeg") 2 Math.sin(Math::PI/3)

Si se omite el receptor, se usa self, el objeto actual:

1 >> self.class
2 => Object
3 >> self.hash
4 => -608576338
5 >> hash
6 => -608576338
7 >> self.object_id
8 => -608576338
9 >> object_id
10 => -608576338

Note que he evitado intencionalmente llamar a class omitiendo el receptor, y ello es porque provoca un error, class es ambiguo, es una palabra clave (keyword) y Ruby cree que usted está tratando de definir una clase, no intenta self.class. En estos casos, use self.class, en general es mejor explicitar el receptor, es más legible y potencialmente evitará estas ambigüedades difíciles de depurar.

También observe con cuidado que no es igual Object.hash a self.hash ya que self es una instancia de Object en el ejemplo y Object.hash es un método de clase, referencian a hash diferentes, uno a la instancia (self), y el otro a la clase Object.

1 >> self.class
2 => Object
3 >> self.object_id
4 => -608576338
5 >> Object.object_id
6 => -608573798

Los paréntesis en la llamada al método para listar los argumentos son opcionales, sin embargo para evitar ambigüedades, le recomendamos fuertemente que siempre use los paréntesis, tal vez sólo en los casos más simples puede evitarse.

Expandiendo arreglos en las llamadas

De forma similar a la invocación con asterisco, en reversa, al invocar un método es posible "expandir" un arreglo para que cada elemento sea tomado como elemento individual.

1   def cinco(a,b,c,d,e)
2     "Argumentos #{a} #{b} #{c} #{d} #{e}" 
3   end
1 >> cinco("un", *["dos", "tres" , "cuatro", "cinco"])
2 => "Argumentos un dos tres cuatro cinco" 
3 >> cinco(*["un", "dos", "tres" , "cuatro", "cinco"])
4 => "Argumentos un dos tres cuatro cinco" 
5 >> cinco("un", *["dos", "tres" , "cuatro", "cinco"])
6 => "Argumentos un dos tres cuatro cinco" 
7 >> cinco("un", *["dos", "tres"] , "cuatro", "cinco")     # Debe ser el último argumento
8 SyntaxError: compile error

Argumentos opcionales

Usando el mismo ejemplo de arriba, intente pasar menos de cinco argumentos:

1 >> cinco
2 ArgumentError: wrong number of arguments (0 for 5)

Los cinco argumentos son obligatorios, una forma de convertirlos en opcionales es asignarles un valor por omisión nil.

1   def cinco(a=nil,b=nil,c=nil,d=nil,e=nil)
2     "Argumentos #{a} #{b} #{c} #{d} #{e}" 
3   end
1 >> cinco
2 => "Argumentos     " 
3 >> cinco("uno")
4 => "Argumentos uno    " 

Sin embargo note que no puede especificar el tercer argumento sin especificar el segundo, debe respetar la secuencialidad en la definición de los argumentos. Veamos la siguiente versión revisada para visualizar los argumentos pasados al método.

1   def cinco(a=nil,b=nil,c=nil,d=nil,e=nil)
2     "Argumentos 1:#{a} 2:#{b} 3:#{c} 4:#{d} 5:#{e}" 
3   end

Use nil para saltar al próximo argumento en la lista cuanda haga la invocación del método.

1 >> cinco
2 => "Argumentos 1: 2: 3: 4: 5:" 
3 >> cinco("A",nil,nil,"D")
4 => "Argumentos 1:A 2: 3: 4:D 5:" 

Simulando argumentos por nombres: hash

Si bien Ruby no tiene un verdadero soporte para "keyword arguments", es decir, pasar argumentos con su nombre y valor, nada nos impide simular dicho comportamiento usando un hash. Ruby incluso tiene un pequeño atajo que nos evita la sintáxis Hash {} para evitarnos confundir con un bloque asociado. El hash debe estar especificado de último en la lista de argumentos.

1 def hash_params(params)
2   p "ID: #{params[:id]}" 
3   p "Clave: #{params[:clave]}" 
4 end
code class="ruby">
>> hash_params(:id => "XXX" , :clave => "Z A Z Z")
"ID: XXX" 
"Clave: Z A Z Z" 
=> nil
>> hash_params(:clave => "1 2 3 4", :id => "007")
"ID: 007" 
"Clave: 1 2 3 4" 
=> nil
>> hash_params({:clave => "1 2 3 4", :id => "007"})   # Sintaxis estricta con las {}
"ID: 007" 
"Clave: 1 2 3 4" 
Loading...
Fudcon-panama-2011_728x90_leaderboard_rotated
Powered by Redmine Poder_fedora Poder_ruby Haproxy Powered_by_apache