Cuándo usar lambda, cuándo usar Proc.new?

votos
316

En Ruby 1.8, hay diferencias sutiles entre proc / lambda por un lado y Proc.newpor 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?
Publicado el 03/08/2008 a las 07:40
fuente por usuario
En otros idiomas...                            


14 respuestas

votos
364

Otra diferencia importante pero sutil entre los procesos creados con lambday los procesos creados con Proc.newes cómo manejan la returninstrucción:

  • En un proceso lambdacreado, la returndeclaración regresa solo desde el proceso en sí
  • En un proceso Proc.newcreado, la returnafirmación es un poco más sorprendente: devuelve el control no solo del proceso, sino también del método que encierra el proceso.

Aquí está el proceso lambdacreado returnen 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í hay un proceso Proc.newcreado returnhaciendo lo mismo. Estás a punto de ver uno de esos casos en los que Ruby rompe el tan cacareado principio de la mínima 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 a menos tipeo), tiendo a favorecer el uso lambdaexcesivo Proc.newal hacer procesos.

Respondida el 03/08/2008 a las 16:21
fuente por usuario

votos
93

Para proporcionar más aclaraciones:

Joey dice que el comportamiento de retorno Proc.newes sorprendente. Sin embargo, cuando se considera que Proc.new se comporta como un bloque, no es sorprendente, ya que es exactamente cómo se comportan los bloques. 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 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). Si bien proporcionar el argumento lambda por defecto no es una opción en Ruby 1.8, ahora es compatible con Ruby 1.9 con la sintaxis alternativa de lambda (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 acerca de los Procs y lambda se comporta de la misma manera con arity en Ruby 1.9. He verificado que todavía mantienen el comportamiento de 1.8 como se especifica arriba.

breaklas declaraciones realmente no tienen mucho sentido en Procs o lambdas. En Procs, el salto lo devolverá de Proc.new que ya se ha completado. Y no tiene sentido romper con una lambda, ya que es esencialmente un método, y nunca se rompería desde el nivel superior de un método.

next, redoY raisese comportan de la misma en los dos procs y lambdas. Mientras retryque no está permitido en ninguno de los dos y generará una excepción.

Y, por último, el procmétodo nunca debe usarse ya que es inconsistente y tiene un comportamiento inesperado. ¡En Ruby 1.8 realmente devuelve una lambda! En Ruby 1.9 esto se ha corregido y devuelve un Proc. Si desea crear un Proc, quédese con Proc.new.

Para obtener más información, recomiendo The Ruby Programming Language de O'Reilly, que es mi fuente de la mayor parte de esta información.

Respondida el 04/10/2009 a las 06:23
fuente por usuario

votos
41

Encontré esta página que muestra la diferencia entre Proc.newy lambda. De acuerdo con la página, la única diferencia es que una lambda es estricta sobre la cantidad de argumentos que acepta, mientras que Proc.newconvierte los argumentos faltantes en nil. Aquí hay una sesión IRB de ejemplo que ilustra la diferencia:

irb (principal): 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 "hola", "mundo"
=> "helloworld"
irb (principal): 004: 0> p.call "hola", "mundo"
=> "helloworld"
irb (principal): 005: 0> l.call "hola"
ArgumentError: número incorrecto de argumentos (1 por 2)
    desde (irb): 1
    desde (irb): 5: en `llamar '
    desde (irb): 5
    de: 0
irb (principal): 006: 0> p.call "hola"
TypeError: no puede convertir nil en String
    desde (irb): 2: en `+ '
    desde (irb): 2
    desde (irb): 6: en `llamar '
    desde (irb): 6
    de: 0

La página también recomienda el uso de 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, aún no he investigado 1.9, pero no creo que lo hayan cambiado demasiado (aunque no crean lo que piensan, parece que han oído hablar de algunos cambios, Probablemente estoy equivocado allí).

Respondida el 03/08/2008 a las 08:28
fuente por usuario

votos
14

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

  1. Si está utilizando proc, lo más probable es que esté utilizando algún tipo de paradigma funcional.
  2. Proc puede regresar fuera del ámbito adjunto (ver respuestas anteriores), que es básicamente una característica de gran desorganización.

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

Respondida el 11/09/2008 a las 00:32
fuente por usuario

votos
11

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 el stabby lambdas en 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 admite 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 construir Ruby1.9 para Leopard o Linux, mira este artículo (autopromoción desvergonzada).

Respondida el 19/11/2008 a las 22:28
fuente por usuario

votos
10

Respuesta corta: Lo que importa es lo que returnhace: vuelve lambda fuera de sí, y proc retornos fuera de sí mismo y la función que la llamó.

Lo que está menos claro es por qué desea utilizar cada uno. lambda es lo que esperamos que las cosas deben hacer en un sentido programación funcional. Se trata básicamente de un método anónimo con el alcance actual obligado automáticamente. De los dos, lambda es el que probablemente debería utilizar.

Proc, por el contrario, es muy útil para la implementación de la propia lengua. Por ejemplo, puede aplicar sentencias "if" o "para" lazos con ellos. Cualquier devolución que se encuentra en el proc volverá a cabo del método que lo llamó, no sólo el del "if". Se trata de cómo funcionan las lenguas, la forma "si" declaraciones trabajan, así que yo creo Rubí utiliza esta bajo las sábanas y que acaba de exponerse porque parecía poderoso.

Usted sólo necesita realmente esto si va a crear nuevas construcciones del lenguaje como bucles, if-else construcciones, etc.

Respondida el 06/10/2011 a las 19:33
fuente por usuario

votos
9

Una buena forma de verlo es que las lambdas se ejecutan en su propio ámbito (como si fuera una llamada de método), mientras que las de Proc se pueden ver como ejecutadas en línea con el método de llamada, al menos esa es una buena forma de decidir cuál usar en cada caso.

Respondida el 09/12/2008 a las 15:17
fuente por usuario

votos
8

No noté ningún comentario sobre el tercer método en el queston, "proc", que está en desuso, pero manejado 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
Respondida el 25/06/2009 a las 12:22
fuente por usuario

votos
7

Closures in Ruby es una buena descripción de cómo funcionan los bloques, lambda y proc en Ruby, con Ruby.

Respondida el 28/08/2008 a las 14:50
fuente por usuario

votos
6

La comprensión de Rubí Bloques, Proc y Lambdas por Robert Sosinski explica claramente estos conceptos de programación y refuerza las explicaciones con ejemplos de código. Método objetos están relacionados y cubiertos también.

Respondida el 23/08/2013 a las 18:07
fuente por usuario

votos
5

lambda funciona como se espera, como en otros idiomas.

El cable Proc.newes sorprendente y confuso.

La returndeclaración en proc creado por Proc.newno sólo devolverá el control sólo de sí mismo, sino también del método encerrándolo .

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

Se puede argumentar que Proc.newinserta el código en el método de cerramiento, al igual que el bloque. Pero Proc.newcrea un objeto, mientras que el bloque son parte de un objeto.

Y hay otra diferencia entre lambda y Proc.new, lo que es su manejo de argumentos (equivocadas). lambda se queja de ello, mientras que Proc.newignora los argumentos adicionales o considera la ausencia de argumentos como 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, procen Ruby 1.8 crea una lambda, mientras que en Ruby 1.9+ comporta como Proc.new, lo que es muy confuso.

Respondida el 29/10/2014 a las 16:22
fuente por usuario

votos
3

Para profundizar en la respuesta del chico acordeón:

Tenga en cuenta que Proc.newcrea un proceso al pasar un bloque. Creo que lambda {...}se analiza como una especie de literal, en lugar de una llamada al método que pasa un bloque. returnDesde el interior de un bloque conectado a un método, la llamada volverá desde el método, no desde el bloque, y el Proc.newcaso es un ejemplo de esto en juego.

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

Respondida el 07/09/2008 a las 03:31
fuente por usuario

votos
2

Estoy un poco tarde en esto, pero hay una gran pero poco que se sabe de Proc.newque no se menciona en el comentario. Como por la documentación :

Proc::newpuede ser llamado sin un bloque sólo dentro de un método con un bloque adjunto, en el que caso de que el bloque se convierte en elProc objeto.

Dicho esto, Proc.newvamos a métodos cadena obteniéndose:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Respondida el 28/04/2015 a las 13:15
fuente por usuario

votos
1

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

Respondida el 11/08/2008 a las 03:09
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more