Grundlagen der Informatik I “Programmierung”
Grundlagen der Informatik I “Programmierung” Grundlagen der Informatik I “Programmierung”
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.
- Seite 53 und 54: Für die Zuordnung zwischen der Obj
- Seite 55 und 56: Diese Form der Definition findet ih
- Seite 57 und 58: K1: Kommutativ-Gesetz E1 ∧E2 ≡
- Seite 59 und 60: Axiomenschemata: L1 A ∨ ¬A L2 (A
- Seite 61 und 62: In dem Kapitel über die Programmve
- Seite 63 und 64: 2. Es werden Quantoren eingeführt,
- Seite 65 und 66: s (T, state) = wahr s (F, state) =
- Seite 67 und 68: • x ist gebunden in p(t1, ..., tn
- Seite 69 und 70: Der Wert einer Aussage mit mehreren
- Seite 71 und 72: Wichtig ist, daß die Auswahl des W
- Seite 73 und 74: Wir geben hier eine Funktion an, di
- Seite 75: wichtigste formale Sprache zur Besc
- Seite 78 und 79: Wir wollen jedoch deutlich darauf h
- Seite 80 und 81: Buch, sondern sollten besser als ei
- Seite 82 und 83: Viel sinnvoller ist es, bei der Bes
- Seite 84 und 85: Entsprechend ihrer beabsichtigten B
- Seite 86 und 87: Klassen sind rein statische Beschre
- Seite 88 und 89: leeren Verweis (d.h. von machen Per
- Seite 90 und 91: haben wie z.B. “(jahr:INTEGER)”
- Seite 92 und 93: und diese erzeugten Objekte mit Ver
- Seite 94 und 95: Bei der Erzeugung von Objekten mitt
- Seite 96 und 97: über den Namen eines Attributs Wer
- Seite 98 und 99: • Die Veränderung und Auswertung
- Seite 100 und 101: 3.5 Copy- und Referenz-Semantik In
- Seite 102 und 103: 3.5.2 expanded: Klassen mit Copy-Se
- Seite 106 und 107: Dieses Problem wird durch das Konze
- Seite 108 und 109: 3.7.1 Zusicherungen Eiffel logische
- Seite 110 und 111: class ARRAY[X] creation make featur
- Seite 112 und 113: class ARRAY[X] creation make featur
- Seite 114 und 115: formulieren und zu überwachen. Eig
- Seite 116 und 117: 3.8.2 Export geerbter Features Norm
- Seite 118 und 119: Ist also p ein Personenobjekt und a
- Seite 120 und 121: Definition 3.8.5 (Konformität) Ein
- Seite 122 und 123: Um die Beschreibung des Typsystems
- Seite 124 und 125: Nachkommenklassen aber folgt entity
- Seite 126 und 127: deferred class LIST[X] feature leng
- Seite 128 und 129: class STUDENT feature universität:
- Seite 130 und 131: Das Problem ist nun, daß beide Elt
- Seite 132 und 133: Prinzipiell wäre es sogar möglich
- Seite 134 und 135: Das bedeutet also, daß die Erbenkl
- Seite 136 und 137: Die eigentliche Montage eines Syste
- Seite 138 und 139: Verständlichkeit der Module: Die L
- Seite 140 und 141: dynamische Semantik: Die dynamische
- Seite 142 und 143: Constant ::= Manifest constant | Id
- Seite 145 und 146: Kapitel 4 Systematische Entwicklung
- Seite 147 und 148: 4.1.2 Grundideen des objektorientie
- Seite 149 und 150: jedoch dringend zu empfehlen, jegli
- Seite 151 und 152: • Ausleihe - Bücher werden nach
- Seite 153 und 154: Der Anwender ist kein echter Klient
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.