E quando você usaria um e não o outro?
E quando você usaria um e não o outro?
Respostas:
Uma diferença está na maneira como eles lidam com argumentos. Criando um proc usando proc {}
e Proc.new {}
é equivalente. No entanto, usar lambda {}
fornece um processo que verifica o número de argumentos passados para ele. De ri Kernel#lambda
:
Equivalente a Proc.new , exceto os objetos Proc resultantes, verifica o número de parâmetros passados quando chamados.
Um exemplo:
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)
Além disso, como Ken aponta, usar return
dentro de um lambda retorna o valor desse lambda, mas usar return
em proc proc retorna do bloco anexo.
lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return
Portanto, para os usos mais rápidos, eles são os mesmos, mas se você desejar a verificação automática de argumentos estritos (que às vezes também pode ajudar na depuração), ou se precisar usar a return
instrução para retornar o valor do proc, use lambda
.
A diferença real entre procs e lambdas tem tudo a ver com palavras-chave de fluxo de controle. Estou a falar de return
, raise
, break
, redo
, retry
etc. - essas palavras de controle. Digamos que você tenha uma declaração de retorno em um proc. Quando você chama seu proc, ele não apenas o despeja, mas também retorna do método anexo, por exemplo:
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
A final puts
do método nunca foi executada, pois quando chamamos nosso proc, a parte return
interna dele nos expulsou do método. Se, no entanto, convertermos nosso proc em um lambda, obteremos o seguinte:
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
O retorno dentro do lambda apenas nos despeja do próprio lambda e o método envolvente continua em execução. A maneira como as palavras-chave do fluxo de controle são tratadas nos procs e lambdas é a principal diferença entre elas
Existem apenas duas diferenças principais.
lambda
verifica o número de argumentos passados, enquanto a proc
não. Isso significa que a lambda
gerará um erro se você passar o número errado de argumentos, enquanto a proc
ignorará argumentos inesperados e os atribuirá nil
a qualquer um que estiver faltando.lambda
retorna, ele passa o controle de volta ao método de chamada; quando proc
retorna, o faz imediatamente, sem voltar ao método de chamada.Para ver como isso funciona, dê uma olhada no código abaixo. Nosso primeiro método chama a proc
; a segunda chama 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!"
Veja como proc
diz "Batman vai vencer!", Isso ocorre porque ele retorna imediatamente, sem voltar ao método batman_ironman_proc.
lambda
No entanto, nosso método retorna ao método após ser chamado, portanto, o método retorna o último código que avalia: "O Homem de Ferro vencerá!"
# Proc Exemplos
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
# Lambda Examples
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)
lam = lambda { puts "Hello World" }
lam.call
Diferenças entre Procs e Lambdas
Antes de abordar as diferenças entre procs e lambdas, é importante mencionar que ambos são objetos Proc.
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'
No entanto, lambdas são um 'sabor' diferente de procs. Essa pequena diferença é mostrada ao retornar os objetos.
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
1. Lambdas verifica o número de argumentos, enquanto procs não
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)
Por outro lado, os procs não se importam se recebem o número errado 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 e procs tratam a palavra-chave 'return' de maneira diferente
'return' dentro de um lambda aciona o código fora do 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 um proc aciona o código fora do método em que o proc está sendo executado
def proc_test
proc = Proc.new { return }
proc.call
puts "Hello world"
end
proc_test # calling proc_test prints nothing
E para responder sua outra pergunta, qual usar e quando? Vou seguir @jtbandes como ele mencionou
Portanto, para os usos mais rápidos, eles são os mesmos, mas se você desejar a verificação automática de argumentos estritos (que às vezes também pode ajudar na depuração), ou se precisar usar a instrução de retorno para retornar o valor do proc, use lambda.
Originalmente publicado aqui
De um modo geral, as lambdas são mais intuitivas que os procs porque são mais semelhantes aos métodos. Eles são bastante rígidos quanto à aridade e simplesmente saem quando você chama de retorno. Por esse motivo, muitos rubiistas usam lambdas como primeira opção, a menos que precisem dos recursos específicos de procs.
Procs: Objetos de classe Proc
. Como blocos, eles são avaliados no escopo em que são definidos.
Lambdas: também objetos de classe, Proc
mas sutilmente diferentes dos procs regulares. São fechamentos como blocos e procs e, como tal, são avaliados no escopo em que são definidos.
Criando Proc
a = Proc.new { |x| x 2 }
Criando lambda
b = lambda { |x| x 2
}
a = proc { |x| x 2 }
é o mesmo quea = Proc.new { |x| x 2 }
Aqui está outra maneira de entender isso.
Um bloco é um pedaço de código anexado à chamada para uma chamada de um método em um objeto. No exemplo abaixo, self é uma instância de uma classe anônima herdada de ActionView :: Base na estrutura do Rails (que inclui muitos módulos auxiliares). cartão é um método que chamamos de auto. Passamos um argumento para o método e sempre anexamos o bloco ao final da invocação do método:
self.card :contacts do |c|
// a chunk of valid ruby code
end
Ok, estamos passando um pedaço de código para um método. Mas como usamos esse bloco? Uma opção é converter o pedaço de código em um objeto. Ruby oferece três maneiras de converter um pedaço de código em um 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
No método acima, o & converte o bloco passado para o método em um objeto e armazena esse objeto no bloco variável local. De fato, podemos mostrar que ele tem o mesmo comportamento que o lambda e o Proc.new:
def add(&block)
block
end
l3 = add { |a| a + 1 }
l3.call(1)
=> 2
Isso é importante. Quando você passa um bloco para um método e o converte usando &, o objeto que ele cria usa Proc.new para fazer a conversão.
Observe que eu evitei o uso de "proc" como uma opção. Isso porque Ruby 1.8, é o mesmo que lambda e no Ruby 1.9, é o mesmo que Proc.new e em todas as versões do Ruby deve ser evitado.
Então você pergunta qual é a diferença entre lambda e Proc.new?
Primeiro, em termos de passagem de parâmetro, o lambda se comporta como uma chamada de método. Irá gerar uma exceção se você passar o número errado de argumentos. Por outro lado, o Proc.new se comporta como atribuição paralela. Todos os argumentos não utilizados são convertidos em zero:
> 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 +
Segundo, lambda e Proc.new tratam a palavra-chave return de maneira diferente. Quando você faz um retorno dentro do Proc.new, ele realmente retorna do método anexo, ou seja, do contexto circundante. Quando você retorna de um bloco lambda, ele apenas retorna do bloco, não do método envolvente. Basicamente, ele sai da chamada para o bloco e continua a execução com o restante do método anexo.
> 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"
Então, por que essa diferença comportamental? O motivo é que, com o Proc.new, podemos usar iteradores dentro do contexto de métodos anexos e tirar conclusões lógicas. Veja este exemplo:
> 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, quando invocarmos o retorno dentro do iterador, ele retornará do método envolvente. Lembre-se de que os blocos passados para os iteradores são convertidos em objetos usando Proc.new e é por isso que quando usamos return, ele sai do método envolvente.
Você pode pensar em lambdas como métodos anônimos, eles isolam blocos individuais de código em um objeto que pode ser tratado como um método. Por fim, pense em um lambda se comportando como um método anômalo e Proc.novo se comportando como código embutido.
Um post útil sobre guias de rubi: blocos, procs e lambdas
Os procs retornam do método atual, enquanto os lambdas retornam do próprio lambda.
Os procs não se importam com o número correto de argumentos, enquanto os lambdas geram uma exceção.
return
retorno da declaração emproc
versuslambda
.