Und wann würden Sie eher das eine als das andere verwenden?
Und wann würden Sie eher das eine als das andere verwenden?
Antworten:
Ein Unterschied besteht in der Art und Weise, wie sie mit Argumenten umgehen. Erstellen eines Prozesses mit proc {}
und Proc.new {}
sind gleichwertig. lambda {}
Wenn Sie jedoch verwenden, erhalten Sie einen Prozess, der die Anzahl der an ihn übergebenen Argumente überprüft. Von ri Kernel#lambda
:
Entspricht Proc.new , außer dass die resultierenden Proc-Objekte die Anzahl der beim Aufruf übergebenen Parameter überprüfen.
Ein Beispiel:
p = Proc.new {|a, b| puts a**2+b**2 } # => #<Proc:0x3c7d28@(irb):1>
p.call 1, 2 # => 5
p.call 1 # => NoMethodError: undefined method `**' for nil:NilClass
p.call 1, 2, 3 # => 5
l = lambda {|a, b| puts a**2+b**2 } # => #<Proc:0x15016c@(irb):5 (lambda)>
l.call 1, 2 # => 5
l.call 1 # => ArgumentError: wrong number of arguments (1 for 2)
l.call 1, 2, 3 # => ArgumentError: wrong number of arguments (3 for 2)
Wie Ken betont, gibt die Verwendung return
in einem Lambda den Wert dieses Lambdas zurück, während die Verwendung return
in einem Proc vom umschließenden Block zurückkehrt.
lambda { return :foo }.call # => :foo
return # => LocalJumpError: unexpected return
Proc.new { return :foo }.call # => LocalJumpError: unexpected return
Für die meisten schnellen Verwendungen sind sie also gleich, aber wenn Sie eine automatische strenge Argumentprüfung wünschen (was manchmal auch beim Debuggen hilfreich sein kann) oder wenn Sie die return
Anweisung verwenden müssen, um den Wert des Prozesses zurückzugeben, verwenden Sie lambda
.
Der wahre Unterschied zwischen procs und lambdas hat alles mit Schlüsselwörtern für den Kontrollfluss zu tun. Ich spreche von return
, raise
, break
, redo
, retry
usw. - diese Steuerwörter. Angenommen, Sie haben eine return-Anweisung in einem Proc. Wenn Sie Ihren Prozess aufrufen, werden Sie nicht nur aus dem Prozess entfernt, sondern es wird auch von der einschließenden Methode zurückgekehrt, z.
def my_method
puts "before proc"
my_proc = Proc.new do
puts "inside proc"
return
end
my_proc.call
puts "after proc"
end
my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
Das Finale puts
in der Methode wurde nie ausgeführt, da uns das return
darin enthaltene Proc aus der Methode geworfen hat, als wir unseren Proc aufgerufen haben . Wenn wir jedoch unseren Proc in ein Lambda umwandeln, erhalten wir Folgendes:
def my_method
puts "before proc"
my_proc = lambda do
puts "inside proc"
return
end
my_proc.call
puts "after proc"
end
my_method
shoaib@shoaib-ubuntu-vm:~/tmp$ ruby a.rb
before proc
inside proc
after proc
Die Rückgabe innerhalb des Lambda entleert uns nur aus dem Lambda selbst und die einschließende Methode wird weiter ausgeführt. Die Art und Weise, wie Kontrollfluss-Schlüsselwörter in procs und lambdas behandelt werden, ist der Hauptunterschied zwischen ihnen
Es gibt nur zwei Hauptunterschiede.
lambda
überprüft a die Anzahl der übergebenen Argumente, während a proc
dies nicht tut. Dies bedeutet, dass a lambda
einen Fehler auslöst, wenn Sie ihm die falsche Anzahl von Argumenten übergeben, während a proc
unerwartete Argumente ignoriert und zuweistnil
fehlenden .lambda
zurückkehrt, gibt es die Kontrolle an die aufrufende Methode zurück. Wenn a proc
zurückkehrt, geschieht dies sofort, ohne zur aufrufenden Methode zurückzukehren.Sehen Sie sich den folgenden Code an, um zu sehen, wie dies funktioniert. Unsere erste Methode nennt a proc
; der zweite ruft a lambda
.
def batman_ironman_proc
victor = Proc.new { return "Batman will win!" }
victor.call
"Iron Man will win!"
end
puts batman_ironman_proc # prints "Batman will win!"
def batman_ironman_lambda
victor = lambda { return "Batman will win!" }
victor.call
"Iron Man will win!"
end
puts batman_ironman_lambda # prints "Iron Man will win!"
Sehen Sie, wie proc
"Batman wird gewinnen!" Sagt, weil es sofort zurückkehrt, ohne zur Methode batman_ironman_proc zurückzukehren.
Unsere lambda
Methode kehrt jedoch nach dem Aufruf zur Methode zurück, sodass die Methode den letzten Code zurückgibt, den sie auswertet: "Iron Man wird gewinnen!"
# Proc Beispiele
p = Proc.new { |x| puts x*2 }
[1,2,3].each(&p) # The '&' tells ruby to turn the proc into a block
proc = Proc.new { puts "Hello World" }
proc.call
# Lambda-Beispiele
lam = lambda { |x| puts x*2 }
[1,2,3].each(&lam)
lam = lambda { puts "Hello World" }
lam.call
Unterschiede zwischen Procs und Lambdas
Bevor ich auf die Unterschiede zwischen Procs und Lambdas eingehe, ist es wichtig zu erwähnen, dass beide Proc-Objekte sind.
proc = Proc.new { puts "Hello world" }
lam = lambda { puts "Hello World" }
proc.class # returns 'Proc'
lam.class # returns 'Proc'
Lambdas sind jedoch ein anderer "Geschmack" von Procs. Dieser geringfügige Unterschied wird bei der Rückgabe der Objekte angezeigt.
proc # returns '#<Proc:0x007f96b1032d30@(irb):75>'
lam # returns '<Proc:0x007f96b1b41938@(irb):76 (lambda)>'
1. Lambdas überprüfen die Anzahl der Argumente, Procs nicht
lam = lambda { |x| puts x } # creates a lambda that takes 1 argument
lam.call(2) # prints out 2
lam.call # ArgumentError: wrong number of arguments (0 for 1)
lam.call(1,2,3) # ArgumentError: wrong number of arguments (3 for 1)
Im Gegensatz dazu ist es Procs egal, ob ihnen die falsche Anzahl von Argumenten übergeben wird.
proc = Proc.new { |x| puts x } # creates a proc that takes 1 argument
proc.call(2) # prints out 2
proc.call # returns nil
proc.call(1,2,3) # prints out 1 and forgets about the extra arguments
2. Lambdas und procs behandeln das Schlüsselwort 'return' unterschiedlich
'return' innerhalb eines Lambda löst den Code direkt außerhalb des Lambda-Codes aus
def lambda_test
lam = lambda { return }
lam.call
puts "Hello world"
end
lambda_test # calling lambda_test prints 'Hello World'
'return' innerhalb eines Prozesses löst den Code außerhalb der Methode aus, in der der Prozess ausgeführt wird
def proc_test
proc = Proc.new { return }
proc.call
puts "Hello world"
end
proc_test # calling proc_test prints nothing
Und um Ihre andere Frage zu beantworten, welche und wann? Ich werde @jtbandes folgen, wie er erwähnt hat
Für die meisten schnellen Verwendungen sind sie also gleich, aber wenn Sie eine automatische strenge Argumentprüfung wünschen (was manchmal auch beim Debuggen hilfreich sein kann) oder wenn Sie die return-Anweisung verwenden müssen, um den Wert des Prozesses zurückzugeben, verwenden Sie Lambda.
Ursprünglich hier gepostet
Im Allgemeinen sind Lambdas intuitiver als Procs, da sie Methoden ähnlicher sind. Sie sind ziemlich streng in Bezug auf Arity und werden einfach beendet, wenn Sie Return anrufen. Aus diesem Grund verwenden viele Rubyisten Lambdas als erste Wahl, es sei denn, sie benötigen die spezifischen Funktionen von Procs.
Procs: Objekte der Klasse Proc
. Wie Blöcke werden sie in dem Bereich ausgewertet, in dem sie definiert sind.
Lambdas: Auch Objekte der Klasse, Proc
aber subtil anders als normale Procs. Sie sind Verschlüsse wie Blöcke und Prozesse und werden als solche in dem Bereich bewertet, in dem sie definiert sind.
Proc erstellen
a = Proc.new { |x| x 2 }
Lambda erstellen
b = lambda { |x| x 2
}}
a = proc { |x| x 2 }
ist das gleiche wiea = Proc.new { |x| x 2 }
Hier ist ein anderer Weg, dies zu verstehen.
Ein Block ist ein Codeabschnitt, der an den Aufruf eines Aufrufs einer Methode für ein Objekt angehängt ist. Im folgenden Beispiel ist self eine Instanz einer anonymen Klasse, die von ActionView :: Base im Rails-Framework (das selbst viele Hilfsmodule enthält) erbt. Karte ist eine Methode, die wir selbst aufrufen. Wir übergeben der Methode ein Argument und hängen den Block immer an das Ende des Methodenaufrufs an:
self.card :contacts do |c|
// a chunk of valid ruby code
end
Ok, wir übergeben einen Codeabschnitt an eine Methode. Aber wie nutzen wir diesen Block? Eine Möglichkeit besteht darin, den Codeabschnitt in ein Objekt umzuwandeln. Ruby bietet drei Möglichkeiten, um einen Codeabschnitt in ein Objekt umzuwandeln
# lambda
> l = lambda { |a| a + 1 }
> l.call(1)
=> 2
# Proc.new
> l2= Proc.new { |a| a + 1 }
> l2.call(1)
=> 2
# & as the last method argument with a local variable name
def add(&block)
end
In der obigen Methode konvertiert & den an die Methode übergebenen Block in ein Objekt und speichert dieses Objekt im lokalen Variablenblock. In der Tat können wir zeigen, dass es das gleiche Verhalten wie Lambda und Proc.new hat:
def add(&block)
block
end
l3 = add { |a| a + 1 }
l3.call(1)
=> 2
Das ist wichtig. Wenn Sie einen Block an eine Methode übergeben und mit & konvertieren, verwendet das erstellte Objekt Proc.new, um die Konvertierung durchzuführen.
Beachten Sie, dass ich die Verwendung von "proc" als Option vermieden habe. Das liegt daran, dass es Ruby 1.8 ist, dasselbe wie Lambda und in Ruby 1.9 dasselbe wie Proc.new und in allen Ruby-Versionen sollte es vermieden werden.
Dann fragen Sie sich, was der Unterschied zwischen Lambda und Proc.new ist.
Erstens verhält sich Lambda in Bezug auf die Parameterübergabe wie ein Methodenaufruf. Es wird eine Ausnahme ausgelöst, wenn Sie die falsche Anzahl von Argumenten übergeben. Im Gegensatz dazu verhält sich Proc.new wie eine parallele Zuweisung. Alle nicht verwendeten Argumente werden in nil konvertiert:
> l = lambda {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb47e40@(irb):19 (lambda)>
> l.call(1)
ArgumentError: wrong number of arguments (1 for 2)
> l2 = Proc.new {|a,b| puts "#{a} + #{b}" }
=> #<Proc:0x007fbffcb261a0@(irb):21>
> l2.call(1)
1 +
Zweitens behandeln Lambda und Proc.new das Schlüsselwort return unterschiedlich. Wenn Sie innerhalb von Proc.new eine Rückgabe durchführen, wird diese tatsächlich von der einschließenden Methode, dh dem umgebenden Kontext, zurückgegeben. Wenn Sie von einem Lambda-Block zurückkehren, kehrt er nur vom Block zurück, nicht von der einschließenden Methode. Grundsätzlich verlässt es den Aufruf des Blocks und setzt die Ausführung mit dem Rest der einschließenden Methode fort.
> def add(a,b)
l = Proc.new { return a + b}
l.call
puts "now exiting method"
end
> add(1,1)
=> 2 # NOTICE it never prints the message "now exiting method"
> def add(a,b)
l = lambda { return a + b }
l.call
puts "now exiting method"
end
> add(1,1)
=> now exiting method # NOTICE this time it prints the message "now exiting method"
Warum also dieser Verhaltensunterschied? Der Grund dafür ist, dass wir mit Proc.new Iteratoren im Kontext einschließender Methoden verwenden und logische Schlussfolgerungen ziehen können. Schauen Sie sich dieses Beispiel an:
> def print(max)
[1,2,3,4,5].each do |val|
puts val
return if val > max
end
end
> print(3)
1
2
3
4
Wir erwarten, dass beim Aufrufen von return innerhalb des Iterators diese von der einschließenden Methode zurückgegeben wird. Denken Sie daran, dass die an Iteratoren übergebenen Blöcke mit Proc.new in Objekte konvertiert werden. Wenn Sie also return verwenden, wird die einschließende Methode beendet.
Sie können sich Lambdas als anonyme Methoden vorstellen. Sie isolieren einzelne Codeblöcke in ein Objekt, das wie eine Methode behandelt werden kann. Stellen Sie sich ein Lambda letztendlich als anomische Methode und Proc.new als Inline-Code vor.
Ein hilfreicher Beitrag zu Ruby Guides: Blöcke, Procs & Lambdas
Procs kehren von der aktuellen Methode zurück, während Lambdas vom Lambda selbst zurückkehren.
Procs kümmern sich nicht um die richtige Anzahl von Argumenten, während Lambdas eine Ausnahme auslösen.
return
Aussage inproc
versus zurückkehrtlambda
.