Verschluss (Funktion)



Eine Closure (oder Funktionsabschluss ) ist ein Konzept der funktionalen Programmierung . Die Beschreibung dieser Funktion , die Zugriffe auf ihren Erstellungskontext enthält. Beim Aufruf greift die Funktion dann auf diesen Erstellungskontext zu. Dieser Kontext (Speicherbereich, Zustand) ist für die Funktion nicht referenzierbar, d.h.

Eine Closure enthält eine Referenz auf die Funktion und den erwarteten Aufbau von Rekonstruktionskontexten – die Funktion und die sichere Speicherstruktur sind in einer Referenz untrennbar abgeschlossen (closed term). Es ist Vergleichbar mit Einem Objekt mit Attributen und Methoden: es Enthält Ein implizite Identität, EIN Zustand und ein verhalten.

In der Programmiersprache Syntax wird diese oft durch zwei verschachtelte Funktre erreicht. Diese Abschlussfunktion enthält die benigte Speicherstruktuur (siehe Beispiele unten). Das ist so konstruiert, das heißt, es ist möglich, auf die innere Funktion mit den entsprechenden Variablen (der Speicherstruktur) zu verweisen. Genuaugenommen ist hier der Verschluss , der die innere Funktion hat, bis zum Ende des Lebens, die innere und äußere Funktion, und die Referenz.

Eine Schließung ist in der Regel als Objekt bekannt. Wird die Closure für das Weben von Verschlüssen verwendet. Die im Closure resignierten Schwärzer aus dem erugugenden Bereich können als Spoiler bezeichnet werden.

Herkunft

Verschlüsse Sind ein Konzept, das aus den Funktional Programmiersprachen stammen, zum Ersten Mal in Lisp auftritt und in Seinem Dialect Schema erstmal Vollständig Unterstützt Wird. Daraufhin wurde es auch in den gesprochenen Funktionen Programmierpraxis (etwa Haskell , Ocaml ) unterstützt.

Vorteile und Eigenschaften

Mit Verschlüssen Können nicht Sichtbare, aber Kontrolliert veränderbaren Bereich Erstellt Werden, beispielsweise Kann DAMIT Datenkapselung Realisiert oder currying umgesetzt Werden.

Die Erzeugung einer Schließung ist mit deutlich weniger Arbeit verbunden als die Erstellung einer Klasse mit nur einer Methode. Objektorientierter Sichtweise nach der Immobilie sich Closures so zur Wiederherstellung Erzeugung einer objektähnlichen Struktur ohne eine Klasse. Oftmals wird als innere Methode eine anonyme Funktion verwendet.

In einer reinfunktionalen Programmierung von Spreading ist es möglich, dass jede der Closure-Dateien verdorben wird, aber es ist möglich, als eine Funktion als ein Parameter in einem einzigen Funktionsmodus zu funktionieren. Im letzten Fall können Sie Ihre Callback-Funktion nutzen, um ein Anwendungsprogramm zu erstellen. Dies wird natürlich von einem System von Closures praktisch sinful ermöglicht. Auf dieser Tatsache beruhen das didaktische Problem, unerfahrenen Programmierern der Heften Anwendung.

Beispiel in Pseudocode

Im folgenden Beispiel wird eine Funktion mutterfunktiondefiniert. Diese Funktion setzt Ihre lokale Variable im Auftrag von kuchentypund definiert Ihre lokale Funktion im Auftrag von kindfunktion.

Funktion mutterfunktion {
 Setzer kuchentyp = 'Apfelkuchen'
 Funktion kindfunktion {
 gib_aus 'Ich ess # {kuchentyp}
 } gib_zurück kindfunktion
}

Bei einem Aufruf gibt es die Lokale mutterfunktionFunktion kindfunktion( nieicht deren Ergebnis!). (Dies ist in nicht Funktional Wie Programmiersprachen C und Verwandten technisch Auch als Funktionszeiger Bekannt. Ein typisierter Funktionszeiger heißt Delegieren .)

Setzer meinkuchen = rufe_auf mutterfunktion

Die globale Variable meinkuchenbetrifft auch die Funktionalität kindfunktion.

rufe_auf meinkuchen

Beim anschließenden Aufruf von meinkuchenwird gelegentlich kindfunktionausgeführt. Obwohl keine Vermittlung globale Variable kuchentypexistiert, Gibt kindfunktionsterben Zeichenkette 'Ich esse Apfelkuchen'aus, Weil sie auf Empfehlung : Ihren Erstellungskontext zugreifen Kann, in ihnen Variable sterben kuchentypmit 'Apfelkuchen'bestimmt ist. Getrennt ist es möglich: Obwohl mutterfunktionschon ein Wert zurückgegeben hat – der Kontext besitzt auch nicht mehr mehr – kann kindfunktiondarauf – kindfunktionist auch eine Closure-Funktion.

[Ausgabe:] Ich esse Apfelkuchen

Mit Einer Änderung im Kodex Wird nun der Wert Die Variable der anzahl_kuchenin der mutterfunktionmit Jedem zugriff auf Schließens-Funktion um eins erhöht, Womit sich ein Zahle Realisiert Lässt. Es gab eine anzahl_kuchenManipulation, die gemacht wurde und essennicht gemacht werden konnte.

Funktion mutterfunktion {
 Setzer anzahl_kuchen = 0
 Funktion kindfunktion {
 Setzer anzahl_kuchen = anzahl_kuchen + 1
 gib_aus 'Ich ess # {} anzahl_kuchen Kuchen'
 } gib_zurück kindfunktion
}

Mit mehrfach aufrufen , wo Mutterfunktion von other Programmteilen aus Kann nur indirekt eigentlich nicht mehr auf die Sichtbaren Wert , wo der Veranstaltungsort Variable <anzahl_kuchen> zugegriffen Werden, und (nur) Innerhalb der kindfunktioncan (gekapselte) Berechnungen mit sonst nicht veränderbaren Wert vorgenommen Werden – das Zeigt sterben Hauptvorteile von Closures

Setzer essen = rufe_auf mutterfunktion
 rufe_auf essen
 rufe_auf essen
 rufe_auf essen
Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

Es gibt einen direkten Link auf die Variable, anzahl_kuchendaher ist es möglich, dass Ihr Wert kann oder ich nicht sofort korrigieren kann. Keinesfalls ist der Wert aber von außen veränderbar, DAMIT Bieten Verschlüsse mehr als Zugriffsschutz ETWA als „private“ deklarierte Felder Wacholder Klasse ETWA in Java oder C # , wo ETWA mit Reflexion einfach zu umgehen ist.

Wer es interpretiert, hängt stark von seinen eigenen Sichtweisen auf Programmiersprachen ab. Die Mutterfunktion Wechsel verkraftet aus objektorientierter Sichtweise stirbt Rolle Einer Klasse, genau Ein objekt (wo Instanz Wacholder-Klasse) und kapselt aus Sicht objektorientierter so Kindvariablen mit Kindfunktion (eins) zu Einer Einheit.

Anders gesehen wird so eine Art aufrufübergreifendes „Gedächtnis“ in der Funktion umgesetzt, ähnlich einer statischen Variablen, nur leistungsfähiger. Beides kann nicht anders betrachtet werden, es ist möglich, die Veränderung des Kontrollflusses zu sehen, der Beispiel sehr gut sieht. Aufzählungen können etwa als Funktionsaufruf umgesetzt werden, da der Fall des Aufrufs (weil des Gedächtnisses) eine andere Möglichkeit war. C # wird als Spezialfall etwa bei der Implementierung von „yield return“ verwendet. Wurde dabei pro Aufruf Schritt für Schritt der nächsten Elements Einer aufzählbare typs Wie Eine List, sozusagen „Faul“ (faul) , AVW ressourcensparend nur bei Bedarf zurückzugeben [1] .

Konzeptionelle Voraussetzungen für Abschluss in Programmiersprachen

Verschlüsse Stellen Wie Erwähnt ein Muster Funktional Programmierung dar, Sie sind für Programmierer nicht Rentiere Funktional Programmiersprachen oftmals schwer zu verstehen, Auch, ideal in zunehmend mehr Programmiersprachen umsetzbar Ist.

Folgende Conceptual „Bausteine“ sind notwendig, um eine Schließung in einer Programmingsprache umsetzbar zu machen.

1. Die Leistung Müssen als Rückgabeobjekte Einer other Funktion Erlaubt sein, über die minde Freund zur Hilfe gezogener Elemente Wie Funktionszeiger , Delegierten oder Lambda Ausdrücke. Hier finden Sie das erstklassige Feature . (Das Gegenteil ist spezifische der Fall, wenn die Funktion als eine Art benannter Befehl betrachtet und verwendet werden kann).

2. In obigem Beispiel muss die innere Funktion der Variablen der äußeren Funktion (Aufrufumgebung) sein. Diese Variablen wurden im Unterschied zu Lokalen erstellt.

3. Es Compiler Durcheinander in der Lage sein, zu Erkennen, Dass der Wert (Zustand) in der die Variablen ausserhalb Derens Eigentlich Gültigkeitsbereich (scope) benötigt Wird, und bei der Compilierung aktiv berücksichtigen sterben. Technisch Werden this Die Variable Dann meist nicht mehr Auf dem Stapel abgelegt, Sondern stirbt Wird anders erhalten eine farblose zB INDEMAR tatsächlich im Hintergrund Eine (anonym) Klasse und Instanz Erzeugt Wird, sterben sterben benötigten (Member) Variable und sterben innere Funktion (ALS Memberfunktion) Enthält .

Jetzt sind alle Bausteine ​​beisammen, um eine verkürzte aber technischere Definition des Begriffes Closure aufzustellen, genau genomen von lexikalischen Closures im engeren Mind:

Verschlüsse sind auch eine Programmiertechnik Strukturen, etwa lexikalische Skopierung (engl. Scope) mit freien Variablen in Sprachen mit First-Class-Funktion umzusetzen.

Dynamischer und lexikalische Verschlüsse

Die erste Implementierung der Closures ist auf die Art der Implementierung von Ausführungsumgebungen in Lisp zurückzuführen. In erster Linie ist Lisp-Implementationungen eine lexikalische Schopierung . Die Ausführungsumgebung einer Anweisung ist als A-Liste verfügbarMeine Variablenbindungen, die nur eine der Referenzstrings sind. Eine Schließung über eine Funktionsdatei aus einem Paar, siehe Funktionsdefinition und Verweis auf die Definitionen der Schließanleitung A-Liste. Dieses durch die Lisp-Funktion FUNCTION erzeugte Paar verbindet Ihren dynamischen Verschluss mit der historischen Bezeichnung FUNARG (FUNKTIONALES ARGUMENT). Gefütterte Krawatte FUNARG später zur Ausführung, so stirbt Schah im Kontext mit Mitigue A-List. [2]

Das heute in Lisp, der in allen anderen leiblichen Skopierung leckt, ist ein Komponist in Sprachen funktionsfähig ist. Sie werden feststellen, dass Sie das Recht haben, den Code zu lesen und zu bearbeiten, der sowohl die Funktion der Funktion als auch den Kontext des Kontextes der Schließung der Zusammenstellung enthält. So funktioniert es – Nonne als Closure – der Aufrufer zur Verfügung gestellt wird. Da diese Variable keine lexikalische Bindung ist, ist es nicht möglich, Stack zu setzen, ohne das Laufzeitsystem auf den Heap zu verlassen.

Implementierungseinheit

Es gibt auch nicht-funktionale Programmierpraktiken, die nicht vertrauenswürdig sind. Dazu gehorig Ada , [3] C ++ (nur ab C ++ 11), C # , Go , stark , Java , [4] JavaScript , [5] Lua , Object Pascal (Delphi), [6] [7] PHP , Perl , Python , Ruby , Smalltalk , Swift und Visual Basic .NET . Apple hat den Gcc und Clangüber Verschlüsse – genannt Block-Literals – für C erweitert und stirbt zur Standardisierung vorgeschlagen. [8]

Beispiel von Implementierungen

Common Lisp

Dieses Beispiel ist ein Beispiel für die elegante Datenbankabfrage zu ermöglichen. Der Verschluss wird von der Funktion name-isgeliefert. Durch Den Sonderfunktionsnamen lambda Erzeugt, Innerhalb Schätzer den Wert des Feldes löst Funktion Wird Eine Name auf Der Gleichheit mit Einer Zeichenkette ngeprüft Wird. Der Aufruf (name-is "Elke")bezieht sich auch auf den Closure als Verbindungus anonymen Funktion und Variablenbindung von nder Zeichenkette „Elke“. Dies kann einen Datensatz auf den Namensgleichheit mit „Elke“ überprüfen. Die Closure kann direkt filterals Functional Mountains bezeichnet werden, die durch das Ergebnis zurückgibt.

( Defparameter * dBase *
 ' (( "Elke" "01/01/1980" ) ( "Gabi" "1981.02.03" ) ( "Heidi" "1982.04.05" )
 ( "Gabi" "1983.05.06" ) ( „Uschi " " 7.8.1984 ")))
( Defune get-Name ( Liste )
 ( erste Liste ))
( Defune username-ist ( Name )
 ( Lambda ( Liste )
 ( gleich ( get-Namen - Liste) Bezeichnung )))
( Defune Filter ( Prädikatsliste ) ( remove-wenn-nicht Prädikat Liste ))

Diese Definitionen machen nun folgende elegante Abfrage möglich:

( print ( Filter ( Name ist "Gabi" ) * DBase * ))

Hier finden Sie Folgendes: Der Funktionsführer befindet (name-is "Gabi")sich im Verschluss. Dies ist ein Link zum Code of Conduct der (equal (get-name list) name)Funktion name-isund der Bindung der Sequenz "Gabi"und der Variable name. Damit verhält es sich semantisch zu Abfrage (equal (get-name list) "Gabi"). Dieser Vergleich wird als Verschluß an die Funktion filterübergeben. Durchführung dieser Filterung führt dann zu dem Ergebnis:

( "Gabi" "2.3.1981" ) ( "Gabi" "5.6.1983" ))

Perl

Der Kontext eines beliebigen Code-Fragments

 # Pragma
 Verwendung streng ;
 Unterfunktion { my ( $ var1 , $ var2 ) = @_ ; # Argumente in benannte Variablen kopieren # Blockcode ... }

Ich habe Beispiel gesehen die Variablen $var1und $var2einen Satz von Funktionen gültig und sichtbar. Beim Verlosen der Funktion werden sie zusammen mit dem verweerdeten Block aufgeräumt („gehen“ out of scope ) und werden dagegen unbekannt. Jeder weitere zugriff wäre ein Fehler.

Verschlüsse Bieten Nonne sterben möglichkeit, die Gültigkeitsbereich Sölch Die Variable über D flat Duft End Hinaus auszudehnen. Dazu wird im Scope einfachine definiert, welche die relevanten Variablen verdorben hat:

 # Pragma
 Verwendung streng ;
 Unterfunktion { my ( $ var1 , $ var2 ) = @_ ; return sub { print "Var: $ var1, $ var2. \ n" }; } meine $ f = Funktion ( "Hallo" , 8 ); meine $ g = Funktion ( "bar" , "y" ); # Aufruf von $ f $ f -> (); # Aufruf von $ g $ g -> ();

Das Laufzeitsystem Stellt jetzt beim verlassene der Funktion functionfest, that noch Referenzen auf sterben Blockvariablen $var1und $var2Bestehen – wo Rückgabewert is a anonyme Unterroutine , sterben ihrerseits Verweise auf sterben Blockvariablen Enthält. $var1und $var2bleibt deshalb mit Empfehlung : Ihr current Wert erhalten. Weil auf diese Weise konserviert, wird sie zur Schließung.

Mit Anderem Wort Kann man Auch nach DM verlassene Dezember Eigentlich Gültigkeitsbereich , wo die Variable Jederzeit des Aufruf $f->()und der Aufruf $g->()ausführen und Werden im Plan Ergebnis immer wieder stirbt bei der Definition , wo die Leistung der gelben Werte bekommen Die Variable angezeigt die.

Stirbt die Ausgabe:

Alert: Hallo, 8.
Alert: Bar, Y.

Die Ente kann man mir nicht sagen, wenn die Variablen außerhalb der Closure mehr zur Verfügung stellen. Dort finden Sie auch eine Funktionsdefinition: Natürlich hätte die Closure die Werte nicht ausgeben können, ohne auch nur einen Code zu schreiben. In den folgenden Varianten sind zum Beispiel Funktionen zum Incrementmentieren und Decrementieren eingeführt:

 # Pragma
 Verwendung streng ;
 # Funktion
 Unterfunktion { my ( $ var1 , $ var2 ) = @_ ; Rückkehr ( sub { Druck "Vars: $ var1, var2 $ \ n" }, sub { $ var1 ++ ; $ var2 ++ ;}, sub { var1 $ - ; $ var2 - ;} ); } # ruft die Funktion mein auf ( $ printer , $ incrementor , $ decrementor
 ) = Funktion ( 3 , 5 );
 # Schließungen verwenden
 $ printer -> ();
 $ inkrementor -> ();
 $ Drucker -> ();
 $ inkrementor -> ();
 $ inkrementor -> ();
 $ Drucker -> ();

Stirbt die Ausgabe:

Frisch: 3, 5.
Frisch: 4, 6.
Frisch: 6, 8.

Verschließschweißungen werden ebenfalls verunreinigt, um den Zugriff auf sensible Daten zu kapseln.

Python

Weiter, einer der besten der Welt, Python, der ohne einen (benannten) Container, der den aktuellen Zählerstand spricht.

 def Verschluss ()
 Behälter = [ 0 ]
 def inc ()
 Behälter [ 0 ] + = 1
 def erhalten ()
 return Behälter [ 0 ]
 return inc , erhalten

Im beispiel Werden Innerhalb der Schließung -Funktion Zwei Funktionsobjekte Erstellt, sterben Beide Werkzeugliste Behälter aus IHREM Jeweils übergeordneten Scope referenzieren. Ist de Schließung auch abgearbeitet (nach einem Aufruf) und beide wurden genommen.Funktionsobjekte referenziert, die Container -Liste mehr gebildet, obwohl der Closure-Scope zum Auswischen vorbereitet wurde. Auf diese Weise wurde auch die Liste in Einem anonymen Umfang konserviert. Man can not direkt auf Werkzeugliste Container zugreifen. Werden sterben Beiden Funktionsobjekte inc und get nicht mehr referenziert, verschwindet Auch der Container.

Die Schließung wird auf folgende Weise verwaltend:

>>> i, g = schließung ()
>>> g ()
0
>>> i ()
>>> i ()
>>> g ()
2

OCaml

OCaml erlaubt das in den

schauen Zähler , inc , zurückgesetzt =
 Look n = ref 0 in
 ( Funktion () -> ! n ), (* Zähler *)
 ( Funktion () -> n : = ! n + 1 ), (* Inkrementierer *)
 ( Funktion () -> n : = 0 ) (* zurücksetzen *)

jetzt ist der Zähler

# Zähler () ;; (* ergibt 0 *)
# inc () ;;
# Zähler () ;; (* ergibt 1 *)
# inc () ; inc () ; inc () ;;
# Zähler () ;; (* ergibt 4 *)
# reset () ;;
# Zähler () ;; (* ergibt 0 *)
# n ;; (* n ist gechippt *)
Ungebundener Wert n

Statt einer Ganzzahl („integer“) können natürlich auf diese Weise Objekte oder Variablen beliemer Typen gehackt werden.

JavaScript

In der Funktion f1 wird eine weitere Funktion f2 als Schließung definiert;

wo f1 = function () { // eine äußere Funktion f1 definieren ...
 var wert = 22 ; // ... und darin einen Namensraum.
 wo f2 = function () { // eigene interne Funktion definieren, ...
 return wert ; // ... was den Namensraum nach außen liest.
 }
 return f2 ; // f2 durch f1 zurückgeben, womit f2 zum closing wird.
}
wo a = f1 (); // a ist von von f1 () zurückgegebene Schließfunktion, ...
Konsole .log ( f1 ()); // ... auch: function () {return wert;}
Konsole . log ( Typeof Wert ); // ist undefinierte
Konsole . log ( a ()); // ergibt 22
Konsole . log ( f1 () ()); // ergibt 22, f2 () ist hier aber nicht abrufbar

Obiges Beispiel formululiert etwas anderes, die innere Funktion wird jetzt direkt aufgerufen:

wo f3 = Funktion () {
 Var Wert = 23 ;
 return function () { // die Funktion f3 gibt gleich die Schließfunktion zurück!
 Rückkehr Wert ;
 };
}
wo b = f3 (); // b ist wieder die von f3 () zurückgegebene Funktion ...
Konsole . log ( b ()); // ... und liefert jetzt als Ergebnis 23
Konsole . log ( b ); // bleibt aber weiterhin ein Funktionsaufruf!
Konsole. log ( f3 () ()); // auch 23

Diese verkettete Funktion dient als ein in den übergeordneten Funktionswert definierter Wert.

Diese Funktion kann als anonyme Funktion definiert werden :

war wert = 24 ;
var c = ( function () { // de äußeren as anonymous Funktion und ...
 zurück wert ; // ... darin die innere Funktion define.
 } ()); // die Funktion jetzt weder mit (); aufrufen.
Konsole . log ( c ); // ergibt 24

Die Schließung kann auch mit einer Konstruktorfunktion erzeugt werden:

wo d = ( neue Funktion ( "return wert;" )) (); // mit einem Konstruktor

Lua

Lua hat eine eingebaute und intuitiv nutzbare Unterstützung für Verschlüsse, deren Implementierungen

Funktion Addierer ( x ) - Funktionserzeuger
 return - Funktion ( y ) - anonymous, zu privatem Addierer Funktion
 return x + y - x stammen hier aus dem Kontext Äußeres
 Ende
Ende

Eine Beispielnutzung sähehe sous:

ADD2 = Addierer ( 2 ) - hier Wird Schließen Erzeugt
Druck ( ADD2 ( 10 )) -> 12 Ausgabe -
Druck ( ADD2 ( - 2 )) -> Version 0

Eine Closure-Implementierung in Lua ist [9] .

Erlang

Erlang als Funktional Sprache Besitzt ebenfalls Verschlüsse, sterben allerdings Funs (Singular Fun , von Funktion ) genannt Werden.

do_something ( Spaß ) -> Spaß ( 4 ).
main () ->
 Var = 37 ,
 F = Spaß ( N ) -> Var + N Ende .
 Ergebnis = do_something ( F ).
 % Ergebnis =: = 41 =: = 37 + 4

C #

C # unterstützt Closures in Form von Delegaten . [10]

private static Aktion CreateClosure ()
{
 // Deklaration einer Die Variable im Raum Der Kontext
 war x = 0 ;
 // Erstellung eines Closure-Delegaten mit Hilfe eines Lambda-Ausrucks
 Aktion Schließen = () => Konsole . Schreibzeile ( x );
 // Änderung am lokalen Kontext
 x = 1 ;
 // Rückgabe wo Schließung im übergeordneten Kontext
 Rückkehr Schließung ;
}
static void Haupt ()
{
 var Schließung = CreateClosure ();
 // Im global Context
 // Variable x wird nur innerhalb der Closure referenziert
 // Führe Closure aus; Schreibt "1" auf die Konsole
 closure ();
}

C ++ 14

C ++ unterstützt Verschlüsse mittels Lambda-Ausdrücken [11] (ab C ++ 11), die sich in Funktionsobjekten befinden , sogenannte Funktoren, des Typs std :: Funktionskapselschweißen.

#include <string>
#include <iostream>
auto create_closure () {
 std :: Zeichenkette kuchen ( "Apfelkuchen" );
 // Lokale Variablen waren hier als Kopieren in das Funktionsobjekt übertragen
 zurück [ = ] () {
 std :: cout << "Ich esse" << kuchen << std :: endl ;
 };
}
int main () {
 automatisches Schließen = create_closure ();
 Schließung();
 Rückkehr EXIT_SUCCESS ;
}

Mit Hilfe des Schlüsselworts wandelbar Kann Aus einer Lambda-Funktion Eine echte Schließung Erstellt Werden sterben nicht nur Ihre Die Variable Besitzt Eigenen, Sondern Auch this Verändern ! Kann:

#include <iostream>
auto mutterfunktion ()
{
 int anzahl_kuchen = 0 ;
 // Diese Kopien der Variablen können hier geändert werden.
 return [ = ] () änderbar { std :: cout << "Ich esse" << ++ anzahl_kuchen << "Cheating. \ n " ; };
}
int main ()
{
 auto essen = mutter function ();
 essen ();
 essen ();
 essen ();
 Rückkehr EXIT_SUCCESS ;
}

Ausgabe dieses Programms:

Ich esse 1 Betrug.
Ich esse 2 Kuchen.
Ich esse 3 Kuchen.

Java

In Java sind auch Closures möglich, wobei dabei einige Hinweise auf die Lambda-Ausdrücke zu beachten sind. Der folgende Code würde zum Beispiel nicht compilieren .

 private static Funktion < String , Lieferant < String >> motherFunc = KuchenName -> {
 int i = 0 ;
 return () -> "Ich esse" + i ++ + "" + KuchenName ; // Fehler: im finalen finalen oder effektiven Endsignal
 };
 public static void main ( String [] Argumente ) {
 Lieferant < String > käsekuchen = MutterFunc . anwenden ( "käsekuchen" );
 System . heraus . println ( käsekuchen . erhalten ());
 System . heraus . println ( käsekuchen . erhalten ());
 System . heraus . println ( käsekuchen . erhalten ());
 }

Java ht davon, dass alle Lambda-Ausdrücke reinen Geist haben, dh gleiche Eingangswerte geben gleiche Ausgangswerte, da keine Seiteneffekt auftritt. Dies wird durch Java zur Übersetzungszeit geprüft und von HotSpot zur Laufzeit zur Optimierung genutzt. Im obigen Beispiel ist das nieicht der Fall, da der zurückgegebene Lieferant durch i++Seiteneffekte hat, war ein Compilerfehler auslöst. Seiteneffekte Sind BESONDERS in Einer Multithreading -Umgebung Einer Quelle für Fehler, und HotSpot according Darf die SPEZIFIKATIONEN bei Seiner Optimierung Davon Ausgehen that Eine Methode BZW. ein Lambda wird nur von einem Thread aufgerufen, solange die Methode, weder ein Objekt noch die Submethode in der Methode von Schlüsselwort istsynchronizedused. Die Täuschung des Compilers durch int i[] = {0}und i[0]++überlistet zwar, verhindert jedoch HotSpot, aber nicht daran Optimierungen zu finden, die sich auf bewegende Threads freuen. Die funktionell korrekte Lösung besteht darin, die Incrementierung durch das Paket java.util.concurrent.atomiczu verwirklichen, zum Beispiel mit AtomicInteger:

 private static Funktion < String , Lieferant < String >> motherFunc = KuchenName -> {
 AtomicInteger atomicInteger = new AtomicInteger ();
 return () -> "Ich esse" + atomicInteger . getAndIncrement () + "" + KuchenName ;
 };
 public static void main ( Zeichenfolge [] args ) {
 Lieferant <String > käsekuchen = MutterFunc . anwenden ( "käsekuchen" );
 System . heraus . println ( käsekuchen . erhalten ());
 System . heraus . println ( käsekuchen . erhalten ());
 System . heraus . println ( käsekuchen . erhalten ());
 }

Diejenigen Methoden der Atomic-Klassen sind zwar nicht synchronized, wer eine Fülle von aber schon auf Prozessorebene hatte, war. Dort wird Code kompiliert, da der Verweis auf den Lambda-Ausdruck unverändert bleibt, und es damit effektiv endgültig ist. Mögliche Seiteneffekt beim Multithreading sind dabei, wenn HotSpot Optimierungen durchführt. Die Ausgabe ist dann:

Ich esse 0 Käsekuchen
Ich esse 1 Käsekuchen
Ich esse 2 Käsekuchen

PHP

PHP Unterstützt Closures ab Version 5.3.0 in Form anonymer Funktion. [12]

Technisch aufgelöstes PHP, das als Funktionsmerkmal verwendet wird, ist eine „Closure“ -Klasse. [13]

$ mutterfunction = function () {
 $ anzahl_kuchen = 0 ;
 $ childfunction = function () verwenden ( & $ anzahl_kuchen ) {
 $ anzahl_kuchen = $ anzahl_kuchen + 1 ;
 "Ich esse { $ anzahl_kuchen } Kuchen \ n " drucken ; }; return $ Kindfunktion ; }; $ essen = $ nut Funktion (); $ ace (); $ ace (); $ essen
();

Die Ausgabe der Aufrufe

Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

Ab PHP 7.0 wurden auch Closures in Form anonymer Klassen unterstützt. [14]

$ essen = neue Klasse () {
 private $ anzahl_kuchen = 0 ;
 öffentliche Funktion __invoke () {
 $ this -> anzahl_kuchen = $ this -> anzahl_kuchen + 1 ;
 drucken "Ich esse { $ this -> anzahl_kuchen } Kuchen \ n " ;
 }
};
$ ace ();
$ ace ();
$ ace ();

Beide Implementersungen liefern identische Ausgaben.

Rest

Rust assistte Closures ab Version 0.1, die Rückgabe von Closures aus Die Funktion muss allerdings über einen Zeiger auf den Heap-Speicher (via Box) geschehen.

fn mutterfunktion () -> Box < FnMut () -> () > { suchen mut anzahl_kuchen = 0 ; einfache Kindheit Funktion = bewegen || { anzahl_kuchen + = 1 ; Druck ! ( "Ich esse {} Kuchen" , anzahl_kuchen ); }; // [Ex.1] Fehler wenn anzahl_kuchen nicht Copy implementieren würde (su) // println! ("Jetzt ist die Anzahl der Kuchen: {}", anzahl_kuchen); Rück Box :: neu
 ( Kinderfunktion );
}
fn main () { Lasse mut essen = mutter function (); essen (); essen (); essen (); }

Ausgabe:

Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

Rust differenziert hierbei between Funktionszeigern und Verschlüsse, Eulen Verschiedenen Verschluss: FnFnMutund FnOnce. Eine Fn-Closure kennt den Context, in dem Sie die Datei nicht bearbeiten können. Eine FnMut-Closure kennt die Variable im Kontext Modifikatoren, die als die bekanntesten mutbekannt ist. Eine- FnOnceClosure-Consumed-Die im Kontext erstellte Variable. Beide waren genau gleich, und sie wurden essen()vom Destruktor anzahl_kuchender Variable geschlossen.

Wenn [Ex.1]auskommentiert wird, ergibt sich

Jetzt ist die Anzahl der Kuchen: 0
Ich esse 1 Kuchen
Ich esse 2 Kuchen
Ich esse 3 Kuchen

Das moveSchlüsselwort wird gebraucht um den Besitz der Variable anzahl_kuchen. Da unsichere Variable anzahl_kuchenkopiert wird (Die Variable der Typen u32implementiert es Copy-Trait), wird die Warteschlange die Variable innerhalb der Mutter-Funktion sein oder vom Besitzer der Wert der Closure übergeben. Beide anzahl_kuchenKopien sind kopiert, Sie können gerne den Code schreiben, den Anzahl für 1 Satz gemacht hat, geben Sie keine Antwort oder 0, bitte vervollständigen Sie die Kopie von Variable ist. Ist der Typ von anzahl_kuchennicht kopbar, gibt der Compiler einen Fehler aus.

Literatur

  • Ralf H. Güting, Martin Erwig, Übersetzerbau . Springer, 1999, ISBN 3-540-65389-9
  • Damian Conway , objektorientiertes Perl
  • Oliver Lau, Andreas Linke, Torsten T. Will: Die Variable zu gehen – Schließungen in der aktuellen Programmiersprache. In: c’t , 17/2013, S. 168ff.

Einzelnachweise

  1. Hochspringen↑ Verschlussbasierter Zustand: C #
  2. Hochspringen↑ John McCarthy und A .: Lisp 1.5 Programmers Manual . (PDF) softwarepreservation.org; abgerufen am 12. März 2014.
  3. Hochspringen↑ John Barnes: Begründung für Ada 2005
  4. Hochspringen↑ Verschlüsse in Java
  5. Hochspringen↑ Verschlüsse in Javascript (Englisch)
  6. Hochspringen↑ Craig Stuntz: Anonyme Methoden verstehen
  7. Hochspringen↑ Barry Kelly: Tiburon: Spaß mit Generika und anonymen Methoden .
  8. Hochspringen↑ N1370: Apples Erweiterungen zu C (PDF; 69KB )
  9. Hochspringen↑ Die Implementierung von Lua 5.0
  10. Hochspringen↑ Dustin Campbell: Was ist in einer Schließung? 9. Februar 2007, abgerufen am 12. April 2014 (englisch).
  11. Hochspringen↑ Lambda-Funktionen. Abgerufen ist der 17. Oktober 2015 (englisch).
  12. Hochspringen↑ Anonyme Funktionen. Abgerufen am 19. Mai 2015 .
  13. Hochspringen↑ Die Closure-Klasse. Abgerufen am 19. Mai 2015 .
  14. Hochspringen↑ Joe Watkin, Phil Sturgeon: Anonyme Klassen. 22. September 2013, abgerufen am 19. Mai 2015 (Englisch).