Von Thomas Grasel Diesmal will ich wie angekündigt, mit lokalen Prozeduren und Funktionen beginnen. Im folgenden werde ich mich auf lokale Prozeduren beschränken. Das Gesagte gilt aber auch für Funktionen. Eine Prozedur ist, wie ja in der letzten und vorletzten Ausgabe schon dargestellt, fast genauso aufgebaut wie ein Pascalprogramm. Was sollte also dagegen sprechen auch in eine Prozedur, in den Deklarations- teil, eine Prozedur einzufügen? Man spricht in diesem Fall von einer lokalen Prozedur. Ein Programm wäre dann folgendermaßen aufgebaut. programm... const ... type ... var ... procedure unter const ... type ... var ... procedure lokal const ... type ... var ... begin (* lokal *) ... end begin (* unter *) ... end begin (* Hauptprogramm *) ... end. Das Einrücken soll hier verdeutlichen, daß 'lokal' eine lokale Prozedur von 'unter' ist. Sie ist quasi das Eigentum von 'unter'. D.h. die Prozedur 'lokal' kann nicht vom Hauptprogramm aus aufge- rufen werden. Für das Hauptprogramm existiert sie somit überhaupt nicht! Nur die Prozedur 'unter' kann auf sie zugreifen. Für die Prozedur 'lokal' ist sozusagen die Prozedur 'unter' ihr Hauptprogramm. D.h. die lokalen Variablen von 'unter' wirken auf 'lokal' wie globale Variablen. Natür- lich sind die globalen Variablen des Hauptprogramms auch in 'lokal' an- sprechbar. Das obige Schema zeigt auch noch einmal deutlich, daß jede Prozedur ihre eigenen lokalen Konstanten, Typen, Variablen und Prozeduren haben kann. Das Ganze möchte ich nun noch einmal anhand eines kurzen Demo's zeigen. Dazu verwende ich das etwas umgeschriebene Listing zum Namenskonflikt aus der letzten Ausgabe: program konfl2 var a: integer procedure y var a: char procedure test begin writeln('a=',a) end begin a:='A' test end begin a:=1 y end. Wie sie sehen ist das Programm nur in wenigen Punkten geändert worden. Die Prozedur 'test' ist nun lokal. Ihr Aufruf aus dem Hauptprogramm ist natürlich weggefallen, denn für das Hauptprogramm existiert sie ja nun nicht mehr. Als Ausgabe erhalten wir nun: a=A Wie kommt das? In der letzten Ausgabe habe ich geschrieben, daß eine Prozedur nur ihre lokalen Variablen und die globalen Variablen aus dem Hauptpro- gramm kennt. Beim Zugriff auf eine Variable, die als lokale und globale Variable definiert wurde, wird auf die zuletzt deklarierte, also die lokale Variable zugegriffen. Dies ist kein Widerspruch zu der Aus- gabe des Programms. Beim Aufruf der Prozedur 'test' wird nicht aus der Prozedur 'y' heraus verzweigt. Es wird immer noch innerhalb der Prozedur 'y' weitergearbeitet. Somit gelten dann natürlich noch die lokalen Variablen von 'y'. Man kann kann also sagen, daß eine lokale Prozedur ihre lokalen Variablen, die Variablen des Hauptprogramms und die der sie aufrufenden Prozedur kennt. Wie man an diesem kurzen Beispiel schon sieht verliert man sehr schnell die Übersicht welches begin/end zu welcher Prozedur bzw. Hauptprogramm gehört. Es gibt 2 Möglichkeiten dies etwas zu verdeutlichen. Man kann z.B. hinter jedes begin/end als Kommentar den Prozedurnamen angeben, oder so wie ich es tun werde, zwischen jede Prozedur eine Reihe von Minuszeichen als Trennung ziehen. Vor das Hauptprogramm kommt eine Reihe von Sternen. Aber nun zu rekursiven Funktionen. Dies sind Funktionen, die sich selbst wieder aufrufen. Dabei muß natürlich darauf geachtet werden, daß eine Abbruch- bedingung eingebaut wird. Das jetzt folgende kann auch mit Prozeduren realisiert werden, jedoch muß dann statt dem Funktionsparameter ein Referenzparameter verwendet werden. In der letzten Ausgabe wurde eine Fakultätsfunktion definiert. Diese soll nun rekursiv programmiert werden. Generell gilt, das jede Funktion die iterativ (also mit Schleifenprogram- mierung) realisiert werden kann, auch rekursiv berechenbar ist! Wo Iteration, und wo Rekursion sinn- voller ist muß der Programmierer je nach Aufgabenstellung selbst ent- scheiden. 5! ist ja 5*4*3*2*1=120, und genau so wird das folgende Programm die Funktion berechnen. function fak(n: integer): real begin if n=1 then fak:=1 else fak:=n * fak(n-1) end Wie sie sehen steht in dieser Funktion der Funktionsname dreimal in der Funktion. Zweimal links und einmal rechts von ':='. Die zwei Funktions- namen links von ':=' bewirken nur, daß der Funktionswert mit dem Wert der rechten Seite beschrieben wird. Der Funktionsname rechts von ':=' bewirkt jedoch einen erneuten Funktionsaufruf. Dies ist vielleicht schwer vorstellbar aber es ist so. Wird die obige Funktion z.B. mit dem Wert 2 aus dem Haupt- programm aufgerufen so passiert folgendes: n ist 2, somit verzweigt die Funktion zur else-Anweisung. Die folgende Zeile lautet nun: fak:=2*fak(2-1) also fak:=2*fak(1) fak(1) bewirkt nun einen erneuten Funktionsaufruf. n wird nun mit 1 geladen. Dieses n ist nicht das n des ersten Funktionsaufrufes! Dieses bleibt erhalten! Nun ist die if-Bedingung erfüllt, fak wird mit 1 geladen und die Funktion beendet. Somit sieht die obige Zeile nun so aus: fak:=n*1 = fak:=2*1 = fak:=2 Nun ist auch diese Funktion beendet, und der Wert 2 wird an das Hauptpro- gramm zurück geliefert. Wieoft sich die Funktion selbst wieder aufruft, nennt man die Rekursionstiefe. In unserem Fall war die Rekursionstiefe also 1. Wie man sieht ist die Abbruch- bedingung (hier if n=1) sehr wichtig. Wird der vom Hauptprogramm übergebene Wert größer, so wird sich die Funktion lediglich mehrmals rekursiv aufrufen. Über den Sinn oder Unsinn der rekursiven Programmierung läßt sich streiten. Tatsache ist das rekursive Funktionen kürzer sind als iterativ programmierte. Bei der Rechenge- schwindigkeit gewinnen jedoch meist die iterativ programmierten Programme. Ebenso ist die Rekursionstiefe beschränkt. Der Rechner legt bei jedem Unterprogrammaufruf seine Rücksprung- adresse auf einem Stapel ab, und dieser Stapel ist nicht beliebig groß. Tatsache ist jedoch, daß man die Rekursion kennen und auch beherrschen sollte! Nun möchte ich noch die y hoch x Funktion rekursiv vorstellen: function hoch(basis: real expo : integer): real begin if expo=1 then hoch:=basis else hoch:=basis*hoch(basis,expo-1) end Versuchen Sie doch mal selber den Programmverlauf nachzuvollziehen wenn die Funktion mit hoch(4,3) aufgerufen wird! Auf der Diskette finden sie 'hoch' und 'fak' als Include-Dateien. Falls du irgend welche Fragen oder Anregungen zu den bisher von mir erschienen Pascalkursen hast, kannst du mir gerne schreiben. Leg jedoch bei Fragen bitte einen frankierten und an euch adressierten Rückumschlag bei. Die Adresse lautet: Thomas Grasel Dillenburgerstraße 61 W-6000 Frankfurt/Main 50 In der nächsten Ausgabe werde ich zwei neue Programme vorstellen. Das eine ist eine weitere Version des schon bekannten SINUS-Programmdemos. Das andere ist ein wesentlich längeres Programm, was nicht nur als Demo sondern als ein kleines Utility gedacht ist. Es bestimmt Lösungen von quadrat- ischen Gleichungen in der komplexen Zahlenebene.