Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas Last Changed: Nov. 19, 1997
This is a conversion from Oberon text to HTML. The converter software is still under development, and some features or information may be missing in this converted version. HTML hypertext facilities are not yet active in this document. To exploit the interactive facilities, use Oberon System 3 and the source of this text, available as ASCII-coded Oberon System 3 documents. A previous version is also available for Oberon V4. To access this and other additional material use ftp.
For the convenience of our students, most of this information and the related material is in German. Sorry if this is not one of your languages.

Einführung in die Programmiersprache Oberon.

G. Sawitzki <gs@statlab.uni-heidelberg.de>



07 Prozeduren, Funktionen

Durch Prozeduren und Funktionen können Module strukturiert werden. Eine Prozedur fasst eine Gruppe von Anweisungen in einem Block zusammen und macht sie unter einem Namen verfügbar, unter dem dieser Anweisungsblock aktiviert werden kann. Eine Prozedur wird durch eine Prozedurdeklaration eingeführt. Diese kann neben dem Anweisungsblock auch formale Parameter und lokale Deklarationen enthalten. Aufgerufen wird eine Prozedur durch ihren Namen, evtl. gefolgt von einer aktuellen Parameterliste.

Wie bei allen Deklarationen kann auch bei Prozedur-Deklarationen eine Export-Markierung beigefügt werden. Es gelten die übliche Regeln: innerhalb des Moduls, in dem die Prozedur deklariert ist, wird eine Prozedur unter ihrem Namen aufgerufen. Soll sie in einem anderen Modul aufgerufen werden, so das Modul, in dem die Prozedur deklariert ist, importiert werden, und die Prozedur wird mit mit ihrem qualifizierten Namen Modulname.Prozedurname aufgerufen.
Prozeduraufruf:
    Prozedurname [aktuelle Parameterliste ]
aktuelle Parameterliste:
    ( [Parameter {, Parameter }] )

Wir haben Prozeduraufrufe bereits kennengelernt. Die Anweisung System.Time ruft die Prozedur Time im Modul System auf. Dies ist eine parameterlose Prozedur. Die Anweisung Texts.WriteString(w,'Hallo') ruft die Prozedur WriteString im Modul Texts auf. Dabei werden zwei Parameter übergeben: der Writer w und der String 'Hallo'. Parameter können auf zwei Arten übergeben werden. Für Parameter wie den String in diesem Beispiel muss tatsächlich der Wert (in diesem Fall 'Hallo') übergeben werden. Ist beim Aufruf der aktuelle Parameter ein Ausdruck, so wird zunächst der Ausdruck ausgewertet und das Resultat als Parameter übergeben, zum Beispiel bei einem Aufruf Texts.WriteInt(w,i+j,10). Für andere Parameter wie den Writer w soll lediglich eine Referenz auf den aktuellen Parameter übergeben werden. Die aufgerufene Prozedur soll tatsächlich mit dem aktuellen Writer arbeiten und ihn gegebenenfalls auch verändern.

Bei der Prozedurdeklaration wird festgelegt, welche Parameter als Wert und welche als Referenz übergeben werden. Parameter, die als Referenz übergeben werden, sind mit VAR gekennzeichnet. Beim Aufruf müssen hier Variable als aktuelle Parameter eingesetzt werden. Parameter, die nicht mit VAR gekennzeichnet sind, werden als Wert übergeben. Hier können als aktuelle Parameter auch Konstanten oder Ausdrücke eingesetzt werden. Implizit wird für jeden Wert-Parameter eine lokale Variable erzugt, die nur innerhalb der Prozedur sichtbar ist und nur dort seine Gültigkeit hat. Der aktuelle Parameter wird bei Prozeduraufruf ausgewertet und das Resultat wird in dieser impliziten lokalen Variablen übergeben.

Prozedur-Syntax:
  PROCEDURE [Ziel] Prozedur-Name [formale Parameterliste];
    Deklarations-Folge
  [BEGIN
    Anweisungs-Folge  ]
  END
Prozedur-Name;

formale Parameterliste
   ( [ formaler Parameter { ; formaler Parameter } ] ) [ : Ergebnistyp ]
formaler Parameter
  [ VAR ] Name { , Name} : Typ

Für Texts.WriteString und Texts.WriteInt sind die Deklarationsköpfe im Modul Texts entsprechend
  PROCEDURE WriteString*(VAR W: Writer; s: ARRAY OF CHAR);
  PROCEDURE WriteInt*(VAR W: Writer; x, n: LONGINT);

Beim Prozeduraufruf werden die aktuellen Parameter den formalen ihrer Position in der Liste nach zugeordnet. Bei Wert-Parametern werden die entsprechenden aktuellen Ausdrücke ausgewertet und das Ergebnis dem jeweiligen formalen Parameter zugewiesen, die dann lokale Variable darstellen. Die aktuelle Parameterliste muss der formalen entsprechen.

Oberon-2 ergänzt die Oberon-Syntax durch einen optionalen Parameter, den Zielparameter. Dieser kann vor dem Prozedurnamen angegeben werden. Der Zielparameter wird besonders behandelt, um einen bestimmten Programmierstil der objektorientierten Programmierung zu unterstützen. Wir kommen darauf in einem späteren Kapitel zurück.

Der Deklarationsteil einer Prozedur entspricht im wesentlichen dem eines Moduls. Jedoch können Deklarationen aus einer Prozedur nicht exportiert werden. Prozeduren definieren lokale Sichtbarkeits- oder Gültigkeitsbereiche. Der Gültigkeitsbereich beginnt beim Ort der Deklaration und erstreckt sich bis zum Ende der Prozedur. Namen dürfen nur innerhalb des Gültigkeitsbereichs ihrer Deklaration benutzt werden. Kein Name darf sich innerhalb seines Gültigkeitsbereichs auf mehr als ein Objekt beziehen. Die Gültigkeitsbereich können jedoch geschachtelt sein: eine (Unter-)prozedur definiert einen neuen Gültigkeitsbereich. Wird ein Name deklariert, der bereits im umgebenden Block definiert ist, so gilt bis zum Ende der Prozedur die lokale Deklaration; die vorherige wird damit bis zum Ende des lokalen Blocks unsichtbar.

Der Anweisungsteil entspricht dem eines Moduls. Er wird dann ausgeführt, wenn die Prozedur aufgerufen wird. Es gibt keine automatische Initialisierung von Prozeduren. Lokale Variable haben zu Beginn der Prozedur undefinierte Werte und hören mit dem Ende der Prozedur auf zu existieren: die Lebensdauer der lokalen Variablen endet mit der Ausführungsdauer der Prozedur. Im Gegensatz dazu bleiben globale Variable eines Moduls erhalten, bis das Modul explizit entladen wird.

Eine Prozedur kann durch eine RETURN-Anweisung verlassen werden. Eine Prozedur kann mehr als eine RETURN-Anweisung enthalten. Prozeduren können einen Ergebnistyp haben; man spricht dann von Funktionsprozeduren, zur Unterscheidung von eigentlichen Prozeduren. Funktionsprozeduren müssen mit einer RETURN-Anweisung verlassen werden. Bei Funktionsprozeduren muss auf die RETURN-Anweisung ein Ausdruck folgen, der verträglich mit dem Ergebnistyp ist. Dieser Ausdruck wird ausgewertet und das Ergebnis als Resultat der Funktionsprozedur übergeben.
RETURN-Anweisung
  RETURN
{ Ausdruck }

Beispiel
  PROCEDURE MIN( a,b:REAL): REAL;
  BEGIN
    IF a<b THEN RETURN a ELSE RETURN b END
  END MIN;

Eine Prozedur kann auch in ihrem eigenen Anweisungsteil aufgerufen werden. Dies führt dann zu einer Rekursion. Von der Schreibweise ist dies in der Regel eine sehr kompakte Darstellung. Von der Ausführung bedeutet ein Prozeduraufruf aber, dass Platz für lokale Variable bereitgestellt werden muss, die Kontrolle an eine Kopie der Prozedur übergeben werden muss, und eine gesicherte Rückkehr zur aufrufenden Kopie vorbereitet werden muss. In der Regel ist dies aufwendiger, als eine sequentielle Ausführung von Anweisungen. Wenn Rekursionen vermieden werden können, sollte dies getan werden. Insbesondere kann eine Rekursion immer dann vermieden werden, wenn sie durch die letzte Anweisung einer Anweisungsfolge ausgelöst wird.




Übungen:

1.) In vielen Programmbibliotheken gibt es Prozeduren zu Berechnung einer Potenzfunktion. Wie würden Sie eine Funktionsprozedur XPwrI programmieren,
  die für x>=0, i>=0 die Potenz xi berechnet.
  PROCEDURE XPwrI(x:LONGREAL; i:LONGINT): LONGREAL;

2.) Schreiben Sie die drei Generatoren aus dem vorherigen Kapitel als Funktionsprozeduren RandLGM, RandPRB, RandUNIX und testen Sie diese Prozeduren.

3a.) Schreiben Sie einen Pseudo-Zufallszahlengenerator, der eine uniforme Verteilung auf [0,1] simuliert. Benutzen Sie dazu RandLGM. Implementieren Sie diesen Generator als Funktionsprozedur.
3b.) Schreiben Sie eine Funktionsprozedur, die exponentialverteilte Pseudo-Zufallszahlen erzeugt. Benutzen Sie dazu den Generator aus 3a. Die Rate soll dabei ein Parameter der Funktionsprozedur sein.

4.) Programmieren Sie die Simulation einer einfachen Warteschlange mit exponentieller Ankunfts- und Bedienzeit. Simulieren Sie dann den Ablauf für 20 Kunden mit a) Ankuftsrate=Bedienrate; b) Ankuftsrate=Bedienrate/2.

5.) Programmieren Sie eine "Schönschreibprozedur", die -werteabhängig- eine reelle Zahl in einem gut lesbaren Format ausschreibt. Beispiel:
  Wert    Ausgabe
  0.0E0     0
  1.2E1     12
  1.2345E2  123.45
Falls Sie dazu mathematische Funktionen brauchen, finden Sie diese evtl. im Modul Math. Hinweis: Sie sollten für diese Aufgabe einen Font mit fester Zeichenbreite wählen.


Übungen:
Vergleichen Sie die beiden folgenden Implementierungen der Fakultätsfunktion. Welchen Aufwand bedeuten sie?

PROCEDURE Fak1(i:INTEGER):LONGINT;
BEGIN
  ASSERT(i>=0);
  IF i<=1 THEN RETURN 1
  ELSE RETURN i*Fak1(i-1)
  END;
END Fak2;

PROCEDURE Fak2(i:INTEGER):LONGINT;
VAR res:LONGINT;
BEGIN
  ASSERT(i>=0);
  res:=1;   WHILE i>1 DO res:=res*i; DEC(i) END;
  RETURN res
END Fak2;



Eine Reihe von Prozeduren sind in Oberon vordefiniert. Eine Liste der vordefinierten Prozeduren finden Sie im Oberon-Report. Die nächste Quelle sind exportierte Prozeduren in bereits vorhandenen Modulen. Nur der letzte Ausweg ist, Prozeduren selbst neu zu schreiben.


Einführung in die Programmiersprache Oberon. Kurs/Kap07.Text
gs (c) G. Sawitzki, StatLab Heidelberg
<http://statlab.uni-heidelberg.de/projects/oberon/kurs/>

Home Up Intro Contents Chapter 1 2 3 4 5 6 7 8 9 10 Design Assert Timing EBNF Report Pas