yielding un bloque
lambda? determina si un objeto Proc es una lambda o un proc
return nos saca del método en el que estamos, no del bloque en el que estamos:
def test
puts "entering method"
1.times { puts "entering block"; return } # Makes test method return
puts "exiting method" # This line is never executed
end
test
return nos saca del método en el que estamos, no del proc en el que estamos:
def test
puts "entering method"
p = Proc.new { puts "entering proc"; return }
p.call # Invoking the proc makes method return
puts "exiting method" # This line is never executed
end
test
return dentro de un proc puede sorprender un poco:
def procBuilder(message) # Create and return a proc
Proc.new { puts message; return } # return returns from procBuilder
end
def test
puts "entering method"
p = procBuilder("entering proc")
p.call # Prints "entering proc" and raises LocalJumpError!
puts "exiting method" # This line is never executed
end
test
Cuando lo ejecutamos obtenemos:
$ ruby procjump.rb entering method entering proc procjump.rb:2:in `block in procBuilder': unexpected return (LocalJumpError)Al convertir el bloque en un objeto podemos pasar el bloque y utilizarlo fuera de su contexto.
return dentro de una lambda
retorna desde la lambda no desde el método que contiene a la lambda
def test
puts "entering method"
p = lambda { puts "entering lambda"; return }
p.call # Invoking the lambda does not make the method return
puts "exiting method" # This line *is* executed now
end
test
return en una lambda
caiga fuera de contexto
def lambdaBuilder(message) # Create and return a lambda
lambda { puts message; return } # return returns from the lambda
end
def test
puts "entering method"
l = lambdaBuilder("entering lambda")
l.call # Prints "entering lambda"
puts "exiting method" # This line is executed
end
test
break transfiere el control a la
primera sentencia que sigue al bucle:
while(line = gets.chop) # A loop starts here break if line == "quit" # If this break statement is executed... puts eval(line) end puts "Good bye" # ...then control is transferred here
break transfiere el control fuera
del bloque, fuera del iterador hasta la primera expresión que sigue
a la invocación del iterador:
f.each do |line| # Iterate over the lines in file f break if line == "quit\n" # If this break statement is executed... puts eval(line) end puts "Good bye" # ...then control is transferred here
Proc.new, el iterador desde el que se sale es
Proc.new. Eso significa que si el proc con el break es llamado
fuera de contexto obtendremos un error:
$ cat procbreak.rb
def test
puts "entering test method"
proc = Proc.new { puts "entering proc"; break }
proc.call # LocalJumpError: iterator has already returned
puts "exiting test method"
end
test
$ ruby procbreak.rb
entering test method
entering proc
procbreak.rb:3:in `block in test': break from proc-closure (LocalJumpError)`
$ cat procbreakwithampersand.rb
def iterator(&proc)
puts "entering iterator"
proc.call # invoke the proc
puts "exiting iterator" # Never executed if the proc breaks
end
def test
iterator { puts "entering proc"; break }
end
test
la cosa funciona bien porque el proc está en contexto:
$ ruby procbreakwithampersand.rb entering iterator entering proc
break funciona igual que dentro de un método:
$ cat lambdabreak.rb
def test
puts "entering test method"
lambda = lambda { puts "entering lambda"; break; puts "exiting lambda" }
lambda.call
puts "exiting test method"
end
test
de hecho el break actúa como un return y nos saca de la lambda:
$ ruby lambdabreak.rb entering test method entering lambda exiting test method
next al nivel mas alto produce el mismo efecto en un bloque,
proc o lambda: hace que se retorne del yield o del call que
invoca el bloque, proc o lambda
next va seguido de una expresión es usada como valor de retorno
del bloque, proc o lambda
redo transfiere el control al comienzo del bloque o lambda
retry no esta permitido ni en procs ni en lambdas
raise
rescue
yield son asignados a los parámetros
de un bloque siguiendo reglas que son mas próximas a las reglas de asignación
de variables que a las reglas de invocación de métodos
yield k,v |
do |key, value| ... end |
funciona como
key, value = k, v
def two; yield 1,2; end # An iterator that yields two values
two {|x| p x } # Ruby 1.8: warns and prints [1,2],
two {|x| p x } # Ruby 1.9: prints 1, no warning
two {|*x| p x } # Either version: prints [1,2]; no warning
two {|x,| p x } # Either version: prints 1; no warning
def five; yield 1,2,3,4,5; end # Yield 5 values five do |head, *body, tail| # Extra values go into body array print head, body, tail # Prints "1[2,3,4]5" end
yield permite bare hashes:
def hashiter; yield :a=>1, :b=>2; end # Note no curly braces
hashiter {|hash| puts hash[:a] } # Prints 1
&
para indicar que será utilizado para recibir el bloque asociado con la invocación
del bloque:
# This Proc expects a block
printer = lambda {|&b| puts b.call } # Print value returned by b
printer.call { "hi" } # Pass a block to the block!
ruby-1.9.2-head :001 > [1,2,3].each { |x,y=2| puts x*y }
2
4
6
=> [1, 2, 3]
o también:
ruby-1.9.2-head :007 > [1,2,3].each &->(x, y=2) { puts x*y }
2
4
6
=> [1, 2, 3]
yield usa semántica yield mientras que la invocación de un método
usa semántica de invocación
p = Proc.new {|x,y| print x,y }
p.call(1) # x,y=1: nil used for missing rvalue: Prints 1nil
p.call(1,2) # x,y=1,2: 2 lvalues, 2 rvalues: Prints 12
p.call(1,2,3) # x,y=1,2,3: extra rvalue discarded: Prints 12
p.call([1,2]) # x,y=[1,2]: array automatically unpacked: Prints 12
l = lambda {|x,y| print x,y }
l.call(1,2) # This works
l.call(1) # Wrong number of arguments
l.call(1,2,3) # Wrong number of arguments
l.call([1,2]) # Wrong number of arguments
l.call(*[1,2]) # Works: explicit splat to unpack the array
Casiano Rodriguez León 2015-01-07