Unterprogramme

Bei längeren Programmen kann es sinnvoll sein, diese in mehrere, übersichtliche Teile zu gliedern. Dieses Vorgehen kann auch dann Vorteile bringen, wenn eine Anweisungsfolge mehrfach in einem Programm auftritt.

Am Beispiel eines Hauses, das mit ColorPlane erstellt wurde, soll das Prinzip erläutert werden. Betrachte den Quelltext und überlege, welche Programmteile welchen Teilen des Hauses entsprechen!

MODULE Haus;
IMPORT C:=ColorPlane;
PROCEDURE ProgMain*;
VAR ch: CHAR; i: INTEGER;

BEGIN
C. Open();
C. SetForeColor (200,200,255); 
C. Bar (100,100, 200,200,1);


C. SetBackColor (255,255,255); 
C. SetForeColor (0,0,0);
C. Bar (120,130,140,150,0);
C. Box (120,130,140,150,1);
C. Bar (120,170,140,190,0);
C. Box (120,170,140,190,1);
C. Bar (160,170,180,190,0);
C. Box (160,170,180,190,1);
C. Bar (160,100,180,150,0);
C. Box (160,100,180,150,1);

C. SetForeColor (255,0,0);

FOR i:=1 TO 100 DO
  C. Line (100+i DIV 2,200+i,200-i DIV 2,200+i,1);
END;


ch:=C. ReadKey();
END ProgMain; END Haus.

Der blau markierte Teil entspricht der hellblauen Front des Hauses. Der schwarze Abschnitt in der Mitte lässt die Fenster zeichnen. Im roten Teil wird das Dach vereinbart (Dabei berechnet i DIV 2 den ganzzahligen Anteil bei der Division von i durch 2, z.B. 1 DIV 2=0, 2 DIV 2 = 1, 3 DIV 2 = 1, 101 DIV 2 = 50).

Geteiltes Leid ist halbes Leid - Aufteilen in Unterprogramme

Ohne die Gestalt des Hauses zu verändern, könnte man diese drei Teile aus dem Hauptprogramm (ProgMain) herauslösen, um so die Übersicht zu erhöhen. Man deklariert dazu drei Unterprogramme (Prozeduren) UntererTeil, Fenster, Dach, die die entsprechenden Teile enthalten. In der Hauptprozedur werden dann die entsprechenden Unterprogramme aufgerufen:

MODULE Haus;
IMPORT C:=ColorPlane;

PROCEDURE UntererTeil;
BEGIN
  C. SetForeColor (200,200,255); 
   C. Bar (100,100, 200,200,1);
END UntererTeil;

PROCEDURE Fenster;
BEGIN
   C. SetBackColor (255,255,255); 
   C. SetForeColor (0,0,0);
   C. Bar (120,130,140,150,0);
   C. Box (120,130,140,150,1);
   C. Bar (120,170,140,190,0);
   C. Box (120,170,140,190,1);
   C. Bar (160,170,180,190,0);
   C. Box (160,170,180,190,1);
   C. Bar (160,100,180,150,0);
   C. Box (160,100,180,150,1);
END Fenster;

PROCEDURE Dach;
VAR i:INTEGER;
BEGIN
C. SetForeColor (255,0,0);
   FOR i:=1 TO 100 DO
        C. Line (100+i DIV 2,200+i,200-i DIV 2,200+i,1);
  END;
END Dach;

PROCEDURE ProgMain*;
VAR ch: CHAR;
BEGIN
   C. Open();
  
UntererTeil;
   Fenster;
   Dach;
  ch:=C. ReadKey();
END ProgMain; END Haus.

Nun soll das Programm so erweitert werden, dass es in der Lage ist, Häuser in beliebiger Größe und an beliebiger Stelle zu zeichnen - man benötigt dafür geeignete Variablen. Da das Programm nun in mehrere Teile zerteilt ist, muss man darauf achten, dass alle Prozeduren die entsprechenden Werte erhalten und geeignet anwenden.

Globale vs. lokale Variable

Man führt die Variablen groesse (gemeint ist die Seitenlänge des großen Quadrats), xpos (x-Wert der linken unteren Ecke des Hauses) und ypos ein und verändert das Programm entsprechend. Bemerkenswert ist hier die Position, an der diese Variablen zu deklarieren sind: Geschieht dies am Anfang der Prozedur "ProgMain" oder einer anderen Prozedur, so stehen sie nur innerhalb dieser Prozedur zur Verfügung, es handelt sich dann um lokale Variable ! Um sie für alle Prozeduren des Moduls gleichermaßen verfügbar zu machen, muss man sie vor der ersten Prozedur deklarieren; man nennt sie dann globale Variable.

MODULE Haus;
IMPORT C:=ColorPlane;
VAR xpos, ypos, groesse:INTEGER;            (*Hier sind xpos, ypos und groesse GLOBAL DEKLARIERT*)
PROCEDURE UntererTeil;
BEGIN
   C.SetForeColor (200, 200, 255);
   C. Bar (xpos,ypos,xpos+groesse,ypos+groesse,1);
END UntererTeil;

PROCEDURE Fenster;
BEGIN
   C. SetBackColor (255,255,255);
   C. SetForeColor (0,0,0);
   C. Bar (xpos+(groesse*20) DIV 100 ,ypos+(groesse*30) DIV 100,xpos+(groesse*40) DIV 100,ypos+(groesse*50) DIV 100,0);
   C. Box (xpos+(groesse*20) DIV 100 ,ypos+(groesse*30) DIV 100,xpos+(groesse*40) DIV 100,ypos+(groesse*50) DIV 100,1);
   C. Bar (xpos+(groesse*20) DIV 100,ypos+(groesse*70) DIV 100,xpos+(groesse*40) DIV 100,ypos+(groesse*90) DIV 100,0);
   C. Box (xpos+(groesse*20) DIV 100,ypos+(groesse*70) DIV 100,xpos+(groesse*40) DIV 100,ypos+(groesse*90) DIV 100,1);
   C. Bar (xpos+(groesse*60) DIV 100,ypos+(groesse*70) DIV 100,xpos+(groesse*80) DIV 100,ypos+(groesse*90) DIV 100,0);
   C. Box (xpos+(groesse*60) DIV 100,ypos+(groesse*70) DIV 100,xpos+(groesse*80) DIV 100,ypos+(groesse*90) DIV 100,1);
   C. Bar (xpos+(groesse*60) DIV 100,ypos,xpos+(groesse*80) DIV 100,ypos+(groesse*50) DIV 100,0);
   C. Box (xpos+(groesse*60) DIV 100,ypos,xpos+(groesse*80) DIV 100,ypos+(groesse*50) DIV 100,1);
END Fenster;

PROCEDURE Dach;
VAR i:INTEGER;
BEGIN
   C. SetForeColor (255,0,0);
  FOR i:=1 TO groesse DO
      C. Line (xpos+i DIV 2,ypos+groesse+i,xpos+groesse-i DIV 2,ypos+groesse+i,1);
  END;
END Dach;

PROCEDURE ProgMain*;
VAR ch: CHAR;    (* xpos, ypos, groesse:INTEGER; Hier wären xpos, ypos und groesse LOKAL DEKLARIERT, wodurch sie die anderen Prozeduren nicht nutzen könnten*)
BEGIN
   xpos:=100; ypos:=100; groesse:=100;  (*Die Werte könnten natürlich auch vom Benutzer eingegeben werden*)
   C. Open();
   UntererTeil;
   Fenster;
   Dach;
   ch:=C. ReadKey();
END ProgMain; 
END Haus.

Warum macht man dann nicht alle Variablen von vornherein global?

Wenn man alle Variablen global deklariert, verschlechtert man wieder die Übersicht, die man mit den Unterprogrammen erzielen wollte; sie sollten ja gerade das Programm in kompakte, durchschaubare Abschnitte gliedern. Die Variablen sollten nur dann global deklariert werden, wenn sie wirklich von allen Prozeduren benötigt werden und auch verändert werden müssen.


Parameterübergabe

Wenn globale Variablen nur dann eingesetzt werden sollen, wenn alle Prozeduren darauf zugreifen, benötigt man noch eine zusätzliche Struktur, die es erlaubt, EINZELNEN Prozeduren Werte mitzuteilen. Dies erfolgt durch die Verwendung von Parametern. 

MODULE strasse;
IMPORT ColorPlane;

PROCEDURE UntererTeil (re:INTEGER;ho:INTEGER;gr:INTEGER);
VAR j:INTEGER;
BEGIN
     j:=1;
     ColorPlane. SetForeColor (200,200,255);
     ColorPlane. Bar (re,ho, re+gr,ho+gr,1);
END UntererTeil;

PROCEDURE Fenster(re:INTEGER;ho:INTEGER;gr:INTEGER);
BEGIN
     ColorPlane. SetBackColor (255,255,255);
     ColorPlane. SetForeColor (0,0,0);
     ColorPlane. Bar (re+(gr*20) DIV 100,ho+(gr*30) DIV 100,re+(gr*40) DIV 100,ho+(gr*50) DIV 100,0);
     ColorPlane. Box (re+(gr*20) DIV 100,ho+(gr*30) DIV 100,re+(gr*40) DIV 100,ho+(gr*50) DIV 100,1);
     ColorPlane. Bar (re+(gr*20) DIV 100,ho+(gr*70) DIV 100,re+(gr*40) DIV 100,ho+(gr*90) DIV 100,0);
     ColorPlane. Box (re+(gr*20) DIV 100,ho+(gr*70) DIV 100,re+(gr*40) DIV 100,ho+(gr*90) DIV 100,1);
     ColorPlane. Bar (re+(gr*60) DIV 100,ho+(gr*70) DIV 100,re+(gr*80) DIV 100,ho+(gr*90) DIV 100,0);
     ColorPlane. Box (re+(gr*60) DIV 100,ho+(gr*70) DIV 100,re+(gr*80) DIV 100,ho+(gr*90) DIV 100,1);
     ColorPlane. Bar (re+(gr*60) DIV 100,ho,re+(gr*80) DIV 100,ho+(gr*50) DIV 100,0);
     ColorPlane. Box (re+(gr*60) DIV 100,ho,re+(gr*80) DIV 100,ho+(gr*50) DIV 100,1);
END Fenster;

PROCEDURE Dach(re:INTEGER;ho:INTEGER;gr:INTEGER);
VAR i:INTEGER;
BEGIN
     ColorPlane. SetForeColor (255,0,0);
     FOR i:=1 TO gr DO
          ColorPlane. Line (re+i DIV 2,ho+gr+i,re+gr-i DIV 2,ho+gr+i,1);
     END;
END Dach;

PROCEDURE haus (rechtswert:INTEGER;hochwert:INTEGER;gross:INTEGER);
BEGIN
     UntererTeil(rechtswert,hochwert,gross);
     Fenster(rechtswert,hochwert,gross);
     Dach(rechtswert,hochwert,gross);
END haus;

PROCEDURE ProgMain*;
VAR ch: CHAR;
BEGIN
     ColorPlane. Open();
     haus (100,100,100);
     haus (200,200,50);
     haus (250,250,25);
     haus (275,275,12);

ch:=ColorPlane. ReadKey();
END ProgMain; END strasse.

Die Prozeduren erhalten hier von den Prozeduren, in denen sie aufgerufen werden, Werte. Bei der Deklaration der Prozedur haus erkennt man, dass dafür drei Platzhalter (rechtswert:INTEGER;hochwert:INTEGER;gross:INTEGER) vom Typ INTEGER bereitgestellt werden (formale Parameter). In der Prozedur ProgMain wird die Prozedur haus mehrmals mit unterschiedlichen Werten (aktuelle Parameter) aufgerufen.

Variable Parameter

Manchmal kann es nützlich sein, wenn ein Unterprogramm, die Parameter, die es erhält, auch in der übergeordneten Prozedur verändern kann, d.h. die aktuellen Parameter sind variabel. Im folgenden Beispiel berechnet die Prozedur haus am Ende die neuen Werte für das nachfolgende haus. Dies wird gekennzeichnet durch ein VAR vor der Deklaration des Parameters.

MODULE strasse;
IMPORT ColorPlane;

PROCEDURE UntererTeil (re:INTEGER;ho:INTEGER;gr:INTEGER);
VAR j:INTEGER;
BEGIN
     j:=1;
     ColorPlane. SetForeColor (200,200,255);
     ColorPlane. Bar (re,ho, re+gr,ho+gr,1);
END UntererTeil;

PROCEDURE Fenster(re:INTEGER;ho:INTEGER;gr:INTEGER);
BEGIN
     ColorPlane. SetBackColor (255,255,255);
     ColorPlane. SetForeColor (0,0,0);
     ColorPlane. Bar (re+(gr*20) DIV 100,ho+(gr*30) DIV 100,re+(gr*40) DIV 100,ho+(gr*50) DIV 100,0);
     ColorPlane. Box (re+(gr*20) DIV 100,ho+(gr*30) DIV 100,re+(gr*40) DIV 100,ho+(gr*50) DIV 100,1);
     ColorPlane. Bar (re+(gr*20) DIV 100,ho+(gr*70) DIV 100,re+(gr*40) DIV 100,ho+(gr*90) DIV 100,0);
     ColorPlane. Box (re+(gr*20) DIV 100,ho+(gr*70) DIV 100,re+(gr*40) DIV 100,ho+(gr*90) DIV 100,1);
     ColorPlane. Bar (re+(gr*60) DIV 100,ho+(gr*70) DIV 100,re+(gr*80) DIV 100,ho+(gr*90) DIV 100,0);
     ColorPlane. Box (re+(gr*60) DIV 100,ho+(gr*70) DIV 100,re+(gr*80) DIV 100,ho+(gr*90) DIV 100,1);
     ColorPlane. Bar (re+(gr*60) DIV 100,ho,re+(gr*80) DIV 100,ho+(gr*50) DIV 100,0);
     ColorPlane. Box (re+(gr*60) DIV 100,ho,re+(gr*80) DIV 100,ho+(gr*50) DIV 100,1);
END Fenster;

PROCEDURE Dach(re:INTEGER;ho:INTEGER;gr:INTEGER);
VAR i:INTEGER;
BEGIN
     ColorPlane. SetForeColor (255,0,0);
     FOR i:=1 TO gr DO
          ColorPlane. Line (re+i DIV 2,ho+gr+i,re+gr-i DIV 2,ho+gr+i,1);
     END;
END Dach;

PROCEDURE haus (VAR rechtswert:INTEGER;VAR hochwert:INTEGER;VAR gross:INTEGER);
BEGIN
     UntererTeil(rechtswert,hochwert,gross);
     Fenster(rechtswert,hochwert,gross);
     Dach(rechtswert,hochwert,gross);
     rechtswert:=rechtswert+gross;
     hochwert:=hochwert+gross;
     gross:=gross DIV 2;

END haus;

PROCEDURE ProgMain*;
VAR ch: CHAR;x,y,g:INTEGER;
BEGIN
     ColorPlane. Open();
     x:=100; y:=100; g:=100;
     REPEAT hausi (x,y,g) UNTIL g<=1;
     ch:=ColorPlane. ReadKey();
END ProgMain; END strasse.