ruby lambda proc

lambda ruby



Cuándo usar lambda, cuándo usar Proc.nuevo? (14)

En Ruby 1.8, hay diferencias sutiles entre proc / lambda por un lado, y Proc.new por el otro.

  • ¿Cuáles son esas diferencias?
  • ¿Puede dar pautas sobre cómo decidir cuál elegir?
  • En Ruby 1.9, proc y lambda son diferentes. ¿Cual es el trato?

Encontré esta página que muestra cuál es la diferencia entre Proc.new y lambda . Según la página, la única diferencia es que un lambda es estricto respecto al número de argumentos que acepta, mientras que Proc.new convierte los argumentos que faltan en nil . Aquí hay un ejemplo de sesión de IRB que ilustra la diferencia:

irb(main):001:0> l = lambda { |x, y| x + y } => #<Proc:0x00007fc605ec0748@(irb):1> irb(main):002:0> p = Proc.new { |x, y| x + y } => #<Proc:0x00007fc605ea8698@(irb):2> irb(main):003:0> l.call "hello", "world" => "helloworld" irb(main):004:0> p.call "hello", "world" => "helloworld" irb(main):005:0> l.call "hello" ArgumentError: wrong number of arguments (1 for 2) from (irb):1 from (irb):5:in `call'' from (irb):5 from :0 irb(main):006:0> p.call "hello" TypeError: can''t convert nil into String from (irb):2:in `+'' from (irb):2 from (irb):6:in `call'' from (irb):6 from :0

La página también recomienda usar lambda a menos que desee específicamente el comportamiento tolerante a errores. Estoy de acuerdo con este sentimiento. Usar una lambda parece un poco más conciso, y con una diferencia tan insignificante, parece ser la mejor opción en la situación promedio.

En cuanto a Ruby 1.9, lo siento, aún no he visto la versión 1.9, pero no creo que lo cambien demasiado (aunque no creas en mi palabra, parece que has oído hablar de algunos cambios, así que Probablemente me equivoque allí.


Estoy un poco atrasado en esto, pero hay una gran cosa poco conocida sobre Proc.new no se menciona en los comentarios. Como por la documentation :

Se puede llamar a Proc::new sin un bloque solo dentro de un método con un bloque adjunto, en cuyo caso ese bloque se convierte en el objeto Proc .

Dicho esto, Proc.new permite encadenar métodos de producción:

def m1 yield ''Finally!'' if block_given? end def m2 m1 &Proc.new end m2 { |e| puts e } #⇒ Finally!


La diferencia en el comportamiento con el return es, en mi opinión, la diferencia más importante entre los 2. También prefiero lambda porque es menos tipográfica que Proc.new :-)


Lambda funciona como se espera, como en otros idiomas.

El Proc.new cableado es sorprendente y confuso.

La declaración de return en proc creada por Proc.new no solo devolverá el control solo desde sí mismo, sino también desde el método que lo contiene .

def some_method myproc = Proc.new {return "End."} myproc.call # Any code below will not get executed! # ... end

Puede argumentar que Proc.new inserta código en el método de Proc.new , al igual que el bloque. Pero Proc.new crea un objeto, mientras que el bloque es parte de un objeto.

Y hay otra diferencia entre lambda y Proc.new , que es su manejo de los argumentos (incorrectos). lambda se queja de ello, mientras que Proc.new ignora los argumentos adicionales o considera que la ausencia de argumentos es nula.

irb(main):021:0> l = -> (x) { x.to_s } => #<Proc:0x8b63750@(irb):21 (lambda)> irb(main):022:0> p = Proc.new { |x| x.to_s} => #<Proc:0x8b59494@(irb):22> irb(main):025:0> l.call ArgumentError: wrong number of arguments (0 for 1) from (irb):21:in `block in irb_binding'' from (irb):25:in `call'' from (irb):25 from /usr/bin/irb:11:in `<main>'' irb(main):026:0> p.call => "" irb(main):049:0> l.call 1, 2 ArgumentError: wrong number of arguments (2 for 1) from (irb):47:in `block in irb_binding'' from (irb):49:in `call'' from (irb):49 from /usr/bin/irb:11:in `<main>'' irb(main):050:0> p.call 1, 2 => "1"

Por cierto, proc en Ruby 1.8 crea un lambda, mientras que en Ruby 1.9+ se comporta como Proc.new , lo que es realmente confuso.


No noté ningún comentario sobre el tercer método en la búsqueda, "proc" que está en desuso, pero se maneja de manera diferente en 1.8 y 1.9.

Aquí hay un ejemplo bastante detallado que facilita ver las diferencias entre las tres llamadas similares:

def meth1 puts "method start" pr = lambda { return } pr.call puts "method end" end def meth2 puts "method start" pr = Proc.new { return } pr.call puts "method end" end def meth3 puts "method start" pr = proc { return } pr.call puts "method end" end puts "Using lambda" meth1 puts "--------" puts "using Proc.new" meth2 puts "--------" puts "using proc" meth3


No puedo decir mucho sobre las sutiles diferencias. Sin embargo, puedo señalar que Ruby 1.9 ahora permite parámetros opcionales para lambdas y bloques.

Aquí está la nueva sintaxis para los lambdas stabby debajo de 1.9:

stabby = ->(msg=''inside the stabby lambda'') { puts msg }

Ruby 1.8 no tenía esa sintaxis. La forma convencional de declarar bloques / lambdas tampoco era compatible con argumentos opcionales:

# under 1.8 l = lambda { |msg = ''inside the stabby lambda''| puts msg } SyntaxError: compile error (irb):1: syntax error, unexpected ''='', expecting tCOLON2 or ''['' or ''.'' l = lambda { |msg = ''inside the stabby lambda''| puts msg }

Ruby 1.9, sin embargo, admite argumentos opcionales incluso con la sintaxis anterior:

l = lambda { |msg = ''inside the regular lambda''| puts msg } #=> #<Proc:0x0e5dbc@(irb):1 (lambda)> l.call #=> inside the regular lambda l.call(''jeez'') #=> jeez

Si quieres compilar Ruby1.9 para Leopard o Linux, consulta este artículo (autopromoción descarada).


Otra diferencia importante pero sutil entre procs creados con lambda y procs creados con Proc.new es cómo manejan la declaración de return :

  • En un proc creado con lambda , la declaración de return devuelve solo desde el propio proc.
  • En un Proc.new proc, la declaración de return es un poco más sorprendente: ¡devuelve el control no solo del proceso, sino también del método que incluye el proceso!

Aquí está el return de proc creado en lambda en acción. Se comporta de una manera que probablemente esperas:

def whowouldwin mylambda = lambda {return "Freddy"} mylambda.call # mylambda gets called and returns "Freddy", and execution # continues on the next line return "Jason" end whowouldwin #=> "Jason"

Ahora aquí está el Proc.new un Proc.new proc haciendo lo mismo. Estás a punto de ver uno de esos casos en los que Ruby rompe el tan aclamado principio de la menor sorpresa:

def whowouldwin2 myproc = Proc.new {return "Freddy"} myproc.call # myproc gets called and returns "Freddy", # but also returns control from whowhouldwin2! # The line below *never* gets executed. return "Jason" end whowouldwin2 #=> "Freddy"

Gracias a este sorprendente comportamiento (y menos escritura), tiendo a preferir el uso de lambda en lugar de Proc.new cuando se realizan procs.


Para profundizar en la respuesta de Accordion Guy:

Observe que Proc.new crea un proc out al pasar un bloque. Creo que lambda {...} se analiza como una especie de literal, en lugar de una llamada de método que pasa un bloque. return desde dentro de un bloque adjunto a una llamada de método regresará del método, no del bloque, y el caso Proc.new es un ejemplo de esto en juego.

(Esto es 1.8. No sé cómo se traduce a 1.9.)


Para proporcionar más aclaraciones:

Joey dice que el comportamiento de retorno de Proc.new es sorprendente. Sin embargo, cuando consideras que Proc.new se comporta como un bloque, esto no es sorprendente, ya que es exactamente cómo se comportan los bloques. Las lambas por otro lado se comportan más como métodos.

Esto realmente explica por qué los Procs son flexibles cuando se trata de arity (número de argumentos), mientras que las lambdas no lo son. Los bloques no requieren que se proporcionen todos sus argumentos, pero sí los métodos (a menos que se proporcione un valor predeterminado). Mientras que proporcionar el argumento lambda por defecto no es una opción en Ruby 1.8, ahora se admite en Ruby 1.9 con la sintaxis lambda alternativa (como lo indica el webmat):

concat = ->(a, b=2){ "#{a}#{b}" } concat.call(4,5) # => "45" concat.call(1) # => "12"

Y Michiel de Mare (el OP) es incorrecto acerca de que Procs y lambda se comportan igual con Arity en Ruby 1.9. He verificado que aún mantienen el comportamiento desde 1.8 como se especifica anteriormente.

break declaraciones de break realidad no tienen mucho sentido, ya sea en Procs o en lambdas. En Procs, el descanso lo devolvería de Proc.nuevo que ya se ha completado. Y no tiene ningún sentido romper con un lambda ya que es esencialmente un método, y nunca se rompería desde el nivel superior de un método.

next , redo y raise comportan de la misma manera tanto en Procs como en lambdas. Mientras que retry no está permitido en ninguno de los dos y generará una excepción.

Y, finalmente, el método proc nunca debe usarse, ya que es inconsistente y tiene un comportamiento inesperado. En Ruby 1.8 en realidad devuelve un lambda! En Ruby 1.9 esto ha sido arreglado y devuelve un Proc. Si quieres crear un Proc, quédate con Proc.new .

Para más información, recomiendo encarecidamente el lenguaje de programación de Ruby de O''Reilly, que es mi fuente para la mayor parte de esta información.


Proc es más viejo, pero la semántica del retorno es muy contradictoria para mí (al menos cuando estaba aprendiendo el idioma) porque:

  1. Si está utilizando proc, lo más probable es que esté usando algún tipo de paradigma funcional.
  2. Proc puede regresar al ámbito de aplicación (ver las respuestas anteriores), que es básicamente un goto, y de naturaleza altamente no funcional.

Lambda es funcionalmente más seguro y más fácil de razonar: siempre lo uso en lugar de proc.


Respuesta corta: lo que importa es lo que hace el return : lambda retorna fuera de sí mismo, y proc vuelve fuera de sí mismo Y la función que lo llamó.

Lo que está menos claro es por qué quieres usar cada uno. lambda es lo que esperamos que las cosas deberían hacer en un sentido de programación funcional. Es básicamente un método anónimo con el alcance actual automáticamente vinculado. De los dos, lambda es la que probablemente deberías usar.

Proc, por otro lado, es realmente útil para implementar el lenguaje en sí. Por ejemplo, puede implementar sentencias "if" o bucles "for" con ellos. Cualquier devolución encontrada en el proceso se devolverá fuera del método que la llamó, no solo la declaración "if". Así es como funcionan los lenguajes, cómo funcionan las afirmaciones "si", así que supongo que Ruby usa esto debajo de las portadas y lo expusieron porque parecía poderoso.

Solo necesitaría esto realmente si está creando nuevas construcciones de lenguaje como bucles, construcciones if-else, etc.


Una buena manera de verlo es que las lambdas se ejecutan en su propio alcance (como si fuera una llamada de método), mientras que Procs puede verse como ejecutado en línea con el método de llamada, al menos esa es una buena forma de decidir cuál usar. en cada caso.


Vale la pena enfatizar que el return en un proceso se realiza desde el método de encierro léxico, es decir, el método en el que se creó el proceso , no el método que llamó al proceso. Esta es una consecuencia de la propiedad de cierre de procs. Entonces el siguiente código no produce nada:

def foo proc = Proc.new{return} foobar(proc) puts ''foo'' end def foobar(proc) proc.call puts ''foobar'' end foo

Aunque el proceso se ejecuta en foobar , se creó en foo y, por lo tanto, el return sale de foo , no solo de foobar . Como Charles Caldwell escribió anteriormente, tiene una sensación de GOTO. En mi opinión, el return está bien en un bloque que se ejecuta en su contexto léxico, pero es mucho menos intuitivo cuando se usa en un proceso que se ejecuta en un contexto diferente.


Closures in Ruby es un buen resumen de cómo funcionan los bloques, lambda y proc en Ruby, con Ruby.