Grundlagen der Informatik I “Programmierung”

Grundlagen der Informatik I “Programmierung” Grundlagen der Informatik I “Programmierung”

22.08.2013 Aufrufe

class LIST[X] creation new feature empty: BOOLEAN is -- Ist die Liste leer ? do...end; new is -- Erzeuge leere Liste do...end; cons(r:X) is -- Hänge r vor die Liste do...end; head: X is -- Erstes Element do...end; tail is -- Entferne erstes Element do...end end -- class LIST[X] Abbildung 3.19: Generische Klassendefinition Der in eckigen Klammern angegebene Name – hier X – heißt formaler generischer Parameter. Es ist durchaus möglich, mehrere formale generische Parameter anzugeben. Diese werden dann durch Kommata von einander getrennt wie z.B. in GENERISCH[X,Y]. Innerhalb der Klasse kann ein formaler generischer Parameter in allen Deklarationen wie ein normaler Datentyp eingesetzt werden, sei es als Typ von Attributen, Funktionen (wie bei head), Parametern in Routinen (wie bei cons) oder von lokalen Variablen. Nach außen hin ist der formale generische Parameter unbekannt. Will man eine generische Klasse benutzen, um eine Größe zu deklarieren, so muß man aktuelle generische Parameter angeben, welche den Platz der formalen einnehmen, wie zum Beispiel bei il: LIST[INTEGER] Selbstverständlich muß die Anzahl der aktuellen und formalen generischen Parameter übereinstimmen. Als aktuelle generische Parameter ist praktisch alles zugelassen, was innerhalb der Kundenklasse bekannt ist, also einfache Typen, Klassentypen und ggf. sogar formale generische Parameter der Kundenklasse, falls diese ebenfalls eine generische Klasse ist. 13 Durchaus erlaubt sind also Deklarationen der Form personenlisten: LIST[LIST[PERSON]] Parametrisierte Klassen sind keine Typen, sondern Typschemata. Erst mit den Argumenten werden sie zu Typen. Ohne aktuelle Argumente kann man ihre Merkmale nicht ausführen und auch nicht austesten. 3.6.2 Typprüfung Generizität hat nur in einer getypten Sprache eine Bedeutung, in der jede Größe einen bestimmten Typ haben muß. Nur dann ist es möglich zu prüfen, ob eine Operation überhaupt sinnvoll sein kann. Andernfalls gibt es keine Möglichkeit, die Typen der in eine Datenstruktur eingehenden Elemente einzuschränken, was normalerweise zu Laufzeitfehlern (im günstigen Falle) oder zu völlig unsinnigen Ergebnissen (z.B. Auslesen der internen Darstellung einer Zeichenkette als ganze Zahl) führt. Beispiel 3.6.1 Nehmen wir einmal an, die Deklaration einer Klasse enthalte die Deklarationen il: LIST[INTEGER] pliste: LIST[PERSON] p: PERSON 13 Im Zusammenhang mit Vererbung werden wir eine weitere Form – Typen der Form like Klassenausdruck – kennenlernen, die als aktueller generischer Parameter möglich ist.

Dann sind die folgenden Anweisungen sinnvoll und gültig pliste.cons(p) -- Hänge p vor die Personenliste il.cons(25) -- Hänge 25 vor die Integerliste p := pliste.head -- Weise das erste Element der Personenliste einer Personengröße zu Völlig unangebracht sind aber Anweisungen wie pliste.cons(25) -- Hänge die Zahl 25 vor die Personenliste il.cons(p) -- Hänge die Personengröße p vor die Integerliste p := il.head -- Weise das erste Element der Integerliste einer Personengröße zu Eiffel ist eine statisch getypte Sprache, bei der alle Typprüfungen während der Übersetzung durchgeführt werden können – also nur vom Programmtext abhängen. Deshalb würden die unsinnigen Anweisungen im Beispiel bereits vom Compiler abgewiesen werden. Bei Sprachen ohne Datentypen, gibt es dagegen keine Möglichkeit, die Typen der in eine Datenstruktur eingehenden Elemente einzuschränken. In solchen Sprachen erfüllen generische Klassen keinerlei Zweck, da sowieso alle Operationen auf alles angewandt werden können. Die Last der Kontrolle liegt hier beim Programmierer. Die Tatsache, daß innerhalb einer generischen Klasse praktisch nichts über eine Größe bekannt ist, deren Typ der formale generische Parameter ist, schränkt allerdings die Operationen auf dieser Größe sehr stark ein. Es dürfen nämlich nur Operationen benutzt werden, die auf jeden beliebigen Typ anwendbar sind. Insbesondere sind Operationen wie !! (Erzeugung), clone, equal oder Anwendungen von Merkmalen auf x nicht erlaubt, da nicht gewährleistet ist, daß der aktuelle generische Parameter ein Klassenyp ist. Abbildung 3.20 stellt die Regeln für die Verwendung formaler generischer Parameter zusammen. Ist in einer Klassendeklaration X ein formaler generischer Parameter und x vom Typ X, dann darf x nur benutzt werden • als linke Seite einer Zuweisung x:=Ausdruck, wobei der Ausdruck der rechten Seite ebenfalls vom Typ X sein muß, • als rechte Seite einer Zuweisung y:=x, wobei die Größe auf der linken Seite ebenfalls vom Typ X sein muß, • als aktuelle Argument im Aufruf einer Routine f(..,x,..), welches einem formalen Argument vom Typ X entspricht (das geht nur, wenn f innerhalb der gleichen Klasse wie x deklariert wurde), • in einem Booleschen Ausdruck der Form x=y oder x/=y (bzw. y=x oder y/=x), wobei y ebenfalls vom Typ X sein muß. Abbildung 3.20: Regeln für formale generische Parameter Wie bei gewöhnlichen Routinen übernehmen bei der Verwendung generischer Klassen die aktuellen Parameter die Rolle der formalen Parameter in der Deklaration und entsprechend die dort vereinbarten Eigenschaften. Ist also f ein von einer generischen Klasse GENERISCH[X,Y] exportiertes Attribut, dessen Typ der formaler generischer Parameter X ist oder eine Funktion mit entsprechendem Ergebnistyp, dann wird bei einer Kundendeklaration der Form h:GENERISCH[INTEGER,PERSON] der Ausdruck h.f (ggf. mit Parametern) innerhalb der Kundenklasse als vom Typ INTEGER aufgefaßt. Der Eiffel-Compiler kann daher prüfen, ob der Ausdruck korrekt benutzt wird. Bei einer ersten Betrachtungen erscheinen die Typ-Einschränkungen an eine generische Klasse zu restriktiv. Man kann sich nämlich durchaus Anwendungsgebiete für Listen vorstellen, die aus verschiedenartigen Elementen bestehen wie z.B. Vektoren und Punkte eines zweidimensionalen Raumes. Da eine generische Klasse jedoch mit einem eindeutig festgelegten aktuellen Parameter instantiiert werden muß, sind – mit den bisherigen Mitteln – nur Listen möglich, die entweder nur aus Punkten oder nur aus Vektoren bestehen.

Dann sind die folgenden Anweisungen sinnvoll und gültig<br />

pliste.cons(p) -- Hänge p vor die Personenliste<br />

il.cons(25) -- Hänge 25 vor die Integerliste<br />

p := pliste.head -- Weise das erste Element <strong>der</strong> Personenliste einer Personengröße zu<br />

Völlig unangebracht sind aber Anweisungen wie<br />

pliste.cons(25) -- Hänge die Zahl 25 vor die Personenliste<br />

il.cons(p) -- Hänge die Personengröße p vor die Integerliste<br />

p := il.head -- Weise das erste Element <strong>der</strong> Integerliste einer Personengröße zu<br />

Eiffel ist eine statisch getypte Sprache, bei <strong>der</strong> alle Typprüfungen während <strong>der</strong> Übersetzung durchgeführt<br />

werden können – also nur vom Programmtext abhängen. Deshalb würden die unsinnigen Anweisungen im<br />

Beispiel bereits vom Compiler abgewiesen werden. Bei Sprachen ohne Datentypen, gibt es dagegen keine<br />

Möglichkeit, die Typen <strong>der</strong> in eine Datenstruktur eingehenden Elemente einzuschränken. In solchen Sprachen<br />

erfüllen generische Klassen keinerlei Zweck, da sowieso alle Operationen auf alles angewandt werden können.<br />

Die Last <strong>der</strong> Kontrolle liegt hier beim Programmierer.<br />

Die Tatsache, daß innerhalb einer generischen Klasse praktisch nichts über eine Größe bekannt ist, <strong>der</strong>en Typ<br />

<strong>der</strong> formale generische Parameter ist, schränkt allerdings die Operationen auf dieser Größe sehr stark ein. Es<br />

dürfen nämlich nur Operationen benutzt werden, die auf jeden beliebigen Typ anwendbar sind. Insbeson<strong>der</strong>e<br />

sind Operationen wie !! (Erzeugung), clone, equal o<strong>der</strong> Anwendungen von Merkmalen auf x nicht erlaubt,<br />

da nicht gewährleistet ist, daß <strong>der</strong> aktuelle generische Parameter ein Klassenyp ist. Abbildung 3.20 stellt die<br />

Regeln für die Verwendung formaler generischer Parameter zusammen.<br />

Ist in einer Klassendeklaration X ein formaler generischer Parameter und x vom Typ X, dann darf<br />

x nur benutzt werden<br />

• als linke Seite einer Zuweisung x:=Ausdruck, wobei <strong>der</strong> Ausdruck <strong>der</strong> rechten Seite ebenfalls<br />

vom Typ X sein muß,<br />

• als rechte Seite einer Zuweisung y:=x, wobei die Größe auf <strong>der</strong> linken Seite ebenfalls vom<br />

Typ X sein muß,<br />

• als aktuelle Argument im Aufruf einer Routine f(..,x,..), welches einem formalen Argument<br />

vom Typ X entspricht (das geht nur, wenn f innerhalb <strong>der</strong> gleichen Klasse wie x<br />

deklariert wurde),<br />

• in einem Booleschen Ausdruck <strong>der</strong> Form x=y o<strong>der</strong> x/=y (bzw. y=x o<strong>der</strong> y/=x), wobei y<br />

ebenfalls vom Typ X sein muß.<br />

Abbildung 3.20: Regeln für formale generische Parameter<br />

Wie bei gewöhnlichen Routinen übernehmen bei <strong>der</strong> Verwendung generischer Klassen die aktuellen Parameter<br />

die Rolle <strong>der</strong> formalen Parameter in <strong>der</strong> Deklaration und entsprechend die dort vereinbarten Eigenschaften.<br />

Ist also f ein von einer generischen Klasse GENERISCH[X,Y] exportiertes Attribut, dessen Typ <strong>der</strong> formaler<br />

generischer Parameter X ist o<strong>der</strong> eine Funktion mit entsprechendem Ergebnistyp, dann wird bei einer<br />

Kundendeklaration <strong>der</strong> Form<br />

h:GENERISCH[INTEGER,PERSON]<br />

<strong>der</strong> Ausdruck h.f (ggf. mit Parametern) innerhalb <strong>der</strong> Kundenklasse als vom Typ INTEGER aufgefaßt. Der<br />

Eiffel-Compiler kann daher prüfen, ob <strong>der</strong> Ausdruck korrekt benutzt wird.<br />

Bei einer ersten Betrachtungen erscheinen die Typ-Einschränkungen an eine generische Klasse zu restriktiv.<br />

Man kann sich nämlich durchaus Anwendungsgebiete für Listen vorstellen, die aus verschiedenartigen Elementen<br />

bestehen wie z.B. Vektoren und Punkte eines zweidimensionalen Raumes. Da eine generische Klasse<br />

jedoch mit einem eindeutig festgelegten aktuellen Parameter instantiiert werden muß, sind – mit den bisherigen<br />

Mitteln – nur Listen möglich, die entwe<strong>der</strong> nur aus Punkten o<strong>der</strong> nur aus Vektoren bestehen.

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!