Zum Inhalt springen

Closure (Funktion)

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 8. Januar 2011 um 19:38 Uhr durch Parzi (Diskussion | Beiträge). Sie kann sich erheblich von der aktuellen Version unterscheiden.
QS-Informatik
Beteilige dich an der Diskussion!
Dieser Artikel wurde wegen inhaltlicher Mängel auf der Qualitätssicherungsseite der Redaktion Informatik eingetragen. Dies geschieht, um die Qualität der Artikel aus dem Themengebiet Informatik auf ein akzeptables Niveau zu bringen. Hilf mit, die inhaltlichen Mängel dieses Artikels zu beseitigen, und beteilige dich an der Diskussion! (+)


Begründung: Siehe Diskussionsseite --Pjacobi 23:20, 16. Mär. 2009 (CET)

Als Closure oder Funktionsabschluss bezeichnet man eine Programmfunktion, die beim Aufruf einen Teil ihres Erstellungskontexts reproduziert, selbst wenn dieser Kontext außerhalb der Funktion schon nicht mehr existiert. Closures „konservieren“ also ihren Kontext.

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

Allerdings existieren auch nicht-funktionale Programmiersprachen, die diese Funktion unterstützen. Hier wären Delphi[1][2], JavaScript[3], Smalltalk, Python, Lua, Ruby, C#, Groovy, PHP und Perl zu nennen, das im Weiteren zur Veranschaulichung dient. 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.[4]

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.

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 ihren 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.

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

>>> i, g = closure()
>>> g()
0
>>> i()
>>> i()
>>> print g()
2
>>> container
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'container' is not defined

OCaml, als funktionelle Sprache, 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.

Literatur

Einzelnachweise

  1. Craig Stuntz: Understanding Anonymous Methods
  2. Barry Kelly: Tiburon: fun with generics and anonymous methods
  3. Closures in Javascript (englisch)
  4. N1370: Apple’s Extensions to C