Blocks y Procs
Los bloques de código (llamados closures en otros lenguajes) son definitivamente una de las
características más divertidas de Ruby. Son bloques de código delimitados por corchetes ({}) o por
las palabras do y end que pueden ser asociados con invocaciones de métodos “casi” como si
fueran parámetros.
Un bloque de código Ruby es una manera de agrupar expresiones y pueden aparecer sólo junto al código de una llamada a un método. El bloque es escrito comenzando en la misma línea que el último parámetro de la llamada al método (o el cierre de paréntesis de la lista do parámetros). El código del bloque no es ejecutado en el momento en el que el intérprete lo encuentra: Ruby recuerda el contexto en el que el bloque aparece (las variables locales, el objeto actual, etc.) y entonces entra al método.
Es un estándar en Ruby usar corchetes ({}) para delimitar bloques de una sola línea y do end
para bloques de más de una línea. Toma en cuenta que la sintaxis que usa corchetes tiene
más alta precedencia que do end.
Matz dice que cualquier método puede ser llamado con un bloque como argumento implícito.
Dentro del método, puedes llamar al bloque usando yield con un valor.
Una vez que has creado un bloque, puedes asociarlo con una llamada a un método.
Usualmente los bloques de código que son pasados a métodos son objetos anónimos
creados instantáneamente. Por ejemplo, en el código siguente, el bloque que contiene
puts "hola" es asociado con la llamada al método saluda:
1 saluda {puts 'Hola'}
Si el método tiene prámetros, deben aparecer antes del bloque
1 saluda("Juan") {puts 'hola'}
Un método puede invocar un bloque asociado una o mås veces usando yield
El programa p022bloques.rb ilustra lo anterior
1 =begin 2 Los bloques de codigo en Ruby son pedazos de codigo 3 entre corchetes o do end que pueden ser asociados 4 a llamadas a metodos. 5 =end 6 def llama_un_bloque 7 puts 'Inicio del metodo' 8 # puedes llamar al bloque usando yield 9 yield 10 yield 11 puts 'Fin del metodo' 12 end 13 # Los bloques de codigo pueden aparecer solo en el codigo 14 # adyacente a la llamada al metodo 15 llama_un_bloque {puts 'Hola desde el bloque'}
El resultado es:
Si proporcionas un bloque a la llamada a un método, dentro del método puedes pasar el control de
la ejecución del código del método al bloque. Es decir, suspender la ejecución
del código del método, ejecutar el código del bloque y regresar el control al
cuerpo del método después de llamar yield.
Puedes pasar parámetros a yield, que serán pasados al bloque. Dentro del bloque, debes enlistar los nombres de los argumentos entre barras verticales (|).
Observa el programa p023bloques2:
1 # Puedes agragar parametros a la llamada a yield: 2 # que seran pasados al bloque 3 def llama_bloque 4 yield('hola', 99) 5 end 6 llama_bloque {|cad, num| puts cad + ' ' + num.to_s}
El resultado es:
Nota que el código del bloque no es ejecutado en el momento en que es encontrado por el el intérprete. Ruby recuerda el contexto en el que el bloque aparece y luego llama al método.
El valor que regresa un bloque (como el de un método) es el valor de la última expresión evaluada. Este valor de retorno está entonces disponible dentro del método como
el valor de regreso de la llamada a yield.
Los bloques no son objetos pero pueden ser convertidos a objetos de la clase Proc. Esto puede lograrse llamando al método lambda del módulo Kernel. Un bloque
creado con lambda actúa como un método en Ruby. Si no especificas el número correcto
de argumentos, no puedes llamar al bloque.
1 prc = lambda {'hola'}
Los objetos de clase Proc son bloques de código que han sido relacionados con
un conjunto de variables. La clase Proc tiene un método llamado call que
invoca al bloque. El programa p024proccall.rb ilustra lo anterior:
1 # Los bloques no son objetos 2 # pueden ser convertidos a objetos de la clase Proc con el metodo lambda 3 prc = lambda {puts 'Hola'} 4 # el metodo call invoca al bloque 5 prc.call 6 7 # otro ejemplo 8 brindis = lambda do 9 puts 'Salud' 10 end 11 brindis.call
El resultado es:
Recuerda que no puedes pasar métodos a otros métodos, (pero puedes pasar procs) y que los métodos no pueden regresar otros métodos (pero pueden regresar procs).
El siguiente ejemplo muestra como los métodos pueden aceptor procs p025mtdproc.rb
1 def un_metodo(un_proc) 2 puts 'Inicio del metodo' 3 un_proc.call 4 puts 'Fin del metodo' 5 end 6 7 saluda = lambda do 8 puts 'Hola' 9 end 10 11 un_metodo saluda
El resultado es:
Este es otro ejemplo de pasar argumentos usando lambda:
1 un_bloque = lambda {|x| puts x} 2 un_bloque.call "Hola Mundo!" 3 4 #el resultado es: Hola Mundo!
Fabio Akita, un entusiasta de Rails brasileño también conocido como “AkitaOnRails” escribió un artículo acerca de los bloques Ruby para los miembros de rubylearning.com.