21
12.12.2006 281 © A. Poetzsch-Heffter, TU Kaiserslautern Ein Algorithmus heißt determiniert, wenn er bei gleichen zulässigen Eingabewerten stets das gleiche Ergebnis liefert. Andernfalls heißt er nicht-determiniert. Begriffsklärung: (determiniert) Beispiele: (Determiniertheit) 1. Jeder Algorithmus, der eine Funktion berechnet, ist determiniert. 2. Der Algorithmus von der letzten Folie ist determiniert. 4.2 Grundkonzepte prozeduraler Programmierung Algorithmen lassen sich mit unterschiedlichen Sprachmitteln beschreiben: - umgangssprachlich - mit graphischen Notationen - mit mathematischer Sprache - mit programmiersprachlichen Mitteln. 12.12.2006 282 © A. Poetzsch-Heffter, TU Kaiserslautern Gemäß des prozeduralen Paradigmas (vgl. F6) wird - der Zustand eines Systems mit Variablen beschrieben - werden die möglichen Systemabläufe algorithmisch formuliert und - bilden Prozeduren das zentrale Strukturierungs- und Abstraktionsmittel. Begriffsklärung: (prozedurales Paradigma) Beispiel: (Prozedurale Systemsicht) Ein Rechensystem (z.B. PC) kann man prozedural wie folgt beschreiben: - Jede Datei ist eine Variable für Listen von Bytes. - Nach dem Starten des Rechners wird ein nicht- terminierender Algorithmus ausgeführt: while( true ) { warte auf Eingabe eines Programmnamens; starte Programm mit eingegebenem Namen als parallelen Algorithmus; } - Jedes Programm entspricht dabei einer Prozedur. 12.12.2006 283 © A. Poetzsch-Heffter, TU Kaiserslautern Der Abschnitt 4.2 stellt diejenigen Grundkonzepte der prozeduralen Programmierung vor, die als Voraussetzung für die objektorientierte Programmierung benötigt werden: Deklaration und Verwendung von Variablen • Anweisungen Deklaration und Verwendung von Prozeduren Zur praktischen Programmierung verwenden wir eine Teilsprache von Java. Vorteile : - Der Übergang zur objektorientierten Programmierung wird einfacher. - Der Zusammenhang zwischen prozeduraler und objektorientierter Programmierung wird klarer. 4.2.1 Sprachliche Basis: Teilsprache von Java Nachteile : - Da klassenlose, rein prozedurale Programmierung von Java nicht unterstützt wird, entsteht ein notationeller Mehraufwand. - Bestimmte Sprachelemente bleiben zunächst unerklärt. 12.12.2006 284 © A. Poetzsch-Heffter, TU Kaiserslautern Basisdatenstrukturen in Java: Java bietet insbesondere Basisdatenstrukturen mit den Typen boolean char int, long, short, float, double sowie entsprechende Funktionen und Konstanten. Die nichtstrikten Operationen sind: _ ? _ : _ statt if _ then _ else _ in ML _ && _ statt _ andalso _ in ML _ || _ statt _ orelse _ in ML Vorgehen : - Basisdatentypen - Ausdrücke - Variablendeklarationen - Programmstruktur und vereinfachte Notation

4.2 Grundkonzepte prozeduraler Programmierung · Zuweisung : (engl. assignment) Syntax in Java: = ; Semantik: Werte den Ausdruck aus und weise das

  • Upload
    others

  • View
    3

  • Download
    0

Embed Size (px)

Citation preview

  • 12.12.2006 281© A. Poetzsch-Heffter, TU Kaiserslautern

    Ein Algorithmus heißt determiniert , wenn erbei gleichen zulässigen Eingabewerten stets dasgleiche Ergebnis liefert.

    Andernfalls heißt er nicht-determiniert .

    Begriffsklärung: (determiniert)

    Beispiele: (Determiniertheit)

    1. Jeder Algorithmus, der eine Funktion berechnet, ist determiniert.

    2. Der Algorithmus von der letzten Folie ist determiniert.

    4.2 Grundkonzepteprozeduraler Programmierung

    Algorithmen lassen sich mit unterschiedlichen Sprachmitteln beschreiben:

    - umgangssprachlich

    - mit graphischen Notationen

    - mit mathematischer Sprache

    - mit programmiersprachlichen Mitteln.

    12.12.2006 282© A. Poetzsch-Heffter, TU Kaiserslautern

    Gemäß des prozeduralen Paradigmas (vgl. F6) wird

    - der Zustand eines Systems mit Variablen beschrieben

    - werden die möglichen Systemabläufe algorithmischformuliert und

    - bilden Prozeduren das zentrale Strukturierungs-und Abstraktionsmittel.

    Begriffsklärung: (prozedurales Paradigma)

    Beispiel: (Prozedurale Systemsicht)

    Ein Rechensystem (z.B. PC) kann man prozeduralwie folgt beschreiben:

    - Jede Datei ist eine Variable für Listen von Bytes.

    - Nach dem Starten des Rechners wird ein nicht-terminierender Algorithmus ausgeführt:

    while( true ) {

    warte auf Eingabe eines Programmnamens;

    starte Programm mit eingegebenem Namenals parallelen Algorithmus;

    }

    - Jedes Programm entspricht dabei einer Prozedur.

    12.12.2006 283© A. Poetzsch-Heffter, TU Kaiserslautern

    Der Abschnitt 4.2 stellt diejenigen Grundkonzepteder prozeduralen Programmierung vor, die als Voraussetzung für die objektorientierte Programmierung benötigt werden:

    • Deklaration und Verwendung von Variablen

    • Anweisungen

    • Deklaration und Verwendung von Prozeduren

    Zur praktischen Programmierung verwenden wir eine Teilsprache von Java.

    Vorteile:

    - Der Übergang zur objektorientierten Programmierungwird einfacher.

    - Der Zusammenhang zwischen prozeduraler undobjektorientierter Programmierung wird klarer.

    4.2.1 Sprachliche Basis: Teilsprache von Java

    Nachteile:

    - Da klassenlose, rein prozedurale Programmierungvon Java nicht unterstützt wird, entsteht ein notationeller Mehraufwand.

    - Bestimmte Sprachelemente bleiben zunächstunerklärt.

    12.12.2006 284© A. Poetzsch-Heffter, TU Kaiserslautern

    Basisdatenstrukturen in Java:

    Java bietet insbesondere Basisdatenstrukturenmit den Typen

    boolean

    char

    int, long, short,

    float, double

    sowie entsprechende Funktionen und Konstanten.

    Die nichtstrikten Operationen sind:

    _ ? _ : _ statt if _ then _ else _ in ML

    _ && _ statt _ andalso _ in ML

    _ || _ statt _ orelse _ in ML

    Vorgehen:

    - Basisdatentypen

    - Ausdrücke

    - Variablendeklarationen

    - Programmstruktur und vereinfachte Notation

  • 12.12.2006 285© A. Poetzsch-Heffter, TU Kaiserslautern

    Ausdrücke in Java:

    Mittels der Konstanten und Funktionen derelementaren Datenstrukturen lassen sich analog zu ML Ausdrücke bilden.

    (Darüber hinaus kann man deklarierte Prozedurenin Ausdrücken aufrufen. Dadurch können bei derAuswertung von Ausdrücken Seiteneffekteentstehen.)

    Beispiel: (Ausdrücke in Java)

    5 + 89

    45 / 6

    47474747L * a für geeignetes a

    3.6 * (-4 + 23)

    d == 78 + e für geeignete d, e

    7 >= 45 == true

    abs(a) + abs(d) für geeignete a, d, abs

    sein | !sein für geeignete Variable sein

    true || tuEs() für geeignete Prozedur tuEs

    a > b ? a : b // liefert max(a,b)

    12.12.2006 286© A. Poetzsch-Heffter, TU Kaiserslautern

    Programmstruktur und -syntax:

    Variablendeklarationen in Java:

    Eine Variablendeklaration hat die Form:

    ;

    und definiert eine neue Variable vom angegebenen Typ mit dem angegebenem Bezeichner. Beispiele:

    int a;

    boolean b;

    float meineGleitkommavariable;

    char cvar;

    Eine Variablendeklarationsliste entsteht durch Aneinanderreihen von einzelnen Deklarationen.

    Ein prozedurales Programm besteht aus:

    - einer Liste von Typdeklaration

    - einer Liste von globalen Variablendeklarationen

    - einer Liste von Prozedurdeklarationen

    - einer Hauptprozedur

    In Java lassen sich prozedurale Programme mit folgendem Programmrahmen formulieren:

    12.12.2006 287© A. Poetzsch-Heffter, TU Kaiserslautern

    public class extends InputOutput {

    static ...static

    static ...static

    static ...static

    public static void main(String[] args)

    }

    .java

    1

    1

    k

    n

    wobei k ≥ 0, m ≥ 0, n ≥ 0.

    Bemerkung:• Die Reihenfolge der Deklarationen kann vertauscht

    werden.

    • Die hier nicht erklärten Sprachkonstrukte werdenin Kapitel 5 behandelt.

    1

    m

    12.12.2006 288© A. Poetzsch-Heffter, TU Kaiserslautern

    • Wir gehen davon aus, dass die Datei InputOutput.ja vaim aktuellen Dateiverzeichnis liegt. Sie stellt ber eit:

    Konventionen:

    public class InputOutput {

    public static int readInt(){...}

    public static String readString(){...}

    public static char readChar(){...}

    public static void print(int i){...}

    public static void println(int i){...}

    public static void print(char c){...}

    public static void println(char c){...}

    public static void print(String s){...}

    public static void println(String s){...}}

    • Im Folgenden werden wir die ersten beiden und die letzte Zeile des Programmschemas der letztenFolie sowie die Schlüsselwörter static undpublic auf Folien der Übersichtlichkeit halber weglassen.

  • 12.12.2006 289© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Notation prozeduraler Programme)

    public class Foo extends InputOutput {

    static int a;

    static boolean bvar;

    static int abs( int n ) {if( n>=0 ){

    return n;} else {

    return -n;}

    }

    static void setA() {a = abs(a);

    }

    public static void main( String[] args ){

    println("Eingabe von a:");

    a = readInt();

    setA();

    println( a );}

    }

    Foo.java

    12.12.2006 290© A. Poetzsch-Heffter, TU Kaiserslautern

    Abkürzend schreiben wir hier:

    int a;

    boolean bvar;

    int abs( int n ) {if( n>=0 ){

    return n;} else {

    return -n;}

    }

    void setA() {a = abs(a);

    }

    void main( String[] args ){

    println("Eingabe von a:");

    a = readInt();

    setA();

    println( a );}

    12.12.2006 291© A. Poetzsch-Heffter, TU Kaiserslautern

    Bemerkung:

    • Die Konzepte sind rekursiv von einander abhängig:

    - Anweisungen benutzen Variablen und Ausdrücke

    - Prozeduren basieren auf Anweisungen

    - Verwendung von Variablen kann nur im Kontextvon Anweisungen und Prozeduren erklärt werden.

    - Ausdrücke erlauben die Verwendung von Prozeduren.

    Überblick übers weitere Vorgehen:

    • Anweisungen

    • Prozeduren

    • Variablen in Programm und Speicher

    • Felder

    • Benutzerdefinierte Typen

    • Sichtbarkeit von Bindungen

    • Iteration und Rekursion

    • Weitere prozedurale Sprachkonzepte

    12.12.2006 292© A. Poetzsch-Heffter, TU Kaiserslautern

    Anweisungen sind programmiersprachliche Be-schreibungsmittel:

    - Einfache Anweisungen beschreiben Aktionen.

    - Zusammengesetzte Anweisungen beschreiben, wie mehrere Aktionen auszuführen sind, alsoTeile von Algorithmen.

    Begriffsklärung: (Anweisung)

    • In der programmiersprachlichen Realisierung der Konzepte verwischen ihre Grenzen; z.B.:

    - Parameter lassen sich wie Variable verwenden.

    - Einfache Anweisungen lassen sich in Aus-drücken verwenden.

    4.2.2 Anweisungen

    while( ) {

    }

    Beispiel: (Anweisungen)

    1. Zuweisung eines Werts an eine Variable:

    2. Schleifenanweisung:

    = ;

  • 12.12.2006 293© A. Poetzsch-Heffter, TU Kaiserslautern

    Wir betrachten die folgenden Anweisungen und ihreRealisierung in Java:

    • Einfache Anweisungen:- Zuweisung- Prozeduraufruf

    • Anweisungsblöcke

    • Schleifenanweisungen:- while-, do-, for-Anweisung

    • Verzweigungsanweisungen:- bedingte Anweisung- Fallunterscheidung- Auswahlanweisung

    • Sprunganweisungen:- Abbruchanweisung- Rückgabeanweisung

    Bemerkung:

    Unterschied: Anweisung / Ausdruck

    - Die Auswertung von Ausdrücken liefert ein Ergebnis.

    - Die Ausführung von Anweisungen verändert den Zustand. Im Allg. liefert sie kein Ergebnis.

    12.12.2006 294© A. Poetzsch-Heffter, TU Kaiserslautern

    Zuweisung: (engl. assignment)

    Syntax in Java:

    = ;

    Semantik:

    Werte den Ausdruck aus und weise das Ergebnisder Variablen zu. (In Java ist eine Zuweisungsyntaktisch ein Ausdruck, liefert also einen Wert u ndzwar das Ergebnis der Auswertung von der rechtenSeite der Zuweisung.)

    Beispiele:

    a = 27 % 23;

    b = true;

    meineGleitkommavariable = 3.14;

    Sprechweise:

    „Variable b ergibt sich zu true “ oder

    „Variable b wird true zugewiesen“ .

    Einfache Anweisungen

    12.12.2006 295© A. Poetzsch-Heffter, TU Kaiserslautern

    Bemerkung:

    In einer Zuweisung (und anderen Sprachkon-strukten) kann eine Variable v mit zwei Bedeutungen vorkommen:

    1. Das Vorkommen links vom Zuweisungszeichenmeint die Variable. Man spricht vom L-Wert(engl. l-value) des Ausdrucks v .

    2. Das Vorkommen rechts vom Zuweisungszeichenmeint den in v gespeicherten Wert. Man spricht vom R-Wert (engl. r-value) des Ausdrucks v .

    Die Unterscheidung wird wichtiger, wenn auch linksdes Zuweisungszeichens komplexere Ausdrückestehen.

    Beispiel: (L-Wert, R-Wert)

    a = 7 + a ;

    L-Wert R-Wert

    12.12.2006 296© A. Poetzsch-Heffter, TU Kaiserslautern

    Prozeduraufruf: (engl. procedure call)

    Syntax:

    ( ) ;

    ε |

    | , Semantik:

    Werte die Ausdrücke der aktuellen Parameter aus.Übergebe die Ergebnisse an die formalen Parameter.Führe die Anweisungen der Prozedur aus.Liefere den Rückgabewert, wenn vorhanden.

    Beispiele:

    1. Prozeduraufruf mit Ergebnis:

    a = ggt(a,abs(45-87)); // Zuweisung

    2. Prozeduraufruf mit Seiteneffekt:

    print( ggt(24,248)); // Anweisung

  • 12.12.2006 297© A. Poetzsch-Heffter, TU Kaiserslautern

    Ein Anweisungsblock (engl. block statement) ist eine Liste bestehend aus Deklarationen und Anweisungen.

    Anweisungsblöcke

    Syntax in Java:

    { }

    � ε |

    |

    |

    |

    Semantik:

    Stelle den Speicherplatz für die Variablen bereit u ndführe die Anweisungen der Reihe nach aus.

    12.12.2006 298© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel:

    { int i; i = 7; int a; a = 27 % i; }

    Üblicherweise schreibt man Deklarationen und Anweisungen untereinander, so dass ein Textblock entsteht.

    {

    int i;int x;i = readInt();x = abs(i);

    }

    Schleifenanweisungen (engl. loop statements)steuern die iterative, d.h. wiederholte Ausführung von Anweisungen/Anweisungsblöcken.

    while-Anweisung:

    Syntax in Java:

    while( )

    Schleifenanweisungen

    12.12.2006 299© A. Poetzsch-Heffter, TU Kaiserslautern

    Semantik:

    Werte den Ausdruck aus, die sogenannte Schleifen-bedingung . Ist die Bedingung erfüllt, führe die Anweisung aus, den sogenannten Schleifenrumpf , und wiederhole den Vorgang. Andernfalls beende die Ausführung der Schleifenanweisung.

    Beispiel:

    Aus der Prozedur ggt:

    while( m>0 )

    { v = n % m;n = m;m = v;

    }

    Bemerkung:

    Der Schleifenrumpf ist in den meisten Fällen einAnweisungsblock. Syntaktisch korrekt ist aber auch z.B.:

    while( true ) print(i);

    while( m>0 ) {v = n % m;n = m;m = v;

    }

    12.12.2006 300© A. Poetzsch-Heffter, TU Kaiserslautern

    do-Anweisung:

    Syntax in Java:

    do while( ) ;

    Semantik:

    Führe die Anweisung aus. Werte danach den boole-schen Ausdruck aus. Ist die Bedingung erfüllt ist, wiederhole den Vorgang. Andernfalls beende die Ausführung der Schleifenanweisung.

    Bemerkung:

    Die do-Anweisung ist immer dann sinnvoll, wenn maneinen Vorgang mindestens einmal ausführen muss.

    In solchen Situationen spart man sich gegenüber derVerwendung der while-Schleife die anfänglich unnöti ge Auswertung der Bedingung.

  • 12.12.2006 301© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel:

    Wir betrachten die Ausgabe der Elemente einer nicht -leeren Liste. Wir gehen davon aus, dass es einen Ty pIntList mit Prozeduren head, tail und isempty gibt:

    IntList grades;

    int g;

    ... // Zuweisung an die Variable noten

    // Ausgabe der nicht-leeren Notenliste

    do {

    g = head(grades);

    println(g);

    grades = tail(grades);

    } while( !isempty(grades) );

    for-Anweisung (Zählanweisung):

    Die for-Anweisung dient vorrangig zur Bearbeitungvon Vektoren und Matrizen, deren einzelneKomponenten über Indizes angesprochen werden.

    Wir betrachten zunächst ein Beispiel:

    12.12.2006 302© A. Poetzsch-Heffter, TU Kaiserslautern

    // Skalarprodukt

    void main( String[] args ){ int i;

    // zwei Vektoren mit jeweils 3 Elementen

    int v1[] = new int[3]; int v2[] = new int[3];int skalprod;

    v1[0] = 1;v1[1] = 3;v1[2] = 8;

    v2[0] = 2;v2[1] = 3;v2[2] = 4;

    skalprod = 0;

    for( i = 0; i

  • 12.12.2006 305© A. Poetzsch-Heffter, TU Kaiserslautern

    Fallunterscheidung:

    Syntax in Java:

    if( )

    else Semantik:

    Werte den Ausdruck aus. Ist das Ergebnis true,führe Anweisung1, andernfalls Anweisung2 aus.

    Beispiel:

    if( i!=0 ) {

    n = k/i;

    n++;

    } else {

    fehlerbehandlung(“Division durch Null“);

    }

    Auswahlanweisung:

    Auswahlanweisungen erlauben es, in Abhängigkeit von dem Wert eines Ausdrucks direkt in einen vonendlich vielen Fällen zu verzweigen. In Javagibt es dafür die switch-Anweisung. Wir verzichtenhier auf eine genaue Beschreibung von Syntax undSemantik und betrachten ein Beispiel:

    12.12.2006 306© A. Poetzsch-Heffter, TU Kaiserslautern

    // AuswahlanweisungsTest

    void main( String[] args ){

    char c;

    boolean machWeiter = true;

    do {

    print("Ein Zeichen aus {a,b,c,e}:");

    c = readChar();

    switch( c ) {

    case 'a': proza(); break;

    case 'b': prozb(); break;

    case 'c': prozc(); break;

    case 'e':

    machWeiter = false;

    break;

    default:

    print("Falsches Eingabezeichen");

    }

    } while( machWeiter );

    }

    Beispiel: (Auswahlanweisung)

    wobei proza, prozb, prozc beliebige Prozeduren sind, zum Beispiel:

    12.12.2006 307© A. Poetzsch-Heffter, TU Kaiserslautern

    void proza() {

    /* hier könnte 'was Interessantes stehen */

    println("Ich bin Prozedur A. Alles gut?");

    println("Probleme mit dem Weina'stress?\n");

    }

    void prozb() {

    /* hier natürlich auch */

    println("Wer A sagt, muss auch B sagen.");

    println("Haben Sie schon A gesagt.\n");

    }

    void prozc() {

    println("C wie no comment. Cool,oder?\n");

    }

    12.12.2006 308© A. Poetzsch-Heffter, TU Kaiserslautern

    Sprunganweisungen

    Sprunganweisungen (engl. jump statements) legeneine Fortsetzungsstelle der Ausführung fest, die möglicherweise weit von der aktuellen Anweisungentfernt liegt. Wir betrachten hier nur Sprünge, di e der Programmstruktur folgen.

    Abbruchanweisung:

    Syntax in Java:

    break;

    Semantik:

    Die Ausführung wird am Ende der umfassenden zusammengesetzen Anweisung fortgesetzt.

    Beispiele:

    1. In Auswahlanweisung: siehe oben.

    2. In Schleifenanweisungen:

    while( true ) { ... if( ) break;...

    }

  • 12.12.2006 309© A. Poetzsch-Heffter, TU Kaiserslautern

    Rückgabeanweisung:

    Syntax in Java:

    return ;

    return ;

    Semantik:

    1. Produktion:Nur in Prozeduren ohne Rückgabewert erlaubt.Beende die Ausführung der Prozedur. Setze die Ausführung an der Aufrufstelle fort.

    2. Produktion: Nur in Prozeduren mit Rückgabewert erlaubt.Werte den Ausdruck aus. Beende die Ausführung der Prozedur.Liefere den Wert des Ausdrucks als Ergebnis und setze die Ausführung an der Aufrufstelle fort.

    Beispiel:

    int abs( int n ) { if( n>=0 ) {

    return n;

    } else {

    return -n;}

    }

    12.12.2006 310© A. Poetzsch-Heffter, TU Kaiserslautern

    Eine Prozedur ist eine Abstraktion einer Anweisung.Sie gibt der Anweisung einen Namen und legt fest,was die Parameter der Anweisung sind. Prozedurenkönnen Ergebnisse liefern.

    Begriffsklärung: (Prozedur)

    Prozeduren erlauben es, von konkreten Anweisungenbzw. Anweisungssequenzen zu abstrahieren.

    D.h. Prozeduren legen die Parameter einer zusammen-gesetzten Anweisung fest und geben ihr einen Namen.Dies ermöglicht:

    - Wiederverwendung,

    - Schnittstellenbildung und Information Hiding.

    Darüber hinaus ermöglichen Prozeduren die rekursiveAusführung von Anweisungen.

    4.2.3 Prozeduren

    12.12.2006 311© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Prozedur als Abstraktion)

    Prozeduren abstrahieren Anweisungen genauso, wie Funktionen Ausdrücke abstrahieren (vgl. Folie 108).

    Bemerkung:

    Aufgabe: Berechne den Absolutbetrag einer ganzen Zahl gespeichert in Variable i und schreibe ihn in die Variable x.

    Algorithmus:Wenn i größer oder gleich 0, liefere i als Ergebnis ;andernfalls –i. Weise das Ergebnis an x zu.

    void main(...){int i;int x;i = readInt();

    int result;if( i>=0 ) {

    result = i;} else {

    result = -i;}

    x = result;}

    Abstraktion bzgl. i ,result wird zum Ergebnis.

    12.12.2006 312© A. Poetzsch-Heffter, TU Kaiserslautern

    Deklaration von Prozeduren/Methoden:

    Syntax in Java:

    ( )

    Semantik:

    Die Semantik ist durch den Aufrufmechanismus und den Anweisungsblock, den sogenannten Prozedurrumpf bestimmt (Genaueres siehe unten).

    int abs( int n ) {if( n>=0 ) {

    return n;} else {

    return -n;}

    }

    void main(...){int i;int x;i = readInt();

    x = abs(i);}

  • 12.12.2006 313© A. Poetzsch-Heffter, TU Kaiserslautern

    Bemerkung:Ein- und Ausgabeoperationen sind typischerweisedurch Prozeduren realisiert.

    Beispiele:

    1. Iterativer Algorithmus zur Berechnung vom ggT:

    2. Rekursiver Algorithmus zur Berechnung vom ggT:

    int ggT ( int m, int n ) { int v;

    while( m>0 ) { v = n % m;n = m;m = v;

    }return n;

    }

    int ggT ( int m, int n ) {

    if( m == 0 ) {

    return n;

    } else {

    return ggT(n%m,m);

    }

    }

    12.12.2006 314© A. Poetzsch-Heffter, TU Kaiserslautern

    Prozeduren, die Ergebnisse liefern, heißenFunktionsprozeduren .

    Definition: (Funktionsprozedur)

    Bemerkung:

    Begriffsklärung: (Effekte)

    Die Ausführung von Prozeduren verändert denSpeicherzustand und bewirkt Ein- und Ausgaben.Zusammenfassend sprechen wir von den Effektender Ausführung einer Prozedur, aufgeteilt in:

    • Haupteffekte : Abliefern von Ergebnissen,Verändern von Variablenparametern;

    • Seiteneffekte : Ein- und Ausgabe, Verändern globaler Variablen, Erzeugen und Löschen dynamischer Variablen (z.B. Instanzvariablen).

    • Variablenparameter und dynamische Variablenbetrachten wir erst später.

    • Eine Funktionsprozedur kann benutzt werden, umeine Funktion zu implementieren (Beispiel ggT).Sie kann aber auch hauptsächlich zur Realisierungvon Seiteneffekten benutzt werden.

    12.12.2006 315© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Prozedur mit Seiteneffekt)

    Prozedur, die Zahlen von der Eingabe liest undaddiert, bis eine Null eingegeben wird. Die Summeder eingegebenen Zahlen wird ausgedruckt, dieAnzahl der Eingaben zurückgeliefert:

    // Summiere

    int summiere() { // Arbeitet nur bei korrekter Eingabe// der Zahlen durch Benutzerint anz = 0;int summe = 0;int eingabe;

    do {print("Summand (0 beendet):");eingabe = readInt();summe += eingabe;anz++;

    } while( eingabe != 0 );

    println( "Summe: " + summe ); return anz-1;

    }

    12.12.2006 316© A. Poetzsch-Heffter, TU Kaiserslautern

    void main( String[] args ) { boolean b = true;

    while( b ) { int anzahl;anzahl = summiere();println("Es wurden "+ anzahl

    + " Zahlen summiert");

    println("Beenden mit j/n:");char c;c = readChar();if( c == 'j' ) {

    b = false;}

    }}

    Anwendung der Prozedur summiere :

    Bemerkung:

    • Die Hauptprozedur main wird vom Betriebssystemaufgerufen. Das Starten eines prozeduralen Programms entspricht dem Aufruf der Hauptprozedur.

    • Argumente vom Programmaufruf werden main als String-Feld mitgegeben.

  • 12.12.2006 317© A. Poetzsch-Heffter, TU Kaiserslautern

    Definition: (rekursive Prozedurdekl.)

    Eine Prozedurdeklaration P heißt direkt rekursiv , wenn der Prozedurrumpf einen Aufruf von P enthält.

    Eine Menge von Prozedurdeklarationen heißenverschränkt rekursiv oder indirekt rekursiv(engl. mutually recursive), wenn die Deklarationengegenseitig voneinander abhängen.

    Eine Prozedurdeklaration heißt rekursiv , wenn sie direkt rekursiv ist oder Element einer Menge verschränkt rekursiver Prozeduren ist.

    Bemerkung:Wir identifizieren Prozeduren mit ihren Prozedur-deklarationen. D.h. zwei Prozedurdeklarationen deklarieren immer zwei unterschiedliche Prozeduren.( Im Unterschied dazu konnten zwei Funktions-

    deklarationen die gleiche Funktion deklarieren.)

    Beispiel: int p1 ( int n ) { return 1; }

    int p2 ( int n ) { return 1; }

    sind zwei unterschiedliche Prozeduren.

    12.12.2006 318© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (rekursive Prozeduren)

    // Aufrufbaum

    final int TAB = 4;int indent = 0 ; // Einruecktiefe

    void drucke_eingerueckt( String s ) {int i;for( i = 0; i 2 ) {

    p( 2 ); q( 2 );r( 2 );

    }indent -= TAB;drucke_eingerueckt("Verlasse p");

    }

    void q( int iq ) { drucke_eingerueckt("Betrete q");indent += TAB;p(iq);indent -= TAB;drucke_eingerueckt("Verlasse q");

    }

    - r und s sind nicht rekursiv.

    - q ist verschränkt rekursiv.

    - p ist direkt und verschränkt rekursiv.

    (Beachte: q wurde vor seiner Deklaration angewendet.)

    12.12.2006 320© A. Poetzsch-Heffter, TU Kaiserslautern

    Mit der Hauptprozedur:

    ergibt sich folgende Ausgabe:

    public static void main( String[] args ) { drucke_eingerueckt("Betrete main");indent += TAB;p(3); r(1);indent -= TAB; drucke_eingerueckt("Verlasse main");

    } }

    Betrete mainBetrete p

    Betrete pVerlasse pBetrete q

    Betrete pVerlasse p

    Verlasse qBetrete rVerlasse r

    Verlasse pBetrete r

    Betrete sVerlasse s

    Verlasse rVerlasse main

    Jeder Balken entspricht einer Prozedurinkarnation.Es gibt mehrere Inkarnationen der gleichen Prozedur .

    Abl

    auf

  • 12.12.2006 321© A. Poetzsch-Heffter, TU Kaiserslautern

    Beim Aufruf einer Prozedur p wird eine neue Inkarnation von p erzeugt. Dabei wird:

    - Speicherplatz für die lokalen Variablen angelegt;

    - die Anweisung gemerkt, an der nach Ausführungder Prozedurinkarnation fortzusetzen ist.

    Die Lebensdauer einer Prozedurinkarnation beginnt mit deren Erzeugung und endet mit der Ausführung des Rumpfes zu dieser Inkarnation. Die Lebensdauer erstreckt sich über einen Teil des Programmablaufs.

    Bemerkung:Eine wichtige Struktur des Ablaufs prozeduralerProgramme ist der Aufrufbaum. Am Beispiel:

    Begriffsklärungen: (zur Prozedurausführung)

    main.1

    p.1

    p.2

    p.3

    q.1 r.1 s.1

    r.2

    Die Lebensdauern von Inkarnationen der gleichenProzedur können sich überlappen (z.B.: p.1 und p.3 ).

    12.12.2006 322© A. Poetzsch-Heffter, TU Kaiserslautern

    Der Prozeduraufrufbaum beschreibt die Prozedur-aufrufstruktur in einem Programm- oder Prozedur-ablauf.

    Seine Baumknoten sind mit Prozedurinkarnationen markiert, so dass jede Inkarnation PI genau dieInkarnationen als Kinder hat, die von PI aus erzeug twurden und zwar in der Reihenfolge des Ablaufs.

    Bemerkung:Die Lebensdauern von Inkarnationen der gleichenProzedur können sich überlappen. Deshalb musses ggf. mehrere Kopien/Instanzen der gleichen lokalen Variablen geben.

    Beispiel:// Lebensdauer

    void p( int i ){ //b(p)

    int a;a = i; //1(p)if( a == 2 ) { //2(p)

    p( a-1 ); //3(p) } println(i); //4(p)

    } //e(p)

    int c;

    void main(...){ //b(m)

    p(2); //1(m)

    } //e(m)

    12.12.2006 323© A. Poetzsch-Heffter, TU Kaiserslautern

    4.2.4 Variablen in Programm und Speicher

    Jede Variablendeklaration definiert eine Programmvariable. Wir unterscheiden:

    - globale Variablen

    - (prozedur-)lokale Variablen

    - Klassenvariablen, Attribute, usw.

    Jeder Programmvariablen entsprechen zurAusführungszeit/Laufzeit des Programms eine odermehrere Speichervariablen:

    - Jeder globalen Variablen ist genau eine Speicher-variable zugeordnet.

    - Ist v eine lokale Variable zur Prozedur p, dann gi btes zu jeder Inkarnation von p eine Speichervariablefür v.

    - (andere Variablentypen behandeln wir später).

    Begriffsklärung: (statisch/dynamisch)Statisch bezeichnet man in der Programmiertechnikalle Aspekte, die sich auf das Programm beziehen un ddie man aus ihm ersehen kann, ohne es auszuführen. Statische Aspekte sind unabhängig von Eingaben.

    Dynamisch bezeichnet man alle Aspekte, die sich aufdie Ausführungszeit beziehen.

    12.12.2006 324© A. Poetzsch-Heffter, TU Kaiserslautern

    Definition: (Lebensdauer von Variablen)

    Die Lebensdauer einer Speichervariable ist der Teil des Ablaufs, in dem sie für die Ausführungbereit steht.

    Die Lebensdauer globaler Variablen erstreckt sich über den gesamten Ablauf des Programms.

    Variablen zu lokalen Deklarationen leben solange wie die zugehörige Prozedurinkarnation bzw. überdie Dauer der Ausführung ihres Blocks.

    Beispiele: (statisch/dynamisch)

    Statisch:

    - Programmvariablen

    - Prozedurdeklarationen

    - Anweisung

    - Aspekte der Übersetzung

    Dynamisch:

    - Speichervariablen

    - Prozedurinkarnationen

    - Ablauf, Ausführung

    - Aufrufbäume

    - Lebensdauer (von Prozedurinkarnationen, usw.)

  • 12.12.2006 325© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Lebensdauer von Variablen)

    Wir betrachten den Ablauf des obigen Programms mit den Markierungen und die Variablenlebensdauer:

    c a.1 a.2

    b(m.1)

    1(m.1)

    b(p.1)

    1(p.1)

    2(p.1)

    3(p.1)

    b(p.2)

    1(p.2)

    2(p.2)

    4(p.2)

    e(p.2)

    4(p.1)

    e(p.1)

    e(m.1)

    Bemerkung:

    Es gibt auch Variablen, deren Lebensdauer direktüber Anweisungen gesteuert werden kann.

    12.12.2006 326© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Lebensdauer von Variablen, die 2.)

    int k = 0;while( k

  • 12.12.2006 329© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Lebensdauer von Feldern)

    int[] erzeugeFeld( int n ) {int size = n < 0 ? 0 : n;return new int[size];

    }

    void eineProz() {int[] a = erzeugeFeld(78);...

    }

    Sei im Folgenden exp ein Ausdruck von einem ganz-zahligen Typ (byte, short, int, long), der sich zu k aus-wertet.

    Ausdrücke zum Lesen und Schreiben eines Felds a:

    - a.length liefert die Anzahl der Feldkomponenten

    - a[exp] erzeugt eine IndexOutOfBounds-Ausnahme

    falls k < 0 oder a.length ≤ k; andernfalls:R-Wert: der in der k-ten Feldkomponente

    gespeicherte Wert;

    L-Wert: die k-te Feldkomponente, d.h. z.B. weist

    a[exp] = 7 ;

    der k-ten Feldkompente den Wert 7 zu.

    12.12.2006 330© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Programmieren mit Feldern)

    Das folgende Programm liest zwei Vektoren ein undgibt die Summe aus:

    void main(String[] args) {

    int i;

    int[] a = new int[3]; // 3-elementiger Vektorint[] b = new int[3];

    for( i=0; i

  • 12.12.2006 333© A. Poetzsch-Heffter, TU Kaiserslautern

    4.2.6 Benutzerdefinierte Typen

    Die meisten prozeduralen Sprachen unterstützenbenutzerdefinierte Typen, insbesondere Verbundtypen.

    Begriffsklärung: (Verbunde)Ein (Variablen-)verbund (engl. Record) ist ein n-Tupel von Variablen nicht notwendig des gleichen Typs. Die einzelnen Variablen nennt man die Komponenten des Verbunds. Die Komponenten werden über deklarierte Namen angesprochen.

    Die genaue Syntax und Semantik von Verbunden istvon Sprache zu Sprache verschieden.

    In Java lassen sich Verbunde nur als vereinfachteObjekte deklarieren und erzeugen. Als Ergebnis der Erzeugung erhält man dabei eine Referenz aufden Verbund. Die Referenz kann man in Variablenvom Verbundtyp speichern. Verbunde mit Referenzennennen wir referenzierte Verbunde .

    Da wir im Folgenden nur referenzierte Verbundebetrachten, sprechen wir einfach von Verbunden.

    12.12.2006 334© A. Poetzsch-Heffter, TU Kaiserslautern

    Sprachmittel für (referenzierte) Verbunde:

    - Deklaration eines Verbundtyps

    - Deklaration von Variablen für (Referenzen auf)Verbunde

    - Erzeugung von Verbunden

    - Lesen der Verbundkomponenten

    - Zuweisen an Verbundkomponenten

    Paarp:

    q: ersteKomp: 3zweiteKomp : -7

    Beispiel: (Veranschaulichung eines referenzierten Verbunds)

    12.12.2006 335© A. Poetzsch-Heffter, TU Kaiserslautern

    Syntax anhand von Beispielen:

    class Paar {int ersteKomp;int zweiteKomp;

    }

    Deklaration einesVerbundtyps Paar :

    Deklaration der Variablen p, q für Referenzen auf Verbunde vom Typ Paar :

    Paar p;Paar q;

    Erzeugung eines Verbundsvom Typ Paar und Zuweisungder Referenz an p:

    p = new Paar();

    Weitergabe einer Referenz, hier von p an q:

    q = p;

    Lesen einer Komponente: int a;a = p.ersteKomp;

    Schreiben einer Komponente: int a;p.ersteKomp = a+1;

    Der Initialwert für Verbund-typen ist null :

    class C {Paar pk;

    }

    new C().pk == null

    12.12.2006 336© A. Poetzsch-Heffter, TU Kaiserslautern

    Verbunde zur Realisierung von Tupeln:

    Deklaration eines Verbundtyps Student mitName und Matrikelnummer.

    class Student {String name;int matNr;

    }

    studentDrucken( Student s ) {println("Name: " + s.name );println("Matriklenummer: " + s.matNr );

    }

    Deklaration einer Prozedur zum Drucken der Daten von Studenten:

    Erzeugen eines Verbunds vom Typ Student,Zuweisung an eine Variable, Initialisieren derKomponenten und Ausdrucken:

    Student sv = new Student();sv.name = "Max Planck";sv.matNr = 12390573;

    studentDrucken( sv );

  • 12.12.2006 337© A. Poetzsch-Heffter, TU Kaiserslautern

    Rekursive referenzierte Verbunde:

    Beispiel: (Einfachverkettete Listen)

    class IntList {int head;IntList tail;

    }

    Bei einfachverketteten Listen wird für jedes Listenelement ein Verbund mit zwei Komponentenangelegt:

    - zum Speichern des Elements

    - zum Speichern der Referenz auf den Rest der Liste.

    IntList

    head: 6 tail:

    IntList IntList

    head: -3 tail:

    head: 84 tail:

    Verbunddeklarationen können rekursiv sein, z.B.:

    Mit rekursiven Verbunden lassen sich Listen,Bäume und allgemeine Graphen realisieren.

    Die Liste [6,-3,84] erhält also folgende Repräsent ation:

    12.12.2006 338© A. Poetzsch-Heffter, TU Kaiserslautern

    /* Prozedur sortiert prüft, ob dieübergebenen List l sortiert ist.Vorbedingung: l darf keinen Zyklus enthalten

    */boolean sortiert( IntList l ){

    if( l == null || l.tail == null ) {return true;

    } else if( l.head

  • 12.12.2006 341© A. Poetzsch-Heffter, TU Kaiserslautern

    /* Prozedur contains prüft, ob Markierung ein b enthalten ist.Vorbedingung: b ist binärer Suchbaum

    */boolean contains( BinTree b, int e ) {

    if( b == null ) {return false;

    } else if( e < b.elem ){return contains(b.left,e);

    } else if( b.elem < e ){return contains(b.right,e);

    } else {return true;

    }}

    /* Prozedur printTree durchläuft den Baum inInorder-Reihenfolge und druckt die Markierungenaus, jede in eine Zeile.

    */void printTree( BinTree b ) {

    if( b != null ) {if( b.left != null ) printTree(b.left);println(b.elem);if( b.right != null ) printTree(b.right);

    }}

    12.12.2006 342© A. Poetzsch-Heffter, TU Kaiserslautern

    /* Sortiertes Einfügen. Erhält Suchbaumeigenschaft */BinTree sorted_insert( BinTree b, int e ) {

    if( b == null ) {return mkBinTree(e);

    } else {modifying_sorted_ins(b,e);return b;

    } }

    void modifying_sorted_ins( BinTree b, int e){if( e < b.elem ) {

    if( b.left == null ) {b.left = mkBinTree(e);

    } else {modifying_sorted_ins(b.left,e);

    }} else if( b.elem < e ) {

    if( b.right == null ) {b.right = mkBinTree(e);

    } else {modifying_sorted_ins(b.right,e);

    }} }

    /* Konstruktorprozedur */BinTree mkBinTree( int e ) {

    BinTree newbt = new BinTree();newbt.elem = e;return newbt;

    }

    12.12.2006 343© A. Poetzsch-Heffter, TU Kaiserslautern

    void main( String[] argf ){

    BinTree bt = mkBinTree(12);

    sorted_insert(bt,3);

    sorted_insert(bt,12);

    sorted_insert(bt,233);

    sorted_insert(bt,11);

    sorted_insert(bt,12343);

    sorted_insert(bt,-2343);

    sorted_insert(bt,233);

    printTree(bt);

    }

    Hauptprozedur zum Testen:

    Bemerkung:

    • Mit referenzierten Verbundstypen lassen sich nicht nur Listen und Bäume in unterschiedlicherForm realisieren, sondern auch allgemeine Graphen.

    • Anspruchsvollere Algorithmen werden in Abschnitt 4.3 behandelt.

    12.12.2006 344© A. Poetzsch-Heffter, TU Kaiserslautern

    4.2.7 Sichtbarkeit von Bindungen

    Deklarationen binden Bezeichner an Programm-elemente (z.B. an Variablen oder an Prozeduren). Die Sichtbarkeitsregeln bestimmen, welche Bindungenwo zulässig und benutzbar sind.

    Beispiel: (Sichtbarkeit von Bindungen)

    public class Sichtbarkeiten {

    static int m;

    static void p( int p ) { int m;int a = 7;{

    int m = 0; // unzulässigif( p > 0 ) {

    p(p-1); }

    }}

    public static void main(String[] a ){

    m = 1;p(m);

    }}

    Folgender Programmtext ist kein Java-Programm,da nur eine Variablenbindung für den Bezeichner m erlaubt ist:

  • 12.12.2006 345© A. Poetzsch-Heffter, TU Kaiserslautern

    Programme sind in Deklarationsbereiche (engl. scopes ) eingeteilt. In der betrachteten Java-Teilsprachegibt es die folgenden Deklarationsbereiche:

    - das gesamte Programm

    - jede Prozedur/Methode

    - jeder Anweisungsblock

    Die Deklarationsbereiche sind geschachtelt(engl. nested ). Ein Deklarationsbereich darf auf seiner äußersten Schachtelungsebene für jeden Bezeichner nur eine Deklaration enthalten.

    Zu jeder Deklaration gibt es einen Gültigkeitsbereich .In der betrachteten Java-Teilsprache gilt:

    Der Gültigkeitsbereich einer Deklaration erstrecktsich bei

    - Prozeduren und globalen Variablen über den gesamten direkt umfassenden Deklarationsbereich;

    - lokalen Variablen von der Deklarationsstelle bis z umEnde des direkt umfassenden Deklarationsbereichs.

    Begriffsklärung: (zur Sichtbarkeit)

    12.12.2006 346© A. Poetzsch-Heffter, TU Kaiserslautern

    Grundsätzlich gilt:

    Eine Bindung B, die in einem Deklarationsbereich Ddeklariert wurde, verschattet (engl. to shadow , to hide ) andere Bindungen mit gleichem Bezeichner, die außerhalb von D vereinbart wurden, in dem Gültigkeitsbereich von B.

    Eine Bindung ist an den Stellen ihres Gültigkeits-bereichs sichtbar , an denen sie nicht verschattet ist.Den Bereich, in dem eine Bindung B sichtbar ist,nennt man den Sichtbarkeitsbereich von B.

    Bemerkung:

    • Statt von Deklarationsbereichen spricht manteilweise von Blöcken und dementsprechend vonder Blockstruktur eines Programms.

    • Die Begriffe Sichtbarkeits-, Gültigkeitsbereich undLebensdauer werden leider häufig auch andersverwendet.

    • Die erläuterten Bereiche sind Bereiche des Program m-texts. Dementsprechend beschreiben alle oben eingeführten Begriffe statische Aspekte.

    • Sichtbarkeitsregeln sind im Allg. sprachspezifisch.

    12.12.2006 347© A. Poetzsch-Heffter, TU Kaiserslautern

    int m;

    void p

    ( int p )

    {

    int m;

    int a = 7;

    {

    int m = 0; //unzulässig

    if( p > 0 )

    {

    p(p-1);

    }

    }

    }

    void main

    ( String[] a )

    {

    m = 1;

    p(m);

    }

    Beispiel: (Deklarationsbereiche)

    1

    1.1

    1.2

    1.1.1

    1.2.1

    12.12.2006 348© A. Poetzsch-Heffter, TU Kaiserslautern

    // Sichtbarkeiten2

    void p

    ( int p )

    { int a = 7;

    { int m = 1;

    println("m:"+ m);

    }

    {

    println("m:"+ m);

    int m = 2;

    println("m:"+ m);

    }

    }

    int m = 0;

    void main

    ( String[] p )

    {

    int m = 3;

    p(m);

    }

    Beispiel: (Sichtbarkeitsanalyse)

    1

    1.1

    1.2

    1.2.1

    1.1.1

  • 12.12.2006 349© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Gültigkeitsbereiche)

    Der Gültigkeitsbereich der globalen Variablen merstreckt sich über das ganze Programm.Der Gültigkeitsbereich der lokalen Variablen mjeweils von der Deklarationstelle bis zum Ende desdirekt umfassenden Anweisungsblocks:

    // Sichtbarkeiten2

    void p

    ( int p )

    { int a = 7;

    { int m = 1;

    println("m:"+ m);

    }

    {

    println("m:"+ m);

    int m = 2;

    println("m:"+ m);

    }

    }

    int m = 0;

    ...

    12.12.2006 350© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Sichtbarkeitsbereiche)Eine Bindung B, die in einem Deklarationsbereich Ddeklariert wurde, verschattet andere Bindungen mit gleichem Bezeichner, die außerhalb von D vereinbart wurden, im Gültigkeitsbereich von B.

    Also wird das globale m von den lokalen verschattet :

    // Sichtbarkeiten2

    void p

    ( int p )

    { int a = 7;

    { int m = 1;

    println("m:"+ m);

    }

    {

    println("m:"+ m);

    int m = 2;

    println("m:"+ m);

    }

    }

    int m = 0;

    ...

    12.12.2006 351© A. Poetzsch-Heffter, TU Kaiserslautern

    Bemerkung:

    • In realistischen Programmiersprachen können dieSichtbarkeitsregeln insgesamt recht komplex sein.

    • Die Kenntnis der Sichtbarkeitsregeln ist wichtig,um Fehlermeldungen vom Übersetzer zu verstehenund um Namensprobleme beim Importierenvon Modulen geeignet behandeln zu können.

    • Sichbarkeitsregeln bieten eine erste Einführung indie Behandlung von Namensräumen.

    12.12.2006 352© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel:

    Berechne das Maximum einer Liste ganzer Zahlen.Für die leere Liste liefere 0 als Ergebnis.

    Wir gehen davon aus, dass es einen Typ IntList mit Funktionsprozeduren head, tail und isempty gibt sowie:

    int max( int m, int n ) {

    return (m > n) ? m : n;

    }

    Wir verzichten auf eine allgemeine Behandlungund betrachten nur ein Beispiel (vgl. Goos, Band 3 ,S. 152 ff)

    4.2.8 Iteration und Rekursion

    Rekursive Prozeduren lassen sich in vielen Fällenin iterative Programme transformieren, d.h. in Programme, die keine Rekursion, sondern nurSchleifen enthalten.

    Die Lösung mit rekursiven Prozeduren kann eleganterund einfacher sein. Demgegenüber ist eine iterativeLösung meist effizienter.

    Die Transformation rekursiver Prozeduren in iterati veProgramme ist ein klassisches Beispiel für optimierende Programmtransformationen.

  • 12.12.2006 353© A. Poetzsch-Heffter, TU Kaiserslautern

    1. Rekursive Ausgangsfassung:

    int maxl( IntList il ) {

    if( isempty(il) ) {

    return 0;

    } else if( isempty(tail(il)) ) {

    return head(il);

    } else { // (Laenge von il) >= 2

    return max(head(il),maxl(tail(il)));

    }

    }

    void main( String[] args ) {

    IntList l = ... ;

    println("maxl: "+ maxl(l) );

    }

    Die Funktionsprozedur maxl ist linear rekursiv, abernicht repetitiv. Führe zur Einbettung einen zusätzl ichenParameter ein, der das Maximum des gesehenenPräfixes mitführt (vgl. Folien 125ff).

    12.12.2006 354© A. Poetzsch-Heffter, TU Kaiserslautern

    2. Repetitive Fassung durch Einbettung:

    int maxlrep( int aktmax, IntList il ) {

    if( isempty(il) ) {

    return aktmax;

    } else {

    return maxlrep( max(aktmax,head(il)),

    tail(il) );

    }

    }

    int maxl( IntList il ) {

    if( isempty(il) ) {

    return 0;

    } else {

    return maxlrep( head(il), tail(il) );

    }

    }

    void main( String[] arges ) {

    IntList l = ... ;

    println("maxl: "+ maxl(l) );

    }

    Die Funktionsprozedur maxl ist nicht mehr rekursivund kann expandiert werden.

    12.12.2006 355© A. Poetzsch-Heffter, TU Kaiserslautern

    3. Repetitive Fassung nach Expansion von maxl:

    int maxlrep( int aktmax, IntList il ) {

    if( isempty(il) ) {

    return aktmax;

    } else {

    return maxlrep( max(aktmax,head(il)),

    tail(il) );

    }

    }

    void main( String[] args ) {

    IntList l = ... ;

    int mx;

    if( isempty(l) ) {

    mx = 0;

    } else {

    mx = maxlrep( head(l), tail(l) );

    }

    println("maxl: " + mx );

    }

    Transformiere die Funktionsprozedur maxlrep in eineProzedur, die ihr Ergebnis in globaler Variable abl iefert.

    12.12.2006 356© A. Poetzsch-Heffter, TU Kaiserslautern

    4. Repetitive Fassung nach Einführung einer Ergebnisvariablen:

    int res;

    void maxlrep( int aktmax, IntList il ) {

    if( isempty(il) ) {

    res = aktmax;

    } else {

    maxlrep( max(aktmax,head(il)), tail(il));

    }

    }

    void main( String[] args ) {

    IntList l = ... ;

    int mx;

    if( isempty(l) ) {

    mx = 0;

    } else {

    maxlrep( head(l), tail(l) );

    mx = res;

    }

    println("maxl: " + mx );

    }

    Nun folgt der zentrale Schritt der Transformation derrepetitiven Fassung in eine iterative Fassung:

  • 12.12.2006 357© A. Poetzsch-Heffter, TU Kaiserslautern

    5. Iterative Fassung mit Prozeduraufruf:

    int res;

    void maxlrep( int aktmaxpar, IntList ilpar ) {

    int aktmax = aktmaxpar;

    IntList il = ilpar;

    while( !isempty(il) ) {

    aktmax = max(aktmax,head(il));

    il = tail(il);}

    res = aktmax;}

    void main( String[] args ) {

    IntList l = ... ;

    int mx;

    if( isempty(l) ) {

    mx = 0;

    } else {

    maxlrep( head(l), tail(l) );

    mx = res;

    }

    println("maxl: " + mx );}

    Expansion von der Prozedur maxlrep:12.12.2006 358© A. Poetzsch-Heffter, TU Kaiserslautern

    6. Iterative Fassung:

    int res;

    void main( String[] args ) {

    IntList l = ... ;

    int mx;

    if( isempty(l) ) {

    mx = 0;

    } else {

    int aktmax = head(l);

    IntList il = tail(l);

    while( !isempty(il) ) {

    aktmax = max(aktmax,head(il));

    il = tail(il);

    }

    res = aktmax;

    mx = res;

    }

    println("maxl: " + mx );

    }

    Vereinfachung durch Elimination unnötiger Variablen.

    12.12.2006 359© A. Poetzsch-Heffter, TU Kaiserslautern

    7. Vereinfachte iterative Fassung:

    void main( String[] args ) {

    IntList l = ... ;

    int mx;

    if( isempty(l) ) {

    mx = 0;

    } else {

    mx = head(l);

    IntList il = tail(l);

    while( !isempty(il) ) {

    mx = max(mx,head(il));

    il = tail(il);

    }

    }

    println("maxl: " + mx );

    }

    Bemerkung:• Formal kann man Programmtransformation mit

    Regelsystemen beschreiben.

    • Programmtransformationen sind nicht nur wichtig zu rEffizienzsteigerung, sondern auch um Programme

    - verständlicher zu machen (Redesign);

    - an neue Anforderungen anzupassen.

    12.12.2006 360© A. Poetzsch-Heffter, TU Kaiserslautern

    Beispiel: (Transformationsregel)

    Transformationsregel: repetitiv � iterativSeien p ein Prozedurname, T1,...,Tn Typbezeichner,

    A1,..,An und B seiteneffektfreie-Ausdrücke,C und D Anweisungen, die keinen Aufruf

    von p enthalten:

    void p( T1 x1, ... , Tn xn) { if( B ) {

    C} else {

    Dp( A1(x1,...xn),..., An(x1,...,xn) );

    }}

    void p( T1 y1, ... , Tn yn) { T1 x1 = y1;...Tn xn = yn;while( !B ) {

    Dx1 = A1(x1,...,xn);... xn = An(x1,...,xn);

    }C

    }

  • 12.12.2006 361© A. Poetzsch-Heffter, TU Kaiserslautern

    Für obiges Transformationsbeispiel (Folie 356)ergibt sich mit der Regel:

    p ≡ maxlrep , T1 ≡ int, x1 ≡ aktmax,T2 ≡ IntList , x2 ≡ il,B ≡ isempty(il)C ≡ res = aktmax;D ≡ // leere Anweisung A1 ≡ max(aktmax,head(il))A2 ≡ tail(il)

    A1,A2 und B sind seiteneffektfrei;C und D enthalten keinen Aufruf von � �� ����;die Regel ist also auf � �� ���� anwendbar undliefert:

    void maxlrep( int y1, IntList y2) { int aktmax = y1;IntList il = y2;while( !isempty(il) ) {

    aktmax = max(aktmax,head(il));il = tail(il);

    }res = aktmax;

    }

    Durch konsistente Parameterumbenennung erhält man die Prozedur von Folie 357.

    12.12.2006 362© A. Poetzsch-Heffter, TU Kaiserslautern

    Bemerkung:

    • Im Prinzip lassen sich Programme mit Transformationsregeln genauso umformen wie mathematische Ausdrücke.

    • Die Transformationsregeln für realistische Programmiersprachen sind sehr komplex.Dies beginnt bereits bei einer präzisen Behandlungvon Namen (vgl. Sichtbarkeitsregeln).

    Wir haben hier nur den Sprachkern prozeduralerSprachen betrachtet. Weitere Sprachkonzepte:

    • Verbünde, variante Verbünde

    • Zeiger ggf. mit Zeigerarithmetik

    • Prozedurparameter

    • Generische/parametrische Typen

    • Modul-/Paketkonzepte

    • Kapselung/Schnittstellen

    4.2.9 Weitere prozedurale Sprachkonzepte