Veja também: o livro Ruby Programming Language, de Matz e Flanagan, abordou esse tópico de forma abrangente. proc se comporta como uma semântica de rendimento de bloco, enquanto que lambda se comporta como uma semântica de chamada de método - método. Também retornar, quebrar, et. todos se comportam com diff em procs n lambdas #
você aceitou a resposta que só diz o que é a diferença entre proc e lambda, enquanto o título da sua pergunta é quando usar essas coisas
Shri
Respostas:
378
Outra diferença importante, mas sutil, entre procs criados com lambdae procs criados com Proc.newé como eles lidam com a returninstrução:
Em um lambdaproc criado, a returninstrução retorna apenas do próprio proc
Em um Proc.newproc criado, a returninstrução é um pouco mais surpreendente: retorna o controle não apenas do proc, mas também do método que o envolve!
Aqui estão os lambdaprocs criados returnem ação. Ele se comporta de uma maneira que você provavelmente espera:
def whowouldwin
mylambda = lambda {return"Freddy"}
mylambda.call# mylambda gets called and returns "Freddy", and execution# continues on the next linereturn"Jason"end
whowouldwin#=> "Jason"
Agora, aqui está um Proc.newproc criado por returnfazer a mesma coisa. Você está prestes a ver um daqueles casos em que Ruby quebra o tão elogiado Princípio da Menor Surpresa:
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"
Graças a este comportamento surpreendente (bem como menos digitação), que tendem a favorecer a utilização de lambdamais Proc.newao fazer procs.
@mattdipasquale Nos meus testes, procage como lambdae não como Proc.newem relação às declarações de retorno. Isso significa que o documento ruby é impreciso.
Kelvin
31
@mattdipasquale Desculpe, eu estava meio certo. procage como lambdaem 1.8, mas age como Proc.newem 1.9. Veja a resposta de Peter Wagenet.
Kelvin
55
Por que esse comportamento "surpreendente"? A lambdaé um método anônimo. Como é um método, ele retorna um valor, e o método que o chamou pode fazer com ele o que quiser, incluindo ignorá-lo e retornar um valor diferente. A Procé como colar em um trecho de código. Não funciona como um método. Então, quando um retorno acontece dentro do Proc, isso é apenas parte do código do método que o chamou.
Arcolye
96
Para fornecer mais esclarecimentos:
Joey diz que o comportamento de retorno de Proc.newé surpreendente. No entanto, quando você considera que o Proc.new se comporta como um bloco, isso não é surpreendente, pois é exatamente assim que os blocos se comportam. lambas, por outro lado, se comportam mais como métodos.
Na verdade, isso explica por que os Procs são flexíveis quando se trata de aridade (número de argumentos), enquanto os lambdas não são. Os blocos não exigem que todos os seus argumentos sejam fornecidos, mas os métodos exigem (a menos que um padrão seja fornecido). Embora o fornecimento do argumento lambda padrão não seja uma opção no Ruby 1.8, agora ele é suportado no Ruby 1.9 com a sintaxe lambda alternativa (conforme observado por webmat):
E Michiel de Mare (o OP) está incorreto sobre os Procs e o lambda se comportando da mesma forma com aridade no Ruby 1.9. Eu verifiquei que eles ainda mantêm o comportamento de 1,8, conforme especificado acima.
breakNa verdade, as instruções não fazem muito sentido em Procs ou lambdas. No Procs, a interrupção retornará você do Proc.new, que já foi concluído. E não faz sentido sair de um lambda, já que é essencialmente um método, e você nunca sairia do nível superior de um método.
next, redoE raisese comportam da mesma em ambos os Procs e lambdas. Considerando que retrynão é permitido em qualquer um e irá gerar uma exceção.
E, finalmente, o procmétodo nunca deve ser usado, pois é inconsistente e tem comportamento inesperado. No Ruby 1.8, ele realmente retorna um lambda! No Ruby 1.9, isso foi corrigido e retorna um Proc. Se você deseja criar um Proc, fique com Proc.new.
Para obter mais informações, recomendo a linguagem de programação Ruby da O'Reilly, que é minha fonte para a maioria dessas informações.
"" "No entanto, quando você considera que o Proc.new se comporta como um bloco, isso não é surpreendente, pois é exatamente assim que os blocos se comportam." "" <- block faz parte de um objeto, enquanto o Proc.new cria um objeto. Tanto lambda como Proc.new criam um objeto cuja classe é Proc, por que diff?
weakish
11
A partir do Ruby 2.5, breakde Procs aumenta LocalJumpError, enquanto que breakde lambdas se comporta exatamente como return( ie , return nil).
Masa Sakano
43
Encontrei esta página que mostra qual a diferença entre Proc.newe lambdasão. De acordo com a página, a única diferença é que um lambda é rigoroso quanto ao número de argumentos que aceita, ao passo que Proc.newconverte argumentos ausentes em nil. Aqui está um exemplo de sessão do IRB que ilustra a diferença:
irb (principal): 001: 0> l = lambda {| x, y | x + y}
=> # <Proc: 0x00007fc605ec0748 @ (irb): 1>
irb (principal): 002: 0> p = Proc.novo {| x, y | x + y}
=> # <Proc: 0x00007fc605ea8698 @ (irb): 2>
irb (principal): 003: 0> l.call "olá", "mundo"
=> "helloworld"
irb (main): 004: 0> p.call "olá", "mundo"
=> "helloworld"
irb (principal): 005: 0> l.call "olá"
ArgumentError: número incorreto de argumentos (1 para 2)
from (irb): 1
from (irb): 5: em `chamada '
from (irb): 5
from: 0
irb (principal): 006: 0> p.call "olá"
TypeError: não é possível converter nada em String
from (irb): 2: em `+ '
from (irb): 2
from (irb): 6: em `chamada '
from (irb): 6
from: 0
A página também recomenda o uso de lambda, a menos que você queira especificamente o comportamento tolerante a erros. Eu concordo com este sentimento. Usar um lambda parece um pouco mais conciso e, com uma diferença tão insignificante, parece a melhor escolha na situação média.
Quanto ao Ruby 1.9, desculpe, ainda não pesquisei o 1.9, mas não imagino que eles mudariam muito (não acredite na minha palavra, parece que você já ouviu falar de algumas mudanças, então Provavelmente estou errado lá).
Parâmetros opcionais dentro da lambda eram muito necessários, fico feliz que eles tenham adicionado em 1.9. Presumo que os blocos também podem ter parâmetros opcionais também (em 1.9)?
mpd
você não está demonstrando parâmetros padrão em blocos, única lambdas
iconoclasta
11
Resposta curta: O que importa é o que returnfaz: lambda retorna de si mesmo, e proc retorna de si mesmo E a função que o chamou.
O que é menos claro é por que você deseja usar cada um. lambda é o que esperamos que as coisas façam no sentido da programação funcional. É basicamente um método anônimo com o escopo atual vinculado automaticamente. Dos dois, lambda é o que você provavelmente deveria estar usando.
Proc, por outro lado, é realmente útil para implementar a própria linguagem. Por exemplo, você pode implementar instruções "if" ou "for" loops com elas. Qualquer retorno encontrado no proc retornará do método que o chamou, não apenas a instrução "if". É assim que as linguagens funcionam, como as declarações "if" funcionam, então, meu palpite é que Ruby usa isso debaixo das cobertas e eles apenas o expuseram porque parecia poderoso.
Você realmente precisaria disso apenas se estiver criando novas construções de linguagem, como loops, construções if-else, etc.
"lambda retorna de si mesmo, e proc retorna de si mesmo E a função que o chamou" é claramente errada e um mal-entendido muito comum. Um proc é um fechamento e retorna do método que o criou. Veja minha resposta completa em outro lugar da página.
ComDubh
10
Uma boa maneira de ver isso é que lambdas são executadas em seu próprio escopo (como se fosse uma chamada de método), enquanto Procs pode ser visto como executado em linha com o método de chamada, pelo menos essa é uma boa maneira de decidir qual usar em cada caso.
Matz havia declarado que planejava preteri-lo porque era confuso ter proc e Proc.new retornando resultados diferentes. Em 1.9, eles se comportam da mesma maneira (proc é um apelido para Proc.new). eigenclass.org/hiki/Changes+in+Ruby+1.9#l47
Dave Rapin
@banister: procretornou um lambda em 1,8; agora foi corrigido para retornar um proc em 1.9 - no entanto, esta é uma mudança inusitada; Portanto, não é recomendado o uso mais #
3089
Eu acho que a picareta diz em uma nota de rodapé em algum lugar que proc é efetivamente privado ou algo assim. Não tenho o número exato da página.
precisa saber é o seguinte
7
Closures em Ruby é uma boa visão geral de como os blocos, lambda e proc funcionam em Ruby, com Ruby.
Parei de ler isso depois de ler "uma função não pode aceitar vários blocos - violando o princípio de que fechamentos podem ser passados livremente como valores". Blocos não são fechamentos. Procs são e uma função pode aceitar vários procs.
ComDubh
5
O lambda funciona como esperado, como em outros idiomas.
O fio Proc.newé surpreendente e confuso.
A returninstrução proc criada por Proc.newnão apenas retornará o controle apenas de si mesmo, mas também do método que o inclui .
def some_method
myproc =Proc.new {return"End."}
myproc.call# Any code below will not get executed!# ...end
Você pode argumentar que Proc.newinsere código no método anexo, assim como o bloco. Mas Proc.newcria um objeto, enquanto o bloco faz parte de um objeto.
E há outra diferença entre lambda e Proc.new, que é o tratamento de argumentos (errados). lambda reclama disso, enquanto Proc.newignora argumentos extras ou considera a ausência de argumentos nulos.
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.callArgumentError: wrong number of arguments (0for1)
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,2ArgumentError: wrong number of arguments (2for1)
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"
BTW, procno Ruby 1.8 cria um lambda, enquanto no Ruby 1.9+ se comporta como Proc.new, o que é realmente confuso.
Observe que Proc.newcria um processo ao receber um bloco. Eu acredito que lambda {...}é analisado como uma espécie de literal, em vez de uma chamada de método que passa um bloco. returnA entrada de dentro de um bloco anexado a uma chamada de método retornará do método, não do bloco, e o Proc.newcaso é um exemplo disso em jogo.
Interessante, ele faz o mesmo que declarar um &blockargumento no def, mas sem ter que fazer isso na lista de argumentos.
Jrochkind
2
Vale ressaltar que returnem um proc retorna do método lexicamente envolvente, ou seja, o método em que o proc foi criado , não o método que chamou o proc. Isso é uma consequência da propriedade de fechamento de procs. Portanto, o código a seguir não gera nada:
Embora o proc seja executado foobar, ele foi criado fooe, portanto, as returnsaídas foo, não apenas foobar. Como Charles Caldwell escreveu acima, ele tem uma sensação GOTO. Na minha opinião, returné bom em um bloco que é executado em seu contexto lexical, mas é muito menos intuitivo quando usado em um proc que é executado em um contexto diferente.
A diferença de comportamento com returnIMHO é a diferença mais importante entre os 2. Eu também prefiro lambda porque é menos digitado que Proc.novo :-)
Para atualizar: procs agora pode ser criado usando proc {}. Não tenho certeza de quando isso entrou em vigor, mas é (um pouco) mais fácil do que precisar digitar Proc.new.
Respostas:
Outra diferença importante, mas sutil, entre procs criados com
lambda
e procs criados comProc.new
é como eles lidam com areturn
instrução:lambda
proc criado, areturn
instrução retorna apenas do próprio procProc.new
proc criado, areturn
instrução é um pouco mais surpreendente: retorna o controle não apenas do proc, mas também do método que o envolve!Aqui estão os
lambda
procs criadosreturn
em ação. Ele se comporta de uma maneira que você provavelmente espera:Agora, aqui está um
Proc.new
proc criado porreturn
fazer a mesma coisa. Você está prestes a ver um daqueles casos em que Ruby quebra o tão elogiado Princípio da Menor Surpresa:Graças a este comportamento surpreendente (bem como menos digitação), que tendem a favorecer a utilização de
lambda
maisProc.new
ao fazer procs.fonte
proc
método É apenas uma abreviação paraProc.new
?proc
é equivalente aProc.new
proc
age comolambda
e não comoProc.new
em relação às declarações de retorno. Isso significa que o documento ruby é impreciso.proc
age comolambda
em 1.8, mas age comoProc.new
em 1.9. Veja a resposta de Peter Wagenet.lambda
é um método anônimo. Como é um método, ele retorna um valor, e o método que o chamou pode fazer com ele o que quiser, incluindo ignorá-lo e retornar um valor diferente. AProc
é como colar em um trecho de código. Não funciona como um método. Então, quando um retorno acontece dentro doProc
, isso é apenas parte do código do método que o chamou.Para fornecer mais esclarecimentos:
Joey diz que o comportamento de retorno de
Proc.new
é surpreendente. No entanto, quando você considera que o Proc.new se comporta como um bloco, isso não é surpreendente, pois é exatamente assim que os blocos se comportam. lambas, por outro lado, se comportam mais como métodos.Na verdade, isso explica por que os Procs são flexíveis quando se trata de aridade (número de argumentos), enquanto os lambdas não são. Os blocos não exigem que todos os seus argumentos sejam fornecidos, mas os métodos exigem (a menos que um padrão seja fornecido). Embora o fornecimento do argumento lambda padrão não seja uma opção no Ruby 1.8, agora ele é suportado no Ruby 1.9 com a sintaxe lambda alternativa (conforme observado por webmat):
E Michiel de Mare (o OP) está incorreto sobre os Procs e o lambda se comportando da mesma forma com aridade no Ruby 1.9. Eu verifiquei que eles ainda mantêm o comportamento de 1,8, conforme especificado acima.
break
Na verdade, as instruções não fazem muito sentido em Procs ou lambdas. No Procs, a interrupção retornará você do Proc.new, que já foi concluído. E não faz sentido sair de um lambda, já que é essencialmente um método, e você nunca sairia do nível superior de um método.next
,redo
Eraise
se comportam da mesma em ambos os Procs e lambdas. Considerando queretry
não é permitido em qualquer um e irá gerar uma exceção.E, finalmente, o
proc
método nunca deve ser usado, pois é inconsistente e tem comportamento inesperado. No Ruby 1.8, ele realmente retorna um lambda! No Ruby 1.9, isso foi corrigido e retorna um Proc. Se você deseja criar um Proc, fique comProc.new
.Para obter mais informações, recomendo a linguagem de programação Ruby da O'Reilly, que é minha fonte para a maioria dessas informações.
fonte
break
de Procs aumentaLocalJumpError
, enquanto quebreak
de lambdas se comporta exatamente comoreturn
( ie ,return nil
).Encontrei esta página que mostra qual a diferença entre
Proc.new
elambda
são. De acordo com a página, a única diferença é que um lambda é rigoroso quanto ao número de argumentos que aceita, ao passo queProc.new
converte argumentos ausentes emnil
. Aqui está um exemplo de sessão do IRB que ilustra a diferença:A página também recomenda o uso de lambda, a menos que você queira especificamente o comportamento tolerante a erros. Eu concordo com este sentimento. Usar um lambda parece um pouco mais conciso e, com uma diferença tão insignificante, parece a melhor escolha na situação média.
Quanto ao Ruby 1.9, desculpe, ainda não pesquisei o 1.9, mas não imagino que eles mudariam muito (não acredite na minha palavra, parece que você já ouviu falar de algumas mudanças, então Provavelmente estou errado lá).
fonte
Proc é mais antigo, mas a semântica do retorno é altamente contra-intuitiva para mim (pelo menos quando eu estava aprendendo o idioma) porque:
O Lambda é funcionalmente mais seguro e fácil de raciocinar - eu sempre o uso em vez de proc.
fonte
Não posso dizer muito sobre as diferenças sutis. No entanto, posso salientar que o Ruby 1.9 agora permite parâmetros opcionais para lambdas e blocos.
Aqui está a nova sintaxe para as lambdas stabby sob 1.9:
O Ruby 1.8 não tinha essa sintaxe. A maneira convencional de declarar blocos / lambdas não suportava argumentos opcionais:
O Ruby 1.9, no entanto, suporta argumentos opcionais, mesmo com a sintaxe antiga:
Se você deseja construir o Ruby1.9 para Leopard ou Linux, confira este artigo (autopromoção sem vergonha).
fonte
Resposta curta: O que importa é o que
return
faz: lambda retorna de si mesmo, e proc retorna de si mesmo E a função que o chamou.O que é menos claro é por que você deseja usar cada um. lambda é o que esperamos que as coisas façam no sentido da programação funcional. É basicamente um método anônimo com o escopo atual vinculado automaticamente. Dos dois, lambda é o que você provavelmente deveria estar usando.
Proc, por outro lado, é realmente útil para implementar a própria linguagem. Por exemplo, você pode implementar instruções "if" ou "for" loops com elas. Qualquer retorno encontrado no proc retornará do método que o chamou, não apenas a instrução "if". É assim que as linguagens funcionam, como as declarações "if" funcionam, então, meu palpite é que Ruby usa isso debaixo das cobertas e eles apenas o expuseram porque parecia poderoso.
Você realmente precisaria disso apenas se estiver criando novas construções de linguagem, como loops, construções if-else, etc.
fonte
Uma boa maneira de ver isso é que lambdas são executadas em seu próprio escopo (como se fosse uma chamada de método), enquanto Procs pode ser visto como executado em linha com o método de chamada, pelo menos essa é uma boa maneira de decidir qual usar em cada caso.
fonte
Não notei nenhum comentário sobre o terceiro método na queston, "proc", que foi descontinuado, mas tratado de maneira diferente em 1.8 e 1.9.
Aqui está um exemplo bastante detalhado que facilita a visualização das diferenças entre as três chamadas semelhantes:
fonte
proc
retornou um lambda em 1,8; agora foi corrigido para retornar um proc em 1.9 - no entanto, esta é uma mudança inusitada; Portanto, não é recomendado o uso mais #Closures em Ruby é uma boa visão geral de como os blocos, lambda e proc funcionam em Ruby, com Ruby.
fonte
O lambda funciona como esperado, como em outros idiomas.
O fio
Proc.new
é surpreendente e confuso.A
return
instrução proc criada porProc.new
não apenas retornará o controle apenas de si mesmo, mas também do método que o inclui .Você pode argumentar que
Proc.new
insere código no método anexo, assim como o bloco. MasProc.new
cria um objeto, enquanto o bloco faz parte de um objeto.E há outra diferença entre lambda e
Proc.new
, que é o tratamento de argumentos (errados). lambda reclama disso, enquantoProc.new
ignora argumentos extras ou considera a ausência de argumentos nulos.BTW,
proc
no Ruby 1.8 cria um lambda, enquanto no Ruby 1.9+ se comporta comoProc.new
, o que é realmente confuso.fonte
Para elaborar a resposta do Acordeão Guy:
Observe que
Proc.new
cria um processo ao receber um bloco. Eu acredito quelambda {...}
é analisado como uma espécie de literal, em vez de uma chamada de método que passa um bloco.return
A entrada de dentro de um bloco anexado a uma chamada de método retornará do método, não do bloco, e oProc.new
caso é um exemplo disso em jogo.(Isso é 1.8. Não sei como isso se traduz em 1.9.)
fonte
Estou um pouco atrasado nisso, mas há uma coisa ótima, mas pouco conhecida, sobre
Proc.new
não mencionar nos comentários. Como na documentação :Dito isto,
Proc.new
vamos encadear métodos de produção:fonte
&block
argumento nodef
, mas sem ter que fazer isso na lista de argumentos.Vale ressaltar que
return
em um proc retorna do método lexicamente envolvente, ou seja, o método em que o proc foi criado , não o método que chamou o proc. Isso é uma consequência da propriedade de fechamento de procs. Portanto, o código a seguir não gera nada:Embora o proc seja executado
foobar
, ele foi criadofoo
e, portanto, asreturn
saídasfoo
, não apenasfoobar
. Como Charles Caldwell escreveu acima, ele tem uma sensação GOTO. Na minha opinião,return
é bom em um bloco que é executado em seu contexto lexical, mas é muito menos intuitivo quando usado em um proc que é executado em um contexto diferente.fonte
A diferença de comportamento com
return
IMHO é a diferença mais importante entre os 2. Eu também prefiro lambda porque é menos digitado que Proc.novo :-)fonte
proc {}
. Não tenho certeza de quando isso entrou em vigor, mas é (um pouco) mais fácil do que precisar digitar Proc.new.