¿Cuál es la diferencia entre un proceso y una lambda en Ruby?


Respuestas:


260

Una diferencia está en la forma en que manejan los argumentos. Crear un proceso usando proc {}y Proc.new {}son equivalentes. Sin embargo, el uso lambda {}le proporciona un proceso que verifica el número de argumentos que se le pasan. De ri Kernel#lambda:

Equivalente a Proc.new , excepto que los objetos Proc resultantes verifican el número de parámetros pasados ​​cuando se llama.

Un ejemplo:

p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)

Además, como señala Ken, usar returndentro de un lambda devuelve el valor de ese lambda, pero usarlo returnen un proceso regresa del bloque que lo encierra.

lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return

Por lo tanto, para la mayoría de los usos rápidos son los mismos, pero si desea la comprobación automática de argumentos estrictos (que a veces también puede ayudar con la depuración), o si necesita usar la returndeclaración para devolver el valor del proceso, use lambda.


8
¿Sería exacto decir que las lambdas son muy parecidas a los métodos (comprobar los argumentos y volverá a partir de ellos) mientras que los procs son muy parecidos a los bloques (los argumentos no se verifican y una devolución volverá del método que contiene o lambda)?
pedz

He estado en Dios sabe cuántos sitios web y artículos ahora y nadie parece hablar sobre la utilidad de Procs vs. métodos vs. lambdas. Cada explicación solo proporciona un detalle desgarrador de cómo los valores de retorno, etc., son diferentes, pero ninguno sobre por qué es importante. Por ahora tengo que concluir que este es un desastre de diseño en Ruby.
ankush981

76

La verdadera diferencia entre procs y lambdas tiene mucho que ver con las palabras clave de control de flujo. Estoy hablando de return, raise, break, redo, retryetc. - esas palabras de control. Digamos que tiene una declaración de devolución en un proceso. Cuando llame a su proceso, no solo lo eliminará, sino que también regresará del método de cierre, por ejemplo:

def my_method
  puts "before proc"
  my_proc = Proc.new do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method

shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc

El final putsen el método nunca se ejecutó, ya que cuando llamamos a nuestro proceso, returnsu contenido nos arrojó fuera del método. Sin embargo, si convertimos nuestro proceso a una lambda, obtenemos lo siguiente:

def my_method
  puts "before proc"
  my_proc = lambda do
    puts "inside proc"
    return
  end
  my_proc.call
  puts "after proc"
end

my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc

El retorno dentro de la lambda solo nos elimina de la propia lambda y el método de cierre continúa ejecutándose. La forma en que las palabras clave de flujo de control se tratan dentro de los procesos y lambdas es la principal diferencia entre ellas.


7

Solo hay dos diferencias principales.

  • Primero, a lambdaverifica el número de argumentos que se le pasan, mientras procque a no. Esto significa que a lambdaarrojará un error si le pasa el número incorrecto de argumentos, mientras procque a ignorará los argumentos inesperados y los asignará nila los que faltan.
  • Segundo, cuando a lambdaregresa, devuelve el control al método de llamada; cuando a procregresa, lo hace de inmediato, sin volver al método de llamada.

Para ver cómo funciona esto, eche un vistazo al código a continuación. Nuestro primer método llama a proc; el segundo llama a lambda.

def batman_ironman_proc
  victor = Proc.new { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_proc # prints "Batman will win!"

def batman_ironman_lambda
  victor = lambda { return "Batman will win!" }
  victor.call
  "Iron Man will win!"
end

puts batman_ironman_lambda # prints "Iron Man will win!"

Vea cómo procdice "¡Batman ganará!", Esto se debe a que vuelve inmediatamente, sin volver al método batman_ironman_proc.

Nuestro lambda, sin embargo, vuelve al método después de ser llamado, por lo que el método devuelve el último código que evalúa: "¡Iron Man ganará!"


5

# Ejemplos de procesos

p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p)              # The '&' tells ruby to turn the proc into a block 

proc = Proc.new { puts "Hello World" }
proc.call

# Ejemplos Lambda

lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)

lam = lambda { puts "Hello World" }
lam.call           

Diferencias entre Procs y Lambdas

Antes de entrar en las diferencias entre procs y lambdas, es importante mencionar que ambos son objetos Proc.

proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }

proc.class # returns 'Proc'
lam.class  # returns 'Proc'

Sin embargo, las lambdas son un 'sabor' diferente de los procs. Esta ligera diferencia se muestra al devolver los objetos.

proc   # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam    # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'

1. Lambdas verifica el número de argumentos, mientras que los procs no

lam = lambda { |x| puts x }    # creates a lambda that takes 1 argument
lam.call(2)                    # prints out 2
lam.call                       # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3)                # ArgumentError: wrong number of arguments (3 for 1)

En contraste, a los procesadores no les importa si se les pasa el número incorrecto de argumentos.

proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2)                   # prints out 2
proc.call                      # returns nil
proc.call(1,2,3)               # prints out 1 and forgets about the extra arguments

2. Lambdas y procs tratan la palabra clave 'return' de manera diferente

'return' dentro de un lambda activa el código justo fuera del código lambda

def lambda_test
  lam = lambda { return }
  lam.call
  puts "Hello world"
end

lambda_test                 # calling lambda_test prints 'Hello World'

'return' dentro de un proceso activa el código fuera del método donde se ejecuta el proceso

def proc_test
  proc = Proc.new { return }
  proc.call
  puts "Hello world"
end

proc_test                 # calling proc_test prints nothing

Y para responder a su otra consulta, ¿cuál usar y cuándo? Seguiré a @jtbandes como él ha mencionado

Por lo tanto, para la mayoría de los usos rápidos son los mismos, pero si desea una verificación automática de argumentos estrictos (que a veces también puede ayudar con la depuración), o si necesita usar la instrucción return para devolver el valor del proceso, use lambda.

Publicado originalmente aquí


1

En términos generales, las lambdas son más intuitivas que los procesos porque son más similares a los métodos. Son bastante estrictos acerca de arity, y simplemente salen cuando llamas a return. Por esta razón, muchos Rubyists usan lambdas como primera opción, a menos que necesiten las características específicas de los procs.

Procs: Objetos de clase Proc. Al igual que los bloques, se evalúan en el ámbito donde se definen. Lambdas también objetos de clase Procpero sutilmente diferentes de los procesos normales. Son cierres como bloques y procesos, y como tales se evalúan en el ámbito donde se definen.

Creando Proc

a = Proc.new { |x| x 2 }

Creando lambda

b = lambda { |x| x 2 }


a = proc { |x| x 2 }es lo mismo quea = Proc.new { |x| x 2 }
lacostenycoder

1

Aquí hay otra forma de entender esto.

Un bloque es un fragmento de código adjunto a la invocación a una llamada de un método en un objeto. En el ejemplo a continuación, self es una instancia de una clase anónima que hereda de ActionView :: Base en el marco de Rails (que a su vez incluye muchos módulos auxiliares). La tarjeta es un método que llamamos a uno mismo. Pasamos un argumento al método y luego adjuntamos siempre el bloque al final de la invocación del método:

self.card :contacts do |c|
  // a chunk of valid ruby code    
end

Ok, entonces estamos pasando una porción de código a un método. Pero, ¿cómo hacemos uso de este bloque? Una opción es convertir el fragmento de código en un objeto. Ruby ofrece tres formas de convertir un fragmento de código en un objeto

# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2 

# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2 

# & as the last method argument with a local variable name
def add(&block)
end

En el método anterior, el & convierte el bloque pasado al método en un objeto y almacena ese objeto en el bloque de variable local. De hecho, podemos mostrar que tiene el mismo comportamiento que lambda y Proc.new:

def add(&block)
  block
end

l3 = add { |a| a + 1 }
l3.call(1)
=> 2

Esto es importante. Cuando pasa un bloque a un método y lo convierte usando &, el objeto que crea usa Proc.new para realizar la conversión.

Tenga en cuenta que evité el uso de "proc" como una opción. Eso es porque es Ruby 1.8, es lo mismo que lambda y en Ruby 1.9, es lo mismo que Proc.new y en todas las versiones de Ruby se debe evitar.

Entonces, ¿cuál es la diferencia entre lambda y Proc.new?

Primero, en términos de paso de parámetros, lambda se comporta como una llamada a un método. Planteará una excepción si pasa el número incorrecto de argumentos. Por el contrario, Proc.new se comporta como una asignación paralela. Todos los argumentos no utilizados se convierten en nulo:

> l = lambda {|a,b| puts "#{a} + #{b}" }
 => #<Proc:0x007fbffcb47e40@(irb):19 (lambda)> 
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)

> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21> 
> l2.call(1)
1 + 

En segundo lugar, lambda y Proc.new manejan la palabra clave return de manera diferente. Cuando realiza un retorno dentro de Proc.new, en realidad regresa del método de inclusión, es decir, el contexto circundante. Cuando regresa de un bloque lambda, simplemente regresa del bloque, no del método de cierre. Básicamente, sale de la llamada al bloque y continúa la ejecución con el resto del método de cierre.

> def add(a,b)
  l = Proc.new { return a + b}
  l.call
  puts "now exiting method"
end
> add(1,1)
=> 2  # NOTICE it never prints the message "now exiting method"

> def add(a,b)
  l = lambda { return a + b }
  l.call
  puts "now exiting method"
end
> add(1,1)
=> now exiting method  # NOTICE this time it prints the message "now exiting method"

Entonces, ¿por qué esta diferencia de comportamiento? La razón es porque con Proc.new, podemos usar iteradores dentro del contexto de los métodos de cierre y sacar conclusiones lógicas. Mira este ejemplo:

> def print(max)
  [1,2,3,4,5].each do |val|
    puts val
    return if val > max
  end
end
> print(3)
1
2
3
4

Esperamos que cuando invoquemos return dentro del iterador, regrese del método de cierre. Recuerde que los bloques pasados ​​a los iteradores se convierten en objetos usando Proc.new y es por eso que cuando usamos return, saldrá del método de cierre.

Puede pensar en lambdas como métodos anónimos, que aíslan bloques de código individuales en un objeto que se puede tratar como un método. En última instancia, piense que una lambda se comporta como un método anónimo y que Proc.new se comporta como un código en línea.


0

Una publicación útil sobre guías de rubí: bloques, procs y lambdas

Los procesos regresan del método actual, mientras que las lambdas regresan de la propia lambda.

A los procesadores no les importa la cantidad correcta de argumentos, mientras que las lambdas generarán una excepción.


-3

La diferencia entre proc y lambda es que proc es solo una copia de código con argumentos reemplazados a su vez, mientras que lambda es una función como en otros lenguajes. (comportamiento de devolución, verificación de argumentos)

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.