346 Stimmen

Wann wird Lambda, wann Proc.new verwendet?

In Ruby 1.8 gibt es feine Unterschiede zwischen proc/lambda auf der einen Seite und Proc.new auf der anderen Seite.

  • Worin bestehen diese Unterschiede?
  • Können Sie Hinweise geben, wie man sich für eine der beiden entscheiden kann?
  • In Ruby 1.9 sind proc und lambda unterschiedlich. Was hat es damit auf sich?

383voto

Joey deVilla Punkte 8233

Ein weiterer wichtiger, aber subtiler Unterschied zwischen Procs, die mit lambda und Procs, die mit Proc.new ist die Art und Weise, wie sie mit dem return Erklärung:

  • In einem lambda -erstellte Proc, die return Anweisung kehrt nur von der proc selbst zurück
  • In einem Proc.new -erstellte Proc, die return Anweisung ist etwas überraschender: Sie gibt die Kontrolle nicht nur von der proc, sondern auch von der Methode, die den proc!

Hier ist lambda -erstellte Proc's return in Aktion. Es verhält sich so, wie Sie es wahrscheinlich erwarten:

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"

Hier ist ein Proc.new -erstellte Proc's return das Gleiche tun. Sie werden gleich einen der Fälle sehen, in denen Ruby das viel gepriesene Prinzip der geringsten Überraschung durchbricht:

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"

Dank dieses überraschenden Verhaltens (und der geringeren Tipparbeit) neige ich dazu, die Verwendung von lambda über Proc.new bei der Erstellung von Procs.

95voto

Peter Wagenet Punkte 4856

Um weitere Klarheit zu schaffen:

Joey sagt, dass das Rückgabeverhalten von Proc.new ist überraschend. Wenn man jedoch bedenkt, dass sich Proc.new wie ein Block verhält, ist dies nicht überraschend, da sich Blöcke genau so verhalten. lambas hingegen verhalten sich eher wie Methoden.

Dies erklärt, warum Procs in Bezug auf die Arithmetik (Anzahl der Argumente) flexibel sind, während Lambdas dies nicht sind. Bei Blöcken müssen nicht alle Argumente angegeben werden, bei Methoden schon (es sei denn, es wird ein Standardwert angegeben). Während die Angabe von Lambda-Argumenten in Ruby 1.8 keine Option ist, wird sie in Ruby 1.9 mit der alternativen Lambda-Syntax unterstützt (wie von webmat erwähnt):

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

Und Michiel de Mare (der OP) ist falsch über die Procs und Lambda verhalten sich die gleichen mit Arität in Ruby 1.9. Ich habe überprüft, dass sie immer noch das Verhalten von 1.8 wie oben angegeben beibehalten.

break Anweisungen machen weder in Procs noch in Lambdas wirklich viel Sinn. In Procs würde die Pause Sie von Proc.new zurückbringen, das bereits abgeschlossen ist. Und es macht keinen Sinn, von einem Lambda abzubrechen, da es im Grunde eine Methode ist, und man würde niemals von der obersten Ebene einer Methode abbrechen.

next , redo y raise verhalten sich in Procs und Lambdas gleich. Während retry ist in beiden nicht erlaubt und führt zu einer Ausnahme.

Und schließlich, die proc Methode sollte niemals verwendet werden, da sie inkonsistent ist und ein unerwartetes Verhalten zeigt. In Ruby 1.8 gibt sie tatsächlich ein Lambda zurück! In Ruby 1.9 wurde dies behoben und sie gibt eine Proc zurück. Wenn Sie eine Proc erstellen wollen, bleiben Sie bei Proc.new .

Für weitere Informationen empfehle ich das Buch von O'Reilly Die Ruby-Programmiersprache die meine Quelle für die meisten dieser Informationen ist.

42voto

Mike Stone Punkte 43560

Ich fand diese Seite die zeigt, was der Unterschied zwischen Proc.new y lambda sind. Laut der Seite besteht der einzige Unterschied darin, dass ein Lambda strikt auf die Anzahl der Argumente beschränkt ist, die es akzeptiert, während Proc.new wandelt fehlende Argumente in nil . Hier ist ein Beispiel für eine IRB-Sitzung, das den Unterschied verdeutlicht:

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

Die Seite empfiehlt auch die Verwendung von Lambda, es sei denn, Sie wollen ausdrücklich das fehlertolerante Verhalten. Ich stimme dieser Meinung zu. Mit einem Lambda scheint ein bisschen mehr prägnant, und mit einem solchen unbedeutenden Unterschied, scheint es die bessere Wahl in der durchschnittlichen Situation.

Was Ruby 1.9 betrifft, so habe ich mir 1.9 leider noch nicht angesehen, aber ich kann mir nicht vorstellen, dass sie so viele Änderungen vorgenommen haben (nehmen Sie mich aber nicht beim Wort, Sie scheinen von einigen Änderungen gehört zu haben, also liege ich da wahrscheinlich falsch).

15voto

Proc ist älter, aber die Semantik von return ist für mich höchst kontraintuitiv (zumindest als ich die Sprache lernte), denn:

  1. Wenn Sie proc verwenden, verwenden Sie höchstwahrscheinlich eine Art funktionales Paradigma.
  2. Proc kann aus dem umschließenden Bereich zurückkehren (siehe vorherige Antworten), was im Grunde ein goto ist und in hohem Maße nicht-funktional ist.

Lambda ist funktional sicherer und einfacher zu begründen - ich verwende es immer anstelle von proc.

11voto

krusty.ar Punkte 3995

Ein guter Weg, um es zu sehen ist, dass Lambdas in ihrem eigenen Bereich ausgeführt werden (als ob es ein Methodenaufruf war), während Procs als Inline mit der aufrufenden Methode ausgeführt werden kann, zumindest ist das ein guter Weg, um zu entscheiden, welche in jedem Fall zu verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X