In Ruby 1.8 gibt es subtile Unterschiede zwischen proc / lambda einerseits und Proc.new
andererseits.
- Was sind diese Unterschiede?
- Können Sie Richtlinien geben, wie Sie entscheiden sollen, welche Sie wählen möchten?
- In Ruby 1.9 unterscheiden sich proc und lambda. Was ist das Problem?
Antworten:
Ein weiterer wichtiger, aber subtiler Unterschied zwischen Procs, die mit
lambda
und Procs erstellt wurden,Proc.new
besteht darin, wie sie mit derreturn
Aussage umgehen :lambda
Prozess wird diereturn
Anweisung nur vom Prozess selbst zurückgegebenProc.new
Proc ist diereturn
Aussage etwas überraschender: Sie gibt die Kontrolle nicht nur vom Proc zurück, sondern auch von der Methode, die den Proc einschließt!Hier sind erstellte
lambda
Procsreturn
in Aktion. Es verhält sich so, wie Sie es wahrscheinlich erwarten:Hier ist ein erstellter
Proc.new
Proc, derreturn
das Gleiche tut. Sie werden einen dieser Fälle sehen, in denen Ruby das viel gepriesene Prinzip der geringsten Überraschung bricht:Dank dieses überraschenden Verhalten (wie auch weniger Typisierung), neige ich dazu , mit zu begünstigen
lambda
über ,Proc.new
wenn Procs zu machen.quelle
proc
Methode. Ist es nur eine Abkürzung fürProc.new
?proc
ist gleichbedeutend mitProc.new
proc
wielambda
und nicht wieProc.new
. Das heißt, das Ruby-Dokument ist ungenau.proc
verhält sich wielambda
in 1.8, verhält sich aber wieProc.new
in 1.9. Siehe die Antwort von Peter Wagenet.lambda
ist eine anonyme Methode. Da es sich um eine Methode handelt, gibt sie einen Wert zurück, und die Methode, die sie aufgerufen hat, kann damit tun, was sie will, einschließlich Ignorieren und Zurückgeben eines anderen Werts. AProc
ist wie das Einfügen eines Code-Snippets. Es verhält sich nicht wie eine Methode. Wenn also eine Rückgabe innerhalb vonProc
erfolgt, ist dies nur ein Teil des Codes der Methode, die sie aufgerufen hat.Zur weiteren Klärung:
Joey sagt, dass das Rückkehrverhalten von
Proc.new
überraschend ist. Wenn Sie jedoch bedenken, 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 tatsächlich, warum Procs flexibel sind, wenn es um Arität (Anzahl der Argumente) geht, Lambdas jedoch nicht. Für Blöcke müssen nicht alle Argumente angegeben werden, für Methoden jedoch (sofern kein Standardwert angegeben ist). Während das Bereitstellen der Lambda-Argument-Standardeinstellung in Ruby 1.8 keine Option ist, wird sie jetzt in Ruby 1.9 mit der alternativen Lambda-Syntax (wie von webmat angegeben) unterstützt:
Und Michiel de Mare (das OP) ist falsch darüber, dass sich Procs und Lambda in Ruby 1.9 mit Arity gleich verhalten. Ich habe überprüft, dass sie das oben angegebene Verhalten von 1.8 beibehalten.
break
Aussagen sind weder in Procs noch in Lambdas sinnvoll. In Procs würde die Pause Sie von Proc.new zurückbringen, das bereits abgeschlossen wurde. Und es macht keinen Sinn, von einem Lambda abzubrechen, da es im Wesentlichen eine Methode ist und Sie niemals von der obersten Ebene einer Methode abbrechen würden.next
,redo
Undraise
verhalten sich die in beiden Procs und Lambdas. Wobei auchretry
nicht erlaubt ist und eine Ausnahme auslöst.Und schließlich sollte die
proc
Methode niemals verwendet werden, da sie inkonsistent ist und unerwartetes Verhalten aufweist. In Ruby 1.8 wird tatsächlich ein Lambda zurückgegeben! In Ruby 1.9 wurde dies behoben und es wird ein Proc zurückgegeben. Wenn Sie einen Proc erstellen möchten, bleiben Sie beiProc.new
.Für weitere Informationen empfehle ich O'Reillys The Ruby Programming Language, die meine Quelle für die meisten dieser Informationen ist.
quelle
break
von Procs wirftLocalJumpError
, währendbreak
von Lambdas verhält sich genauso wiereturn
( dh ,return nil
).Ich habe diese Seite gefunden, die zeigt, was der Unterschied zwischen
Proc.new
undlambda
ist. Laut der Seite besteht der einzige Unterschied darin, dass ein Lambda hinsichtlich der Anzahl der akzeptierten Argumente streng ist, währendProc.new
fehlende Argumente in konvertiert werdennil
. Hier ist eine IRB-Beispielsitzung, die den Unterschied veranschaulicht:Auf dieser Seite wird außerdem die Verwendung von Lambda empfohlen, es sei denn, Sie möchten ausdrücklich das fehlertolerante Verhalten. Ich stimme diesem Gefühl zu. Die Verwendung eines Lambda scheint etwas prägnanter zu sein, und mit einem so unbedeutenden Unterschied scheint es in der durchschnittlichen Situation die bessere Wahl zu sein.
Was Ruby 1.9 betrifft, tut mir leid, ich habe mich noch nicht mit 1.9 befasst, aber ich kann mir nicht vorstellen, dass sie das alles so sehr ändern würden (nehmen Sie mein Wort nicht dafür, es scheint, als hätten Sie von einigen Änderungen gehört, also Ich liege dort wahrscheinlich falsch.
quelle
Proc ist älter, aber die Semantik der Rückkehr ist für mich sehr uninteressant (zumindest als ich die Sprache lernte), weil:
Lambda ist funktional sicherer und leichter zu überlegen - ich benutze es immer anstelle von proc.
quelle
Ich kann nicht viel über die subtilen Unterschiede sagen. Ich kann jedoch darauf hinweisen, dass Ruby 1.9 jetzt optionale Parameter für Lambdas und Blöcke zulässt.
Hier ist die neue Syntax für die Stabby Lambdas unter 1.9:
Ruby 1.8 hatte diese Syntax nicht. Die herkömmliche Art, Blöcke / Lambdas zu deklarieren, unterstützte auch keine optionalen Argumente:
Ruby 1.9 unterstützt jedoch optionale Argumente auch mit der alten Syntax:
Wenn Sie Ruby1.9 für Leopard oder Linux erstellen möchten, lesen Sie diesen Artikel (schamlose Eigenwerbung).
quelle
Kurze Antwort: Was zählt, ist was
return
tut: Lambda kehrt aus sich selbst zurück und proc kehrt aus sich selbst UND der Funktion zurück, die es aufgerufen hat.Was weniger klar ist, ist, warum Sie jeden verwenden möchten. Lambda ist das, was wir erwarten, dass die Dinge im Sinne einer funktionalen Programmierung funktionieren sollten. Grundsätzlich handelt es sich um eine anonyme Methode, bei der der aktuelle Bereich automatisch gebunden wird. Von den beiden ist Lambda dasjenige, das Sie wahrscheinlich verwenden sollten.
Proc hingegen ist sehr nützlich, um die Sprache selbst zu implementieren. Beispielsweise können Sie mit ihnen "if" -Anweisungen oder "for" -Schleifen implementieren. Jede im proc gefundene Rückgabe wird aus der Methode zurückgegeben, die sie aufgerufen hat, und nicht nur aus der "if" -Anweisung. So funktionieren Sprachen, so funktionieren "if" -Anweisungen. Ich vermute, Ruby verwendet dies unter der Decke und sie haben es nur enthüllt, weil es mächtig schien.
Sie würden dies nur dann wirklich benötigen, wenn Sie neue Sprachkonstrukte wie Schleifen, if-else-Konstrukte usw. erstellen.
quelle
Eine gute Möglichkeit, dies zu erkennen, besteht darin, dass Lambdas in ihrem eigenen Bereich ausgeführt werden (als wäre es ein Methodenaufruf), während Procs als im Einklang mit der aufrufenden Methode ausgeführt angesehen werden kann. Zumindest ist dies eine gute Methode, um zu entscheiden, welche Methode verwendet werden soll in jedem Fall.
quelle
Ich habe keine Kommentare zur dritten Methode in der Quest "proc" bemerkt, die veraltet ist, aber in 1.8 und 1.9 anders gehandhabt wird.
Hier ist ein ziemlich ausführliches Beispiel, das es einfach macht, die Unterschiede zwischen den drei ähnlichen Aufrufen zu erkennen:
quelle
proc
gab ein Lambda in 1.8 zurück; Es wurde nun behoben, dass ein Proc in 1.9 zurückgegeben wird - dies ist jedoch eine bahnbrechende Änderung. daher nicht mehr zu empfehlenClosures in Ruby bietet einen guten Überblick darüber, wie Blöcke, Lambda und Proc in Ruby mit Ruby funktionieren.
quelle
Lambda funktioniert wie erwartet, wie in anderen Sprachen.
Das Kabel
Proc.new
ist überraschend und verwirrend.Die von
return
in proc erstellte AnweisungProc.new
gibt nicht nur die Kontrolle nur von sich selbst zurück, sondern auch von der Methode, die sie einschließt .Sie können argumentieren, dass
Proc.new
Code genau wie Block in die einschließende Methode einfügt. AberProc.new
ein Objekt erstellt, während der Block sind Teil eines Objekts.Und es gibt einen weiteren Unterschied zwischen Lambda und
Proc.new
, nämlich den Umgang mit (falschen) Argumenten. Lambda beschwert sich darüber,Proc.new
ignoriert zusätzliche Argumente oder betrachtet das Fehlen von Argumenten als Null.Übrigens,
proc
in Ruby 1.8 wird ein Lambda erstellt, während sich in Ruby 1.9+ so verhältProc.new
, was wirklich verwirrend ist.quelle
Um die Antwort von Accordion Guy näher zu erläutern:
Beachten Sie, dass
Proc.new
ein Proc erstellt wird, indem ein Block übergeben wird. Ich glaube, daslambda {...}
wird eher als eine Art Literal analysiert als als ein Methodenaufruf, der einen Block übergibt.return
Wenn Sie aus einem Block heraus arbeiten, der an einen Methodenaufruf angehängt ist, wird von der Methode und nicht vom Block zurückgekehrt, und derProc.new
Fall ist ein Beispiel dafür.(Dies ist 1.8. Ich weiß nicht, wie dies zu 1.9 übersetzt wird.)
quelle
Ich bin etwas spät dran, aber es gibt eine großartige, aber wenig bekannte Sache
Proc.new
, die in Kommentaren überhaupt nicht erwähnt wird. Wie durch Dokumentation :Das heißt, lassen Sie
Proc.new
uns Ertragsmethoden verketten:quelle
&block
Arguments in derdef
, aber ohne dies in der def arg-Liste tun zu müssen.Es ist hervorzuheben, dass
return
in einem Proc die lexikalisch einschließende Methode zurückgegeben wird, dh die Methode, in der der Proc erstellt wurde , nicht die Methode, die den Proc aufgerufen hat. Dies ist eine Folge der Schließungseigenschaft von procs. Der folgende Code gibt also nichts aus:Obwohl der Proc ausgeführt wird
foobar
, wurde er erstelltfoo
und damit diereturn
Exitsfoo
, nicht nurfoobar
. Wie Charles Caldwell oben schrieb, hat es ein GOTO-Gefühl. Meiner Meinung nachreturn
ist es in einem Block in Ordnung, der in seinem lexikalischen Kontext ausgeführt wird, aber viel weniger intuitiv, wenn es in einem Prozess verwendet wird, der in einem anderen Kontext ausgeführt wird.quelle
Der Unterschied im Verhalten mit
return
ist meiner Meinung nach der wichtigste Unterschied zwischen den 2. Ich bevorzuge auch Lambda, weil es weniger tippt als Proc.new :-)quelle
proc {}
. Ich bin mir nicht sicher, wann dies in Kraft getreten ist, aber es ist (etwas) einfacher, als Proc.new eingeben zu müssen.