| 

.NET C# Java Javascript Exception

Groovy- und Grails-Wiki: Closure-Einführung

Dies ist das Archiv des ehemaligen Forums zum Thema Groovy, Grails, Griffon und Bean Scripting Framework, welches unter groovy-forum.de existierte. Die neue Adresse des Groovy-Forums ist: http://codekicker.de/fragen/themen/groovy.


|
Home
G’day (anonymous guest)
Log in
My Prefs
Closure-Einführung
view
edit
clone
find
Quick search
(type ahead)
Recent Searches
(Clear)
Your trail: 
More...
View Page Source
More...
Letzte Änderungen
Alle Seiten
About
SystemInfo
UngenutzteSeiten
AnzulegendeSeiten
View
Attach (1)
Info
> Groovy-Buch > Closure-Einführung
Table of Contents
Problemstellung
Zum Begriff
Die ersten Gehversuche
Eine eigene Closure definieren
klassische Java-Implementierung
Einsatz der Closure
Aufbau einer Closure
Verweise
Problemstellung
Durch die aus Java bekannte Vererbung ließen sich Methoden bislang nur so erweitern, daß der super-Anteil innerhalb der abgeleiteten Methode aufgerufen wird.
Bei allgemeinen Algorithmen (Iteration über Bäume, wie in-order usw.) ist es jedoch notwendig, den spezialisierten Anteil in den inneren Teil eines Algorithmus einfließen zu lassen.
Dieser Unterschied ist im folgenden Schaubild dargestellt.
Im linken Teil des Schaubildes ist der „klassische“ Fall zu sehen. Der super-Anteil (orange) ist in den spezialisierten Anteil (rot) eingebettet.
Im rechten Teil des Schaubildes ist der mit einer Closure mögliche Fall zu sehen. Der super-Anteil (orange) bettet den spezialisierten Anteil (grün) ein.
Dieses Problem ist zwar mit der Objektorientierung zu lösen, führt aber gelegentlich zu einer komplexeren Struktur, die die Lesbarkeit und Verständlichkeit reduziert.
Zum Begriff
Wenn von Closures als Sprachelement die Rede ist, sind offene Funktionsterme gemeint. Der Vorgang der Schließung eines solchen offenen Terms nennt man Closure. Wenn jemand sagt, dass eine Programmiersprache "Closures" unterstützen würde, meint er eigentlich, dass die Programmiersprache offene Funktionsterme erlaubt. Diese Haarspalterei ist für Groovy interessant, weil nicht jede Funktion eine Closure ist (aber oft fälschlich als solche bezeichnet wird).
Beispiel für eine "normale" Funktion:
def c = {print 'x'}
Diese Funktion hat keine freie Variable und somit gibt es auch nichts zu schließen. Eine "Closure" (Schließung) des Funktionsterms ist also weder möglich noch nötig.
Beispiel für einen offenen Funktionsterm (Closure):
def c = {print x}
x ist im Funktionsterm undefiniert. Der Funktionsterm ist also noch offen und muss mit einem Wert von x geschlossen werden. Diesen Vorgang der Schließung (Einfangen von x aus der Umgebung) nennt man Closure oder Funktionsschließung.
Die ersten Gehversuche
Schleifen begegnen dem Entwickler allerorten. Ihr Aufbau ist daher allgemein bekannt, wie hier am Beispiel einer Liste:
List liste = [];
for (Iterator i = liste.iterator(); i.hasNext(); ) {
...
}
In Groovy verfügt jede Sammlung (Liste, Map, usw.) über eine Funktion namens each, die als Parameter eine Closure erwartet, deren Inhalt für jedes Element der Sammlung ausgeführt wird.
liste.each { it ->
...
}
Die Variable it enthält dabei das aktuelle Element.
Eine Besonderheit ist, daß der Abschnitt it -> auch hätte entfallen können. Groovy nimmt dann it als Vorgabe an.
Genauso lässt sich it durch einen beliebigen anderen Bezeichner ersetzen, wie z.B. element:
liste.each { element ->
...
}
Neben der kürzeren und verständlicheren Schreibweise ist zu beachten, daß hier die JVM ggf. die Möglichkeit hat Mehrkernprozessoren besser zu unterstützen. Die erste – auch aus Java bekannte – Variante erzwingt das iterative Abarbeiten aller Elemente, während die Variante mit einer Closure lediglich besagt, daß für jedes Element der Sammlung etwas getan werden soll.
Eine eigene Closure definieren
Als Beispiel dient der in-order-Algorithmus, der die einzelnen Knoten eines binären Baumes "von links nach rechts" abarbeitet.
klassische Java-Implementierung
public abstract class BaumInOrder {
public void inorder (Baum baum) {
if (baum==null) return;
inorder(baum.links);
behandle(baum);
inorder(baum.rechts);
}
abstract protected void behandle(Baum baum);
}
Damit muss bei Verwendung des Algorithmus' immer Folgendes geschrieben werden:
new BaumInOrder() {
protected void behandle(Baum baum) {
... mache was ...
}
}.inorder(baum);
Der eigentliche Zweck (der in-order-Durchlauf) wird durch das Instanzieren verschleiert. Wann die Methode behandle ausgeführt wird, muss explizit in der Dokumentation nachgelesen werden. Der Quelltext ist schwerer verständlich.
Selbstverständlich lässt sich auch eine Schnittstelle (Kommando-Entwurfsmuster) nutzen, um eine geeignete Implementierung als Eingabeparameter in die Methode inorder zu übergeben, sodaß auf die Verwendung einer abstrakten Klasse verzichtet werden kann.
Die grundsätzliche Idee bleibt aber die gleiche, da dann an der nutzenden Stelle eben diese Schnittstelle implementiert und instanziert werden muss.
Einsatz der Closure
Mit einer Closure ist eine Umkehrung der Injizierung gegenüber der Vererbung möglich, die weitere Freiheitsgrade zulässt.
Daher sieht das vorherige Beispiel bei Verwendung einer Closure wie folgt aus:
class Baum {
... wie gehabt den Baum definieren ...
/** Closure als Teil des Baumes! */
def inorder { def behandle ->
if (links) links.inorder(behandle);
behandle(baum);
if (rechts) rechts.inorder(behandle);
}
}
Zur Beachtung: der in-order-Algorithmus ist jetzt in der Klasse Baum untergebracht.
Und jetzt noch der zugehörige Aufruf:
baum.inorder{ knoten ->
... mache was ...
});
Somit ist deutlicher, daß ein in-order-Durchlauf eines Baumes durchgeführt werden soll, sowie für jeden Knoten der angegebene Quelltextabschnitt ausgeführt werden soll.
Dies ist auch ohne Studium der Dokumentation möglich. Der Quelltext ist kürzer und verständlicher.
Aufbau einer Closure
def variable = { parameter ->
...
}
Der Abschnitt def variable = { } weist die Closure einer Variablen zu. Damit ist ersichtlich, daß eine Closure als Parameter in jeder beliebigen Methode dienen kann – sowohl als Eingabe- als auch als Ausgabeparameter.
Der optionale Abschnitt parameter -> dient der Deklaration von Eingabeparametern. Eine Besonderheit hierbei ist, daß der Datentyp komplett entfallen kann. In diesem Fall wird immer der (untypisierte) Typ def angenommen. Das Zeichen -> beendet die Deklaration der Eingabeparameter.
Der Abschnitt ... steht als Platzhalter für beliebigen Quelltext.
Der Aufruf der obigen Closure sieht wie folgt aus:
vaiable("Eingabeparameter")
Die Definition und Nutzung der Closure ist analog zu folgendem Pseudo-Quelltext:
def variable = new Object implements Entwurfsmuster_Kommando () {
public def execute(def parameter=null) {
...
}
}
variable.execute("Eingabeparameter")
Verweise
Das Closure-Konstrukt ist dem inner-Konstrukt der Programmiersprache BETA recht ähnlich.
Add new attachment
Only authorized users are allowed to upload new attachments.
List of attachments
Kind
Attachment Name
Size
Version
Date Modified
Author
Change note
png
Closure-Verhalten.png
0.261 kB
1
Thu Jun 05 00:25:51 CEST 2008
MarcPompl
Closure -- Verhalten der Injizierung gegenüber der Vererbung
«
This page (revision-3) was last changed on 29-Dec-2009 10:59 by MarcDzaebel
G’day (anonymous guest)
Log in
My Prefs
Groovy-Buch
Groovy-Kochbuch
Grails-Buch
Grails-Kochbuch
Spezialseiten
Letzte Änderungen
Anzulegende Seiten
Ungenutzte Seiten
Liste aller Seiten
Suchen
WikiEtiquette
-Syntax
JSPWiki v2.6.2
Home
JSPWiki v2.6.2

Diese Seite zeigt Informationen zu "Groovy- und Grails-Wiki: Closure-Einführung" der ehemaligen Webseite groovy-forum.de, welche durch einen Serverunfall zerstört wurde. codekicker.de hat viele Konversationen über die beliebte Programmiersprache Groovy und zugehörige Frameworks wie das Grails-Framework retten können.

Hast Du eine Frage zum Thema Groovy, Grails oder allgemein Java? Viele ehemalige groovy-forum.de Mitglieder beantworten dir auf codekicker.de deine Frage! Stelle jetzt eine Frage!

Viele weitere Diskussionen zu Grails und Groovy befinden sich auf der Threadübersicht des alten groovy-forum.de.