Von Thomas Grasel Ich hoffe ihr habt mittlerweile ein wenig mit Prozeduren herumexperimen- tiert. Zunächst einmal ein paar Tips zu den Prozeduren: Man sollte generell anstreben möglichst wenig Parameter zu übergeben. Variablen die nur in einem Programmteil gebraucht werden, sollten als lokale Variablen definiert werden. Referenzparameter belegen nur wenig neuen Speicher, da ja nur ihre Adresse kopiert wird. Nicht so bei Werteparametern, hier wird wirklich eine Kopie der Variablen erstellt. Daran sollte man besonders denken, wenn man z.B. große Felder übergibt. Manchmal tritt der Fall auf, daß man in einer Prozedur einer lokalen Variablen einen Namen geben will, dieser im Hauptprogramm aber schon verwendet worden ist. Dabei stellt sich die Frage ob man den Namen nun doppelt verwenden darf oder nicht ! Dazu folgendes Beispiel: program konflikt var a: integer procedure test begin writeln('a=',a) end procedure y var a: char begin a:='A' test end begin a:=1 test y end. Hier ist die Reihenfolge der Prozeduren nicht beliebig! Die Prozedur 'y' ruft die Prozedur 'test' auf, somit muß diese vor ihr deklariert worden sein. Das kann man sich ganz einfach klar machen, wenn man weiß wie der Pascal- compiler arbeitet. Als erstes wird nach dem Start des Compilers die Syntax des Programms auf Fehler untersucht (z.B. Semikolon oder 'end' vergessen). Danach wird von vorn begonnen, und das Programm in Maschinencode umgewandelt. Um nun einen Sprung zu einer Prozedur ausführen zu können, muß der Compiler wissen, wo diese entgültigt im Speicher steht. Das kann er nur, wenn er sie schon in Maschinencode übersetzt hat, sie also vor dem Prozeduraufruf im Programm steht. Aber nun zum Namenskonflikt im Programm: Die Variable 'a' wird zweimal ver- wendet, einmal als Integer im Haupt- programm (globale Variable) und in der Prozedur 'y' (lokale Variable) als char. Eine Prozedur kann nicht unterscheiden von wo aus sie angesprochen worden ist. Sie kennt nur ihre! lokalen Variablen und die globalen Variablen des Haupt- programms. Beim Zugriff auf eine Variable die lokal und global deklariert wurde, wird auf die zuletzt deklarierte, also die lokale Variable zugegriffen. Die Prozedur 'test', aus dem obigen Beispiel, kennt also nur die globale Variable (Integer!) Variable 'a', egal ob sie direkt vom Hauptprogramm oder von der Prozedur 'y' aus angesprochen worden ist. Das heißt unser Demoprogramm wird folgende Ausgabe erzeugen: a=1 a=1 Man sollte also unbedingt darauf achten den Variablen unterschiedliche Namen zu geben, da sonst nur schwer festzu- stellen ist, auf welche Variable nun wirklich zugegriffen wird. Dies wird besonders in der nächsten Ausgabe deutlich, wo ich den Namenskonflikt noch einmal im Zusammenhang mit lokalen Prozeduren betrachten werde. Soviel nun erst einmal zu Prozeduren. Nun will ich auf die Funktionen ein- gehen. Pascal besitzt bereits eine ganze Reihe von ihnen fest implemen- tiert. Da wären zum Beispiel sin( ), cos( ), sqrt( ) usw. Aber wie pro- grammiert man nun eigene Funktionen? Da hilft uns zum Glück der Rückgriff auf die Prozeduren. Funktionen unter- scheiden sich nur in wenigen Punkten von den Prozeduren. D.h. alle Aussagen zu Prozeduren sind auf Funktionen anwendbar. Die Unterschiede will ich anhand eines kleinen Beispiels erklären: function expo(x,y: real): real begin expo:=exp(x*ln(y)) end Ein Unterschied der sofort ins Auge springt ist das hinter der Parameter- liste, durch einen Doppelpunkt getrennt, ein Datentyp folgt. Er bezeichnet den Typ des Funktionswertes. Zugelassen sind hier einfache Daten- typen oder der Zeigertyp, auf den ich später eingehen werde. Der Funktionswert wird einfach dadurch festgelegt, daß man den Funktionsnamen als 'Funktionsvariable' benutzt. Das heißt, man schreibt den gewünschten Wert einfach in eine Variable, die den Namen der Funktion hat. Das obige Beispiel simuliert die Funktion y hoch x, die standartmäßig nicht implementiert ist. Wie aber viel- leicht aus dem Mathematikunterricht bekannt ist, kann man y hoch x auch anders berechnen. e hoch x*ln(y) erfüllt die gleiche Aufgabe, und diese Befehle kennt Pascal bereits. So kann man sich relativ einfach neue Funktionen erstellen die Pascal nicht direkt unterstützt. Zu Beachten ist natürlich das y nur positive Zahlen annehmen darf da der Logarithmus nur für diese Zahlen definiert ist. Die Funktion kann z.B. mit writeln(expo(3.7,2.5)) aufgerufen werden. Hier dürfen direkt Zahlen in den Funktionsaufruf eingesetzt werden, da sie Werteparameter sind und somit nicht zurückgeliefert werden. Wären x und y im Funktionskopf Referenzparameter, so müßte der Funktionsaufruf mit Variablen erfolgen ! Hier besteht ein Unterschied zu den Prozeduren. Während man bei den Pro- zeduren anstreben sollte Referenz- parameter zu benutzen, ist es bei Funktionen oft sinnvoller Werte- parameter zu verwenden. Dadurch ist es möglich direkt Werte in den Funktions- aufruf zu schreiben. Zu Beachten ist außerdem, daß der Funktionsname in der Funktion nur links des ':=' stehen darf. Steht er rechts davon so hat dies eine völlig andere Funktion, auf die ich in der nächten Ausgabe eingehen werde. Das obige Teilprogramm kann zum Beispiel unter dem Namen 'EXPO.I' abgespeichert werden. Benötigt man dann später in einem Programm den y hoch x Befehl so kann man einfach anstatt der Funktion, #i D:EXPO.I ins Programm schreiben. Das 'i' steht dabei für Include (Einfügen). Dadurch wird das eigentliche Programm kürzer und somit übersichtlicher. Man sollte jedoch nicht mehere solche Funktionen unter einem Namen z.B. 'MATHE.I' zusammen- gefaßt abspeichern und dann gegebenen- falls mit #i D:MATHE.I einbinden, da somit alle Funktionen in Maschinencode übersetzt werden, egal ob sie vom Programm verwendet werden oder nicht! Das kostet viel Zeit beim Kompilieren und macht die erstellten Files unnötig lang. Das gleiche gilt natürlich auch für Prozeduren. Zum Abschluß will ich das im Pascalkurs #7 vorgestellte Programm zur Sinusbe- rechnung mit Hilfe von Funktionen auf- bauen. Die Reihenentwicklung von Sinus lautet: Summe n von 0 bis unendlich n (-1) 2n+1 ------- * x (2n+1)! Es wird also eine Funktion y hoch x benötigt, bei der y eine reele und x eine natürliche Zahl ist. Außerdem wird noch die Fakultäts- funktion benötigt. Die oben eingeführte Funktion 'expo' ist leider nicht in der Lage die Funktion y hoch x zu simulieren, da hier die Basis negativ ist. Folgende Funktion erledigt diese Aufgabe: function hoch(basis : real exponent: integer): real var i : integer zw: real begin zw:=1 for i:=1 to exponent do zw:=zw*basis hoch:=zw end Und nun noch die Fakulät: function fak(n: integer): real var zw: real begin zw:=1 for i:=2 to n do zw:=zw*i fak:=zw end Hier ist die Frage berechtigt, warum der Funktionswert eine real-Zahl ist, denn der Fakultätwert ist doch natürliche Zahl! Die Frage ist leicht zu beant- worten. 10! ergibt z.B. den Wert 3628800, also eine Zahl die zu groß für Integer-Zahlen ist die ja nur im Bereich von -32768 bis 32767 sein dürfen. Aber nun das Programm: program sinus2 var n, k : integer x, si, i: real #i D:FAK.I #i D:HOCH.I begin i:=-1 write('Wieviel Summierungen : ') readln(n) write('Welches Argument : ') readln(x) writeln si:=x for k:=1 to n do si:=si + hoch(i,k) * hoch(x,2*k+1) / fak(2*k+1) writeln('Sinus(', x:6:5, ') = ', sin(x):6:5) writeln writeln('Summenwert : ', si:6:5) writeln('Differenz : ', abs(si-sin(x)):6:5) end. Mit #i D:FAK.I und #i D:HOCH.I werden die obigen Funktion eingebunden. Das setzt natürlich voraus, daß sie unter diesem Namen abgespeichert wurden. Das auf der Diskette vorhandene File 'SINUS2' hat der Einfachheit halber die Funktionen direkt im Programm. Zum Programm will ich nur noch zwei Dinge erwähnen. Im Programm heißt es hoch(i,k) obwohl es ja einfacher wäre zu schreiben hoch(-1,k). Die erste Stelle ist ein Werteparameter, von daher ist es korrekt, direkt eine Zahl einzusetzen. Jedoch muß auch der Variblentyp übereinstimmen. 'basis' ist eine real-Zahl, -1 aber eine Integer- Zahl. Also muß man entweder direkt hoch(-1.0,k) (Simulation einer real- Zahl) schreiben oder, wie im Programm gemacht, eine Integervariable dafür verwenden. Die einzige Wandlung von Datentypen die Pascal standartmäßig toleriert, ist die direkte Wandlung von Integer zu real-Zahlen. Z.B. 'b:=a', wobei b eine real, und a eine integer- Zahl ist. Dies funktioniert aber nur direkt und nicht bei Variablenübergaben von Unterprogrammen ! Zum zweiten muß man darauf achten nicht mehr als 34 Summierungen anzugeben. Größere Werte führen dazu das der Befehl 'fak' den Bereich der real- Zahlen verläßt, und den Rechner 'abstürzen' läßt. So das war's für dieses Mal. Im nächsten Pascal-Kurs werde ich ein Programm zur Bestimmung von Nullstellen bei quadratischen Funktionen vorstellen das nicht nur als Demo gedacht ist. Neu einführen werde ich lokale und rekursive Prozeduren bzw. Funktionen.