Informatik 3 Sitzung 9 - Theorie Roland Mühlenbernd Einleitung: Überblick Themen der 9. Session: I Kapitel 7: Typen & Datenkonzepte I Typen I Reihungen I Zeichenketten I Verbunde & Mengen I Einfache Typen I Konstanten I Kapitel 8: Ablaufsteuerung & algorithmische Konzepte I Routinen: Prozeduren & Funktionen I Zusicherungen I Anweisungen 7. Typen & Datenkonzepte:Gemeinsames Gemeinsamkeiten der Typsysteme der 5 Sprachen I Typbindung: Jede Variable, jeder Parameter, jeder Ausdruck besitzt einen zur Kompilationszeit festgelegten, unveränderlichen 'statischen Typ'. I Klassen und Typen: Das Klassenkonzept ist in das Typkonzept integriert. Jede Klasse beschreibt einen Typ. (Eine generische Klasse beschreibt eine Menge von Typen.) I Es gibt sprachdenierte und programmiererdenierte Typen. I Statische Typprüfung: Der Kompilierer prüft Typverträglichkeit in Ausdrücken, bei Zuweisungen, Parameterübergaben usw. I Bei numerischen Basistypen gibt es Regeln zur Typanpassung impliziten I Gröÿen, die sich auf Objekte (Exemplare von Klassen) beziehen, haben auch einen I Konformität: dynamischen Typ. Der dynamische Typ einer Objekt-Gröÿe muss einer Unterklasse ihres statischen Typs entsprechen. 7. Typen & Datenkonzepte: Typsysteme I Component Pascal, C++: Es gibt Typen, die nicht durch Klassen beschrieben sind. Die Typ-Spezialisierungsstruktur ist typisch für hybride Sprachen: I sprachdenierte Typen (predened, built-in): einfache Basistypen I programmiererdenierte Typen: gewöhnliche Typen, Klassen I Eiel: Jeder Typ ist durch eine Klasse beschrieben. Die Typ-Spezialisierungsstruktur ist typisch für rein objektorientierte Sprachen: I sprachdenierte Klassen: Standardbibliotheksklassen, Basisklassen I programmiererdenierte Klassen I Java: Bis auf primitive Typen ist jeder Typ durch eine Klasse beschrieben. I sprachdenierte Typen: Standardbibliotheksklassen, primitive Typen, Hüllenklassen I programmiererdenierte Typen: Klassen I C#: Jeder Typ kann durch eine Klasse beschrieben werden. class oder eine Struktur struct 7. Typen & Datenkonzepte: Sprachdenierte Typen Sprachdenierte Typen mit Operatoren 7. Typen & Datenkonzepte: Sprachdenierte Typen Sprachdenierte Typen mit Operatoren 7. Typen & Datenkonzepte: Sprachdenierte Typen Sprachdenierte Typen mit Operatoren 7. Typen & Datenkonzepte: Sprachdenierte Typen Sprachdenierte Typen mit Operatoren 7. Typen & Datenkonzepte: Sprachdenierte Typen Sprachdenierte Typen mit Operatoren decimal hat im Vergleich zu Gleitkommatypen höhere Genauigkeit und kleineren Wertebereich. 7. Typen & Datenkonzepte: Sprachdenierte Typen Sprachdenierte Typen mit Operatoren 7. Typen & Datenkonzepte: Programmiererdef. Typen Programmiererdenierte Typen 7. Typen & Datenkonzepte: Programmiererdef. Typen Operatoren programmiererdenierte Typen I Component Pascal: Programmiererdenierte Typen sind Typen zweiter Klasse: Operatoren sind nämlich für sie nicht durch den Programmierer denierbar. I Eiel: Operatoren sind syntaktische Varianten von Funktionen, die in einer Klasse (re-)deniert werden. Operatoren können mit beliebigen Sonderzeichen als Namen in Präx- und Inxschreibweise deniert werden. Konsistente Semantik von Operatoren wird über Abstraktionen und Vererbungsbeziehungen erreicht. I C++ nutzt vor allem den Mechanismus des Überladens. Nur Operatorsymbole, die schon in der Sprache deniert sind, können gewählt werden. Operatoren können global oder an Klassen gebunden sein. I Java: Hier sind die nicht-Klassen Typen (primitive Typen, Reihungen) Typen zweiter Klasse. Für Klassen-Typen werden Operatoren als Funktionen in der Klasse deniert. I C#: Ähnlich wie in Eiel für Klassen, wie auch für Strukturen. 7. Typen & Datenkonzepte: Typdeklaration & -denition Typdeklarationen, Typdenitionen I Component Pascal: I Eine Typdeklaration führt einen neuen Typ mit neuem Namen ein und hat die Form TYPE Typname = Typangabe. Beispiel: TYPE GraphObjekt* = POINTER TO ABSTRACT RECORD farbe*: INTEGER; END; Punkt* = POINTER TO RECORD (GraphObjekt) x*, y*: INTEGER; END; Linie* = POINTER TO RECORD (GraphObjekt) xStart*, yStart*, xEnde*, yEnde*: INTEGER; END; I Typangaben bilden eine syntaktische Einheit. I / Die Namen sprachdenierter Typen können mit anderer Bedeutung belegt werden. I Eiel I Es gibt keine Typdenitionen im Sinne von Component Pascal oder C++. I Jede Klasse erhält in ihrer Deklaration einen Namen. 7. Typen & Datenkonzepte: Typdeklaration & -denition Typdeklarationen, Typdenitionen I C++ I Eine Typdenition gibt einem Typ einen neuen Namen, sie führt aber keinen neuen Typ ein und hat näherungsweise die Form typedef Typangabe-Teil1 Typname Typangabe-Teil2; I Beispiele: typedef unsigned int uint; typedef struct { double re, double im; } complx; I Java I Typen werden über den Namen einer Klassen deklariert: class Typname {...Felder, Methoden... } I C# I Typen werden über den Namen eines Namensraumes, einer Klassen oder einer Struktur deklariert: namespace Typname {... }, class Typname {... }, struct Typname {... } 7. Typen & Datenkonzepte: Wertprüfung zur Laufzeit Wertprüfung zur Laufzeit Bei allen fünf Sprachen ist Unter- und Überlauf von Wertebereichen zur Laufzeit i.A. möglich. Alle Sprachen bis auf C++ prüfen die Einhaltung von Wertebereichen zur Laufzeit, d.h. nur C++ ist in dieser Hinsicht nicht typsicher. 7. Typen & Datenkonzepte: Implizite Typanpassung Implizite Typanpassung I I Component Pascal: Numerische Typen werden in arithmetischen Ausdrücken, bei Zuweisungen und Parameterübergaben implizit vom kleineren zum gröÿeren Typ angepasst, sodass keine Information, höchstens Genauigkeit verlorengeht. Die Kompatibilitätsregeln beruhen auf dem Typeinschluss: BYTE ⊆ SHORTINT ⊆ INTEGER ⊆ LONGINT ⊆ SHORTREAL ⊆ REAL Der Kompilierer prüft Typkompatibilität. Eiel: Numerische Typen werden in arithmetischen Ausdrücken, bei Zuweisungen und Parameterübergaben implizit vom leichteren zum schwereren Typ angepasst, sodass keine Information, höchstens Genauigkeit verlorengeht. Die Konformitätsregeln beruhen auf der Balancierungsregel INTEGER ⊆ REAL ⊆ DOUBLE Der Kompilierer prüft Typkonformität. 7. Typen & Datenkonzepte: Implizite Typanpassung Implizite Typanpassung I I C++: Alle Fundamentaltypen können in Ausdrücken, bei Zuweisungen und Parameterübergaben beliebig eingesetzt werden, sie werden implizit in den geforderten Typ ohne Rücksicht auf Verlust an Genauigkeit oder Information konvertiert. Es gibt daher weder Kompatibilitätsprüfungen des Kompilierers noch Laufzeitprüfungen. Java, C#: Wie bei Component Pascal und Eiel wird implizit verlustlos vom kleineren zum gröÿeren Typ angepasst. 7. Typen & Datenkonzepte: Explizite Typanpassung Explizite Typanpassung I Component Pascal, Eiel: Es gibt kein Sprachkonstrukt zur Konversion programmiererdenierter Typen oder Klassen. Der Programmierer kann natürlich selbst Konversionsfunktionen schreiben, wenn solche benötigt werden. I Component Pascal: Zur expliziten Typanpassung bestimmter Basistypen gibt es die sprachdenierten Funktionen CHR(x) (INT to CHAR), ENTIER(x) (REAL to INT), LONG(x) (to the next 'longer' type), ORD (CHAR to INT), SHORT (to the next 'shorter' type). I Eiel: Die Bibliotheksklasse BASIC_ROUTINES enthält für Basisklassen die Konversionsfunktionen charcode, charconv, double_to_integer, double_to_real, real_to_integer. I C++: Die aus C übernommene Typecast-Notation zur Typkonversion kann auf beliebige Typen, also auch auf Klassen angewandt werden: Some_type * obj_some = (Some_type*) obj; ((Some_type *) obj)-> feature_of_Some_type 7. Typen & Datenkonzepte: Explizite Typanpassung Explizite Typanpassung I Java: Es gibt mehrere Arten, hier eine kleine Auswahl: I Widening Primitive Conversion für primitive Typen (kein Informationsverlust): I byte to short, int, long, oat, or double I short to int, long, oat, or double I char to int, long, oat, or double I int to long, oat, or double I long to oat or double I oat to double I Beispiel: int num = 1; oat real = (oat)num; I Narrowing Primitive Conversion für primitive Typen (mit Informationsverlust): I short to byte or char I char to byte or short I int to byte, short, or char I long to byte, short, char, or int I oat to byte, short, char, int, or long I double to byte, short, char, int, long, or oat I Beispiel: oat approx = 1.3; int value = (int)approx; 7. Typen & Datenkonzepte: Explizite Typanpassung Explizite Typanpassung I Java: Es gibt mehrere Arten, hier eine kleine Auswahl: I I I Boxing Conversion von primitive Typen zu Hüllenklassen. Beispiel: r.intValue() == p weiÿt Variable r von Typ Integer den Wert der Variable p vom Typ int zu. Unboxing Conversion von Hüllenklassen zu primitive Typen. Beispiel: r.intValue() konvertiert Variable r von Typ Integer in Typ int. C#: Explizite Typanpassung durch casts wie in C++ oder durch Helferklassen, zum Beispiel System.Convert oder Int32.Parse 7. Typen & Datenkonzepte: Statischer & Dynamischer Typ I I I I Eine Unterscheidung zwischen statischem und dynamischem Typ ist nur in Zusammenhang mit Vererbung relevant. Der statische Typ einer Gröÿe ist der Typ, mit dem sie (zur Kompilationszeit) vereinbart ist. Der dynamische Typ einer Gröÿe ist der Typ ihres Werts zur Laufzeit. Der dynamische Typ entscheidet, welche Version einer gerufenen Operation ausgeführt wird 7. Typen & Datenkonzepte: Statischer & Dynamischer Typ I I I I I Component Pascal: Bei einer Zeigervariablen oder einem Referenzparameter eines Verbundtyps kann der dynamische Typ eine Erweiterung des statischen Typs sein. Eiel: Bei Gröÿen mit Referenzsemantik kann der dynamische Typ eine Erweiterung des statischen Typs sein. C++: Im Unterschied zu Component Pascal und Eiel besitzt die Gröÿe in C++ nicht einen dynamischen Typ, sondern wird vom statischen Typ in diesen konvertiert. Java: nur Referenzsemantik, wie Eiel C#: wie in C++ 7. Typen & Datenkonzepte: Typinformation zur Laufzeit Typinformation zur Laufzeit I Component Pascal: Es gibt das Konstrukt Typtest. IF obj IS SomeType THEN ... ELSIF obj IS OtherType THEN ... END I Eiel: Viele Probleme lassen sich mit dem Mechanismus verankerter Typen ( anchored types) und der Kovarianzregel ohne Typtests lösen. Die ANY und damit jede Klasse enthält das Merkmal gemeinsame Oberklasse generator : STRING, das den Klassennamen als Zeichenkette liefert: if obj.generator.is_equal(SOME_TYPE) then ... elseif obj.generator.is_equal(OTHER_TYPE) then ... end Den Typvergleich zweier Objekte erlaubt das in ANY denierte Merkmal conforms_to(other : like Current) : BOOLEAN bei dem ein verankerter Typ verwendet wird: if obj_some.conforms_to(obj_other) then ... 7. Typen & Datenkonzepte: Typinformation zur Laufzeit Typinformation zur Laufzeit I C++: Der Operator typeid, der der Abfrage des dynamischen Typs eines Objekts zur Laufzeit dient, gehört zu den jüngsten Elementen des Sprachstandards. if (typeid(obj) == typeid(SomeType)) { ... } else if (typeid(obj) == typeid(OtherType)) { ... }; Der Klassenname steht auch als Zeichenkette bereit: if (strcmp(typeid(obj).name(), typeid(SomeType).name())) ... I Java: Name des Typs eines Klassen-Objekts kann durch objekt.getClass() erfragt werden. Des Weiteren enthält java.lang.reect Hilfsklassen zur Aufbereitung von Typinformationen. I C# bietet die Operatoren is und typeof: if (obj is ClassA) {} if (obj.GetType() == typeof(ClassA)) {} 7. Typen & Datenkonzepte: Reihungen Reihungen I Component Pascal: I Reihungen werden statisch vereinbart. Die Elementzahl ist durch eine positive ganze Zahl gegeben, der Indexbereich beginnt immer bei 0, der Elementtyp ist beliebig: VAR a : ARRAY 10 OF REAL; I Der Wert der Variablen a ist das Feld. Zugri auf die Elemente mit indizierten Variablen: a [0], a [9]. Die Überschreitung von Bereichsgrenzen wird zur Laufzeit überprüft. I Mehrdimensionale Reihungen sind möglich: VAR b : ARRAY 8, 7 OF REAL;. b [1][2] und b [1, 2] sind äquivalent. I Als formale Parameter sind oene Reihungen (ohne Angabe der Elementzahl) erlaubt, e.g. ARRAY OF INTEGER; I Reihungen können dynamisch erzeugt werden, wenn ein Zeiger auf eine oene Reihung vereinbart ist: VAR openArr : POINTER TO ARRAY OF REAL; ... NEW (openArr, 64); 7. Typen & Datenkonzepte: Reihungen Reihungen I Eiel: I Reihungen werden als Objekte einer Bibliotheksklasse dynamisch erzeugt. Der Indexbereich ist durch zwei ganzzahlige Werte festgelegt, der Elementtyp ist beliebig: a : ARRAY[REAL]. I Der Wert der Variablen a ist ein Bezug auf ein Objekt des Typs ARRAY[REAL], der von der generischen Klasse ARRAY abgeleitet ist. I Erzeugung eines Objekts: create a.make(0, 9); I Zugri auf die Elemente erfolgt ausschlieÿlich über Zugrisroutinen: a.put (0, x), x := a.item (9) I Für den Lesezugri gibt es auch eine Inx-Notation: x := a @ 9 I Die Überschreitung von Bereichsgrenzen wird durch Zusicherungen (Vorbedingungen, Invarianten) geprüft. I Mehrdimensionale Reihungen sind durch geschachtelte Vereinbarungen b : ARRAY [ARRAY [REAL]] oder spezielle Klassen möglich. 7. Typen & Datenkonzepte: Reihungen Reihungen I C++: I Eigentlich gibt es in C/C++ keine Reihungen, sondern nur eine Reihungsschreibweise für Zeiger. I Die Elementzahl ist durch eine positive ganze Zahl gegeben, der Indexbereich beginnt immer bei 0, der Elementtyp ist beliebig: oat a [10]; I Der Wert der Variablen a ist ein Bezug auf das Feld. I Zugri auf die Elemente mit indizierten Variablen oder Zeigerarithmetik: a[0] ist äquivalent mit *a; a[9] ist äquivalent mit *(a + 9) I Die Überprüfung der Überschreitung von Bereichsgrenzen ist eine seltene Kompiliereroption. Die Bereichsgrenzen sind zudem zur Laufzeit nicht bekannt! Sie müssen daher bei Bedarf in zusätzlichen Variablen mitgeführt werden. I Es empehlt sich, in C++ auf die von C übernommenen Reihungen sowie die Zeigerarithmetik weitgehend zu verzichten und stattdessen die generische vector-Bibliotheksklasse der STL zu verwenden, da diese sicherer ist. 7. Typen & Datenkonzepte: Reihungen Reihungen I Java: type Verbund (Record) mit n + 1 Attributen (ein zusätzlichs Attribut enthält die Länge) a[i] bezeichnet den Zugri auf die i -te Komponente der I In Java wird eine Reihung mit n Elementen vom Typ aufgefasst als ein Zeiger auf einen I Reihung a, a.length gibt die Länge der Reihung an I Deklaration einer Reihung mit Elementen vom Typ Länge n: type und type[] var = new type[n]; I Standardwerte (Defaultwerte) sind z.B.: int: 0, double: 0.0, boolean: false I Sofort Anfangswerte zuweisen (Initialisierung): type[] var = {v0 , ..., v n −1 } I Mehrdimensionale Reihungen sind durch Deklarierung n n ]; mit Dimensionsangabe {f0 ,..., f }; type[]...[] var = new type[ 1 ]...[ i oder geschachtelte Initialisierung type[][] var = f ( 0 ,..., f n sind Reihungen vom Typ type) möglich. n 7. Typen & Datenkonzepte: Zeichenketten I Component Pascal: Zeichenketten sind Reihungen von Zeichen. I Sie werden mit dem Zeichen 0X abgeschlossen (siehe C++) I Deklarierung: VAR s : ARRAY 10 OF CHAR; I Es gibt ein Bibliotheksmodul Strings mit Prozeduren zur Zeichenkettenverarbeitung. I Eiel: Zeichenketten werden als Objekte einer Bibliotheksklasse dynamisch erzeugt. I Die Länge ist durch einen ganzzahligen Wert festgelegt. I Der Wert der Gröÿe s ist ein Bezug auf ein Objekt der Klasse STRING: s : STRING I Erzeugung eines Objekts: create s.make(10) I Die Klasse enthält viele Routinen zur Zeichenkettenverarbeitung. 7. Typen & Datenkonzepte: Zeichenketten I C++: Zeichenketten sind als Zeiger auf Zeichen oder als Reihungen von Zeichen darstellbar. I char * s;, char t[10]; I Eine Zeichenkette wird durch das Zeichen 0 oder ' \0' abgeschlossen. Dies erlaubt eine eziente Speicherung: char str1[] = { 'F', 'e', 'i', 'e', 'r', '\0' }; I Die Überschreitung von Bereichsgrenzen wird i.A. nicht geprüft (kein Laufzeitfehler), sondern führt zu fehlerhaften Zugrien auf fremde Speicherbereiche. I Es gibt eine umfangreiche ANSI-C-Bibliothek string.h mit C++- Funktionen zur Zeichenkettenverarbeitung, sowie eine Bibliotheksklasse string, deren Benutzung vorzuziehen ist. 7. Typen & Datenkonzepte: Zeichenketten I Java: Zeichenketten werden durch den Datentyp String realisiert: I Der Datentyp String ist in Java kein einfacher Datentyp wie etwa int. Eine Variable vom Typ String enthält einen Verweis auf ein Objekt vom Typ String. Dies ist deswegen nötig, weil Strings unterschiedliche Längen annehmen können. I Strings werden in Anführungszeichen gesetzt: String s; s = Hallo!; I C#: Zeichenketten sind Objekte der Klasse String I Die Klasse String ist Standardklasse in System I Ein String-Objekt ist eine sequenzielle Auistung von System.Char-Objekten, die eine Zeichenfolge darstellen I Für beide Sprachen gilt: Ein String-Objekt wird unveränderlich (schreibgeschützt) genannt, weil dessen Wert nach dem Erstellen nicht mehr geändert werden kann. Methoden, die ein String-Objekt scheinbar ändern, geben in Wirklichkeit ein neues String-Objekt zurück, das die Änderung enthält. 7. Typen & Datenkonzepte: Verbunde Verbunde I Component Pascal: Die Typen der Attribute sind beliebig: I Beispiel: VAR r : RECORD name : ARRAY 20 OF CHAR; age : INTEGER; salary : REAL; END; I Zugri auf Elemente: r.name, r.age, r.salary I Eiel: Verbunde können als Klassen realisiert werden. Die Attribute sind von auÿen nicht zugreifbar. Schreibzugrie müssen mit Zugrisroutinen realisiert werden. Dies entspricht dem Konzept der Datenabstraktion. 7. Typen & Datenkonzepte: Verbunde Verbunde I C++: Die Typen der Attribute sind beliebig: I Beispiel: struct { char name[20]; int age; oat salary; } r; I Zugri auf Elemente: r.name, r.age, r.salary I I In Java als Klasse realisierbar: class Verbund { Elementliste }; In C# als Klasse (wie Java) oder durch struct realisierbar. 7. Typen & Datenkonzepte: Mengen Mengen I I I I Component Pascal: Die Elemente einer Menge sind ganze Zahlen aus kleinen Wertebereich (0 ... 31): VAR s : SET; Es gibt Mengenoperatoren und -prozeduren. Eiel: Mengen mit beliebigem Elementtyp werden als Objekte einer generischen Bibliotheksklasse dynamisch erzeugt. Der Elementtyp ist eine beliebige Klasse, e.g.: s : SET [INTEGER]; Die Klasse enthält viele Routinen, darunter Mengenoperatoren und Zugrisroutinen auf Elemente. Java: Für Mengen sieht die Java-Bibliothek die Schnittstelle java.util.Set vor. Eine Menge deniert neben Operationen für Anfrage und Einfügen von Elementen auch Funktionen für Schnitt und Vereinigung von Mengen. C#: Keine direkte Unterstützung. Es gibt u.a. HashSet<T> (> .NET 3.5) und SortedSet<T> (> .NET 4.0). 7. Typen & Datenkonzepte: Einfache Typen Zeichen & Zahlen I I Component Pascal: Zeichen und Zahlen sind unterscheidbare Typen mit unterschiedlichen Operatoren. Die Anpassung erfolgt explizit mit Standardfunktionen: VAR c : CHAR; i : INTEGER; c := 'A'; c := CHR (65); i := 65; i := ORD (0AX); i := ORD ('A') + ORD ('B'); Eiel bietet konzeptuell dasselbe wie Component Pascal, nur mit den Funktionen charconv und charcode der Klasse BASIC_ROUTINES statt CHR und ORD. 7. Typen & Datenkonzepte: Einfache Typen Zeichen & Zahlen I C++: Zeichen und Zahlen werden kaum unterschieden. Auf Zeichen sind arithmetische Operationen anwendbar: char c; int i; c = 'A'; c = 65; i = 65; i = 'A' + 'B'; I Java: Zwischen Zeichen und Zahlen kann gecastet werden. Zuordnungswerte sind durch den Unicode festgelegt: char c; int i; c = 'A'; c = (char)65; i = (int)c; i = (int)'A'; I C#: Umwandlung zwischen Zeichen und Zahlen erfolgt e.g. durch Funktionen der Systemklasse Convert 7. Typen & Datenkonzepte: Einfache Typen Aufzählungen I Component Pascal: Die aus Pascal und Modula-2 bekannten Aufzählungstypen elen dem Prinzip der Minimierung der Sprachkonzepte zum Opfer. Aufzählungen müssen mit ganzzahligen Konstanten realisiert werden, wobei Typsicherheit verlorengeht: CONST red = 0; green = 1; blue = 2; numColours = 3; VAR colour : INTEGER; ASSERT ((0 <= colour) & (colour <= numColours), BEC.invariant); I Eiel: Die den Bezeichnern red, green, blue zugeordneten Werte sind klassenweit eindeutige konsekutive positive Ganzzahlen. Die Einhaltung des Wertebereichs wird hier mit einer Invariante geprüft. red, green, blue : INTEGER is unique colour : INTEGER invariant colour = red or colour = green or colour = blue 7. Typen & Datenkonzepte: Einfache Typen Aufzählungen I C++: Die Aufzählungstypen sind aus C übernommen. enum Colour {red, green, blue}; Colour colour; Aufzählungen sind mit ganzen Zahlen verträglich. Der Aufzählungstyp ist implementierbar: enum Colour {red = 0, green = 9, blue = -1 }; I Java: Aufzählungen können durch Klassen implementiert werden: public class Colour { public static nal red = 0; public static nal green = 1; public static nal blue = 2; } public class ColourClient { int colour = Colour.red; } I C#: enum-Konstrukt ähnlich wie in C++ 7. Typen & Datenkonzepte: Konstanten Konstanten I Component Pascal: I Beispiele für literale Konstanten: 'a', 1, 2.3, 4.5E-6, ghe I Konstantennamen für einfache Werte sind in einer Konstantenvereinbarung denierbar. Es handelt sich um Übersetzungszeitkonstanten: CONST pi = 3.14; I Eiel: I Beispiele für literale Konstanten: 'a', 1, 2.3, 4.5E-6, ghe I Namen für Werte von Basistypen sind in einer Merkmals- vereinbarung einer Klasse denierbar: pi : REAL = 3.14; I In diesem Beispiel ist eine Einmalfunktion günstig einsetzbar: pi : REAL once Algorithmus zur pi-Berechnung Result := Ausdruck end I Kommt es auf den Wert nicht an, kann das Wortsymbol unique verwendet werden: left, right : INTEGER is unique; I Einmalfunktionen können auch für konstante Objekte und zur Bildung von Laufzeitkonstanten verwendet werden. 7. Typen & Datenkonzepte: Konstanten Konstanten I C++: I Beispiele für literale Konstanten: 'a', 1, 2.3, 4.5E-6, ghe, 012 (Oktal), 0xAFFE (Hexadezimal) I C kennt keine Konstantennamen. Solche sind aber mit einer Vorübersetzerdirektive denierbar. Sie werden vor der Kompilation substituiert: #dene pi (3.14) I C++ bietet aber richtige Konstanten, deren Benutzung man vorziehen sollte: const oat pi = 3.14; I Java bietet das nal Konstrukt, welches in Klassen nur in Kombination mit static Sinn macht: public static nal oat pi = 3.14; I C#: bietet entsprechend das const Konstrukt: public const double pi = 3.14; 8. Ablaufsteuerung & alg. Konzepte: Routinen Unter Routinen verstehen wir Prozeduren und Funktionen. Die syntaktischen Einheiten zur Vereinbarung von Routinen sind in jeder Sprache anders bezeichnet. 8. Ablaufsteuerung & alg. Konzepte: Prozeduren Prozeduren in den verschiedenen Sprachen I Component Pascal: I I C++, Java, C#: Eiel: 8. Ablaufsteuerung & alg. Konzepte: Funktionen Funktionen in den verschiedenen Sprachen I Component Pascal: I I C++, Java, C#: Eiel: 8. A& AK: Parameter & Ergebnisübergabe 8. Ablaufsteuerung & alg. Konzepte: Zusicherungen Zusicherungen in Component Pascal I I I Es gibt allgemeine Zusicherungen in Form vordeklarierter Prozeduren: ASSERT (Bedingung); ASSERT (Bedingung, Fehlernummer); Ist die Bedingung falsch, so wird der Programmablauf mit einem Trap abgebrochen. Zur Information werden die Fehlernummer, der Aufrufkeller mit allen Variablen und die Abbruchstelle im Quellprogramm angezeigt. 8. Ablaufsteuerung & alg. Konzepte: Zusicherungen Zusicherungen in Eiel I I I Ein wichtiges Entwurfsziel ist, dass sich die Sprache als Spezikationssprache eignen und die Entwicklung korrekter Programme unterstützen soll. Den Sprachkonstrukten liegt das Kunden-Lieferanten-Modell (client-supplier-model) zugrunde, auf dem auch eine Entwicklungsmethode aufbaut. Dabei werden Prozeduren mit Vor- und Nachbedingungen, Klassen mit Invarianten speziziert. 8. Ablaufsteuerung & alg. Konzepte: Zusicherungen Entsprechend bietet Eiel die folgenden Arten von Zusicherungen: I Allgemeine Zusicherung als Anweisung: I Vor- und Nachbedingungen von Routinen als Abschnitte: check Bedingung end; require Vorbedingungen ensure Nachbedingungen Diese Abschnitte werden vererbt, d.h. eine Redenition einer Routine muss sich an die geerbte Spezikation halten. Sie darf nur die Vorbedingungen abschwächen und die Nachbedingungen verstärken. I Invarianten von Klassen als Abschnitt: invariant Konsistenzbedingungen Die Konsistenzbedingungen müssen vor und nach jedem Aufruf einer Routine gelten. Der Abschnitt wird vererbt, d.h. eine Unterklasse einer Klasse muss sich an die geerbte Spezikation halten. Sie darf die Invarianten nur verstärken. I Invarianten und Varianten von Schleifen als Abschnitte: invariant Bedingung variant nichtnegativ-ganzzahliger Ausdruck 8. Ablaufsteuerung & alg. Konzepte: Zusicherungen Zusicherungen in Eiel I I I Die require-, ensure- und invariant-Abschnitte sind in objektorientierte Konzepte integriert. Ist eine Prüfbedingung falsch, so wird eine sprachdenierte Ausnahme (exception) ausgelöst. Diese Ausnahme kann an denierter Stelle auf programmiererdenierte Weise behandelt werden. Ist keine Ausnahmebehandlung programmiert, so wird der Programmablauf abgebrochen. Die Überprüfung von Zusicherungen ist selektiv zur Laufzeit steuerbar. 8. Ablaufsteuerung & alg. Konzepte: Zusicherungen Zusicherungen in C++ I Zusicherungen sind durch eine Bibliothek mit der Schnittstellendatei assert.h unterstützt. Dort ndet sich von ANSI-C das Vorübersetzermakro assert(Bedingung); I Ist die Prüfbedingung falsch, so wird der Programmablauf durch Aufruf der Funktion abort () abgebrochen. I Zur Information werden der Wert der Bedingung (die ein beliebiger Ausdruck ist), der Name der Quelldatei und die Zeilennummer der Abbruchstelle ausgegeben. I Die Vorübersetzerdirektive #dene NDEBUG schaltet die Überprüfung der Zusicherungen ab (vor der Kompilation). I C++ bietet zusätzlich die generische Funktion Assert (Bedingung, Ausnahme); I Ist die Prüfbedingung falsch, so wird die übergebene programmiererdenierte Ausnahme (exception) ausgelöst. Die Reaktion hängt dann von der Ausnahme und ihrer Behandlung ab. I Die Überprüfung dieser Zusicherungen ist auch mit NDEBUG steuerbar. Mit Hilfe von Ausnahmen kann man auch eigene Prüunktionen programmieren. 8. Ablaufsteuerung & alg. Konzepte: Zusicherungen Zusicherungen in Java I Java bietet Zusicherungen mit Release 1.4 in Form eines Sprachkonstrukts I Zusicherungen werden mit dem Schlüsselwort assert eingeleitet, dem booleschen Ausdruck kann eine Zeichenkette folgen: assert Bedingung : Zeichenkette; I Beim Aufruf des Interpretierers, der Java Virtual Machine, ist anzugeben, ob die Zusicherungen zur Laufzeit geprüft werden soll: java -enableassertions MyClass java -ea MyClass oder nicht: java -disableassertions MyClass java -da MyClass I Die Standardeinstellung ist, Zusicherungen nicht zu prüfen I Verletzte Zusicherung führt zu Abbruch mit Fehlermeldung, z.B.: Exception in thread "main" java.lang.AssertionError: Zeichenkette at MyClass.main(MyClass.java:5) 8. Ablaufst. & alg. Konzepte: Anweisungen Example: WITH t: CenterTree DO i := t.width; c := t.subnode END 8. Ablaufst. & alg. Konzepte: Syntaktischer Zucker Ein Programmkonstrukt weist eine Unstetigkeitsstelle auf, wenn zwei Programme, die dieses Konstrukt verwenden und sich in nur einem Zeichen unterscheiden, verschiedene Semantik haben. Component Pascal, Eiel: I Alle Steuerkonstrukte benutzen Wortsymbole, um Anweisungsfolgen zu klammern. I Deshalb ist keine Verbundanweisung erforderlich. Das schlieÿende END bzw. end beseitigt Unstetigkeitsstellen und Zweideutigkeiten älterer Sprachen. I In Component Pascal ist das Semikolon ; Endezeichen bei Vereinbarungen und Trennzeichen zwischen Anweisungen. I In Eiel ist das Semikolon bei Vereinbarungen und Anweisungen ein optionales Trennzeichen, d.h. es kann auch weggelassen werden, weil die Syntax so deniert ist, dass es auf ein Semikolon mehr oder weniger nicht ankommt. I Der empfohlene Stil ist jedoch, trennende Semikola zwecks besserer Lesbarkeit hinzuschreiben. 8. Ablaufst. & alg. Konzepte: Syntaktischer Zucker C++: I Steuerkonstrukte erwarten immer eine einzelne Anweisung und enden meist ohne Wortsymbol. Deshalb ist eine Verbundanweisung erforderlich, sie verwendet die geschweiften Klammern: { Anweisungen } I Manche Konstrukte haben Unstetigkeitsstellen. Beispiel: while (i < 9) ; i++; while (i < 9) i++; I Manche Konstrukte liefern Zweideutigkeiten, die durch zusätzliche Sprachregeln aufgelöst werden müssen. Beispiel: if (i < 9) if (k > 1) i = k--; else i = k++; I Das Semikolon ist bei manchen Anweisungen ein Endezeichen (Deklaration, Ausdruck, do, break, return), bei anderen nicht. Ein defensiver Schreibstil ist, das Semikolon als Endezeichen bei jeder Anweisung zu setzen. 8. Ablaufst. & alg. Konzepte: Alternative Werte C++ Component Pascal I Eiel case dient nur als Einsprungmarke, von da an werden alle folgenden Anweisungen ausgeführt bis zur ersten break-Anweisung; diese führt zu einem Sprung ans Ende der switch-Anweisung. I Vergessene break-Anweisungen sind eine Fehlerquelle. I Es gibt keine Listen und Intervalle von Fällen, diese können durch mehrere case ohne abschlieÿendes break simuliert werden. 8. A & AK: Wiederholungsanweisung Component Pascal C++ Eiel Konklusion Der 'Informatik 3'-Student sollte I abstrakte (programmiersprachenunabhängige) Problemstellungen und Konzepte I erfassen und/oder modellieren können I in den verschiedenen Programmiersprachen implementieren und (teil-)spezizieren können I Programm(teil)e von einer in eine andere Programmiersprache übersetzen können I Einsatzmöglichkeiten von Programmiersprachen erkennen können (wann ist Sprache A der Sprache B vorzuziehen und warum?) I mit den folgenden Konzepten vertraut sein: I I Typen: Datentypen, Semantiken, Morph. & Dyn. Binden... Strukturen: Algorithmen, Funktionen, Methoden, Module, Abstrakte Datenstrukturen, Klassen... I Objektorientierte Programmierung: Klassen, abstrakte Datentypen, Vererbung/Erweiterung... I Abstraktion: Datenabstraktion, Schnittstellen, Generizität...