6 Prozeduren:

Zum vorigen Kapitel Zum Inhaltsverzeichnis Zum nächsten Kapitel


Soll in einem Programm eine bestimmte Folge von Anweisungen mehrfach ausgeführt werden, dann bietet sich der Einsatz einer Prozedur an. Sie stellt sozusagen eine Erweiterung der verwendeten Programmiersprache dar. Ein Beispiel soll das verdeutlichen:

Wenn in einem Turtle-Programm viele Quadrate eventuell verschiedener Größe gezeichnet werden sollen, dann könnte man die entsprechende WHILE-Schleife so oft wie nötig im Programmtext wiederholen. Dies führt aber zu ziemlich unübersichtlichen Programmen. Besser ist die Verwendung einer Prozedur, der als Parameter die jeweils gewünschte Seitenlänge übergeben wird; ist z.B. der Name der Prozedur "zeichneQuadrat", dann soll der Aufruf
       zeichneQuadrat (50)
ein Quadrat der Seitenlänge 50 an der aktuellen Position der Turtle zeichnen. Dies ist im übrigen völlig analog zur Verwendung von den bisher bekannten Turtle-Befehlen, zum Beispiel
in Delphi:
    FD (100)
in Java:
    draw (100)
was die Turtle eben um 100 (Pixel) zeichnend voranschiebt. Die "Turtle-Befehle" sind also ebenfalls Prozeduren.

Bevor die Prozedur "zeichneQuadrat" im Programm verwendet werden kann, muss sie deklariert und implementiert werden:

In Delphi geschieht die Deklaration im "private"-Abschnitt der Typdeklaration des Formulars, und zwar dort nach der Deklaration eventueller Variablen des Formulars:
   TForm1 = class(TForm)
          Button1 : TButton;    { zum Beispiel }
          { .....und weitere Komponenten.....}
     private
          { Zuerst kommen hier eventuelle Variablen : }
          Turtle1 : TTurtle;    { zum Beispiel }
          procedure zeichneQuadrat (seite: Double);
          { .....und danach eventuell weitere Prozeduren....}
     end;
Im "implementation" - Teil der Unit muss dann diese Prozedur erklärt werden:
   procedure TForm1.zeichneQuadrat (seite: Double);
     var i: Integer;     { lokale Laufvariable }
     begin
     i := 0;
     While i < 4 do begin
       Turtle1.FD(seite);
       Turtle1.LT(90);
       i := i + 1;
       end;
     end;
Aus anderen Prozeduren des Formulars, z.B. der Klick-Prozedur des Knopfes Button1, kann diese Prozedur dann aufgerufen werden, also etwa so:
   procedure TForm1.Button1Click(Sender: TObject);
     var k : Integer;
     begin
     k := 0;
     While k < 6 do begin
       zeichneQuadrat (30 + 10*k);
       Turtle1.PU;
       Turtle1.RT (135);
       Turtle1.FD (7);
       Turtle1.LT (135);
       Turtle1.PD;
       k := k + 1;
       end;
     end;
In Java werden Deklaration und Implementierung gleich gemeinsam vorgenommen. Schreiben Sie einfach in den Quelltext Ihres Programms (also z.B. in die Datei "TurtleTest.java", in der Ihr Formular deklariert ist) einen zusätzlichen Absatz der folgenden Form:
   public void zeichneQuadrat (double seite) {
     int i = 0;   // lokale Laufvariable
     while (i < 4) {
       turtle1.draw(seite);
       turtle1.turn(90);
       i := i + 1;
     }
   }
Das (in Java) reservierte Schlüsselwort "public" bewirkt, dass dieser Teil des Quelltextes von außerhalb der aktuellen Java-Datei sichtbar ist und daher auch aus anderen Quelltexten heraus aufgerufen werden kann. Das ist für uns derzeit noch nicht so schrecklich wichtig, weil unsere Programme meist nur aus einer einzigen ".java"-Datei bestehen. In diesem Fall würde hier statt "public" auch das Schlüsselwort "private" genügen, welches die Sichtbarkeit auf die aktuelle Quelltext-Datei begrenzt. Weitere "Sichtbarkeits-Modifizierer" werden Sie später im Rahmen der OOP kennenlernen, wo diese Sprachmittel eine tragende Rolle spielen.
Das zweite Schlüsselwort "void" weist den folgenden Textabschnitt als eine Prozedur aus. Die tiefere Bedeutung dieses Wortes werden Sie ebenfalls erst später kennen und schätzen lernen ;-)

Aus anderen Prozeduren des Formulars, z.B. der Klick-Prozedur des Knopfes button1, kann diese Prozedur dann aufgerufen werden, z.B.:
   public void button1_ActionPerformed(ActionEvent evt) {
     int k = 0;
     while (k < 6) {
       zeichneQuadrat (30 + 10*k);
       turtle1.turn(-135);
       turtle1.move(   7);
       turtle1.turn( 135);
       k = k + 1;
     }
   }

Der hier an die Prozedur "zeichneQuadrat" übergebene Parameterwert "30 + 10*k" ist der Wert, der dann zur Laufzeit die Seitenlänge des zu zeichnenden Quadrats bestimmt: der Parameter "seite", der im Kopf der Prozedur deklariert wurde, ist nämlich eine lokale Variable, die beim Aufruf der Prozedur mit dem aktuell übergebenen Wert initialisiert wird. Damit zeichnet also z.B. die obige Klick-Prozedur sechs ineinanderliegende Quadrate verschiedener Größe: das erste hat die Seitenlänge 30, das zweite 40, dann 50, usw. bis zum letzten, das die Seitenlänge 80 hat.


Damit man in Delphi nicht vor jedem Turtle-Befehl "Turtle1." schreiben muss, gibt es die WITH-Anweisung:
     with Turtle1 do begin...end
sagt dem Compiler, dass er alle Anweisungen zwischen "begin" und "end" mit vorangestelltem "Turtle1." interpretieren soll, sofern das möglich ist. Damit sieht die Klick-Prozedur von Button1 dann so aus:
   procedure TForm1.Button1Click(Sender: TObject);
     var k : Integer;
     begin
     k := 0;
     While k < 6 do begin
       ZeichneQuadrat (30 + 10*k);
       With Turtle1 do begin
         PU;
         RT (135);
         FD (7);
         LT (135);
         PD;
         end;
       k := k + 1;
       end;
     end;
Leider gibt es in Java keine "with"-Anweisung, mit deren Hilfe man sich das Präfix "turtle1." vor jedem Turtle-Befehl sparen könnte. Sie haben aber trotzdem eine einfache Möglichkeit, Ihre Tipparbeit zu minimieren, indem Sie der Turtle einen kürzeren Namen geben. Normalerweise lassen wir die vom Quelltext-Editor vorgegebenen Namen ungeändert stehen; im vorliegenden Fall ist jedoch die Arbeitsersparnis so groß, dass wir von dieser bewährten Praxis einmal abweichen sollten. Wenn Sie im JavaEditor die turtle1-Komponente in den Objekt-Inspektor laden, können Sie dort den Namen von "turtle1" in "t" ändern. Dadurch wird der von Ihnen zu tippende Quelltext deutlich schlanker:
   public void button1_ActionPerformed(ActionEvent evt) {
     int k = 0;
     while (k < 6) {
       zeichneQuadrat (30 + 10*k);
       t.turn(-135);
       t.move(   7);
       t.turn( 135);
       k = k + 1;
     }
   }

Bearbeiten Sie die folgenden Aufgaben mit Hilfe selbstdefinierter Prozeduren:


  1. Das Tunnel-Problem:


  2. Schreiben Sie ein Programm, das eine Turtle-Komponente und einen Knopf enthält. Auf Knopfdruck soll durch eine Serie von ineinanderliegenden Quadraten der Blick in einen langen Tunnel dargestellt werden.

    Schreiben Sie zunächst eine Prozedur "Quadrat" mit dem Parameter "seite" vom Typ Double. Beginnen Sie die Zeichnung mit dem größten Quadrat und experimentieren Sie, mit welchem Algorithmus man den "besten" Tiefeneindruck erzeugt. (Z.B. könnte die Quadratseite in jedem Schritt um denselben Betrag abnehmen; oder jedes folgende Quadrat hat 80% der Seitenlänge seines Vorgängers; oder.... Entsprechend sind Varianten für die Plazierung der Folge-Quadrate denkbar.)

    Achten Sie aber darauf, dass Ihre While-Schleife nicht endlos läuft!
    Lösungsvorschlag [Delphi] [Java]


  3. Die Häuser in unserer Straße:


  4. Ein Programm soll eine Serie nebeneinanderstehender Häuser derselben Form zeichnen. Jedes Haus besteht aus einem Rechteck der Höhe a und Breite b, als Dach wird ein gleichschenkliges Dreieck der Höhe b/2 aufgesetzt. Und natürlich braucht so ein Haus auch eine Türe und mehrere Fenster!

    Schreiben Sie zunächst eine Prozedur zum Zeichnen eines Rechtecks. Dieser Prozedur sollen zwei Parameter vom Typ "Double" übergeben werden, nämlich die Höhe und die Breite des Rechtecks. Die Prozedur wird also folgendermaßen deklariert:
    in Delphi:
         zeichneRechteck (hoehe, breite : Double);
    in Java:
         zeichneRechteck (double hoehe, double breite)
    Achten Sie darauf, dass die Turtle am Ende der Prozedur an derselben Stelle steht und in dieselbe Richtung schaut wie am Anfang der Prozedur! Nur wenn dies erfüllt ist, können Sie die Prozedur sinnvoll einsetzen; andernfalls verlieren Sie unweigerlich die Orientierung!

    Als nächstes erstellen Sie eine Prozedur "zeichneHaus()", der ebenfalls 2 Double-Parameter übergeben werden sollen, nämlich die Höhe des Hauses und seine Breite. In dieser Prozedur sollen Sie von der zuvor programmierten Prozedur "zeichneRechteck()" heftigen Gebrauch machen, denn sowohl der Hauskörper als auch die Türen und Fenster werden durch Rechtecke dargestellt!

    Schließlich rufen Sie in der Klick-Prozedur eines Knopfes die Prozedur "zeichneHaus()" so oft mit verschiedenen Parametern auf, bis die Straße Ihren Vorstellungen entspricht. Geht das in einer Schleife?


    Versionen für Spezialisten :

    Lassen Sie das Programm die Maße der Häuser zufällig wählen!
    In Delphi ist für das Erzeugen von (Pseudo-) Zufallszahlen die Funktion RANDOM(n) verfügbar, die einen Integer-Parameter übergeben bekommt. Sie liefert eine ganze Zufallszahl aus dem Bereich [0, 1, 2, 3,..., n-1]. Mit
         b := 30 + RANDOM(21) 
    wird b also ein zufälliger Integerwert zwischen 30 und 50 (inklusive der Grenzen) zugewiesen.
    In Java gibt es zur Erzeugung von (Pseudo-) Zufallszahlen die "Math.random()"-Funktion: sie liefert eine zufällige Double-Zahl aus dem Intervall [0..1). Diese "Quelle" kann man benutzen, um z.B. zufällige Zahlen aus dem Intervall [30, 50) zu generieren. Wenn b eine Variable vom Typ double ist, dann wird dieser Variablen mit
         b = 30 + Math.random() * 20;  
    eine Fließkommazahl aus dem Intervall [30, 50) zugewiesen.

    Achten Sie bei der Dimensionierung Ihrer Häuser darauf, dass eine gewisse Mindestbreite eingehalten wird, die Sie selbst vorgeben, und dass die Höhe des Hauses (ohne Dach) nicht kleiner als die Breite, aber höchstens 4 mal so groß wie die Breite ist. Zeichnen Sie nur soviele Häuser, wie im Fenster vollständig zu sehen sind.

    Lösungsvorschlag [Delphi][Java]



Zum vorigen Kapitel Zum Inhaltsverzeichnis Zum nächsten Kapitel