Pergunta

No Ruby 1.8, existem diferenças sutis entre proc/lambda por um lado, e Proc.new no outro.

  • Quais são essas diferenças?
  • Você pode dar orientações sobre como decidir qual escolher?
  • No Ruby 1.9, proc e lambda são diferentes.Qual é o problema?
Foi útil?

Solução

Outra diferença importante, mas sutil, entre procs criados com lambda e procs criados com Proc.new é como eles lidam com o return declaração:

  • Em um lambda-proc criado, o return instrução retorna apenas do próprio proc
  • Em um Proc.new-proc criado, o return declaração é um pouco mais surpreendente:ele retorna o controle não apenas do proc, mas também do método que envolve o proc!

Aqui está lambda-procs criados return em ação.Ele se comporta da 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 line

  return "Jason"

end


whowouldwin
#=> "Jason"

Agora aqui está um Proc.new-procs criados return fazendo a mesma coisa.Você está prestes a ver um daqueles casos em que Ruby quebra o tão alardeado 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 esse comportamento surpreendente (além de menos digitação), tendo a preferir o uso lambda sobre Proc.new ao fazer procs.

Outras dicas

Para fornecer mais esclarecimentos:

Joey diz que o comportamento de retorno de Proc.new é surpreendente.No entanto, quando você considera que 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.Os blocos não exigem que todos os seus argumentos sejam fornecidos, mas os métodos sim (a menos que um padrão seja fornecido).Embora fornecer argumento lambda padrão não seja uma opção no Ruby 1.8, agora é suportado no Ruby 1.9 com a sintaxe lambda alternativa (conforme observado pelo webmat):

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

E Michiel de Mare (o OP) está incorreto sobre o Procs e o lambda se comportando da mesma forma com aridade no Ruby 1.9.Verifiquei que eles ainda mantêm o comportamento do 1.8 conforme especificado acima.

break Na verdade, as declarações não fazem muito sentido em Procs ou lambdas.No Procs, o intervalo retornaria do Proc.new que já foi concluído.E não faz sentido romper com um lambda, já que é essencialmente um método, e você nunca romperia com o nível superior de um método.

next, redo, e raise se comporta da mesma forma em Procs e lambdas.Enquanto retry também não é permitido e gerará uma exceção.

E finalmente, o proc O método nunca deve ser usado, pois é inconsistente e tem comportamento inesperado.No Ruby 1.8 ele retorna um lambda!No Ruby 1.9 isso foi corrigido e retorna um Proc.Se você deseja criar um Proc, continue com Proc.new.

Para mais informações, recomendo fortemente O'Reilly's A linguagem de programação Ruby que é minha fonte para a maior parte dessas informações.

eu encontrei esta página que mostra qual é a diferença entre Proc.new e lambda são.De acordo com a página, a única diferença é que um lambda é rigoroso quanto ao número de argumentos que aceita, enquanto Proc.new converte argumentos ausentes em nil.Aqui está um exemplo de sessão IRB que ilustra a diferença:

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

A página também recomenda o uso de lambda, a menos que você queira especificamente o comportamento tolerante a erros.Eu concordo com esse 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 olhei para o 1.9, mas não imagino que eles mudariam tanto (não acredite apenas na minha palavra, parece que você já ouviu falar de algumas mudanças, então Provavelmente estou errado aí).

Proc é mais antigo, mas a semântica de return é altamente contra-intuitiva para mim (pelo menos quando eu estava aprendendo o idioma) porque:

  1. Se você estiver usando proc, provavelmente estará usando algum tipo de paradigma funcional.
  2. Proc pode retornar fora do escopo envolvente (veja as respostas anteriores), que é basicamente um goto e de natureza altamente não funcional.

Lambda é funcionalmente mais seguro e mais fácil de raciocinar - eu sempre o uso em vez de proc.

Não posso dizer muito sobre as diferenças sutis.Porém, posso ressaltar que Ruby 1.9 agora permite parâmetros opcionais para lambdas e blocos.

Aqui está a nova sintaxe para lambdas stabby abaixo de 1.9:

stabby = ->(msg='inside the stabby lambda') { puts msg }

Ruby 1.8 não tinha essa sintaxe.Nem a forma convencional de declarar blocos/lambdas suportava argumentos opcionais:

# 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, entretanto, suporta argumentos opcionais mesmo com a sintaxe antiga:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

Se você deseja construir Ruby1.9 para Leopard ou Linux, dê uma olhada Este artigo (autopromoção sem vergonha).

Resposta curta:O que importa é o que return faz:lambda retorna de si mesmo e proc retorna de si mesmo E da função que o chamou.

O que está menos claro é por que você deseja usar cada um.lambda é o que esperamos que as coisas façam no sentido de programação funcional.É basicamente um método anônimo com o escopo atual vinculado automaticamente.Dos dois, lambda é o que você provavelmente deveria usar.

Proc, por outro lado, é muito útil para implementar a própria linguagem.Por exemplo, você pode implementar instruções "if" ou loops "for" com elas.Qualquer retorno encontrado no proc retornará do método que o chamou, não apenas da instrução "if".É assim que as linguagens funcionam, como funcionam as instruções "if", então meu palpite é que Ruby usa isso nos bastidores e eles apenas expuseram porque parecia poderoso.

Você só precisaria disso se estivesse criando novas construções de linguagem, como loops, construções if-else, etc.

Uma boa maneira de ver isso é que os lambdas são executados em seu próprio escopo (como se fosse uma chamada de método), enquanto os Procs podem ser vistos como executados em linha com o método de chamada, pelo menos essa é uma boa maneira de decidir qual usar. em cada caso.

Não notei nenhum comentário sobre o terceiro método na questão, "proc", que está obsoleto, mas tratado de forma diferente em 1.8 e 1.9.

Aqui está um exemplo bastante detalhado que facilita ver as diferenças entre as três chamadas semelhantes:

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

Fechamentos em Ruby é uma boa visão geral de como blocos, lambda e proc funcionam em Ruby, com Ruby.

lambda funciona conforme o esperado, como em outras linguagens.

O com fio Proc.new é surpreendente e confuso.

O return declaração em proc criada por Proc.new não apenas retornará o controle apenas de si mesmo, mas também do método que o envolve.

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

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

Você pode argumentar que Proc.new insere código no método envolvente, assim como o bloco.Mas Proc.new cria um objeto, enquanto bloco são 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.new ignora argumentos extras ou considera a ausência 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 FALAR NISSO, proc em Ruby 1.8 cria um lambda, enquanto em Ruby 1.9+ se comporta como Proc.new, o que é realmente confuso.

Para elaborar a resposta do Accordion Guy:

Notar que Proc.new cria um proc out ao receber um bloco.Acredito que lambda {...} é analisado como uma espécie de literal, em vez de uma chamada de método que passa um bloco. returnentrar de dentro de um bloco anexado a uma chamada de método retornará do método, não do bloco, e o Proc.new caso é um exemplo disso em jogo.

(Este é 1,8.Não sei como isso se traduz em 1.9.)

Estou um pouco atrasado nisso, mas há uma coisa excelente, mas pouco conhecida, sobre Proc.new não mencionado nos comentários.Como por documentação:

Proc::new pode ser chamado sem um bloco apenas dentro de um método com um bloco anexado, caso em que bloco é convertido para o Proc objeto.

Dito isto, Proc.new vamos encadear métodos de produção:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!

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.new :-)

Vale ressaltar que return em um proc retorna do método lexicalmente fechado, ou seja, o método onde o proc foi criado, não o método que chamou o proc.Isto é uma consequência da propriedade de fechamento de procs.Portanto, o código a seguir não gera nada:

def foo
  proc = Proc.new{return}
  foobar(proc)
  puts 'foo'
end

def foobar(proc)
  proc.call
  puts 'foobar'
end

foo

Embora o proc seja executado em foobar, foi criado em foo e então o return saídas foo, não apenas foobar.Como Charles Caldwell escreveu acima, tem uma sensação GOTO.Na minha opinião, return funciona bem em um bloco executado em seu contexto léxico, mas é muito menos intuitivo quando usado em um processo executado em um contexto diferente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top