Grundlagen der Informatik I “Programmierung”

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

22.08.2013 Aufrufe

Entsprechend ihrer beabsichtigten Bedeutung muß die Funktion empty eine Liste in einen booleschen Wert abbilden, die Funktion tail eine Liste in eine Liste und die Funktion head eine Liste in ein Element von X. Dabei ist jedoch zu berücksichtigen, daß die Funktion head nicht auf allen Listen definiert ist, also eine partielle Funktion ist. Dies wird in einem Teil der Literatur (zum Beispiel in [Meyer, 1988]) durch einen modifizierten Pfeil (→) besonders herausgehoben, obwohl dies wegen der Nennung von Vorbedingungen eigentlich überflüssig ist. Die Erzeugung einer neuen (leeren) Liste durch die Funktion new wird dadurch ausgedrückt, daß new keinen Argumenttyp besitzt und somit immer dasselbe Ergebnis liefert. new: → List[X] Aus der Sicht der Mathematik sind Funktionen ohne Argumente wie new (sogennante Konstruktor- Funktionen) und ihr Ergebnis (die Konstante ) praktisch identisch, da es von von außen betrachtet keine Rolle spielt, ob eine Dienstleistung zur Bereitstellung eines festen Wertes diesen erst berechnen muß oder auf einen bereits existierenden Wert zugreifen kann. Deshalb können alle Dienstleistungen als Funktionen beschrieben werden. Diese Sichtweise spiegelt sich auch in der Syntax der Sprache Eiffel wieder, welche die gleiche Philosophie verfolgt. • Partielle Funktionen sind in fast allen Programmierproblemen eine unausweichliche Tatsache, aber auch eine mögliche Quelle für Fehler. Aus diesem Grunde müssen die Anforderungen an die Anwendbarkeit jeder partiellen Funktion klar formuliert werden. Dies ist der Zweck des Abschnitts PRECONDITIONS. In unserem Beispiel ist die einzige Vorbedingung, daß head nur auf nichtleere Listen angewandt werden darf: pre head(L:List[X]) = (not empty(L)) • Der Abschnitt AXIOMS schließlich beschreibt die semantischen Mindestanforderungen, welche sicherstellen, daß die genannten Funktionen die gewünschten Eigenschaften besitzen. So soll die Funktion empty nur für die von new erzeugte leere Liste wahr sein, nicht aber bei Listen, die durch cons erzeugt wurden. head und tail zerlegen eine durch cons erzeugte Liste wieder in ihre Bestandteile während auf eine leere Liste nur tail anwendbar ist und wieder eine leere Liste liefert. Die abstrakte mathematische Beschreibung von Datentypen und Operationen in einem gemeinsamen Konzept erlaubt es, präzise Aussagen über die Effekte einer Berechnung aufzustellen und durch Verwendung der Axiome zu beweisen. Die Durchführung einer Berechnung kann nämlich als algebraische Vereinfachung (“Reduktion”) betrachtet werden, in der die linken (langen) Seiten eines Axioms durch die rechten (kurzen) ersetzt werden. So läßt sich zum Beispiel auch ohne Verwendung der anschaulich erklärten Bedeutung der Operationen tail, cons und new beweisen, daß das Ergebnis der Berechnung von tail(tail(cons(4,new()))) der Wert new() ist. Hierzu braucht man nur das vierte und dann das fünfte Axiom anzuwenden. Diese Aspekte abstrakter Datentypen haben sie zur Grundlage vieler Forschungen auf den Gebieten der formalen Spezifikation, der symbolischen Berechnung, der Verifikation von Programmen, dem Software-Prototyping und nicht zuletzt auch der Datenstrukturbeschreibung werden lassen. In objektorientierten Sprachen wie Eiffel wird daher jedes Programm um Klassen von Datenstrukturen herum organisiert, die auf der Grundlage abstrakter Datentypen beschrieben werden. Auf diese Art wird ein ausgewogenes Verhältnis zwischen Daten und Funktionen hergestellt. Die Struktur eines Softwaresystems beruht auf den Datenstrukturen, aber diese sind wiederum auf der Grundlage abstrakter Funktionen definiert: Objektorientierter Entwurf ist die Entwicklung von Softwaresystemen als strukturierte Sammlung von Implementierungen abstrakter Datentypen.

3.2.2 Klassen in Eiffel Mit den abstrakten Datentypen haben wir eine mathematische Beschreibungsform gegeben, die es erlaubt, Klassen von Objekten vollständig, genau und eindeutig zu charakterisieren und dabei gleichzeitig eine Überspezifikation zu vermeiden. Wir wollen nun zeigen, wie man eine solche Beschreibungsform in einer Programmiersprache ausdrücken kann. In der Denkweise der Programmiersprachen hat sich anstelle des Namens “abstrakter Datentyp” der Begriff “Klasse” zur Beschreibung der Gemeinsamkeiten einer Gruppe von Objekten eingebürgert. In Eiffel ist das Konzept der Klasse nahezu identisch mit dem der abstrakten Datentypen. Gegenüber der mathematischen Sichtweise ändern sich nur einige der verwendeten Begriffe und Schlüsselworte. Die einfachste Form einer Klassendefinition ist etwas ähnliches wie ein Verbundtyp in Pascal, in dem nur die gemeinsame Struktur einer Gruppe von Objekten festgelegt wird: class PERSON feature name, vornamen: STRING; geburtsjahr, todesjahr: INTEGER; nationalität: STRING end -- class PERSON Abbildung 3.5: Eine einfache Klassendefinition Die Klasse in Abbildung 3.5 beschreibt die Struktur einer Menge von Objekten, die Instanzen oder Exemplare dieser Klasse genannt werden. Die Klasse erhält den Namen PERSON und stellt Komponenten wie name, vorname, etc. zur Verfügung. Die Komponenten einer Klasse werden als Dienstleistungen der Klasse betrachtet, die für eventuelle Benutzer dieser Klasse nach außen hin verfügbar sind. Dabei spielt es – von außen betrachtet – keine Rolle, ob diese Dienstleistungen erbracht werden, indem auf eine abgespeicherte Komponente zugegriffen wird oder indem eine Berechnung (z.B. zur Bestimmung einer eindeutigen internen Kennung) durchgeführt wird. Diese Sicht deckt sich mit der im Abschnitt 3.2.1 angesprochenen Philosophie der abstrakten Datentypen, die alle Dienstleistungen als Funktionen ansieht, von denen einige vielleicht keine Eingaben benötigen. Um dieser Sichtweise Rechnung zu tragen, werden in Eiffel alle nach außen hin sichtbaren Dienstleistungen einer Klasse mit dem Oberbegriff feature 6 bezeichnet. Im einfachsten Fall sind Features nur Attribute, also Bestandteile von Klassen, die tatsächlich eine Komponente der Objekte der Klasse bezeichnen. Features können aber auch andere von der Klasse bereitgestellte Dienstleistungen sein, nämlich Routinen, die Operationen auf den Objekten der Klasse beschreiben, wie zum Beispiel die Bestimmung aller derzeit verfügbaren (nicht ausgeliehenen) Bücher, die von einem bestimmten Autor geschrieben wurden. Hierauf werden wir im Abschnitt 3.3 zurückkommen. Die Schlüsselworte class, feature und end, gedruckt in einem anderen Schriftsatz, werden zur syntaktischen Aufteilung einer Klassendefinition in Klassendeklaration und Deklaration von Features benutzt. Features vom selben Typ können in Deklaration gruppiert werden. Für die Aneinanderreihung von Deklarationen (und später auch von anderen Instruktionen) wird als Trennzeichen das Semikolon “;” verwendet und für Kommentare der doppelte Bindestrich “--”. Es hat sich als gute Konvention bewährt, am Ende einer Klassendefinition den Namen der Klasse im Kommentar zu wiederholen. Klassen sind die Grundkomponenten, aus denen alle Eiffel-Programme aufgebaut werden. Sie beschreiben nicht nur die Datenstrukturen, die in einem Softwareprodukt verwendet werden, sondern ebenso alle Module, aus denen dieses Softwareprodukt aufgebaut ist – einschließlich dem “Hauptprogramm”. Diese Vorgehensweise entspricht einer konsequenten Umsetzung der objektorientierten Sichtweise. Ein Programm ist nichts 6 Der deutsche Begriff für feature ist Merkmal. Der englische Begriff hat sich jedoch auch in der deutsprachigen Literatur eingebürgert, da er in seinem Verwendungszweck eindeutiger zu sein scheint. Wir werden dieser Tatsache Rechnung tragen und trotz des sich daraus ergebenden Mißbrauchs der deutschen Sprache und Grammatik den Begriff feature weiterverwenden.

Entsprechend ihrer beabsichtigten Bedeutung muß die Funktion empty eine Liste in einen booleschen<br />

Wert abbilden, die Funktion tail eine Liste in eine Liste und die Funktion head eine Liste in ein Element<br />

von X. Dabei ist jedoch zu berücksichtigen, daß die Funktion head nicht auf allen Listen definiert ist,<br />

also eine partielle Funktion ist. Dies wird in einem Teil <strong>der</strong> Literatur (zum Beispiel in [Meyer, 1988])<br />

durch einen modifizierten Pfeil (→) beson<strong>der</strong>s herausgehoben, obwohl dies wegen <strong>der</strong> Nennung von<br />

Vorbedingungen eigentlich überflüssig ist.<br />

Die Erzeugung einer neuen (leeren) Liste durch die Funktion new wird dadurch ausgedrückt, daß new<br />

keinen Argumenttyp besitzt und somit immer dasselbe Ergebnis liefert.<br />

new: → List[X]<br />

Aus <strong>der</strong> Sicht <strong>der</strong> Mathematik sind Funktionen ohne Argumente wie new (sogennante Konstruktor-<br />

Funktionen) und ihr Ergebnis (die Konstante ) praktisch identisch, da es von von außen betrachtet<br />

keine Rolle spielt, ob eine Dienstleistung zur Bereitstellung eines festen Wertes diesen erst berechnen<br />

muß o<strong>der</strong> auf einen bereits existierenden Wert zugreifen kann. Deshalb können alle Dienstleistungen<br />

als Funktionen beschrieben werden. Diese Sichtweise spiegelt sich auch in <strong>der</strong> Syntax <strong>der</strong> Sprache Eiffel<br />

wie<strong>der</strong>, welche die gleiche Philosophie verfolgt.<br />

• Partielle Funktionen sind in fast allen Programmierproblemen eine unausweichliche Tatsache, aber auch<br />

eine mögliche Quelle für Fehler. Aus diesem Grunde müssen die Anfor<strong>der</strong>ungen an die Anwendbarkeit je<strong>der</strong><br />

partiellen Funktion klar formuliert werden. Dies ist <strong>der</strong> Zweck des Abschnitts PRECONDITIONS.<br />

In unserem Beispiel ist die einzige Vorbedingung, daß head nur auf nichtleere Listen angewandt werden<br />

darf:<br />

pre head(L:List[X]) = (not empty(L))<br />

• Der Abschnitt AXIOMS schließlich beschreibt die semantischen Mindestanfor<strong>der</strong>ungen, welche sicherstellen,<br />

daß die genannten Funktionen die gewünschten Eigenschaften besitzen.<br />

So soll die Funktion empty nur für die von new erzeugte leere Liste wahr sein, nicht aber bei Listen,<br />

die durch cons erzeugt wurden. head und tail zerlegen eine durch cons erzeugte Liste wie<strong>der</strong> in ihre<br />

Bestandteile während auf eine leere Liste nur tail anwendbar ist und wie<strong>der</strong> eine leere Liste liefert.<br />

Die abstrakte mathematische Beschreibung von Datentypen und Operationen in einem gemeinsamen Konzept<br />

erlaubt es, präzise Aussagen über die Effekte einer Berechnung aufzustellen und durch Verwendung <strong>der</strong> Axiome<br />

zu beweisen. Die Durchführung einer Berechnung kann nämlich als algebraische Vereinfachung (“Reduktion”)<br />

betrachtet werden, in <strong>der</strong> die linken (langen) Seiten eines Axioms durch die rechten (kurzen) ersetzt werden.<br />

So läßt sich zum Beispiel auch ohne Verwendung <strong>der</strong> anschaulich erklärten Bedeutung <strong>der</strong> Operationen tail,<br />

cons und new beweisen, daß das Ergebnis <strong>der</strong> Berechnung von tail(tail(cons(4,new()))) <strong>der</strong> Wert new()<br />

ist. Hierzu braucht man nur das vierte und dann das fünfte Axiom anzuwenden.<br />

Diese Aspekte abstrakter Datentypen haben sie zur Grundlage vieler Forschungen auf den Gebieten <strong>der</strong> formalen<br />

Spezifikation, <strong>der</strong> symbolischen Berechnung, <strong>der</strong> Verifikation von Programmen, dem Software-Prototyping<br />

und nicht zuletzt auch <strong>der</strong> Datenstrukturbeschreibung werden lassen. In objektorientierten Sprachen wie Eiffel<br />

wird daher jedes Programm um Klassen von Datenstrukturen herum organisiert, die auf <strong>der</strong> Grundlage<br />

abstrakter Datentypen beschrieben werden. Auf diese Art wird ein ausgewogenes Verhältnis zwischen Daten<br />

und Funktionen hergestellt. Die Struktur eines Softwaresystems beruht auf den Datenstrukturen, aber diese<br />

sind wie<strong>der</strong>um auf <strong>der</strong> Grundlage abstrakter Funktionen definiert:<br />

Objektorientierter Entwurf ist die Entwicklung von Softwaresystemen als strukturierte Sammlung<br />

von Implementierungen abstrakter Datentypen.

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

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!