Was ist ein Verschluss?
Ein Closure ist ein Funktionscodeblock mit Variablen, die an die Umgebung gebunden sind, in der der Closure aufgerufen wird.
Ein Verschluss hat einige wichtige Eigenschaften:
- es kann wie ein Objekt übergeben werden (aber das bedeutet nicht unbedingt, dass es ein Objekt ist, wie wir unten diskutieren werden),
- ein Abschluss kann in einem Bereich definiert und in einem völlig anderen Bereich aufgerufen werden,
- es merkt sich die Variablen in seinem Bereich zum Zeitpunkt der Erstellung; wenn es aufgerufen wird, kann es auf diese Variablen zugreifen, auch wenn sie sich möglicherweise nicht in diesem aktuellen Bereich befinden.
Blöcke
Blöcke sind eine einfache Form eines Abschlusses in Ruby. Im Gegensatz zu allem anderen in Ruby sind Blöcke keine Objekte.
Blöcke werden mit einem do...end
oder mit {}
konstruiert.
Sie sollten bereits mit Blöcken aus der Arbeit mit Iteratoren und Enumeratoren vertraut sein.
Hier sind zwei bekannte Beispiele für Blöcke:
Die Enumeratormethode .each
akzeptiert einen Block als Argument und ruft den angegebenen Block einmal für jedes Element in der Auflistung auf.
array.each do |x| puts xend#orarray.each { |x| puts x }
Sie haben dies möglicherweise in einem RSpec-Test gesehen; Die before(:each)
-Methode erhält einen Block, zu dem sie für jeden der Tests im selben Kontext führt.
before(:each) do @language = "Ruby"end
Procs
Procs sind eine Art von Closure in Ruby, die sich sehr ähnlich wie Blöcke verhalten, jedoch einige wesentliche Unterschiede aufweisen:
- ein proc kann einer lokalen Variablen zugewiesen werden,
- Eine proc-Instanz wird ausgeführt, indem die
call
-Methode aufgerufen wird, und - mehr als ein proc kann an eine Methode übergeben werden.
Aber im Wesentlichen ist ein proc ein Block, der in ein Objekt umgewandelt wird, indem er einer Instanz der Proc-Klasse zugewiesen wird.
Daher sieht die Erstellung eines proc der Instanziierung einer Klasse sehr ähnlich.
Hier weisen wir einer Instanz der Proc-Klasse einen Block zu und weisen ihn einer Variablen zu. Dann rufen wir die Methode .call
auf:
greeting = Proc.new { "Hello!" }greeting.call=> "Hello!"
Procs können auch Argumente akzeptieren, die an die .call
-Methode übergeben werden:
greeting = Proc.new { |name| "Hello, #{name}!" }greeting.call("Amanda")=> "Hello, Amanda!"
Lambdas
Ein Lambda unterscheidet sich in der Funktionalität nicht wesentlich von einem proc .
Hier wird jeweils ein Lambda an den Enumerator übergeben. Das &
bevor es es in einen Block verwandelt, den jeder als Argument erwartet.
plus_one = lambda { |n| puts n + 1 }array = array.each(&plus_one)=> 2=> 3=> 4
Ein Lambda kann auch auf diese lustige Weise deklariert werden:
plus_one = ->(n) { puts n + 1 }
Lambdas und Procs werden fast synonym verwendet, aber es gibt einige Unterschiede zwischen den beiden:
- lambdas erwarten eine bestimmte Anzahl von Argumenten und suchen nach ihnen; procs gibt nur
nil
für das fehlende Argument zurück, wirft aber keinen Fehler - Sie kehren anders zurück: Lambdas kehren zur aufrufenden Methode zurück und procs kehren sofort zurück, ohne zum Aufrufer zurückzukehren — im Wesentlichen verhalten sich Lambdas eher wie Methodenaufrufe: Sie können sie sich als anonyme Funktionen vorstellen
Abschließende Gedanken
Wenn dies für Sie verwirrend ist, insbesondere für die Procs und Lambdas, machen Sie sich keine Sorgen! Procs und Lambdas sind großartig und ermöglichen es Ihnen, interessanten, leistungsstarken Code zu schreiben. Tag für Tag werden Sie jedoch nicht viel mit ihnen arbeiten. Blöcke, mit denen Sie jedoch jeden Tag interagieren werden. Konzentriere dich darauf, sie am besten kennenzulernen.