Neben den Variablenparametern gibt es in Pascal eine weitere Möglichkeit, Resultate aus Unterprogrammen zurückzuliefern. Diese Methode lehnt sich an die Notation von Funktionen in der Mathematik an. Dort schreibt man zum Beispiel
x = ggT (7,29)
Der Name der Funktion ggT repräsentiert also gleichzeitig den Wert der Berechnung. In Pascal definiert man solche Prozeduren, die nur einen skalaren Wert als Ergebnis liefern, als Funktionen:
PROGRAM FUNKTION(OUTPUT); VAR W: INTEGER; FUNCTION GGT(A,B: INTEGER): INTEGER; BEGIN WHILE A<>B DO IF A>B THEN A:= A-B ELSE B:= B-A; GGT:= A END; (* GGT *) BEGIN (* HAUPTPROGRAMM *) W:= GGT(12345, 25325); WRITELN(W, GGT(9,3)); W:= W + GGT( GGT(1234, 432), 444); END.
Bild 44: Der ggT als Funktion
Wenn Sie Bild 44 mit Bild 40 vergleichen, so werden Sie folgende Unterschiede zwischen einer Funktion und einer »normalen« Prozedur feststellen: Im Prozedurkopf wird das Wortsymbol PROCEDURE durch FUNCTION ersetzt und am Ende der Parameterliste steht zusätzlich nach einem Doppelpunkt der Ergebnistyp der Funktion. An dieser Stelle muß (wie bei einer Parameterliste) ein Typ-Name stehen:
TYPE SHORTSTRING = STRING[16];
FUNCTION REVERSE(S: SHORTSTRING): SHORTSTRING; RICHTIG!
also nicht:
FUNCTION REVERSE(S: STRING[16]): STRING[16]; FALSCH!
Als Ergebnistyp einer Funktion sind im report nur skalare Typen und Zeiger (s. Abschnitt 2.18) erlaubt, während Pascal 2.0 auch beliebige zusammengesetzte Typen als Funktionsergebnis unterstützt.
Die exakte Syntax einer Prozedur- und Funktionsdeklaration ist natürlich wieder dem Anhang A (Syntaxdiagramm BLOCK und PARAMETERLISTE) zu entnehmen.
Innerhalb der Funktion muß das Funktionsergebnis an mindestens einer Stelle durch eine Zuweisung an den Funktionsnamen festgelegt werden. In der Funktion GGT geschieht dies mit der Zuweisung GGT:=A. GGT ist also nicht nur der Name der Funktion, sondern auch der Name für das Ergebnis der Funktion. Die Anweisungen im Hauptprogramm in Bild 44 zeigen, daß man Funktionsaufrufe wie in der Mathematik üblich beliebig geschachtelt innerhalb von Ausdrücken verwenden kann.
Funktionsaufrufe sind nicht wie Prozeduraufrufe als eigenständige Anweisungen möglich:
W:= GGT(3,4) RICHTIG! BEGIN GGT(3,4) END FALSCH !
Das folgende Programm beinhaltet mehrere Funktionsdeklarationen, die recht häufig in alltäglichen Programmen verwendet werden.
PROGRAM FUNCTIONS (INPUT,OUTPUT); CONST VEKTORLAENGE = 3; TYPE VEKTOR = ARRAY [1..VEKTORLAENGE] OF INTEGER; VAR V : VEKTOR; S : STRING; I : INTEGER; FUNCTION MAX(A,B: INTEGER): INTEGER; (* BESTIMME DAS MAXIMUM DER BEIDEN ZAHLEN *) BEGIN IF A>B THEN MAX:=A ELSE MAX:=B; END; (* MAX *) FUNCTION INTRANDOM(LIMIT: INTEGER): INTEGER; (* GANZE ZUFALLSZAHL ZWISCHEN 1 UND LIMIT ERZEUGEN *) BEGIN INTRANDOM:= 1 + INT(RANDOM(0) * LIMIT) END; (* INTRANDOM *) FUNCTION FAKULTAET(N: INTEGER): REAL; (* N ! = 1 * 2 * 3 * ... * N BERECHNEN *) VAR P: REAL; BEGIN P:=1; WHILE N>1 DO BEGIN P:= P*N; N:= N-1 END; FAKULTAET:= P; END; (* FAKULTAET *) FUNCTION CENTER (S: STRING; FIELD: INTEGER): STRING; (* ZENTRIERE S IN EINEM STRING DER LAENGE FIELD *) BEGIN WHILE LENGTH(S)<FIELD-1 DO S:= ' ' + S + ' '; IF LENGTH(S)=FIELD-1 THEN CENTER:= S + ' ' ELSE CENTER:= S END; (* CENTER *) FUNCTION BETRAG (V: VEKTOR): REAL; (* BERECHNE DEN BETRAG (EUKLIDISCHE NORM / LAENGE) DES *) (* VEKTORS V *) VAR I : INTEGER; SIGMA: REAL; BEGIN SIGMA:= 0.0; FOR I:= 1 TO VEKTORLAENGE DO SIGMA:= SIGMA + SQR(V[I]); BETRAG:= SQRT(SIGMA) END; (* BETRAG *) BEGIN (* HAUPTPROGRAMM *) FOR I:= 1 TO VEKTORLAENGE DO BEGIN V[I]:= INTRANDOM(200); WRITE(V[I]:4) END; WRITELN; WRITELN('Das Maximum ist:', MAX(MAX(V[1],V[2]),V[3])); WRITELN('Der Betrag ist:', BETRAG(V)); S:= CENTER('*', 40); WRITELN( CENTER('Dieser Text wird auf ',40)); WRITELN( CENTER('40 Spalten zentriert', 40)); WRITELN; WRITELN(S); FOR I:= 0 TO 15 DO WRITELN(I:5, '! =', FAKULTAET(I)); END.
Bild 45: Beispiele für Funktionen
Bereits bei Ihren ersten Schritten in Pascal hatten Sie die Anweisungen WRITE und READ sowie arithmetische Funktionen wie SIN, COS und ABS verwendet.
Im Unterschied zu den Wortsymbolen (BEGIN, END, WHILE, OR, MOD ...) handelt es sich hierbei um vordefinierte Namen für Prozeduren und Funktionen. Aus den in Abschnitt 2.11.1 beschriebenen Sichtbarkeitsregeln für Namen geht hervor, daß diese Standardbezeichner auch durch benutzerdefinierte Namen verdeckt werden können. Diese Tatsache ist besonders wichtig, wenn es um die Verwendung von Namen geht, die nur in Pascal 2.0 eine vordefinierte Bedeutung besitzen: Falls Sie z.B. aus der Literatur ein Programm übernehmen wollen, das den Namen CIRCLE vewendet, so können Sie nicht in Konflikte mit der Prozedur CIRCLE von Pascal 2.0 geraten, da die Prozedur CIRCLE in einem imaginären Block deklariert ist, der das gesamte Programm umgibt.
Als Beispiel zeigt Bild 46 eine Deklaration der Quadratwurzelfunktion SQRT. Durch die Vereinbarung der Funktion SQRT wird die Standardfunktion SQRT verdeckt, so daß spätere Aufrufe der Funktion SQRT nur die benutzerdefinierte Funktion aktivieren:
PROGRAM NEWSQRT (INPUT,OUTPUT); CONST EPS = 1.0E-7; (* Genauigkeit *) VAR X: REAL; FUNCTION SQRT(X: REAL): REAL; VAR Y,Z: REAL; BEGIN IF X<0 THEN BEGIN WRITELN('Fehler in SQRT'); HALT END ELSE BEGIN Y:= 2; (* Startwert Z berechnen *) REPEAT Z:= Y; Y:= Y*Y UNTIL Y>X REPEAT (* Iteration *) Y:= Z; Z:= 0.5 * (Y + X/Y) UNTIL ABS(Y-Z)<= EPS END; SQRT:= Z END; (* SQRT *) BEGIN X:= 3.0; REPEAT WRITELN(X:13, SQRT(X):13); X:= 10 * X; UNTIL X>1E9; END.
Bild 46: Deklaration der Quadratwurzelfunktion
Durch eine Erniedrigung der Genauigkeit EPS läßt sich bei Bedarf die Geschwindigkeit der Routine erhöhen. Die Eigenschaft mancher vordefinierter Prozeduren, eine variable Anzahl von Parametern zu besitzen (z.B. WRITE, READ), läßt sich mit benutzerdefinierten Prozeduren nicht simulieren.
Die Wirkung jeder einzelnen Standardprozedur und -funktion wird in Kapitel 4.4.4 erläutert.
In Abschnitt 2.11.1 über die Schachtelung von Prozeduren wurde erklärt, daß in einem Block jede Prozedur aufgerufen werden kann, deren Name sichtbar ist. Dies bedeutet, daß sich eine Prozedur auch selbst aufrufen kann. Dieser Vorgang wird Rekursion genannt.
Durch die Tatsache, daß bei jedem Aufruf einer Prozedur Speicherplatz für die lokalen Objekte bereitgestellt wird, werden bei rekursiven Aufrufen einer Prozedur P verschiedene Inkarnationen der lokalen Variablen von P erzeugt.
Wegen dieser Methode der Speicherverwaltung werden beim rekursiven Aufruf einer Prozedur die alten Werte der lokalen Variablen nicht überschrieben. Dies läßt sich am besten mit einem einfachen Beispielprogramm verdeutlichen (Bild 47).
PROGRAM REKURSION (OUTPUT); PROCEDURE REKURSIV (N: INTEGER); VAR LOKAL: INTEGER; BEGIN LOKAL:= N; WRITELN (' ': N, 'LOKAL =', LOKAL); IF N<4 THEN REKURSIV(N+1); (* <<-------- *) WRITELN (' ': N, 'LOKAL =', LOKAL); END; (* REKURSIV *) BEGIN REKURSIV(1) END.
Bild 47: Eine einfache Rekursion
Die Prozedur REKURSIV speichert zunächst (zu Demonstrationszwecken) den Wert des Parameters N in einer lokalen Variablen (LOKAL). Dieser Wert wird nun in zwei identischen Ausgabeanweisungen um N Stellen eingerückt gedruckt. Die eigentlich interessante Anweisung ist mit einem Pfeil markiert: Ist beim Aufruf der Prozedur der Parameter noch nicht gleich 4, so ruft sich die Prozedur selbst auf. Als Parameter für diesen Selbstaufruf wird die Zahl N+1 verwendet.
Da jeder Aufruf der Prozedur REKURSIV seine eigenen Variablen N und LOKAL besitzt, wird durch diesen rekursiven Aufruf der Inhalt der Variablen N und LOKAL in der aufrufenden Umgebung nicht verändert. Deshalb ergibt sich folgende Ausgabe:
LOKAL = 1 LOKAL = 2 LOKAL = 3 LOKAL = 4 LOKAL = 4 LOKAL = 3 LOKAL = 2 LOKAL = 1
Jeweils zwei Zeilen, die gleich weit eingerückt sind, stammen von derselben Inkarnation der Prozedur REKURSIV. Dieses Programm ist zwar nicht sehr sinnvoll, zeigt aber deutlich die geschachtelten Aufrufe:
REKURSIV(1) ruft REKURSIV(2) ruft REKURSIV(3) ruft REKURSIV(4)
Außerdem sehen Sie, daß diese Folge rekursiver Aufrufe irgendwann beendet werden muß. Deshalb werden rekursive Aufrufe immer durch eine Bedingung kontrolliert. Im Beispiel aus Bild 47 ist dies die If-Anweisung IF N<4 THEN.
Schon jetzt möchte ich Sie davor warnen, rekursive Prozeduren Schritt für Schritt nachzuvollziehen, indem Sie sich die Werte aller lokalen Variablen notieren und so den Programmablauf zu analysieren versuchen. Dabei werden Sie nach wenigen Schritten schon an einem heillosen Durcheinander verzweifeln. Vielmehr muß man rekursive Prozeduren deklarativ verstehen. So kann man z.B. die Prozedur REKURSIV wie folgt beschreiben:
Um Ihr deklaratives Verständnis zu trainieren, sollten Sie jetzt ein Blatt Papier zur Hand nehmen und die Ausgabe notieren, die Sie beim Aufruf REKURSIV2(1) in Bild 48 erwarten. Im Unterschied zur Prozedur REKURSIV druckt REKURSIV2 den eingerückten Block zweimal. (Ein Tip: Die Ausgabe ist genau 30 Zeilen lang!)
PROGRAM REKURSION2 (OUTPUT); PROCEDURE REKURSIV2 (N: INTEGER); VAR LOKAL: INTEGER; BEGIN LOKAL:= N; WRITELN (' ': N, 'LOKAL =', LOKAL); IF N<4 THEN REKURSIV2(N+1); (* <<-------- *) IF N<4 THEN REKURSIV2(N+1); (* <<-------- *) WRITELN (' ': N, 'LOKAL =', LOKAL); END; (* REKURSIV *) BEGIN REKURSIV2(1) END.
Bild 48: Rekursion (2)
Das Prinzip der Rekursion besteht darin, daß man zur Lösung einer großen Aufgabe die Lösung der Aufgabe für kleinere Werte benutzt. Diese etwas tautologisch anmutende Strategie soll ein kleines grafisches Beispielprogramm illustrieren:
PROGRAM DRAWCIRCLES (INPUT,OUTPUT, GRAPHIC); CONST XMAX = 319; YMAX = 199; VAR T : INTEGER; PROCEDURE QUADCIRCLE (TIEFE: INTEGER; MX, MY, R : INTEGER); (* ZEICHNE REKURSIV EINEN KREIS, DER VIER KREISE *) (* ENTHÄLT, DIE SICH GEGENSEITIG BERÜHREN. *) VAR R2: INTEGER; BEGIN CIRCLE (1, MX, MY, R,,0,360,0,10); IF TIEFE>1 THEN BEGIN (* Bilde einen Kreis aus 4 kleineren Kreisen *) R2:= R DIV 2; QUADCIRCLE(TIEFE-1, MX-R2, MY ,R2); QUADCIRCLE(TIEFE-1, MX , MY-R2 ,R2); QUADCIRCLE(TIEFE-1, MX+R2, MY ,R2); QUADCIRCLE(TIEFE-1, MX , MY+R2, R2) END; (* --- Punkt 1 -- *) END; (* QUADCIRCLE *) BEGIN COLOR(0,1); COLOR(1,2); COLOR(5,5); WRITE('Welche Tiefe? :4' #157); READLN(T); GRAPHIC(1,1); QUADCIRCLE(T, XMAX DIV 2, YMAX DIV 2, YMAX DIV 2); REPEAT UNTIL KEYPRESSED; GRAPHIC(0) END.
Bild 49: Rekursives Grafikprogramm
Dieses Programm zeichnet ein Muster aus geschachtelten Kreisen. Ein Muster der Tiefe N wird durch folgende (rekursive) Regel beschrieben:
Wenn Sie das Programm nacheinander für die Tiefen 1, 2, 3 und 4 ausführen lassen, werden Sie das obige Konstruktionsprinzip sehr deutlich erkennen. Außerdem werden Sie feststellen, daß durch Rekursion die Programmlaufzeit nach dem Schneeballsystem exponentiell ansteigen kann:
Tiefe | Anzahl der Kreise |
1 | 1= 1 |
2 | 1 + 4= 5 |
3 | 1 + 4 ( 1 + 4)= 21 |
4 | 1 + 4 ( 1 + 4 ( 1 + 4))= 85 |
5 | 1 + 4 ( 1 + 4 ( 1 + 4 ( 1 + 4)))= 341 |
Grafische Programme sind sehr gut geeignet, das Prinzip der Rekursion anschaulich darzustellen, da optisch die Selbstbezüglichkeit einer Struktur unmittelbar zu erkennen ist. Eine Rekursion wie in Bild 49 läßt sich auch »von hinten« aufzäumen, indem man zunächst die Muster niedriger Tiefe zeichnet. Sie sollten deshalb zum Vergleich die Anweisung
CIRCLE (1, MX, MY, R,,0,360,0,10);
an die Stelle (* --- Punkt1 --- *) stellen. Zwar ist das endgül- tige Muster unverändert, jedoch werden Sie erkennen, daß die Zeichenstrategie sich geändert hat. Auf der mitgelieferten Pro- grammdiskette findet sich das Programm »TRIA.P«. Es arbeitet nach dem gleichen Rekursionsschema wie das Programm in Bild 49, um geschachtelte Dreiecke wählbarer Verzerrung zu zeichnen.
Nachdem Sie einmal das Prinzip der Rekursion verstanden haben, werden Sie eine Vielzahl von Problemlösungen durch rekursive Algorithmem formulieren. Ein sinnvolles Beispiel für die Verwendung der Rekursion zeigt Bild 50. Das Programm soll alle möglichen Anordungen (Permutationen) eines Strings mit N Zeichen Länge drucken. Für den String 'ABC' mit drei Zeichen gibt es diese 6 Anordnungen:
ABC BAC CBA BCA ACB CAB
Der rekursive Algorithmus zur Ausgabe lautet folgendermaßen:
Für alle Positionen i von 1 bis N-1: Tausche das i. Zeichen mit dem letzen (N-ten) Zeichen im String. Drucke alle Möglichkeiten für diese Anordnung. Mache die Vertauschung rückgängig.
So formuliert läßt sich der Algorithmus direkt in ein Pascal-Programm umsetzen (s. Bild 50).
PROGRAM ANORDNUNGEN (INPUT,OUTPUT); VAR I: INTEGER; A: STRING; PROCEDURE ANORDNUNG(S: STRING; N: INTEGER); (* Drucke alle Anordnungen der ersten N Zeichen im *) (* String S, ohne die übrigen Zeichen zu verändern. *) VAR C: CHAR; I: INTEGER; BEGIN IF N=1 THEN WRITE(S,' ') ELSE BEGIN ANORDNUNG (S,N-1); FOR I:= 1 TO N-1 DO BEGIN C:= S[I]; S[I]:= S[N]; S[N]:= C; ANORDNUNG(S,N-1); C:= S[I]; S[I]:= S[N]; S[N]:= C; END END END; (* ANORDNUNG *) BEGIN READLN(A); ANORDNUNG (A, LENGTH(A)); WRITELN END.
Bild 50: Permutationen
Zugegebenermaßen erfordern rekursive Algorithmen eine gewisse geistige Leistung, da es bei ihnen im allgemeinen nicht mehr möglich ist, Anweisung für Anweisung den Programmablauf zu verfolgen. Vielmehr muß man eine rekursive Prozedur folgendermaßen »verstehen«:
Aus 1 und 2 zusammen folgt, daß für Strings beliebiger Länge alle Anordnungen bestimmt werden.
Natürlich gibt es auch Fälle, in denen eine Rekursion unnötig ist, da ein gleichwertige iterative Lösung existiert. Man könnte z.B. die Funktionen CENTER und FAKULTAET aus Bild 45 auch folgendermaßen rekursiv definieren:
FUNCTION FAKULTAET(N: INTEGER): REAL; (* N ! = 1 * 2 * 3 * ... * N BERECHNEN *) BEGIN IF N=0 THEN FAKULTAET:= 1.0 ELSE FAKULTAET:= N * FAKULTAET(N-1); END; (* FAKULTAET *) FUNCTION CENTER (S: STRING; FIELD: INTEGER): STRING; (* ZENTRIERE S IN EINEM STRING DER LAENGE FIELD *) BEGIN IF LENGTH(S)<FIELD-1 THEN CENTER:= CENTER(' ' + S + ' ') ELSE IF LENGTH(S)=FIELD-1 THEN CENTER:= S + ' ' ELSE CENTER:= S END; (* CENTER *)
Bild 51: Unnötige Rekursionen
Jedoch erfordert jeder rekursive Aufruf abgesehen von dem Speicherplatz für die lokalen Variablen auch einen gewissen Verwaltungsaufwand, der bei zusammengesetzten Anweisungen (For-, While- und Repeat-Schleifen) nicht auftritt.
Zum Abschluß der Ausführungen über Rekursion wird der in Kapitel 2.9.1 angekündigte verbesserte Sortieralgorithmus vorgestellt. Die Aufgabe ist wiederum, ein Array A mit N (ganzen) Zahlen aufsteigend zu sortieren.
Die Idee zu einer rekursiven Lösung besteht darin, das Array A in zwei Teile A[1..K] und A[K+1..N] aufzuteilen, so daß jeder Teil für sich, ohne Kenntnis der Zahlen im anderen Teil, sortiert werden kann. Diese Zerlegung und den Index K findet man mit der folgenden Strategie:
Die Tatsache, daß in jedem Schritt die Arraygröße um mindestens ein Element sinkt, ist wichtig, damit die Rekursion auch korrekt terminiert. Betrachten Sie diese Strategie an einem Zahlenbeispiel:
8 5 7 6 3 4 8 4
Da acht Elemente vorliegen, wählt man in Schritt 1 das 4. Element (X=6) im Array. Die Zerlegung in zwei Teile sieht nach dem 2. Schritt wie folgt aus:
4 5 4 3 6 7 8 8
Links und rechts von der Zahl 6 sind (in beliebiger Reihenfolge) die Zahlen kleiner bzw. größer als 6 aufgeführt. Da es vier Zahlen kleiner als 6 gibt, besitzt der Index K den Wert K= 4 + 1 = 5. Im dritten Schritt werden nun die Teilarrays getrennt sortiert
3 4 4 5 6 7 8 8
Damit ist die gesamte Folge sortiert. Für Schritt 2 (die Zerlegung in zwei Teilarrays) muß noch ein effizienter Algorithmus angegeben werden:
2.1 Setze zwei Zeiger I und J auf das erste und letzte Element im Array.
2.2 Lasse I nach rechts wandern, bis es auf ein Element zeigt, das größer als X ist. Lasse J nach links wandern, bis es auf ein Element zeigt, das kleiner als X ist.
2.3 Da A[I] und A[J] jeweils auf der falschen Seite von X stehen, tausche beide Elemente aus.
2.4 Wiederhole diese Schritte, bis sich beide Zeiger I und J treffen.
Der skizzierte Algorithmus wurde von seinem Erfinder C.A.R.Hoare Quicksort getauft und findet sich in Bild 52 als ein vollständiges Pascalprogramm. Um die Korrektheit für alle Belegungen des Arrays A zu sichern, ist die exakte Formulierung der Bedingungen in den Schleifen nötig. Die Diskussion solcher Details und die Berechnung der Rechenzeit finden Sie in [2] (s. Anhang E).
Die Sortierung erfolgt in der rekursiven Prozedur QUICK, deren Parameter L und R den Index des ersten und letzten Elementes im zu sortierenden Array A enthalten. In Bild 52 sind zur Verdeutlichung die Nummern der obigen Schritte als Kommentare angegeben.
PROGRAM SORTIEREN (INPUT,OUTPUT); CONST N = 16; TYPE ELEMENT = INTEGER; VAR A: ARRAY [1..N] OF ELEMENT; PROCEDURE ERZEUGEN; VAR I: INTEGER; BEGIN WRITELN('Geben Sie eine unsortierte Folge'); WRITELN('von ', N, ' ganzen Zahlen an:'); FOR I:= 1 TO N DO READ(A[I]); READLN; END; (* ERZEUGEN *) PROCEDURE AUSGEBEN; VAR I: INTEGER; BEGIN WRITELN('Die Folge lautet:'); FOR I:= 1 TO N DO WRITE(A[I]:5); WRITELN END; (* AUSGEBEN *) PROCEDURE QUICK (L,R: INTEGER); (* Sortiere das Array im Bereich A[L..R] *) VAR I, J: INTEGER; X, Y: ELEMENT; BEGIN X:= A[(L+R) DIV 2]; (* 1. *) I:=L; J:=R; REPEAT WHILE A[I]<X DO I:= I+1;(* 2.1 *) WHILE A[J]>X DO J:= J-1:(* 2.2 *) IF I<=J THEN BEGIN Y:= A[I]; A[I]:= A[J]; A[J]:= Y; (* 2.3 *) I:= I+1; J:= J-1 END; UNTIL I>J;(* 2.4 *) IF I<R THEN QUICK(I,R); (* 3 *) IF L<J THEN QUICK(L,J); (* 3 *) END; (* QUICK *) BEGIN ERZEUGEN; AUSGEBEN; QUICK(1, N); (* vom 1. bis zum N. Element *) AUSGEBEN END.
Bild 52: Quicksort
In sehr speziellen Anwendungen kann eine indirekte Rekursion der Prozeduren A,B C in der folgenden Form notwendig sein:
A ruft B A ruft C C ruft B B ruft CIn diesem Fall läßt sich keine Reihenfolge der Prozedurdeklarationen finden, in der die Deklaration jeder Prozedur vor ihrer Anwendung steht. Für diese zyklischen Referenzen existiert in Pascal die FORWARD Deklaration von Prozeduren und Funktionen:
PROGRAM FORWARDPROCEDURES (OUTPUT); PROCEDURE B(I: INTEGER); FORWARD; PROCEDURE C(X: CHAR); BEGIN IF X = '+' THEN B(ORD(X)); END; (* C *) PROCEDURE B; BEGIN IF CHR(I)<>'+' THEN C(CHR(I)); END; (* B *) PROCEDURE A; BEGIN B(13); C('+'); END; (* A *) BEGIN A END.
Bild 53: Beispiel für FORWARD
Indem man nur den Kopf der Prozedur B gefolgt von dem Wortsymbol FORWARD nennt, kann man B in der Prozedur C verwenden, obwohl der eigentliche Anweisungsteil der Prozedur B erst hinter der Prozedur C steht. Der Anweisungsteil jeder FORWARD-deklarierten Prozedur muß nachträglich hinter einem Prozedurkopf ohne Parameterliste aufgeführt werden. Ansonsten meldet der Compiler
119 UNSATISFIED 'FORWARD' DECLARATION IN PROGRAM