Cuándo usar lambda, cuándo usar Proc.¿nuevo?
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. ¿Cuál es el trato?
14 answers
Otra diferencia importante pero sutil entre procs creado con lambda
y procs creado con Proc.new
es cómo manejan la declaración return
:
- En un proc
lambda
-creado, la instrucciónreturn
devuelve solo del proc mismo - En un proc
Proc.new
-creado, la instrucciónreturn
es un poco más sorprendente: devuelve el control no solo desde el proc, sino también desde el método que encierra el proc!
Aquí está lambda
-creado proc return
en acto. 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í hay un Proc.new
-creado proc return
haciendo lo mismo. Estás a punto de ver uno de esos casos en los que Ruby rompe el tan cacareado 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 comportamiento sorprendente (así como menos tipeo), tiendo a preferir usar lambda
sobre Proc.new
al hacer procs.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-30 06:28:30
Para proporcionar más aclaraciones:
Joey dice que el comportamiento de retorno de Proc.new
es sorprendente. Sin embargo, cuando se considera 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 los lambdas no lo son. Los bloques no requieren que se proporcionen todos sus argumentos, pero sí los métodos (a menos que por defecto se proporciona). Si bien proporcionar el argumento lambda default no es una opción en Ruby 1.8, ahora está soportado en Ruby 1.9 con la sintaxis lambda alternativa (como se indica en webmat):
concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1) # => "12"
Y Michiel de Mare (el OP) es incorrecto sobre el Procs y lambda comportándose igual con arity en Ruby 1.9. He verificado que todavía mantienen el comportamiento de 1.8 como se especifica anteriormente.
break
las declaraciones en realidad no tienen mucho sentido ni en Procs ni en lambdas. En Procs, el break te devolvería de Proc.nuevo que ya se ha completado. Y no tiene ningún sentido romper con una lambda ya que es esencialmente un método, y nunca romperías con el nivel superior de un método.
next
, redo
, y raise
se comportan igual tanto en Procs como en lambdas. Considerando 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 es en realidad devuelve un lambda! En Ruby 1.9 esto ha sido arreglado y devuelve un Proc. Si desea crear un Proc, siga con Proc.new
.
Para más información, recomiendo altamente O'Reilly El Lenguaje de Programación Ruby que es mi fuente para la mayor parte de esta información.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-10-04 05:23:22
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 una lambda es estricta sobre el número de argumentos que acepta, mientras que Proc.new
convierte los argumentos que faltan a nil
. Aquí hay un ejemplo de sesión del 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 la mejor opción en la situación promedio.
En cuanto a Ruby 1.9, lo siento, todavía no he mirado en 1.9, pero no me imagino que lo cambiarían tanto (no tome mi palabra para ello, sin embargo, parece que ha oído hablar de algunos cambios, así que probablemente estoy equivocado allí).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-21 18:36:33
Proc es más antiguo, pero la semántica del retorno es altamente contraintuitiva para mí (al menos cuando estaba aprendiendo el idioma) porque:
- Si está utilizando proc, lo más probable es que esté utilizando algún tipo de paradigma funcional.
- Proc puede volver fuera del ámbito que lo encierra (ver respuestas anteriores), que es un goto básicamente, 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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-04 18:39:59
No puedo decir mucho sobre las diferencias sutiles. Sin embargo, puedo señalar que Ruby 1.9 ahora permite parámetros opcionales para lambdas y bloques.
Aquí está la nueva sintaxis para las lambdas stabby bajo 1.9:
stabby = ->(msg='inside the stabby lambda') { puts msg }
Ruby 1.8 no tenía esa sintaxis. Tampoco la forma convencional de declarar bloques/lambdas soporta args 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, soporta argumentos opcionales incluso con la sintaxis antigua:
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 construir Ruby1. 9 para Leopard o Linux, echa un vistazo este artículo (shameless self promotion).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-11-19 21:28:28
Respuesta corta: Lo que importa es lo que hace return
: lambda devuelve fuera de sí mismo, y proc devuelve fuera de sí mismo Y la función que lo llamó.
Lo que es menos claro es por qué desea utilizar cada uno. lambda es lo que esperamos que las cosas deben hacer en un sentido de programación funcional. Es básicamente un método anónimo con el ámbito actual enlazado automáticamente. De los dos, lambda es la que probablemente deberías usar.
Proc, por otro lado, es realmente útil para implementar la el lenguaje mismo. Por ejemplo, puede implementar sentencias "if" o bucles " for " con ellas. Cualquier retorno encontrado en el proc regresará del método que lo llamó, no solo de la instrucción "if". Así es como funcionan los lenguajes, cómo funcionan las declaraciones "if", así que mi conjetura es que Ruby usa esto debajo de las cubiertas y simplemente lo expusieron porque parecía poderoso.
Solo necesitará esto si está creando nuevas construcciones de lenguaje como bucles, construcciones if-else, etc.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-11 21:54:05
Una buena manera de verlo es que los lambdas se ejecutan en su propio ámbito (como si fuera una llamada a un método), mientras que los Procs se pueden ver como ejecutados en línea con el método que llama, al menos esa es una buena manera de decidir cuál usar en cada caso.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-09 14:17:51
No noté ningún comentario sobre el tercer método en queston, "proc", que está en desuso, pero se maneja de manera diferente en 1.8 y 1.9.
Aquí hay un ejemplo bastante detallado que hace que sea fácil 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-06-25 11:22:32
Closures in Ruby es un buen resumen de cómo funcionan los bloques, lambda y proc en Ruby, con Ruby.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-08 06:19:51
Understanding Ruby Blocks, Procs and Lambdas de Robert Sosinski explica claramente estos conceptos de programación y refuerza las explicaciones con código de ejemplo. Los objetos del método están relacionados y cubiertos también.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-03 19:32:14
Lambda funciona como se espera, como en otros idiomas.
El wired Proc.new
es sorprendente y confuso.
La instrucción return
en proc creada por Proc.new
no solo devolverá control solo de sí misma, sino también del método que lo encierra.
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 inclusión, al igual que block.
Pero Proc.new
crea un objeto, mientras que los bloques son parte de un objeto.
Y hay otra diferencia entre lambda y Proc.new
, que es su manejo de argumentos (erróneos).
lambda se queja de ello, mientras que Proc.new
ignora argumentos adicionales o considera la ausencia de argumentos como nil.
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 una lambda, mientras que en Ruby 1.9+ se comporta como Proc.new
, lo que es realmente confuso.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-21 14:40:45
Para elaborar la respuesta del chico del Acordeón:
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 a un método que pasa un bloque. return
ing desde dentro de un bloque unido a una llamada a un método regresará desde el método, no desde el bloque, y el caso Proc.new
es un ejemplo de esto en juego.
(Esto es 1.8. No se como esto se traduce a 1.9.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-07 02:31:40
Estoy un poco atrasado en esto, pero hay una gran pero poco conocida cosa sobre Proc.new
que no se menciona en los comentarios en absoluto. Como en la documentación :
Proc::new
se puede llamar sin un bloque solo dentro de un método con un bloque adjunto, en cuyo caso ese bloque se convierte en elProc
objeto.
Dicho esto, Proc.new
permite encadenar métodos de rendimiento:
def m1
yield 'Finally!' if block_given?
end
def m2
m1 &Proc.new
end
m2 { |e| puts e }
#⇒ Finally!
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-28 13:15:25
La diferencia de comportamiento con return
es en MI humilde opinión la diferencia más importante entre el 2. También prefiero lambda porque es menos mecanografía que Proc.nuevo :-)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-08-11 02:09:46