Quando usar lambda, quando usar Proc.new?

votos
316

Em Ruby 1.8, há diferenças sutis entre proc / lambda, por um lado, e Proc.newpor outro.

  • Quais são essas diferenças?
  • você pode dar orientações sobre como decidir qual escolher?
  • Em Ruby 1.9, proc e lambda são diferentes. Qual é o problema?
Publicado 03/08/2008 em 07:40
fonte usuário
Em outras línguas...                            


15 respostas

votos
364

Outra diferença importante, mas sutil entre procs criados com lambdae procs criado com Proc.newé como lidar com a returndeclaração:

  • Em um lambdaproc -Criado, a returninstrução retorna somente a partir do próprio proc
  • Em um Proc.newproc -Criado, a returndeclaração é um pouco mais surpreendente: ele retorna o controle não apenas do proc, mas também a partir do método que envolve o proc!

Aqui está lambda-Criado proc do returnem ação. Ele se comporta de uma maneira que você provavelmente esperar:

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"

Ora aqui está um Proc.new-Criado proc de returnfazer a mesma coisa. Você está prestes a ver um daqueles casos em que o Ruby infringe o princípio tão apregoada 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.

Respondeu 03/08/2008 em 16:21
fonte usuário

votos
93

Para fornecer mais esclarecimentos:

Joey diz que o comportamento retorno de Proc.newé surpreendente. No entanto, quando se considera que Proc.new se comporta como um bloco isso não é surpreendente como isso é exatamente como os blocos se comportam. Lambas, por outro lado se comportar mais como métodos.

Isso realmente explica por que Procs são flexíveis quando se trata de arity (número de argumentos), enquanto que lambdas não são. Blocos não necessitam de todos os seus argumentos a serem fornecidos, mas métodos de fazer (a menos que haja um padrão). Embora fornecendo padrão argumento lambda não é uma opção no Ruby 1.8, é agora suportado no Ruby 1.9 com a sintaxe lambda alternativa (como observado por webmat):

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

E Michiel de Mare (OP) está incorreta sobre os Procs e lambda comportando o mesmo com arity no Ruby 1.9. Tenho verificado que eles ainda mantêm o comportamento de 1,8, conforme especificado acima.

breakdeclarações na verdade, não faz muito sentido em qualquer Procs ou lambdas. Em Procs, a quebra voltaria lo de Proc.new que já foi concluída. E isso não faz qualquer sentido de romper com um lambda, já que é essencialmente um método, e você nunca iria quebrar a partir do nível superior de um método.

next, redoE raisese comportam da mesma em ambos os Procs e lambdas. Considerando retrynão é permitido em qualquer e irá gerar uma exceção.

E, finalmente, o procmétodo nunca deve ser usado como ele é inconsistente e tem um comportamento inesperado. Em Ruby 1.8 que realmente retorna um lambda! Em Ruby 1,9 isso foi corrigido e que devolve um Proc. Se você quiser criar um Proc, ficar com Proc.new.

Para mais informações, eu recomendo de O'Reilly Linguagem de Programação The Ruby que é minha fonte para a maioria desta informação.

Respondeu 04/10/2009 em 06:23
fonte usuário

votos
41

Eu encontrei esta página que mostra o que a diferença entre Proc.newe lambdasão. De acordo com a página, a única diferença é que um lambda é rigoroso sobre o número de argumentos que aceita, enquanto Proc.newconvertidos faltando argumentos para nil. Aqui está um exemplo sessão IRB ilustrando 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 "Olá", "mundo"
=> "Helloworld"
irb (main): 004: 0> p.call "Olá", "mundo"
=> "Helloworld"
irb (main): 005: 0> l.call "Olá"
ArgumentError: número errado de argumentos (1 para 2)
    a partir de (IVB): 1
    a partir de (IVB): 5: `na chamada'
    a partir de (IVB): 5
    de: 0
irb (main): 006: 0> p.call "Olá"
TypeError: não é possível converter nulo em Cadeia
    a partir de (IVB): 2: em '+'
    a partir de (IVB): 2
    a partir de (IVB): 6: `na chamada'
    a partir de (IVB): 6
    de: 0

A página também recomenda o uso de lambda a menos que você quer especificamente o comportamento tolerante erro. Concordo com este sentimento. Usando um lambda parece um pouco mais concisa, e com uma diferença tão insignificante, parece a melhor escolha na situação média.

Quanto Ruby 1.9, desculpe, eu não olhei para 1,9, mas eu não imagino que iria mudar tudo que muito (não tome minha palavra para ela, porém, parece que você já ouviu falar de algumas mudanças, por isso, Eu sou provavelmente errado lá).

Respondeu 03/08/2008 em 08:28
fonte usuário

votos
14

Proc é mais velho, mas a semântica de retorno são altamente contra-intuitivo para mim (pelo menos quando eu estava aprendendo a língua) porque:

  1. Se você estiver usando proc, você provavelmente está usando algum tipo de paradigma funcional.
  2. Proc pode retornar para fora do alcance vedante (ver respostas anteriores), o qual é basicamente um goto, e altamente não-funcional na natureza.

Lambda é funcionalmente mais seguro e mais fácil raciocinar sobre - Eu sempre usá-lo em vez de proc.

Respondeu 11/09/2008 em 00:32
fonte usuário

votos
11

Eu não posso dizer muito sobre as diferenças sutis. No entanto, posso salientar que Ruby 1.9 agora permite que os parâmetros opcionais para lambdas e blocos.

Aqui está a nova sintaxe para os lambdas stabby sob 1.9:

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

Ruby 1.8 não tem essa sintaxe. Nem a maneira convencional de declarar blocos / lambdas apoiar 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, no entanto, suporta argumentos opcionais, mesmo com a velha sintaxe:

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ê quer construir Ruby1.9 para o Leopard ou Linux, confira este artigo (auto-promoção descarada).

Respondeu 19/11/2008 em 22:28
fonte usuário

votos
10

Resposta curta: O que importa é o que returnfaz: retorna lambda fora de si, e proc retorna fora de si mesmo e da função que a chamou.

O que é menos claro é por que você quer usar cada um. lambda é o que nós esperamos que as coisas devem fazer no sentido de programação funcional. É basicamente um método anônimo com o escopo atual ligado automaticamente. Dos dois, lambda é o que você provavelmente deve estar usando.

Proc, por outro lado, é realmente útil para a implementação da linguagem em si. Por exemplo, você pode implementar "se" declarações ou "para" loops com eles. Qualquer retorno encontrada no proc voltará fora do método que o chamou, não o apenas o "if". Isto é como línguas funcionam, como "se" declarações trabalhar, então o meu palpite é Ruby usa esta debaixo das cobertas e eles simplesmente expôs porque parecia poderoso.

Você só precisa realmente isso se você estiver criando novas construções de linguagem como loops, if-else construções, etc.

Respondeu 06/10/2011 em 19:33
fonte usuário

votos
9

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 um wich de usar em cada caso.

Respondeu 09/12/2008 em 15:17
fonte usuário

votos
8

Eu não notei qualquer comentário sobre o terceiro método no queston "proc", que é obsoleto, mas tratado de forma diferente em 1,8 e 1,9.

Aqui está um exemplo bastante detalhado que torna fácil 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
Respondeu 25/06/2009 em 12:22
fonte usuário

votos
7

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

Respondeu 28/08/2008 em 14:50
fonte usuário

votos
6

Compreender rubi Blocos, Procs e Lambdas por Robert Sosinski explica claramente os conceitos de programação e reforça as explicações com exemplo de código. Objetos método estão relacionados e cobertos também.

Respondeu 23/08/2013 em 18:07
fonte usuário

votos
5

lambda funciona como esperado, como em outros idiomas.

O fio Proc.newé surpreendente e desconcertante.

A returndeclaração em proc criado por Proc.newnão só irá retornar o controle apenas de si mesmo, mas também a partir do método encerrando-o .

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 delimitador, assim como bloco. Mas Proc.newcria um objeto, enquanto bloco são parte de um objeto.

E há uma outra diferença entre lambda e Proc.new, o que é a sua manipulação de (erradas) argumentos. lambda reclama, enquanto Proc.newignora argumentos extras ou considera a ausência de argumentos como nulo.

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"

BTW, procno Ruby 1.8 cria um lambda, enquanto em Ruby 1.9+ se comporta como Proc.new, o que é realmente confuso.

Respondeu 29/10/2014 em 16:22
fonte usuário

votos
3

Para elaborar a resposta do acordeão Guy:

Observe que Proc.newcria um proc por que está sendo passado um bloco. Eu acredito que lambda {...}é analisado como uma espécie de literal, ao invés de uma chamada de método que passa um bloco. returning de dentro de um bloco anexado a uma chamada de método irá retornar a partir do método, não o bloco, eo Proc.newcaso é um exemplo disso no jogo.

(Este é 1.8. Eu não sei como isso se traduz em 1.9.)

Respondeu 07/09/2008 em 03:31
fonte usuário

votos
2

Eu sou um pouco tarde sobre isso, mas há uma grande mas pouco coisa conhecida sobre Proc.newnão mencionado nos comentários em tudo. Como por documentação :

Proc::newpode ser chamado, sem um bloco somente dentro de um método com um bloco ligado, neste caso, que bloco é convertido para oProc objecto.

Dito isto, Proc.newvamos aos métodos cadeia produzindo:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!
Respondeu 28/04/2015 em 13:15
fonte usuário

votos
1

É importante ressaltar que returnem um proc retorna do método lexically encerrando, ou seja, o método onde o proc foi criado , não o método que chamou o proc. Esta é uma consequência da propriedade fechamento de procs. Assim, o código seguinte resulta em nada:

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

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

foo

Embora o proc executa em foobar, que foi criado em fooe assim as returnsaídas foo, e não apenas foobar. Como Charles Caldwell escrevi acima, tem um GOTO para ele se sentir. Na minha opinião, returné bom em um bloco que é executado em seu contexto lexical, mas é muito menos intuitiva quando usado em um proc que é executado em um contexto diferente.

Respondeu 15/11/2018 em 10:00
fonte usuário

votos
1

A diferença de comportamento com returné IMHO a diferença mais importante entre o 2. Eu também prefiro lambda porque é menos digitação de Proc.new :-)

Respondeu 11/08/2008 em 03:09
fonte usuário

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