yield
.
Una de las características de los bloques es su carácter anónimo.
Tampoco son pasados al método en la forma tradicional: no figuran en la lista de argumentos.
En vez de eso, son invocados por medio de yield
.
Por ejemplo:
~/rubytesting$ cat -n iter.rb 1 def seq(n,m,c) 2 i=0 3 while (i < n) 4 yield i*m+c 5 i += 1 6 end 7 end 8 9 seq(5,2,2) { |x| puts x }Que usamos así:
~/rubytesting$ ruby iter.rb 2 4 6 8 10
Si se prefiere tener un mayor control sobre el bloque (por ejemplo, se desea pasar el bloque
como argumento a otro método)
lo añadiremos como argumento final a nuestro método, prefijado por un ampersand &
.
Dentro del método, el argumento asociado con el bloque pertenecerá a la clase Proc
y se podrá invocar usando el método call
del objeto Proc
en vez de yield
:
~/rubytesting$ cat -n iter2.rb 1 def seq(n,m,c, &b) 2 i=0 3 while (i < n) 4 b.call(i*m+c) 5 i += 1 6 end 7 end 8 9 seq(5,2,2) { |x| puts x }Nótese que el asunto afecta a la definición del método pero que la forma de la llamada (línea 9) no cambia.
Ejecución:
~/rubytesting$ ruby iter2.rb 2 4 6 8 10
También podemos contruir un objeto Proc y pasarlo explícitamente
al método. En este caso se trata de un objeto ordinario y ni usamos el &
ni la llamada tiene la sintáxis con bloque:
# This version expects an explicitly-created Proc object, not a block def sequence4(n, m, c, b) # No ampersand used for argument b i = 0 while(i < n) b.call(i*m + c) # Proc is called explicitly i += 1 end end p = Proc.new {|x| puts x } # Explicitly create a Proc object sequence4(5, 2, 2, p) # And pass it as an ordinary argument
Los siguientes dos métodos son legales:
def sequence5(args, &b) # Pass arguments as a hash and follow with a block n, m, c = args[:n], args[:m], args[:c] i = 0 while(i < n) b.call(i*m + c) i += 1 end end
# Expects one or more arguments, followed by a block def max(first, *rest, &block) max = first rest.each {|x| max = x if x > max } block.call(max) max end
yield
también funciona sobre un parámetro
definido con &
.
yield
incluso si el parámetro ha sido convertido a un objeto Proc
y pasado como un argumento.
&
can be used in both method definitions and method invocations.
&
prefijando un parámetro en una definición de método nos permite explicitar el bloque
asociado con la invocación y usarlo como un objeto de la clase Proc
dentro del cuerpo del método
&
en una invocación/llamada prefijando un
objeto Proc trata al Proc como si fuera
un bloque que sigue la invocación
a, b = [1,2,3], [4,5] # Start with some data. sum = a.inject(0) {|total,x| total+x } # => 6. Sum elements of a. sum = b.inject(sum) {|total,x| total+x } # => 15. Add the elements of b in.
Al reescribirlo como sigue evitamos que el intérprete Ruby parsee dos veces el mismo código:
a, b = [1,2,3], [4,5] # Start with some data. summation = Proc.new {|total,x| total+x } # A Proc object for summations. sum = a.inject(0, &summation) # => 6 LLamada como argumento sum = b.inject(sum, &summation) # => 15 ordinario
ruby-1.9.2-head :008 > def f(a,b); yield a+b; end => nil ruby-1.9.2-head :009 > f(1,2) { |x| puts x } 3 => nilNo podemos llamarlo como si fuera el bloque que sigue:
ruby-1.9.2-head :010 > qq = lambda { |x| puts x } => #<Proc:0x007f93e387d3d0@(irb):10 (lambda)> ruby-1.9.2-head :011 > f(1,2) &qq LocalJumpError: no block given (yield)Así tampoco:
ruby-1.9.2-head :012 > f(1,2, qq) ArgumentError: wrong number of arguments (3 for 2)Es necesario hacer la conversión con
&
y pasarlo como último argumento:
ruby-1.9.2-head :013 > f(1,2, &qq) 3 => nil
Realmente, la invocación con &
puede usarse con cualquier objeto que disponga de un método to_proc
.
En Ruby 1.9 la clase Symbol dispone de un método to_proc
, lo que permite a los símbolos ser prefijados
con &
y ser pasados a los iteradores:
pry(main)> words = ['and', 'but', 'car'] # An array of words => ["and", "but", "car"] pry(main)> uppercase = words.map &:upcase # Convert to uppercase with String.upcase => ["AND", "BUT", "CAR"]Este código es equivalente a:
upper = words.map {|w| w.upcase } # This is the equivalent code with a block
Casiano Rodriguez León 2015-01-07