Verschluss (Computerprogrammierung)
Im Programmiersprachen, a Schließung, Auch lexikalische Schließung oder Funktionsverschluss, ist eine Technik zur Implementierung lexikalisch abgebildet Namebindung in einer Sprache mit erstklassige Funktionen. Operativ, eine Schließung ist a Aufzeichnung Speichern a Funktion[a] zusammen mit einer Umgebung.[1] Die Umgebung ist eine Kartierung, die jeweils assoziiert freie Variable der Funktion (Variablen, die lokal verwendet, aber in einem umschließenden Bereich definiert werden) mit dem Wert oder Hinweis an die der Name gebunden war, als der Verschluss geschaffen wurde.[b] Im Gegensatz zu einer einfachen Funktion ermöglicht ein Verschluss die Funktion, auf diese zuzugreifen erfasste Variablen durch die Kopien ihrer Werte oder Referenzen des Verschlusses, selbst wenn die Funktion außerhalb ihres Geltungsbereichs aufgerufen wird.
Geschichte und Etymologie
Das Konzept der Schließungen wurde in den 1960er Jahren für die mechanische Bewertung von Ausdrücken in der entwickelt λ-Kalkulus und wurde erstmals 1970 als Sprachmerkmal in der vollständige implementiert KUMPEL Programmiersprache zur Unterstützung von Lexikalisch abgebildet erstklassige Funktionen.[2]
Peter J. Landin definierte den Begriff Schließung im Jahr 1964 als eine Umweltteil und ein Kontrollteil wie von ihm verwendet SECD -Maschine zur Bewertung der Ausdrücke.[3] Joel Moses Credits Landin mit der Einführung des Begriffs Schließung sich auf a beziehen Lambda -Ausdruck deren offene Bindungen (freie Variablen) wurden durch (oder gebunden) in der lexikalischen Umgebung geschlossen, was zu a führte geschlossener Ausdruck, oder Schließung.[4][5] Diese Verwendung wurde anschließend von übernommen von Sussman und Steele wenn sie definierten Planen 1975,,[6] eine lexikalisch abgeschottete Variante von Lispelnund wurde weit verbreitet.
Sussman und Abelson Verwenden Sie auch den Begriff Schließung In den 1980er Jahren mit einer zweiten, nicht verwandten Bedeutung: die Eigenschaft eines Bedieners, der Daten Daten hinzufügt Datenstruktur Auch in der Lage sein, verschachtelte Datenstrukturen hinzuzufügen. Diese Verwendung des Begriffs stammt von Die Nutzung der Mathematik und nicht die vorherige Verwendung in der Informatik. Die Autoren betrachten diese Überschneidung in der Terminologie als "unglücklich".[7]
Anonyme Funktionen
Der Begriff Schließung wird oft als Synonym für verwendet Anonyme FunktionObwohl streng eine anonyme Funktion ist eine Funktion wörtlich Ohne einen Namen, während eine Schließung eine Instanz einer Funktion ist, a Wert, deren nicht lokale Variablen entweder an Werte oder an gebunden wurden Speicherorte (abhängig von der Sprache; siehe die lexikalische Umgebung Abschnitt unten).
Zum Beispiel im Folgenden Python Code:
def f(x): def g(y): Rückkehr x + y Rückkehr g # Eine Schließung zurückgeben. def h(x): Rückkehr Lambda y: x + y # Eine Schließung zurückgeben. # Zuweisen bestimmter Schließungen zu Variablen. a = f(1) b = h(1) # Verwenden der in Variablen gespeicherten Schließungen. behaupten a(5) == 6 behaupten b(5) == 6 # Schließungen verwenden, ohne sie zuerst an Variablen zu binden. behaupten f(1) (5) == 6 # f (1) ist der Verschluss. behaupten h(1) (5) == 6 # H (1) ist der Verschluss.
die Werte von a
und b
sind Schließungen, in beiden Fällen, die durch Rückgabe einer verschachtelten Funktion mit einer freien Variablen aus der beigefügten Funktion erzeugt werden, so dass die freie Variable an den Wert des Parameters bindet x
der eingeschlossenen Funktion. Die Schließungen in a
und b
sind funktionell identisch. Der einzige Unterschied in der Implementierung besteht darin, dass wir im ersten Fall eine verschachtelte Funktion mit einem Namen verwendeten. g
Während wir im zweiten Fall eine anonyme verschachtelte Funktion verwendeten (unter Verwendung des Python -Schlüsselworts Lambda
zum Erstellen einer anonymen Funktion). Der ursprüngliche Name, falls vorhanden bei der Definition, ist irrelevant.
Eine Schließung ist ein Wert wie jeder andere Wert. Es muss nicht einer Variablen zugeordnet werden und können stattdessen direkt verwendet werden, wie in den letzten beiden Zeilen des Beispiels gezeigt. Diese Verwendung kann als "anonyme Schließung" angesehen werden.
Die verschachtelten Funktionsdefinitionen sind nicht selbst Schließungen: Sie haben eine freie Variable, die noch nicht gebunden ist. Erst wenn die einschließende Funktion mit einem Wert für den Parameter bewertet wird, ist die freie Variable der verschachtelten Funktion gebunden, wodurch ein Verschluss erzeugt wird, der dann aus der eingeschlossenen Funktion zurückgegeben wird.
Schließlich unterscheidet sich ein Verschluss nur von einer Funktion mit freien Variablen, wenn sie außerhalb des Umfangs der nicht lokalen Variablen, ansonsten die Definitionsumgebung und die Ausführungsumgebung übereinstimmen, und es gibt nichts zu unterscheiden (statische und dynamische Bindung kann nicht unterschieden werden, weil sie weil, weil sie weil es nicht unterschieden werden kann, weil sie weil Die Namen werden auf die gleichen Werte gelöst). Zum Beispiel funktioniert im folgenden Programm eine kostenlose Variable x
(gebunden an die nicht lokale Variable x
mit globalem Umfang) werden in derselben Umgebung ausgeführt, in der x
ist definiert, daher ist es unerheblich, ob es sich tatsächlich um Schließungen handelt:
x = 1 nums = [1, 2, 3] def f(y): Rückkehr x + y Karte(f, nums) Karte(Lambda y: x + y, nums)
Dies wird am häufigsten durch eine Funktionsrendite erreicht, da die Funktion im Rahmen der nicht lokalen Variablen definiert werden muss. In diesem Fall ist normalerweise sein eigener Bereich kleiner.
Dies kann auch durch erreicht werden variabler Schatten (Dies reduziert den Umfang der nicht-lokalen Variablen), obwohl dies in der Praxis weniger verbreitet ist, da er weniger nützlich ist und das Schatten entmutigt wird. In diesem Beispiel f
kann als Schließung angesehen werden, weil x
im Körper von f
ist an die gebunden x
im globalen Namespace nicht der x
Lokal zu g
:
x = 0 def f(y): Rückkehr x + y def g(z): x = 1 # Lokale X Shadows Global x Rückkehr f(z) g(1) # bewertet 1, nicht 2
Anwendungen
Die Verwendung von Schließungen ist mit Sprachen verbunden, in denen Funktionen sind erstklassige Objekte, in denen Funktionen als Ergebnisse zurückgegeben werden können Funktionen höherer Ordnung, oder als Argumente an andere Funktionsaufrufe übergeben; Wenn Funktionen mit kostenlosen Variablen erstklassig sind, schafft die Rückgabe eines eine Schließung. Das beinhaltet Funktionale Programmiersprachen wie zum Beispiel Lispeln und Mlsowie viele moderne, multi-paradigme Sprachen, wie z.JuliaAnwesendPython undRost. Verschlüsse werden auch häufig mit verwendet Rückrufe, besonders für Event -Handlerwie in JavaScript, wo sie für Interaktionen mit a verwendet werden Dynamische Webseite.
Schließungen können auch in a verwendet werden Fortsetzungsstil zu Zustand verbergen. Konstrukte wie Objekte und Kontrollstrukturen kann also mit Schließungen implementiert werden. In einigen Sprachen kann ein Verschluss auftreten, wenn eine Funktion in einer anderen Funktion definiert wird und die innere Funktion auf lokale Variablen der äußeren Funktion bezieht. Bei LaufzeitWenn die äußere Funktion ausgeführt wird, wird ein Verschluss gebildet, der aus dem Code und Referenzen der inneren Funktion (den Upvalues) auf alle Variablen der äußeren Funktion besteht, die durch den Verschluss erforderlich ist.
Erstklassige Funktionen
Verschlüsse erscheinen typischerweise in Sprachen mit erstklassige Funktionen- Mit anderen Worten, solche Sprachen ermöglichen es, Funktionen als Argumente zu übergeben, von Funktionsaufrufen zurückgegeben, an Variablennamen usw. gebunden zu sein, genau wie einfachere Typen wie Zeichenfolgen und Zahlen. Betrachten Sie beispielsweise Folgendes Planen Funktion:
; Geben Sie eine Liste aller Bücher mit mindestens verkauften Schwellenkopien zurück. (definieren (Bestseller-Bücher Schwelle) (Filter (Lambda (Buchen) (> = (Buchverkauf Buchen) Schwelle)) Bücherliste))
In diesem Beispiel die Lambda -Ausdruck (Lambda (Buch) (> = (Buchverkaufsbuch) Schwelle)))
erscheint innerhalb der Funktion Bestseller-Bücher
. Wenn der Lambda -Ausdruck bewertet wird, erzeugt das Schema einen Verschluss, der aus dem Code für den Lambda -Ausdruck besteht, und einen Verweis auf die Schwelle
Variable, das ist a freie Variable innerhalb des Lambda -Ausdrucks.
Die Schließung wird dann an die übergeben Filter
Funktion, die es wiederholt aufruft, um zu bestimmen, welche Bücher zur Ergebnisliste hinzugefügt werden sollen und welche verworfen werden sollen. Weil die Schließung selbst einen Hinweis darauf hat Schwelle
Es kann diese Variable jedes Mal verwenden Filter
nennt es. Die Funktion Filter
selbst kann in einer vollständig separaten Datei definiert werden.
Hier ist das gleiche Beispiel umgeschrieben in JavaScript, eine weitere populäre Sprache mit Unterstützung für Schließungen:
// Geben Sie eine Liste aller Bücher mit mindestens "Schwellenwert" -Kopien zurück. Funktion Bestsellerbücher(Schwelle) { Rückkehr Bücherliste.Filter( Funktion (Buchen) { Rückkehr Buchen.Verkauf > = Schwelle; } ); }
Das Funktion
Schlüsselwort wird hier anstelle von verwendet Lambda
, und ein Array.filter
Methode[8] statt eines globalen Filter
Funktion, aber ansonsten sind die Struktur und der Effekt des Codes gleich.
Eine Funktion kann wie im folgenden Beispiel einen Verschluss erzeugen und zurückgeben:
// eine Funktion zurückgeben, die dem Ableitung von F annähert // Verwenden eines Intervalls von DX, das angemessen klein sein sollte. Funktion Derivat(f, dx) { Rückkehr Funktion (x) { Rückkehr (f(x + dx) - f(x)) / dx; }; }
Weil die Schließung in diesem Fall die Ausführung der Funktion, die sie erstellt, überlebt, die Variablen f
und dx
Lebe nach der Funktion weiter Derivat
Renditen, obwohl die Ausführung ihren Geltungsbereich verlassen hat und sie nicht mehr sichtbar sind. In Sprachen ohne Schließungen fällt die Lebensdauer einer automatischen lokalen Variablen mit der Ausführung des Stapelrahmens zusammen, in dem diese Variable deklariert wird. In Sprachen mit Schließungen müssen Variablen weiterhin existieren, solange vorhandene Schließungen auf sie hinweisen. Dies wird am häufigsten mit einer Form von implementiert Müllsammlung.
Zustandsrepräsentation
Ein Verschluss kann verwendet werden, um eine Funktion mit einem Satz von "zu assoziieren" zu assoziieren "Privatgelände"Variablen, die über mehrere Einladungen der Funktion bestehen bleiben. Umfang Die Variable umfasst nur die geschlossene Überwachungsfunktion, sodass nicht von einem anderen Programmcode zugegriffen werden kann. Diese sind analog zu private Variablen in Objekt orientierte Programmierungund tatsächlich sind Schließungen analog zu einer Art von Objekt, speziell Funktionsobjektemit einer einzelnen öffentlichen Methode (Funktionsaufruf) und möglicherweise vielen privaten Variablen (die geschlossenen Variablen).
In staatlichen Sprachen können Schließungen daher verwendet werden, um Paradigmen für die staatliche Repräsentation zu implementieren und Informationen verstecken sichDa die Upvalues des Verschlusses (seine geschlossenen Variablen) von unbestimmte Zeit sind AusmaßDaher bleibt ein in einer Anrufung festgelegter Wert im nächsten verfügbar. Auf diese Weise verwendete Schließungen haben nicht mehr Referenztransparenzund sind also nicht mehr reine Funktionen; Trotzdem werden sie üblicherweise in unreinen funktionalen Sprachen verwendet, wie sie Planen.
Andere Verwendungen
Schließungen haben viele Verwendungszwecke:
- Weil die Bewertung der Schließungen verzögert - d. H. Tun, bis sie angerufen werden - können sie verwendet werden, um Kontrollstrukturen zu definieren. Zum Beispiel alle von SmalltalkDie Standardkontrollstrukturen, einschließlich Verzweigungen (if/then/sonst) und Schleifen (während und für), werden unter Verwendung von Objekten definiert, deren Methoden Verschlüsse akzeptieren. Benutzer können auch ihre eigenen Kontrollstrukturen auch einfach definieren.
- In Sprachen, die die Zuordnung implementieren, können mehrere Funktionen erzeugt werden, die sich in derselben Umgebung nähern, sodass sie privat kommunizieren können, indem diese Umgebung geändert wird. Im Schema:
(definieren Foo #f) (definieren Bar #f) (Lassen ((Geheime Nachricht "keiner")) (einstellen! Foo (Lambda (Nachricht) (einstellen! Geheime Nachricht Nachricht))) (einstellen! Bar (Lambda () Geheime Nachricht))) (Anzeige (Bar)) ; Drucke "keine" (Neue Zeile) (Foo "Treffen Sie mich an den Docks um Mitternacht") (Anzeige (Bar)) ; Drucke "Treffen Sie mich von den Docks um Mitternacht"
HINWEIS: Einige Redner nennen eine Datenstruktur, die a bindet lexikalisch Umgebung Ein Schließung, aber der Begriff bezieht sich normalerweise speziell auf Funktionen.
Implementierung und Theorie
Verschlüsse werden normalerweise mit einem Spezifischen implementiert Datenstruktur das enthält a Zeiger auf den Funktionscode, plus eine Darstellung der lexikalischen Umgebung der Funktion (d. H. Der Satz verfügbarer Variablen) zum Zeitpunkt, an dem der Schließung geschaffen wurde. Die Referenzumgebung bindet Die nicht-lokalen Namen zu den entsprechenden Variablen in der lexikalischen Umgebung zum Zeitpunkt des Schließens, der zusätzlich ihre Lebensdauer auf mindestens so lange verlängert wie die Lebensdauer des Verschlusses selbst. Wenn der Verschluss zu einem späteren Zeitpunkt eingegeben wird, möglicherweise mit einer anderen lexikalischen Umgebung, wird die Funktion mit ihren nicht lokalen Variablen ausgeführt, die sich auf die von der Schließung erfassten und nicht die aktuelle Umgebung beziehen.
Eine Sprachimplementierung kann keine vollständigen Verschlüsse unterstützen, wenn ihr Laufzeitgedächtnismodell alle zuteilt Automatische Variablen auf einem linearen Stapel. In solchen Sprachen werden die automatischen lokalen Variablen einer Funktion bei Rückgabe der Funktion behandelt. Eine Schließung erfordert jedoch, dass die freien Variablen, in denen sie verweist, die Ausführung der beigefügten Funktion überleben. Daher müssen diese Variablen so zugeteilt werden, dass sie bis nach nicht mehr benötigt werden, normalerweise über Haufen Allokation, und nicht auf dem Stapel, und ihre Lebensdauer muss verwaltet werden, damit sie überleben, bis alle Verschlüsse, die auf sie verweisen, nicht mehr verwendet werden.
Dies erklärt, warum in der Regel Sprachen, die Verschlüsse nativ unterstützen Müllsammlung. Die Alternativen sind manuellem Speichermanagement nicht-lokaler Variablen (explizit auf dem Haufen zuweisen und bei der Verwendung freiwillig befreien) oder bei Verwendung der Stapelzuweisung, damit die Sprache akzeptiert, dass bestimmte Anwendungsfälle zu akzeptieren, zu undefiniertes Verhalten, wegen Zeiger baumeln automatische Variablen befreit, wie in Lambda -Ausdrücken in C ++ 11[10] oder verschachtelte Funktionen in GNU C.[11] Das Funarg Problem (oder "funktionales Argument" Problem) beschreibt die Schwierigkeit, Funktionen als erstklassige Objekte in einer stackbasierten Programmiersprache wie C oder C ++ zu implementieren. Ähnlich in D Version 1 wird angenommen, dass der Programmierer weiß, was er tun soll Delegierte und automatische lokale Variablen, da ihre Referenzen nach der Rückkehr aus dem Definitionsbereich ungültig sind (automatische lokale Variablen befinden sich im Stapel) - dies ermöglicht immer noch viele nützliche Funktionsmuster, aber für komplexe Fälle sind explizit Haufen Allokation Für Variablen. D Version 2 löste dies durch Erkennung, welche Variablen auf dem Haufen gespeichert werden müssen, und führt eine automatische Zuweisung durch. Da D in beiden Versionen die Müllsammlung verwendet, müssen in beiden Versionen Variablen nicht nachgewiesen werden, sobald sie übergeben werden.
In strengen funktionalen Sprachen mit unveränderlichen Daten (z.B. Erlang) Es ist sehr einfach, die automatische Speicherverwaltung (Müllsammlung) zu implementieren, da die Referenzen von Variablen keine möglichen Zyklen enthalten. In Erlang werden beispielsweise alle Argumente und Variablen auf dem Haufen zugewiesen, aber Verweise darauf werden zusätzlich auf dem Stapel gespeichert. Nach einer Funktionsrückgabe sind die Referenzen weiterhin gültig. Die Haufenreinigung erfolgt durch inkrementelle Müllsammler.
In ML sind lokale Variablen lexikalisch abgelegt und definieren daher ein stapelartiges Modell der Programmierer.
Planen, was eine hat Algol-Das ähnliche lexikalische Bereichssystem mit dynamischen Variablen und Müllsammlung fehlt ein Stack-Programmiermodell und leidet nicht unter den Einschränkungen von Stack-basierten Sprachen. Schließungen werden natürlich im Schema ausgedrückt. Die Lambda -Form schließt den Code ein, und die freien Variablen seiner Umgebung bestehen im Programm bestehen, solange sie möglicherweise zugegriffen werden können, und so können sie so frei verwendet werden wie jedes andere Schema -Ausdruck.
Schließungen sind eng mit den Akteuren in der verwandt Schauspielermodell von gleichzeitiger Berechnung, bei der die Werte in der lexikalischen Umgebung der Funktion aufgerufen werden Bekannte. Ein wichtiges Thema für Schließungen in gleichzeitige Programmierung Sprachen sind, ob die Variablen in einem Verschluss aktualisiert werden können und wie diese Updates synchronisiert werden können. Akteure bieten eine Lösung.[12]
Schließungen sind eng mit dem verwandt mit Funktionsobjekte; Die Transformation von ersteren zu letzterem ist als bekannt als Defunktionalisierung oder Lambda Heben; siehe auch Schließumwandlung.
Unterschiede in der Semantik
Lexikalische Umgebung
Da verschiedene Sprachen nicht immer eine gemeinsame Definition der lexikalischen Umgebung haben, können auch ihre Definitionen des Verschlusses variieren. Die allgemein gehaltene minimalistische Definition der lexikalischen Umgebung definiert sie als eine Reihe von allen Bindungen von Variablen Im Bereich, und das ist auch das, was Schließungen in jeder Sprache erfassen müssen. Allerdings die Bedeutung von a Variable Die Bindung unterscheidet sich auch. In imperativen Sprachen binden Variablen an relative Stellen im Speicher, die Werte speichern können. Obwohl sich der relative Ort einer Bindung zur Laufzeit nicht ändert, kann der Wert an der gebundenen Stelle. In solchen Sprachen, da der Verschluss die Bindung erfasst, werden jede Operation der Variablen, unabhängig davon, ob er aus dem Verschluss durchgeführt wird oder nicht, an demselben relativen Speicherort durchgeführt. Dies wird oft als erfasst die Variable "durch Referenz" bezeichnet. Hier ist ein Beispiel, das das Konzept in veranschaulicht in ECMaskript, was eine solche Sprache ist:
// JavaScript var f, g; Funktion Foo() { var x; f = Funktion() { Rückkehr ++x; }; g = Funktion() { Rückkehr --x; }; x = 1; Alarm("Innerhalb foo, rufen Sie F () an:" + f()); } Foo(); // 2 Alarm('Rufen Sie zu g ():' + g()); // 1 (--x) Alarm('Rufen Sie zu g ():' + g()); // 0 (--x) Alarm('Rufen Sie F () an:' + f()); // 1 (++ x) Alarm('Rufen Sie F () an:' + f()); // 2 (++ x)
Funktion Foo
und die von Variablen genannten Schließungen f
und g
Alle verwenden den gleichen relativen Speicherort, der von der lokalen Variablen angegeben ist x
.
In einigen Fällen kann das obige Verhalten unerwünscht sein, und es ist notwendig, einen anderen lexikalischen Verschluss zu binden. Wieder in ECMascript würde dies mit dem getan werden Function.bind ()
.
Beispiel 1: Verweis auf eine ungebundene Variable
var Modul = { x: 42, getx: Funktion() {Rückkehr Dies.x; } } var ungebunden = Modul.getx; Konsole.Protokoll(ungebunden()); // Die Funktion wird im globalen Bereich aufgerufen // Emites undefiniert als 'x' ist in globaler Umfang nicht angegeben. var Grenzwert = ungebunden.binden(Modul); // Geben Sie das Objektmodul als Verschluss an Konsole.Protokoll(Grenzwert()); // emitiert 42
Beispiel 2: Zufälliger Hinweis auf eine gebundene Variable
In diesem Beispiel wäre das erwartete Verhalten, dass jeder Link seine ID beim Klicken aussenden sollte. Aber weil die Variable 'e' den obigen Bereich gebunden ist und auf Klick faul bewertet wird, ist das, was tatsächlich auf dem Click -Ereignis die ID des letzten Elements in 'Elements' am Ende der for -Schleife ausgibt.[14]
var Elemente= dokumentieren.GetElementsByTagName('a'); // Falsch: E ist an die Funktion gebunden, die die 'für' Schleife enthält, nicht an die Schließung von "Handle" zum (var e von Elemente) { e.ONCLICK=Funktion handhaben() { Alarm(e.Ich würde);} }
Wieder hier variabel e
müsste an den Umfang des Blocks gebunden werden Handle.bind (this)
oder der Lassen
Stichwort.
Andererseits viele funktionale Sprachen, wie z. MlBinden Sie Variablen direkt an Werte. In diesem Fall gibt es keine Möglichkeit, den Wert der Variablen zu ändern, sobald sie gebunden ist, es ist nicht erforderlich, den Zustand zwischen Schließungen zu teilen - sie verwenden nur die gleichen Werte. Dies wird oft als Erfassungsvariable "nach Wert" bezeichnet. Javas lokale und anonyme Kurse fallen ebenfalls in diese Kategorie - sie verlangen, dass er erfasste lokale Variablen ist Finale
, was auch bedeutet, dass es keinen Grund zur Teile des Staates gibt.
Einige Sprachen ermöglichen es Ihnen, zwischen dem Wert einer Variablen oder ihrem Standort zu erfassen. Zum Beispiel werden in C ++ 11 erfasste Variablen entweder mit deklariert mit [&]
, was bedeutet, durch Referenz oder mit erfasst zu werden [=]
, was bedeutet, von Wert erfasst.
Noch eine Teilmenge, faul funktionale Sprachen wie z. HaskellBinden Sie Variablen an die Ergebnisse zukünftiger Berechnungen und nicht an Werte. Betrachten Sie dieses Beispiel in Haskell:
- Haskell Foo :: Fraktional a => a -> a -> (a -> a) Foo x y = (\z -> z + r) wo r = x / y f :: Fraktional a => a -> a f = Foo 1 0 hauptsächlich = drucken (f 123)
Die Bindung von r
erfasst durch den in der Funktion definierten Schließ Foo
ist zur Berechnung (x / y)
- Was in diesem Fall zu einer Teilung um Null führt. Da es sich jedoch um die Berechnung handelt, die erfasst wird, und nicht der Wert, manifestiert sich der Fehler nur, wenn der Verschluss aufgerufen wird, und versucht tatsächlich, die erfasste Bindung zu verwenden.
Schließung gehen
Noch mehr Unterschiede manifestieren sich im Verhalten anderer lexikalisch abgeschreckter Konstrukte wie z. Rückkehr
, Unterbrechung
und fortsetzen
Aussagen. Solche Konstrukte können im Allgemeinen im Hinblick auf das Aufrufen von an betrachtet werden Flucht Fortsetzung durch eine umschließende Steuererklärung (im Fall von festgelegt Unterbrechung
und fortsetzen
Eine solche Interpretation erfordert, dass Schleifenkonstrukte als rekursive Funktionsaufrufe berücksichtigt werden. In einigen Sprachen wie ECMascript, Rückkehr
bezieht sich auf die Fortsetzung, die durch die Schließung lexikalisch innerste in Bezug auf die Aussage festgelegt wurde - dh a Rückkehr
Innerhalb eines Verschlusses überträgt die Kontrolle über den Code, der ihn nannte. Allerdings in Smalltalk, der oberflächlich ähnliche Operator ^
ruft die für die Methodenaufruf festgelegte Fluchtdauer auf und ignoriert die Fluchtdauer aller verschachtelten Schließungen. Die Fluchtdauer eines bestimmten Verschlusses kann nur in SmallTalk implizit aufgerufen werden, indem das Ende des Verschlusses Code erreicht wird. Die folgenden Beispiele in ECMascript und SmallTalk unterstreichen den Unterschied:
"Smalltalk" Foo | xs | xs : = #(1 2 3 4). xs tun: [:x | ^x]. ^0 Bar Transkript Show: (selbst Foo Druckstring) "Drucke 1"
// ecmascript Funktion Foo() { var xs = [1, 2, 3, 4]; xs.für jeden(Funktion (x) { Rückkehr x; }); Rückkehr 0; } Alarm(Foo()); // Drucke 0
Die obigen Code -Ausschnitte verhalten sich anders, weil der SmallTalk ^
Operator und JavaScript Rückkehr
Der Bediener ist nicht analog. Im Beispiel des ECMaskripts,, Rückkehr x
wird die innere Schließung lassen, um eine neue Iteration der zu beginnen für jeden
Schleife, während im SmallTalk -Beispiel, ^x
abbricht die Schleife ab und kehrt aus der Methode zurück Foo
.
Common Lisp Bietet ein Konstrukt, das eine der oben genannten Aktionen ausdrücken kann: Lisp (Return-from Foo x)
verhält sich als Smalltalk ^x
, während Lisp (Rückkehr von Nil x)
verhält sich als JavaScript Rückkehr x
. Daher ermöglicht SmallTalk eine erfasste Fluchtdauer, um das Ausmaß zu überleben, in dem sie erfolgreich aufgerufen werden kann. In Betracht ziehen:
"Smalltalk" Foo ^[ :x | ^x ] Bar | f | f : = selbst Foo. f Wert: 123 "Error!"
Wenn der Verschluss nach der Methode zurückkehrte Foo
wird aufgerufen, es versucht, einen Wert aus der Aufruf von zurückzugeben Foo
Das schuf die Schließung. Da dieser Anruf bereits zurückgegeben wurde und das SmallTalk -Methode -Aufrufmodell nicht dem folgt Spaghetti Stack Disziplin Um mehrere Rückgaben zu erleichtern, führt dieser Betrieb zu einem Fehler.
Einige Sprachen, wie z. RubinErmöglichen Sie dem Programmierer, den Weg zu wählen Rückkehr
ist gefangen. Ein Beispiel in Ruby:
# Ruby # Schließung mit einem Proc def Foo f = Proc.Neu { Rückkehr "Kehren Sie von Foo von Inside Proc zurück" } f.Anruf # Control verlässt Foo hier Rückkehr "Rückkehr von Foo" Ende # Schließung mit einem Lambda def Bar f = Lambda { Rückkehr "Rückkehr aus Lambda" } f.Anruf # Die Kontrolle verlässt die Bar hier nicht Rückkehr "Rückkehr aus der Bar" Ende stellt Foo # Drucke "Rückkehr von Foo von Inside Proc" zurück " stellt Bar # Drucke "Rückkehr aus der Bar"
Beide Proc.New
und Lambda
In diesem Beispiel sind Möglichkeiten, einen Verschluss zu schaffen, aber die Semantik der so erzeugten Verschlüsse unterscheidet sich in Bezug auf die Rückkehr
Aussage.
Im Planen, Definition und Umfang der Rückkehr
Die Kontrollanweisung ist explizit (und nur willkürlich "Rückgabe" für das Beispiel). Das Folgende ist eine direkte Übersetzung der Ruby -Probe.
; Planen (definieren Rufen Sie/cc Call-with-Strom-Einsatz) (definieren (Foo) (Rufen Sie/cc (Lambda (Rückkehr) (definieren (f) (Rückkehr "Kehren Sie von Foo von Inside Proc zurück")) (f) ; Kontrollverlasst Foo hier (Rückkehr "Rückkehr von Foo")))) (definieren (Bar) (Rufen Sie/cc (Lambda (Rückkehr) (definieren (f) (Rufen Sie/cc (Lambda (Rückkehr) (Rückkehr "Rückkehr aus Lambda")))) (f) ; Die Kontrolle verlässt die Bar hier nicht (Rückkehr "Rückkehr aus der Bar")))) (Anzeige (Foo)) ; Drucke "Rückkehr von Foo von Inside Proc" zurück " (Neue Zeile) (Anzeige (Bar)) ; Drucke "Rückkehr aus der Bar"
Verschlussähnliche Konstrukte
Einige Sprachen haben Merkmale, die das Verhalten von Schließungen simulieren. In Sprachen wie Java, C ++, Objective-C, C#, VB.NET und D sind diese Funktionen das Ergebnis des objektorientierten Paradigmas der Sprache.
Rückrufe (c)
Etwas C Bibliotheken unterstützenRückrufe. Dies wird manchmal implementiert, indem zwei Werte bereitgestellt werden, wenn der Rückruf bei der Bibliothek registriert wird: ein Funktionszeiger und ein separat Leere*
Zeiger auf willkürliche Daten der Wahl des Benutzers. Wenn die Bibliothek die Rückruffunktion ausführt, wird der Datenzeiger entlang geleitet. Dies ermöglicht den Rückruf, den Zustand zu pflegen und auf Informationen zu verweisen, die zum Zeitpunkt der Registrierung der Bibliothek erfasst wurden. Die Idiom ähnelt Verschlüssen in der Funktionalität, jedoch nicht der Syntax. DasLeere*
Zeiger ist nicht Geben Sie sicher Diese C-Idiom unterscheidet sich also von Typ-Safe-Verschlüssen in C#, Haskell oder ML.
Rückrufe werden in GUI ausführbar verwendet Widget -Toolkits implementieren Ereignisgesteuerte Programmierung Durch die Zusammenarbeit mit allgemeinen Funktionen grafischer Widgets (Menüs, Schaltflächen, Kontrollkästchen, Schieberegler, Spinner usw.) mit anwendungsspezifischen Funktionen, die das spezifische Verhalten für die Anwendung implementieren.
Verschachtelter Funktion und Funktion Zeiger (c)
Mit einer GCC -Erweiterung a verschachtelte Funktion Kann verwendet werden und ein Funktionszeiger kann Verschluss emulieren, sofern die Funktion nicht den enthaltenden Bereich beendet. Das folgende Beispiel ist ungültig, weil Addierer
ist eine Definition der obersten Ebene (abhängig von der Compiler-Version kann sie ein korrektes Ergebnis erzielen, wenn sie ohne Optimierung kompiliert wird, d. H. Bei -O0
):
#enthalten Typedef int (*fn_int_to_int) (int); // Art der Funktion int-> int fn_int_to_int Addierer(int Nummer) { int hinzufügen (int Wert) { Rückkehr Wert + Nummer; } Rückkehr &hinzufügen; // & Operator ist hier optional, da der Name einer Funktion in C ein Zeiger ist, der auf sich selbst zeigt } int hauptsächlich(Leere) { fn_int_to_int Add10 = Addierer(10); printf("%d\n", Add10(1)); Rückkehr 0; }
Aber bewegen Addierer
(und optional die Typedef
) in hauptsächlich
macht es gültig:
#enthalten int hauptsächlich(Leere) { Typedef int (*fn_int_to_int) (int); // Art der Funktion int-> int fn_int_to_int Addierer(int Nummer) { int hinzufügen (int Wert) { Rückkehr Wert + Nummer; } Rückkehr hinzufügen; } fn_int_to_int Add10 = Addierer(10); printf("%d\n", Add10(1)); Rückkehr 0; }
Wenn dies ausgeführt wird, druckt dies jetzt aus 11
wie erwartet.
Lokale Kurse und Lambda -Funktionen (Java)
Java ermöglicht Klassen im Inneren definiert werden Methoden. Diese nennt man Lokale Klassen. Wenn solche Klassen nicht genannt werden, sind sie als bekannt als als bekannt Anonyme Klassen (oder anonym innere Klassen). Eine lokale Klasse (entweder genannt oder anonym) kann sich auf Namen in lexikalisch einschließlichen Klassen oder schreibgeschützten Variablen beziehen (markiert als Finale
) bei der lexikalisch einschließenden Methode.
Klasse Berechnungswindow erweitert JFrame { Privatgelände flüchtig int Ergebnis; // ... Öffentlichkeit Leere Berechnen Sie die Einseparatethead(Finale Uri Uri) { // Der Ausdruck "new Runnable () {...}" ist eine anonyme Klasse, die die Schnittstelle 'Runnable' implementiert. Neu Faden( Neu Laufbar() { Leere Lauf() { // Es kann endgültige lokale Variablen lesen: Berechnung(Uri); // Es kann auf private Felder der beigefügten Klasse zugreifen: Ergebnis = Ergebnis + 10; } } ).Anfang(); } }
Die Erfassung von Finale
Mit Variablen können Sie Variablen nach Wert erfassen. Auch wenn die Variable, die Sie erfassen möchten, nicht istFinale
Sie können es jederzeit zu einem vorübergehenden kopieren Finale
Variable kurz vor der Klasse.
Das Erfassen von Variablen durch Referenz kann durch Verwendung a emuliert werden Finale
Verweise auf einen veränderlichen Container beispielsweise ein Einzel-Element-Array. Die lokale Klasse kann den Wert der Containerreferenz selbst nicht ändern, aber in der Lage sein, den Inhalt des Containers zu ändern.
Mit dem Aufkommen der Lambda -Ausdrücke von Java 8,,[15] Der Verschluss bewirkt, dass der obige Code als:
Klasse Berechnungswindow erweitert JFrame { Privatgelände flüchtig int Ergebnis; // ... Öffentlichkeit Leere Berechnen Sie die Einseparatethead(Finale Uri Uri) { // Der Code () -> {/ * Code */} ist ein Verschluss. Neu Faden(() -> { Berechnung(Uri); Ergebnis = Ergebnis + 10; }).Anfang(); } }
Lokale Klassen sind eine der Arten von innere Klasse die im Körper einer Methode deklariert werden. Java unterstützt auch innere Klassen, die als als deklariert werden Nichtstatische Mitglieder einer umschließenden Klasse.[16] Sie werden normalerweise als "innere Klassen" bezeichnet.[17] Diese sind im Körper der beigefügten Klasse definiert und haben vollen Zugriff auf Instanzvariablen der beigefügten Klasse. Aufgrund ihrer Bindung an diese Instanzvariablen darf eine innere Klasse nur mit einer expliziten Bindung an eine Instanz der umschließenden Klasse unter Verwendung einer speziellen Syntax instanziiert werden.[18]
Öffentlichkeit Klasse Einschließungsklasse { / * Definieren Sie die innere Klasse */ Öffentlichkeit Klasse Innerklasse { Öffentlichkeit int IncrementAndReturnCounter() { Rückkehr Zähler++; } } Privatgelände int Zähler; { Zähler = 0; } Öffentlichkeit int Getcounter() { Rückkehr Zähler; } Öffentlichkeit statisch Leere hauptsächlich(Saite[] Args) { Einschließungsklasse Einschließungsklassinstanz = Neu Einschließungsklasse(); / * Die innere Klasse instanziieren, mit Bindung an die Instanz */ Einschließungsklasse.Innerklasse Innenklasse = Einschließungsklassinstanz.Neu Innerklasse(); zum (int i = Einschließungsklassinstanz.Getcounter(); (i = Innenklasse.IncrementAndReturnCounter()) < 10; / * Inkrementschritt weggelassen */) { System.aus.println(i); } } }
Nach Ausführung wird die Ganzzahlen von 0 bis 9 gedruckt. Vorsicht, dass diese Art von Klasse nicht mit der verschachtelten Klasse verwechselt wird, die auf die gleiche Weise mit einer begleiteten Verwendung des "statischen" Modifikators deklariert ist. Diese haben nicht den gewünschten Effekt, sondern sind stattdessen nur Klassen ohne besondere Bindung in einer umschließenden Klasse.
Ab Java 8Java unterstützt Funktionen als erstklassige Objekte. Lambda -Ausdrücke dieser Form werden als Typ betrachtet Funktion
Mit T ist die Domäne und u der Bildtyp. Der Ausdruck kann mit seinem aufgerufen werden .Apply (t t)
Methode, aber nicht mit einem Standard -Methodenaufruf.
Öffentlichkeit statisch Leere hauptsächlich(Saite[] Args) { Funktion<Saite, Ganze Zahl> Länge = s -> s.Länge(); System.aus.println( Länge.anwenden("Hallo Welt!") ); // wird 13 drucken. }
Blöcke (C, C ++, Objektiv-C 2.0)
Apfel eingeführt Blöcke, eine Form der Schließung, als nicht standardmäßige Erweiterung in C, C ++, Ziel-C 2.0 und in MAC OS X 10.6 "Schneeleopard" und iOS 4.0. Apple stellte seine Implementierung für die GCC- und Clang -Compiler zur Verfügung.
Zeiger zu blockieren und blockieren Literale sind mit markiert ^
. Normale lokale Variablen werden beim Erstellen des Blocks nach Wert erfasst und sind im Block nur schreibgeschützt. Variablen, die durch Referenz erfasst werden sollen __Block
. Blöcke, die außerhalb des Bereichs, in dem sie erstellt werden, bestehen müssen, müssen möglicherweise kopiert werden.[19][20]
Typedef int (^Intblock) (); Intblock Downcounter(int Anfang) { __Block int i = Anfang; Rückkehr [[ ^int() { Rückkehr i--; } Kopieren] Autorelease]; } Intblock f = Downcounter(5); Nslog(@"%d", f()); Nslog(@"%d", f()); Nslog(@"%d", f());
Delegierte (C#, vb.net, d)
C# Anonyme Methoden und Lambda -Ausdrücke unterstützen die Schließung:
var Daten = Neu[] {1, 2, 3, 4}; var Multiplikator = 2; var Ergebnis = Daten.Auswählen(x => x * Multiplikator);
Visual Basic .net, das viele Sprachmerkmale aufweist, die denen von C#ähneln, unterstützt auch Lambda -Ausdrücke mit Schließungen:
Schwach Daten = {1, 2, 3, 4} Schwach Multiplikator = 2 Schwach Ergebnis = Daten.Auswählen(Funktion(x) x * Multiplikator)
Im D, Schließungen werden von Delegierten, einem Funktionszeiger implementiert, der mit einem Kontextzeiger gepaart ist (z. B. eine Klasseninstanz oder ein Stapelrahmen auf dem Haufen im Fall von Schließungen).
Auto Test1() { int a = 7; Rückkehr delegieren() { Rückkehr a + 3; }; // Anonyme Delegiertekonstruktion } Auto Test2() { int a = 20; int Foo() { Rückkehr a + 5; } // innere Funktion Rückkehr &Foo; // Andere Möglichkeit, Delegierter zu konstruieren } Leere Bar() { Auto dg = Test1(); dg(); // = 10 // ok, test1.a ist in einem Verschluss und existiert immer noch dg = Test2(); dg(); // = 25 // ok, test2.a ist in einem Verschluss und existiert immer noch }
D Version 1 hat eine begrenzte Unterstützung für die Schließung. Beispielsweise funktioniert der obige Code nicht richtig, da sich die Variable A auf dem Stapel befindet und nach der Rückkehr von test () nicht mehr gültig ist, ihn zu verwenden (höchstwahrscheinlich wird Foo über DG () ein Ruf zurück. zufällige 'Ganzzahl). Dies kann gelöst werden, indem die Variable "a" auf Heap explizit zugewiesen wird, oder mit Strukturen oder Klassen alle benötigten geschlossenen Variablen speichern und einen Delegierten aus einer Methode konstruieren, die denselben Code implementiert. Schließungen können an andere Funktionen übergeben werden, solange sie nur verwendet werden, während die referenzierten Werte weiterhin gültig sind (z. In der Praxis ist oft kein Problem.
Diese Einschränkung wurde in D -Version 2 festgelegt - die Variable 'A' wird automatisch auf dem Haufen zugewiesen, da sie in der inneren Funktion verwendet wird, und ein Delegierter dieser Funktion kann dem aktuellen Bereich entkommen (über Zuordnung zu DG oder Rückgabe). Andere lokale Variablen (oder Argumente), auf die nicht von Delegierten verwiesen wird oder auf die nur von Delegierten verwiesen wird, auf die nicht dem aktuellen Bereich entkommen, bleiben auf dem Stapel, was einfacher und schneller als die Haufen -Zuordnung ist. Gleiches gilt für die Klassenmethoden von Inner, die die Variablen einer Funktion verweisen.
Funktionsobjekte (C ++)
C ++ ermöglicht das Definieren Funktionsobjekte durch Überlastung Operator()
. Diese Objekte verhalten sich ähnlich wie Funktionen in einer funktionalen Programmiersprache. Sie können zur Laufzeit erstellt werden und enthalten Zustand, erfassen jedoch nicht implizit lokale Variablen wie Schließungen. Ab Die Revision 2011Die C ++ - Sprache unterstützt auch Verschlüsse, bei denen es sich um eine Art von Funktionsobjekt handelt, die automatisch aus einem speziellen Sprachkonstrukt aufgerufen wurden Lambda-Expression. Ein C ++ - Verschluss kann seinen Kontext erfassen, indem sie Kopien der zugegriffenen Variablen als Mitglieder des Verschlussobjekts oder durch Bezugnahme speichern. Im letzteren Fall entgeht das Verschlussobjekt dem Umfang eines referenzierten Objekts und beruft sich seine Operator()
verursacht undefiniertes Verhalten, da C ++ - Verschlüsse nicht die Lebensdauer ihres Kontextes verlängern.
Leere Foo(Saite mein Name) { int y; Vektor<Saite> n; // ... Auto i = std::find_if(n.Start(),, n.Ende(),, // Dies ist der Lambda -Ausdruck: [&] (Const Saite& s) { Rückkehr s ! = mein Name && s.Größe() > y; } ); // 'i' ist jetzt entweder 'n.end ()' oder zeigt auf die erste Zeichenfolge in 'n' // das ist nicht gleich 'myname' und dessen Länge größer ist als 'y' }
Inline -Agenten (Eiffel)
Eiffel Beinhaltet Inline -Agenten, die Schließungen definieren. Ein Inline-Agent ist ein Objekt, das eine Routine darstellt, die durch Angabe des Code der Routine inline definiert wird. Zum Beispiel in
OK_Button.Click_Event.Abonnieren ( Agent (x, y: GANZE ZAHL) tun Karte.Country_at_coordinates (x, y).Anzeige Ende )
das Argument an Abonnieren
ist ein Agent, der ein Verfahren mit zwei Argumenten darstellt; Das Verfahren findet das Land in den entsprechenden Koordinaten und zeigt es an. Der gesamte Agent wird dem Ereignisart "abonniert" Click_Event
Für eine bestimmte Schaltfläche, damit bei dieser Schaltfläche eine Instanz des Ereignisentyps auftritt - da ein Benutzer auf die Schaltfläche geklickt hat -, wird die Prozedur mit den Mauskoordinaten ausgeführt, die als Argumente für übergeben werden x
und y
.
Die Hauptbegrenzung von Eiffeltonenten, die sie von Schließungen in anderen Sprachen unterscheiden, besteht darin, dass sie nicht lokale Variablen vom umschließenden Bereich verweisen können. Diese Entwurfsentscheidung hilft bei der Vermeidung von Mehrdeutigkeiten, wenn es um einen lokalen variablen Wert in einer Schließung geht. Sollte dies der neueste Wert der Variablen oder des Wertes sein, der beim Erstellen des Agenten erfasst wird? Nur Aktuell
(Ein Hinweis auf das aktuelle Objekt, analog zu Dies
in Java) können seine Merkmale und Argumente des Agenten selbst aus dem Agentenkörper zugänglich sein. Die Werte der äußeren lokalen Variablen können durch zusätzliche geschlossene Operanden an den Agenten übergeben werden.
C ++ Builder __cLosure reserviertes Wort
Embarcadero C ++ Builder bietet das Reservewort __CLOSURE, um einen Zeiger auf eine Methode mit einer ähnlichen Syntax wie einem Funktionszeiger bereitzustellen.[21]
In Standard C könnten Sie a schreiben Typedef für einen Zeiger auf a Funktionstyp Verwenden der folgenden Syntax:
Typedef Leere (*TMyFunctionPointer) ( Leere );
In ähnlicher Weise können Sie a deklarieren Typedef Für einen Zeiger auf eine Methode unter Verwendung der folgenden Syntax:
Typedef Leere (__Schließung *Tmymethodpointer) ();
Siehe auch
- Anonyme Funktion
- Blocks (C language extension)
- Befehlsmuster
- Fortsetzung
- Currying
- Funarg Problem
- Lambda -Kalkül
- Faule Bewertung
- Teilweise Anwendung
- Spaghetti Stack
- Syntactic closure
- Wertschöpfungsprogrammierung
Anmerkungen
- ^ Die Funktion kann als speichert werden Hinweis zu einer Funktion wie a Funktionszeiger.
- ^ Diese Namen beziehen sich am häufigsten auf Werte, veränderliche Variablen oder Funktionen, können aber auch andere Entitäten wie Konstanten, Typen, Klassen oder Etiketten sein.
Verweise
- ^ Sussman und Steele. "Schema: Ein Dolmetscher für erweiterte Lambda -Kalkül". "... eine Datenstruktur, die einen Lambda -Ausdruck enthält, und eine Umgebung, die verwendet werden soll, wenn dieser Lambda -Ausdruck auf Argumente angewendet wird." (Wikisource)
- ^ David A. Turner (2012). "Einige Geschichte funktionaler Programmiersprachen". Trends bei der funktionellen Programmierung '12. Abschnitt 2, Anmerkung 8 enthält den Anspruch zu M-Expressionen.
- ^ P. J. Landin (1964), Die mechanische Bewertung von Ausdrücken
- ^ Joel Moses (Juni 1970), Die Funktion der Funktion in Lisp oder warum das Funarg -Problem als Umweltproblem bezeichnet werden sollte, HDL:1721.1/5854, KI -Memo 199,
Eine nützliche Metapher für den Unterschied zwischen Funktion und Zitat in LISP ist das Zitat als eine poröse oder offene Abdeckung der Funktion, da freie Variablen zur aktuellen Umgebung entkommen. Funktion wirkt als geschlossene oder nicht poröse Abdeckung (daher der von Landin verwendete Begriff "Verschluss"). So sprechen wir von "offenen" Lambda -Ausdrücken (Funktionen in Lisp sind normalerweise Lambda -Ausdrücke) und "geschlossen" Lambda -Ausdrücke. [...] Mein Interesse am Umweltproblem begann, während Landin, der das Problem tief verstand, von 1966 bis 1967 besuchte. Ich habe dann die Korrespondenz zwischen den Funarg -Listen festgestellt, die die Ergebnisse der Bewertung von "geschlossenen" Lambda -Ausdrücken in sind LISPELN und ICH SCHWIMME'S Lambda -Schließungen.
- ^ Åke Wikström (1987). Funktionelle Programmierung mit Standard -ML. ISBN 0-13-331968-7.
Der Grund, warum es als "Verschluss" bezeichnet wird, ist, dass ein Ausdruck, der freie Variablen enthält, als "offener" Ausdruck bezeichnet wird. Durch die Assoziation der Bindungen seiner freien Variablen schließen Sie ihn.
- ^ Gerald Jay Sussman und Guy L. Steele, Jr. (Dezember 1975), Schema: Ein Dolmetscher für den erweiterten Lambda -Kalkül, KI -Memo 349
- ^ Abelson, Harold; Sussman, Gerald Jay; Sussman, Julie (1996). Struktur und Interpretation von Computerprogrammen. Cambridge, MA: MIT Press. p. 98–99. ISBN 0262510871.
- ^ "Array.filter". Mozilla Developer Center. 10. Januar 2010. Abgerufen 9. Februar 2010.
- ^ "Re: FP, OO und Relations. Trump jemand die anderen?". 29. Dezember 1999. archiviert von das Original am 26. Dezember 2008. Abgerufen 23. Dezember 2008.
- ^ Lambda -Ausdrücke und Schließungen C ++ Standards Ausschuss. 29. Februar 2008.
- ^ GCC -Handbuch, 6.4 verschachtelte Funktionen, "Wenn Sie versuchen, die verschachtelte Funktion nach der Angabe der enthaltenden Funktionen durch ihre Adresse zu bezeichnen, bricht die Hölle los. Wenn Sie versuchen, sie nach einem Ablauf des Umfangs zu rufen, und wenn sie sich auf einige der Variablen bezieht, die nicht mehr sind, In Scope mögen Sie Glück haben, aber es ist nicht ratsam, das Risiko einzugehen. Wenn sich die verschachtelte Funktion jedoch nicht auf etwas bezieht, das aus dem Umfang gegangen ist, sollten Sie sicher sein. "
- ^ Grundlagen der Schauspielersemantik Will klebern. MIT -Mathematik -Dissertation. Juni 1981.
- ^ "Function.prototype.bind ()". MDN Web Docs. Abgerufen 20. November 2018.
- ^ "Schließungen". MDN Web Docs. Abgerufen 20. November 2018.
- ^ "Lambda Ausdrücke (die Java -Tutorials)".
- ^ "Verschachtelte, innere, Mitglieds- und Top-Level-Klassen".
- ^ "Beispiel für innere Klasse (die Java -Tutorials> Lernen der Java -Sprache> Klassen und Objekte)".
- ^ "Verschachtelte Klassen (die Java -Tutorials> Lernen der Java -Sprache> Klassen und Objekte)".
- ^ Apple Inc. "Blöcke programmieren Themen". Abgerufen 8. März 2011.
- ^ Joachim Bengtsson (7. Juli 2010). "Programmierung mit C -Blöcken auf Apple -Geräten". Archiviert von das Original am 25. Oktober 2010. Abgerufen 18. September 2010.
- ^ Vollständige Dokumentation finden Sie unter http://docwiki.embarcadero.com/radstudio/rio/en/clouscut
Externe Links
- Original "Lambda Papers": Eine klassische Reihe von Papieren von Guy Steele und Gerald Sussman Unter anderem über die Vielseitigkeit von Schließungen im Kontext des Schemas (wo sie als erscheinen Lambda Ausdrücke).
- Neal Gafter (28. Januar 2007). "Eine Definition von Schließungen".
- Gilad Bracha, Neal Gafter, James Gosling, Peter von der Ahé. "Schließungen für die Java -Programmiersprache (v0.5)".
{{}}
: Cs1 montiert: Mehrfachnamen: Autorenliste (Link) - Schließungen: Ein Artikel über Schließungen in dynamisch getippt imperative Sprachen von, von Martin Fowler.
- Sammelverschlussmethoden: Ein Beispiel für einen technischen Bereich, in dem die Verwendung von Verschlüssen von Martin Fowler bequem ist.