Grundlagen der Informatik I “Programmierung”

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

22.08.2013 Aufrufe

Der häufigste Fehler, der bei der Bestimmung der Vorbedingung einer Wertzuweisung gemacht wird, ist die Umkehr der Richtung. Wie man sich leicht am Beispiel y=1 und b=2 klarmachen kann, gilt nicht { y 0 x > -m wp(x:=x+m, a>0) ≡ a>0 keine Änderung! wp(x:=l, x>m) ≡ l>m 4.3.2 Routinenaufruf Die Verwendung von Routinen haben wir schon im Abschnitt 3.3 angesprochen. Wir wollen dies nun vertiefen und um die zugehörigen Verifikationsregeln ergänzen. Routinen sind ein wichtiges Strukturierungsmittel bei der Implementierung von Klassen. Sie unterstützen die schrittweise Verfeinerung, da sie ermöglichen, die Programmiersprache durch selbstdefinierte Anweisungen und Ausdrücke an das jeweiligen Problem anzupassen. Dabei müssen wir unterscheiden zwischen Prozeduren und Funktionen: • Die Definition einer Prozedur entspricht der Beschreibung einer komplexeren Anweisung. Der Aufruf einer Prozedur ist somit die Durchführung einer selbstdefinierten Anweisung. Prozeduraufrufe sind neben der Wertzuweisung die einzige Form einer elementaren Anweisung. 20 Alle anderen Sprachkonstrukte bieten nur die Möglichkeit, gegebene Anweisungen zu neuen zusammenzusetzen. • Im Kontrast dazu beschreibt eine Funktion einen komplexeren Ausdruck im Sinne von Abschnitt 4.4 und nicht etwa eine Anweisung. Ein Funktionsaufruf führt also zur Berechnung eines selbstdefinierten Ausdrucks. Dies ermöglicht auch in imperativen Sprachen ein weitgehend funktionales Programmieren, wo dies von der Problemstellung her angebracht ist – wie zum Beispiel bei der Berechnung arithmetischer Funktionen wie der Fakultät oder des größten gemeinsamen Teilers. Die Verwendung von Routinen, um Probleme zu verfeinern, ist ein grundlegendes Programmierkonzept vieler Programmiersprachen. In Pascal und ähnlichen Sprachen ist es daher erlaubt, innerhalb von Routinen weitere Routinen zu definieren, um diese noch stärker zu strukturieren. In Eiffel (und C) geht das nicht, da Eiffel Routinen als Dienstleistungen von Klassen versteht und nicht etwa als selbständiges Strukturierungskonzept. In Eiffel dürfen innerhalb von Routinen keine weiteren Routinen deklariert werden. Routinen sind im wesentlichen als Kurzbeschreibung längerer Programmteile anzusehen. Der Name der Routine ist eine Abkürzung für den Anweisungsteil, der beim Aufruf der Routine ausgeführt wird. 21 Durch die Verwendung formaler Argumente wird dieser Anweisungsteil vielseitiger anwendbar, da nun die Routine zur Abkürzung aller Programmstücke benutzt werden kann, die bis auf bestimmte Parameter identisch sind. 20 Der Aufruf einer Initialisierungsprozedur ist in diesem Zusammenhang eine spezielle Form des Prozeduraufrufs. 21 Es sei an dieser Stelle noch erwähnt, daß das Routinenkonzept über den Dienstleistungs- und Strukturierungscharakter hinaus noch die Einbindung externer Routinen ermöglicht, die in anderen Programmiersprachen implementiert wurden. Dies ermöglicht es, “alte” und zum Teil sehr effiziente Software weiterzuverwenden, anstatt sie erneut in Eiffel codieren zu müssen, und dennoch klar definierte Eiffel-Schnittstellen zur Verfügung zu haben. Eine ausführlichere Beschreibung dieser Möglichkeit findet man in [Meyer, 1992, Kapitel 24].

4.3.2.1 Die Rolle formaler Parameter In erster Näherung kann man Routinenaufrufe als versteckte Textersetzungen im Programm auffassen: anstelle des Routinennamens wird der Anweisungsteil eingesetzt, in dem wieder jedes Vorkommen eines formalen Parameters durch den aktuell angegebenen Wert ersetzt wird. Die tatsächliche Realisierung eines Routinenaufrufs wird zwar (zugunsten von Effizienz, lokalen Variablen und der Möglichkeit von Rekursion) völlig anders gestaltet, aber diese Sichtweise erklärt die Beschränkungen von Eiffel im Umgang mit formalen Argumenten: Entwurfsprinzip 4.3.3 (Geschützte formale Argumente) Die formalen Argumente y1,...,yn einer durch r(y1:T1,...,yn:Tn) is do ... end definierten Routine sind innerhalb des Anweisungsteils von r geschützt und dürfen nicht direkt verändert werden. Eine direkte Veränderung von yi ist dabei eine Wertzuweisung der Form yi:=Ausdruck oder eine Veränderung des in yi enthaltenen Verweises durch Aufruf von Initialisierungsprozeduren, falls der Typ von yi eine Klasse ist. Die Art der Operationen, die eine Routine auf ihren Argumenten ausführen darf, ist also stark eingeschränkt: aktuelle Parameter werden als Werte übergeben, die nicht verändert werden dürfen. Man beachte jedoch, daß dieses Verbot nur für direkte Veränderungen gilt. Es ist durchaus erlaubt, ein Objekt zu ändern, auf das ein formales Argument yi verweist, denn hierdurch wird der Wert von yi selbst ja nicht verändert. Ein Aufruf wie yi.copy(yj) ist also durchaus erlaubt, nicht jedoch yi:=yj. Die Konsequenz davon ist, daß beim Aufruf einer Routine beliebige Ausdrücke als formale Parameter angegeben werden dürfen, solange die Typbedingung eingehalten wird. Es gibt jedoch nur eine Möglichkeit, Berechnungsergebnisse an die aufrufende Stelle zurückzuliefern, nämlich als Resultat einer Funktion. Da dieses Resultat seinerseits von einem (ggf. expanded) Klassentyp sein darf, wird hierdurch die Möglichkeit, mehrere Werte gleichzeitig zu übergeben, nicht eingeschränkt. Auf diese Art erzielt man eine klare Trennung von Wertberechnungen und Veränderungen von Objekten, was erheblich zur Verständlichkeit von Routinen beiträgt. Prozeduren (“O-Funktionen”) sind ausschließlich zur Veränderungen von Objekten da und können keine Werte zurückliefern. Funktionen (“V-Funktionen”) berechnen Werte (values) und sollten Objekte – zumindest ihre äußere Erscheinung – unverändert lassen. 22 Letzteres kann jedoch vom Compiler nicht erzwungen werden und muß daher ein methodischer Hinweis bleiben. 4.3.2.2 Lokale und qualifizierte Aufrufe Der Aufruf einer Prozedur 23 r als Anweisung kann entweder lokal oder entfernt geschehen. • Ein lokaler Aufruf bezieht sich auf das aktuelle Exemplar und ist unqualifiziert wie in r (ohne Argumente) oder r(A1,...,An) (mit Argumenten). • Ein entfernter Aufruf wird auf ein Objekt angewandt, das durch einen beliebigen Ausdruck dargestellt werden darf, und ist qualifiziert wie in entity.r (einfache Qualifizierung, ohne Argumente) oder f(a).r ( Qualifizierung durch Funktion, ohne Argumente) oder g(f(a)).h(b,x).z.r(A1,...,An) (mehrstufige geschachtelte Qualifizierung mit Argumenten). Ein mehrstufig qualifizierter Aufruf u.v.r kann als Abkürzung für x:=u.v ; x.r angesehen werden, bei der man sich die Zwischenvariable x erspart. 22 Es ist durchaus möglich, die interne Darstellung eines Objektes zu ändern, ohne daß dies nach außen Wirkung hat. Diese Möglichkeit ist zuweilen auch innerhalb von Funktionen sinnvoll und wird ausführlicher in [Meyer, 1988, Kapitel 7.7] diskutiert. 23 Funktionsaufrufe sind keine Anweisungen sondern nur Ausdrücke, die auf der rechten Seite einer Wertzuweisung, oder in den aktuellen Parametern und Qualifizierungen eines Prozeduraufrufs vorkommen dürfen – wobei Funktionsaufrufe allerdings beliebig geschachtelt sein dürfen. Die hier vorgestellten Regeln gelten allerdings für Funktionen und Prozeduren gleichermaßen.

Der häufigste Fehler, <strong>der</strong> bei <strong>der</strong> Bestimmung <strong>der</strong> Vorbedingung einer Wertzuweisung gemacht wird, ist die<br />

Umkehr <strong>der</strong> Richtung. Wie man sich leicht am Beispiel y=1 und b=2 klarmachen kann, gilt nicht<br />

{ y 0 x > -m<br />

wp(x:=x+m, a>0) ≡ a>0 keine Än<strong>der</strong>ung!<br />

wp(x:=l, x>m) ≡ l>m<br />

4.3.2 Routinenaufruf<br />

Die Verwendung von Routinen haben wir schon im Abschnitt 3.3 angesprochen. Wir wollen dies nun vertiefen<br />

und um die zugehörigen Verifikationsregeln ergänzen.<br />

Routinen sind ein wichtiges Strukturierungsmittel bei <strong>der</strong> Implementierung von Klassen. Sie unterstützen die<br />

schrittweise Verfeinerung, da sie ermöglichen, die Programmiersprache durch selbstdefinierte Anweisungen<br />

und Ausdrücke an das jeweiligen Problem anzupassen. Dabei müssen wir unterscheiden zwischen Prozeduren<br />

und Funktionen:<br />

• Die Definition einer Prozedur entspricht <strong>der</strong> Beschreibung einer komplexeren Anweisung. Der Aufruf<br />

einer Prozedur ist somit die Durchführung einer selbstdefinierten Anweisung. Prozeduraufrufe sind neben<br />

<strong>der</strong> Wertzuweisung die einzige Form einer elementaren Anweisung. 20 Alle an<strong>der</strong>en Sprachkonstrukte<br />

bieten nur die Möglichkeit, gegebene Anweisungen zu neuen zusammenzusetzen.<br />

• Im Kontrast dazu beschreibt eine Funktion einen komplexeren Ausdruck im Sinne von Abschnitt 4.4<br />

und nicht etwa eine Anweisung. Ein Funktionsaufruf führt also zur Berechnung eines selbstdefinierten<br />

Ausdrucks. Dies ermöglicht auch in imperativen Sprachen ein weitgehend funktionales Programmieren,<br />

wo dies von <strong>der</strong> Problemstellung her angebracht ist – wie zum Beispiel bei <strong>der</strong> Berechnung arithmetischer<br />

Funktionen wie <strong>der</strong> Fakultät o<strong>der</strong> des größten gemeinsamen Teilers.<br />

Die Verwendung von Routinen, um Probleme zu verfeinern, ist ein grundlegendes Programmierkonzept vieler<br />

Programmiersprachen. In Pascal und ähnlichen Sprachen ist es daher erlaubt, innerhalb von Routinen weitere<br />

Routinen zu definieren, um diese noch stärker zu strukturieren. In Eiffel (und C) geht das nicht, da Eiffel<br />

Routinen als Dienstleistungen von Klassen versteht und nicht etwa als selbständiges Strukturierungskonzept.<br />

In Eiffel dürfen innerhalb von Routinen keine weiteren Routinen deklariert werden.<br />

Routinen sind im wesentlichen als Kurzbeschreibung längerer Programmteile anzusehen. Der Name <strong>der</strong> Routine<br />

ist eine Abkürzung für den Anweisungsteil, <strong>der</strong> beim Aufruf <strong>der</strong> Routine ausgeführt wird. 21 Durch die<br />

Verwendung formaler Argumente wird dieser Anweisungsteil vielseitiger anwendbar, da nun die Routine zur<br />

Abkürzung aller Programmstücke benutzt werden kann, die bis auf bestimmte Parameter identisch sind.<br />

20 Der Aufruf einer Initialisierungsprozedur ist in diesem Zusammenhang eine spezielle Form des Prozeduraufrufs.<br />

21 Es sei an dieser Stelle noch erwähnt, daß das Routinenkonzept über den Dienstleistungs- und Strukturierungscharakter<br />

hinaus noch die Einbindung externer Routinen ermöglicht, die in an<strong>der</strong>en Programmiersprachen implementiert wurden. Dies<br />

ermöglicht es, “alte” und zum Teil sehr effiziente Software weiterzuverwenden, anstatt sie erneut in Eiffel codieren zu müssen,<br />

und dennoch klar definierte Eiffel-Schnittstellen zur Verfügung zu haben. Eine ausführlichere Beschreibung dieser Möglichkeit<br />

findet man in [Meyer, 1992, Kapitel 24].

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

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!