Was ist der Unterschied zwischen einem Proc und einem Lambda in Ruby?

175

Und wann würden Sie eher das eine als das andere verwenden?

Tom Lehman
quelle
Zusätzlich zur Antwort von jtbandes gibt es auch einen Unterschied darin, von was die returnAussage in procversus zurückkehrt lambda.
Ken Bloom
5
Hier ist ein guter Blog auf dem gleichen awaxman11.github.io/blog/2013/08/05/…
Arup Rakshit
2
Hier ist eine detailliertere Antwort: stackoverflow.com/questions/626/…
Dan KK

Antworten:

259

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 returnin einem Lambda den Wert dieses Lambdas zurück, während die Verwendung returnin 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 returnAnweisung verwenden müssen, um den Wert des Prozesses zurückzugeben, verwenden Sie lambda.

jtbandes
quelle
8
Wäre es richtig zu sagen, dass Lambdas Methoden sehr ähnlich sind (Check-Argumente und Rückgabe werden von ihnen zurückgegeben), während Procs Blöcken sehr ähnlich sind (Argumente werden nicht überprüft und eine Rückgabe wird von der enthaltenen Methode oder Lambda zurückgegeben)?
Pedz
Ich war bei Gott weiß, wie viele Websites und Artikel es inzwischen gibt, und niemand scheint über die Nützlichkeit von Procs vs. Methoden vs. Lambdas zu sprechen. Jede Erklärung liefert nur ein haarspaltendes Detail darüber, wie unterschiedlich Rückgabewerte usw. sind, aber keine darüber, warum es wichtig ist. Im Moment muss ich zu dem Schluss kommen, dass dies ein Design-Chaos in Ruby ist.
ankush981
75

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, retryusw. - 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 putsin der Methode wurde nie ausgeführt, da uns das returndarin 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

shoaib
quelle
6

Es gibt nur zwei Hauptunterschiede.

  • Zunächst lambdaüberprüft a die Anzahl der übergebenen Argumente, während a procdies nicht tut. Dies bedeutet, dass a lambdaeinen Fehler auslöst, wenn Sie ihm die falsche Anzahl von Argumenten übergeben, während a procunerwartete Argumente ignoriert und zuweistnil fehlenden .
  • Zweitens, wenn a lambdazurückkehrt, gibt es die Kontrolle an die aufrufende Methode zurück. Wenn a proczurü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 lambdaMethode kehrt jedoch nach dem Aufruf zur Methode zurück, sodass die Methode den letzten Code zurückgibt, den sie auswertet: "Iron Man wird gewinnen!"

Rajkaran Mishra
quelle
5

# 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

Arvind singh
quelle
1

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, Procaber 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 }}

spirito_libero
quelle
a = proc { |x| x 2 }ist das gleiche wiea = Proc.new { |x| x 2 }
lacostenycoder
1

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.

Donato
quelle
0

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.

Sindhu Shree
quelle
-3

Der Unterschied zwischen proc und lambda besteht darin, dass proc nur eine Kopie des Codes ist, dessen Argumente nacheinander ersetzt werden, während lambda eine Funktion wie in anderen Sprachen ist. (Verhalten der Rückgabe, Argumentprüfung)

Nighon
quelle