Navigation


Closure

16.05.2012 @ 12:54, ,

{{Vorlage:QS-Informatik|Siehe Diskussionsseite --Pjacobi 23:20, 16. Mär. 2009 (CET)|Knacknüsse=Ja}}

Als Closure oder Funktionsabschluss bezeichnet man eine Programmfunktion, die sich ihren Erstellungskontext „merkt“. Beim Aufruf kann die Funktion dann auf diesen zugreifen, selbst wenn der Kontext außerhalb der Funktion schon nicht mehr existiert.

Closures sind ein Konzept, das aus den funktionalen Programmiersprachen stammt, zum ersten Mal in LISP auftrat und in seinem Dialekt Scheme erstmals vollständig unterstützt wurde. Daraufhin wurde es auch in den meisten späteren funktionalen Programmiersprachen (etwa Haskell, Ocaml) unterstützt.

Es existieren auch nicht-funktionale Programmiersprachen, die diese Funktion unterstützen. Hier wären Ada[http://www.adaic.org/resources/add_content/standards/05rat/html/Rat-3-4.html John Barnes: Rationale for Ada 2005], Delphi[http://blogs.teamb.com/craigstuntz/2008/08/04/37828/ Craig Stuntz: Understanding Anonymous Methods][http://barrkel.blogspot.com/2008/08/tiburon-fun-with-generics-and-anonymous.html Barry Kelly: Tiburon: fun with generics and anonymous methods], JavaScript[http://www.jibbering.com/faq/faq_notes/closures.html Closures in Javascript] (englisch), Smalltalk, Python, Lua, Ruby, C#, C++11, Groovy, Scala, Go, PHP und Perl zu nennen. Auch die objektorientierte Programmiersprache Java unterstützt eine limitierte Variante von Funktionsabschlüssen in Form von anonymen und lokalen Klassen. Apple hat den gcc und Clang um Closures – genannt Block-Literals – für C erweitert und dies zur Standardisierung vorgeschlagen.[http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1370.pdf N1370: Apple’s Extensions to C]

Beispiele von Implementierungen

Pseudocode


Im folgenden Beispiel wird zunächst eine Funktion mutter_funktion definiert. Diese Funktion setzt eine lokale variable namens kuchen und definiert eine lokale Funktion namens kind_funktion. Bei einem Aufruf gibt sie die lokale Funktion kind_funktion zurück.

Die globale variable kind bekommt also die Funktion kind_funktion zugeweisen, sodass beim anschließenden Aufruf von kind folglich kind_funktion ausgeführt wird. Obwohl keine globale variable kuchen existiert, gibt kind_funktion die Zeichenkette 'Ich esse Apfelkuchen' aus, weil sie auf ihren Erstellungskontext zugreifen kann, in dem die variable kuchen mit 'Apfelkuchen' definiert ist. Wichtig zu beachten: Obwohl mutter_funktion schon einen Wert zurückgegeben hat - der Kontext also eigentlich nicht mehr existiert - kann kind_funktion darauf zugreifen - kind_funktion ist also eine Closure-Funktion.

funktion mutter_funktion {
setze kuchen = 'Apfelkuchen'
funktion kind_funktion {
gebe aus 'Ich esse #{kuchen}'
}
gebe zurück kind_funktion
}
setze kind = rufe auf mutter_funktion

rufe auf kind

Ich esse Apfelkuchen

Perl


Der Kontext eines beliebigen Code-Fragments wird unter anderem durch die zur Verfügung stehenden Symbole bestimmt:

# pragma
use strict;
sub function {
my ($var1, $var2) = @_; # Argumente in benannte Variablen kopieren
# block code
...
}

Im oben gezeigten Beispiel sind die Variablen $var1 und $var2 an jeder Stelle der Funktion gültig und sichtbar. Beim Verlassen der Funktion werden sie zusammen mit dem verlassenen Block aufgeräumt („gehen“ out of scope) und sind anschließend unbekannt. Jeder weitere Zugriff wäre ein Fehler.

Closures bieten nun die Möglichkeit, den Gültigkeitsbereich solcher Variablen über dessen offizielles Ende hinaus auszudehnen. Dazu wird im Scope einfach eine Funktion definiert, die die betreffenden Variablen verwendet:

# pragma
use strict;
sub function {
my ($var1, $var2) = @_;
return sub { print "Vars: $var1, $var2.\n" };
}
my $f = function("Hallo", 8);
my $g = function("bar", "Y");

Das Laufzeitsystem stellt jetzt beim Verlassen der Funktion function fest, dass noch Referenzen auf die Blockvariablen $var1 und $var2 bestehen – der Rückgabewert ist eine anonyme Subroutine, die ihrerseits Verweise auf die Blockvariablen enthält. ''$var1 und $var2 bleiben deshalb mit ihren aktuellen Werten erhalten. Weil die Funktion auf diese Weise die Variablen konserviert, wird sie zur Closure.

Mit anderen Worten kann man auch nach dem Verlassen des eigentlichen Gültigkeitsbereichs der Variablen jederzeit den Aufruf $f->() und den Aufruf $g->() ausführen und wird im Ergebnis immer wieder die bei der Definition der Funktionen gültigen Werte der Variablen angezeigt bekommen.

Ändern kann man diese Werte nicht mehr, da die Variablen außerhalb der Closure nicht mehr verfügbar sind. Das liegt aber vor allem an der Funktionsdefinition: natürlich hätte die Closure die Werte nicht nur ausgeben, sondern auch bearbeiten oder auch aufrufendem Code wieder per Referenz zur Verfügung stellen können. In der folgenden Variante werden beispielsweise Funktionen zum Inkrementieren und Dekrementieren eingeführt:

# pragma
use strict;
# function
sub function
{
my ($var1, $var2) = @_;

return (
sub {print "Vars: $var1, $var2.\n"},
sub {$var1++; $var2++;},
sub {$var1--; $var2--;}
);
}
# call the function
my ($printer, $incrementor, $decrementor) = function(3,5);
# use closures
$printer->();
$incrementor->();
$printer->();
$incrementor->();
$incrementor->();
$printer->();

Closures lassen sich also beispielsweise dazu verwenden, den Zugriff auf sensible Daten zu kapseln.

Python


Folgend ein einfaches Beispiel für einen Zähler in Python, der ohne einen (benannten) Container auskommt, der den aktuellen Zählerstand speichert.

def closure():
container = [0]
def inc():
container[0] += 1
def get():
return container[0]
return inc, get

Im Beispiel werden innerhalb der closure-Funktion zwei Funktionsobjekte erstellt, die beide die Liste container aus ihrem jeweils übergeordneten Scope referenzieren. Ist die closure-Funktion also abgearbeitet (nach einem Aufruf) und werden die beiden zurückgegebenen Funktionsobjekte weiter referenziert, dann existiert die container-Liste weiter, obwohl der Closure-Scope bereits verlassen wurde. Auf diese Weise wird also die Liste in einem anonymen Scope konserviert. Man kann nicht direkt auf die Liste container zugreifen. Werden die beiden Funktionsobjekte inc und get nicht mehr referenziert, verschwindet auch der Container.

Die Closure im vorigen Beispiel wird dann auf die folgende Weise verwendet:

>>> i, g = closure()
>>> g()
0
>>> i()
>>> i()
>>> g()
2
>>> container
Traceback (most recent call last):
File "", line 1, in ?

NameError: name 'container' is not defined

OCaml


OCaml erlaubt das in folgender Weise :

let counter, inc, reset =
let n = ref 0 in
(function () -> !n), (* counter *)
(function () -> n:= !n + 1), (* incrementor *)
(function () -> n:=0 ) (* reset *)

jetzt ist der Zähler wie folgt anwendbar :

# counter();; (* ergibt 0 *)
# inc();;
# counter();; (* ergibt 1 *)
# inc();inc();inc();;
# counter();; (* ergibt 4 *)
# reset();;
# counter();; (* ergibt 0 *)
# n;; (* n ist gekapselt *)
Unbound value n

Statt eines Integers können natürlich auf diese Weise beliebige Objekte oder Variablen beliebiger Typen gekapselt werden.

Javascript


In der Funktion f1 wird eine weitere Funktion f2 als Closure definiert;

var f1 = function () { // eine äußere Funktion f1 definieren ...
var wert = 22; // ... und darin einen Namensraum erstellen.
var f2 = function () { // eine innere Funktion definieren, ...
return wert; // ... die den Namensraum nach außen reicht.
}
return f2; // f2 durch f1 zurückgeben, womit f2 zum closure wird.
} var a = f1(); // a ist die von f1() zurückgegebene closure-Funktion, ...
document.write(f1() +"
"); // ... also: function() {return wert;}
document.write(typeof wert +"
"); // ist undefined
document.write(a()+"
"); // ergibt 22
document.write(f1()() +"
"); // ergibt 22, f2() ist hier aber nicht abrufbar

Obiges Beispiel etwas anders formuliert, die innere Funktion wird jetzt direkt aufgerufen:

var f3 = function () {
var wert = 23;
return function () { // die Funktion f3 gibt gleich die closure-Funktion zurück!
return wert;
};
} var b = f3(); // b ist wieder die von f3() zurückgegebene Funktion ...
document.write(b()+"
"); // ... und liefert jetzt als Ergebnis 23
document.write(b+"
"); // b bleibt aber weiterhin ein Funktionsaufruf!
document.write(f3()() +"
"); // liefert ebenfalls 23

Die eingebettete Funktion dient jeweils als Lieferant des in der übergeordneten Funktion definierten Wertes.

Die übergeordnete Funktion kann auch als anonyme Funktion definiert werden:

var wert = 24;
var c = (function() { // die äußere als anonyme Funktion und ...
return wert; // ... darin die innere Funktion definieren.
}()); // die Funktion jetzt noch mit (); aufrufen.
document.write(c+"
"); // ergibt 24

Die Closure kann auch mit einer Konstruktorfunktion erzeugt werden:

var d = (new Function("return wert;"))(); // mit einem Konstruktur definieren und aufrufen

Literatur

* Ralf H. Güting, Martin Erwig, Übersetzerbau, Springer (1999), ISBN 3-540-65389-9

* Damian Conway, Object Oriented Perl

Einzelnachweise


Kategorie:Programmierung

bg:Затваряне (информатика)
el:Κλείσιμο (επιστήμη υπολογιστών)
Closure (computer science)
es:Clausura (informática)
et:Sulund (informaatika)
fi:Sulkeuma (ohjelmointi)
Fermeture (informatique)
he:סגור (שפות תכנות)
it:Chiusura (informatica)
ja:クロージャ
nl:Closure
pl:Domknięcie (programowanie)
pt:Closure (ciência da computação)
ru:Замыкание (программирование)
simple:Closure (computer science)
th:ส่วนปิดคลุม (วิทยาการคอมพิวเตอร์)
uk:Замикання (програмування)
zh:闭包 (计算机科学)

weiter

Text und Bilder dieses Beitrags stammen aus dem Artikel Closure der freien Enzyklopädie Wikipedia und stehen unter der GNU Free Documentation License. Die Liste der Autoren ist in der Wikipedia unter dieser Seite verfügbar, der Original-Artikel lässt sich hier bearbeiten.