Subsecciones


En que Forma las Lambdas Difieren de los Procs

  1. Un proc es la representación como objeto de un bloque y se comporta como un bloque
  2. Una lambda se comporta mas como un método que como un bloque
  3. LLamar una lambda es como invocar un método
  4. LLamar un proc es como yielding un bloque
  5. El predicado lambda? determina si un objeto Proc es una lambda o un proc


Return en bloques, procs y lambdas

  1. 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
    
  2. 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
    
  3. Un 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.
  4. Un 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
    
  5. Eso significa que no tenemos que preocuparnos por que un 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 en bloques, procs y lambdas

  1. Cuando se usa dentro de un bucle 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
    

  2. Cuando se usa dentro de un bloque 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
    

  3. Cuando creamos un proc con 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)`
    
  4. Si creamos el proc como argumento del iterador:
    $ 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
    
  5. Dentro de una lambda 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
    

Otras sentencias de control en bloques, procs y lambdas

  1. Un 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
  2. Si next va seguido de una expresión es usada como valor de retorno del bloque, proc o lambda
  3. redo transfiere el control al comienzo del bloque o lambda
  4. El uso de retry no esta permitido ni en procs ni en lambdas

  5. raise
  6. rescue

Paso de argumentos en procs y lambdas

  1. Los valores de los argumentos que siguen un 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

  2. Un código como:

      yield k,v
    
    do |key, value|
      ...
    end
    

    funciona como

    key, value = k, v
    
  3. 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
    
  4. En Ruby 1.9 l el argumento splat no tiene por que ser el último:
    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
    
  5. La sentencia yield permite bare hashes:
    def hashiter; yield :a=>1, :b=>2; end  # Note no curly braces
    hashiter {|hash| puts hash[:a] }       # Prints 1
    
  6. Si el parámetro final es un bloque puede ser prefijado con & 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!
    
  7. Es legal dar valores por defecto a los parámetros de un bloque:
    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]
    
  8. La sentencia yield usa semántica yield mientras que la invocación de un método usa semántica de invocación
  9. Invocar un proc sigue semántica yield mientras que invocar una lambda sigue 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
    
  10. Las Lambdas no son tan flexibles; al igual que los métodos, son mas estrictas con el número de argumentos pasados:
    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-06-18