#headandbottom("1","EUMEL-Benutzerhandbuch","TEIL 2 : ELAN","2")# #pagenr("%",1)##setcount(1)##block##pageblock# #headeven# #center#EUMEL-Benutzerhandbuch #center#____________________________________________________________ #end# #headodd# #center#TEIL 2 : ELAN #center#____________________________________________________________ #end# #bottomeven# #center#____________________________________________________________ 2 - % #right#GMD #end# #bottomodd# #center#____________________________________________________________ GMD #right# 2 - % #end# TEIL 2: ELAN 2.1 Besondere Eigenschaften von ELAN Kerneigenschaften von ELAN sind das #ib#Paketkonzept#ie# und die Methode des #ib#Refinements#ie#. #on("b")#Paketkonzept:#off("b")# ELAN bietet die Möglichkeit, neue Datentypen sowie Prozeduren und Operatoren auf diesen Datentypen zu definieren. Eine solche Definition von Algorithmen und Daten­ typen kann zu einer logischen Einheit, einem Paket, zusammengefaßt werden. Pakete können in einer Task vorübersetzt werden und erweitern damit automatisch den Sprachumfang. #on("b")#Methode des Refinements:#off("b")# Die Methode des Refinements erlaubt das schrittweise Herleiten von Problemlösungen von der jeweils geeigneten Terminologie herunter zu den von ELAN standardmäßig angebotenen Sprachelementen. Durch diese Vorgehensweise wird in äußerst starkem Maße ein strukturierter Programmentwurf gemäß dem Top-Down-Prinzip gefördert. Die Programmiersprache ELAN wird im EUMEL-System zu folgenden Zwecken eingesetzt: - Systemimplementationssprache - Kommandosprache - Anwenderprogrammiersprache #page# 2.2 Lexikalische Elemente Unter lexikalischen Elementen einer Programmiersprache versteht man die Elemente, in denen ein Programm notiert wird. In ELAN sind dies: - Schlüsselwörter - Bezeichner - Sonderzeichen - Kommentare 2.2.1 Schlüsselwörter Einige Wörter haben in ELAN eine feste Bedeutung und können somit nicht frei gewählt werden. Solche Wörter werden im EUMEL-System in Großbuchstaben geschrieben, Leerzeichen dürfen nicht enthalten sein. Beispiele: VAR INT WHILE Wie später beschrieben wird, gibt es in ELAN auch die Möglichkeit, neue Schlüssel­ wörter einzuführen. #page# 2.2.2 Bezeichner Bezeichner oder Namen werden benutzt, um Objekte in einem Programmtext zu benennen und zu identifizieren (z.B: Variablennamen, Prozedurnamen). Namen werden in ELAN folgendermaßen formuliert: Das erste Zeichen eines Namens muß immer ein Kleinbuchstabe sein. Danach dürfen bis zu 254 Kleinbuchstaben, aber auch Ziffern folgen. Zur besseren Lesbarkeit können Leerzeichen in einem Namen erscheinen, die aber nicht zum Namen zählen. Sonder­ zeichen sind in Namen nicht erlaubt. Beispiele für #on("b")#korrekte#off("b")# Bezeichner: das ist ein langer name x koordinate nr 1 Beispiele für #on("b")#falsche#off("b")# Bezeichner: x*1 1 exemplar Nr 1 #page# 2.2.3 Sonderzeichen Sonderzeichen sind Zeichen, die weder Klein- oder Großbuchstaben, noch Ziffern sind. Sie werden in ELAN als Trennzeichen oder als Operatoren benutzt. In ELAN gibt es folgende Trennungszeichen: - das Semikolon (';') trennt Anweisungen - der Doppelpunkt (':') trennt Definiertes und Definition - der Punkt ('.') wird als Endezeichen für bestimmte Programmabschnitte, als Dezi­ malpunkt und als Selektor-Zeichen für Datenstrukturen benutzt - das Komma (',') trennt Parameter - Klammernpaare ('(', ')') werden zum Einklammern von Parameterlisten oder Teil­ ausdrücken benutzt - mit Anführungszeichen ('"') werden Text-Denoter umrahmt - eckige Klammernpaare ('[', ']') werden zur Subskription benutzt. Als Operatornamen sind folgende Sonderzeichen erlaubt: - ein Sonderzeichen, sofern es nicht als Trennzeichen benutzt wird: ! $ % & ' * + - / < = > ? § ^ ' ~ - eine Kombination von zwei Sonderzeichen. Diese Kombination muß jedoch bereits in ELAN existieren: := <= >= <> ** #page# 2.2.4 Kommentare Kommentare dienen ausschließlich der Dokumentation eines Programms. Sie werden vom Compiler überlesen und haben somit keinen Einfluß auf die Ausführung eines Programms. Sie dürfen an beliebigen Stellen eines Programmtextes geschrieben werden, jedoch nicht innerhalb von Schlüsselwörtern und Namen. Ein Kommentar darf über mehrere Zeilen gehen. In ELAN sind Kommentare nur in wenigen Fällen notwen­ dig, da Programme durch andere Mittel gut lesbar geschrieben werden können. Ein Kommentar in ELAN wird durch Kommentarklammern eingeschlossen. Es gibt folgende Formen von Kommentarklammern: (* Kommentar *) { Kommentar } \#( Kommentar )# Die letzte Version '\#( Kommentar )\#' wird im EUMEL-System nicht unterstützt; statt dessen gibt es noch folgende Möglichkeit: \# Kommentar \# Da bei der Kommentarkennzeichnung mit \# für Kommentaranfang und -ende das gleiche Zeichen benutzt wird, ist eine Schachtelung hier nicht möglich. #page# 2.3 Datenobjekte Eine Klasse von Objekten mit gleichen Eigenschaften wird in Programmiersprachen Datentyp genannt. Dabei hat ein Datentyp immer einen Namen, der die Klasse von Objekten sinnvoll kennzeichnet. Als ein Datenobjekt wird ein Exemplar eines Daten­ typs (also ein spezielles Objekt einer Klasse) bezeichnet. Datentypen sind in ELAN ein zentrales Konzept. Jedes der in einem ELAN- Programm verwandten Datenobjekte hat einen Datentyp; somit kann man Datentypen auch als Eigenschaften von Datenobjekten ansehen. Für jeden Datentyp sind nur spezielle Operationen sinnvoll. Man kann nun Compilern die Aufgabe überlassen zu überprüfen, ob stets die richtige Operation auf einen Datentyp angewandt wird. 2.3.1 Elementare Datentypen Einige Datentypen spielen bei der Programmierung eine besondere Rolle, weil sie häufig benötigt werden. In ELAN sind das die Datentypen für - ganze Zahlen (INT) - reelle Zahlen (REAL) - Zeichen und Zeichenfolgen (TEXT) - Wahrheitswerte (BOOL). Diese Datentypen sind von der Sprache ELAN vorgegeben und werden elementare Datentypen genannt. Für effiziente Rechnungen mit elementaren Datentypen gibt es in den meisten Rechnern spezielle Schaltungen, so daß die Hervorhebung und be­ sondere Rolle, die sie in Programmiersprachen spielen, gerechtfertigt ist. Zudem hat man Werte-Darstellungen (Denoter) innerhalb von Programmen für die elementaren Datentypen vorgesehen. 2.3.1.1 Denoter für elementare Datentypen Die Darstellung eines Werts in einem Rechner zur Laufzeit eines Programms wird Repräsentation genannt. Wenn es eindeutig ist, daß es sich nur um die Repräsenta­ tion im Rechner handelt, spricht man kurz von Werten. Um mit Objekten elementarer Datentypen arbeiten zu können, muß es in einem Programm die Möglichkeit geben, Werte eines Datentyps zu bezeichnen (denotieren). Die Werte-Darstellungen, die in ELAN Denoter genannt werden, sind für jeden Datentyp unterschiedlich. Wie bereits erwähnt, haben alle Datenobjekte in ELAN (also auch Denoter) nur einen - vom Compiler feststellbaren - Datentyp. Aus der Form eines Denoters ist also der Daten­ typ erkennbar: INT-Denoter: Sie bestehen aus einer Aneinanderreihung von Ziffern. Beispiele: 17 007 32767 0 Führende Nullen spielen bei der Bildung des Wertes keine Rolle (sie werden vom ELAN-Compiler überlesen). Negative INT-Denoter gibt es nicht. Negative Werte werden durch eine Aufeinanderfolge des monadischen Operators '-' (siehe 2.4.1.1) und eines INT- Denoters realisiert. REAL-Denoter: Hier gibt es zwei Formen: Die erste besteht aus zwei INT-Denotern, die durch einen Dezimalpunkt getrennt werden. Beispiele: 0.314159 17.28 Der Dezimalpunkt wird wie ein Komma in der deutschen Schreibweise benutzt. Nega­ tive REAL-Denoter gibt es wiederum nicht. Die zweite Form wird als "wissenschaftliche Notation" bezeichnet. Sie findet dann Verwendung, wenn sehr große Zahlen oder Zahlen, die nahe bei Null liegen, darge­ stellt werden müssen. Beispiele: 3.0 e5 3.0e-5 Der INT-Denoter hinter dem Buchstaben #on("b")#e#off("b")# gibt an, wie viele Stellen der Dezimal­ punkt nach rechts (positive Werte) bzw. nach links (negative Werte) zu verschieben ist. Dieser Wert wird Exponent und der Teil vor dem Buchstaben #on("b")#e#off("b")# Mantisse genannt. TEXT-Denoter: Sie werden in Anführungszeichen eingeschlossen. Beispiele: "Das ist ein TEXT-Denoter" "Jetzt ein TEXT-Denoter ohne ein Zeichen: ein leerer Text" "" Zu beachten ist, daß das Leerzeichen ebenfalls ein Zeichen ist. Soll ein Anführungs­ zeichen in einem TEXT erscheinen (also gerade das Zeichen, welches einen Denoter beendet), so muß es doppelt geschrieben werden. Beispiele: "Ein TEXT mit dem ""-Zeichen" "Ein TEXT-Denoter nur mit dem ""-Zeichen:" """" Manchmal sollen Zeichen in einem TEXT-Denoter enthalten sein, die auf dem Eingabegerät nicht zur Verfügung stehen. In diesem Fall kann der Code-Wert des Zeichens angegeben werden. Beispiel: "da"251"" ist gleichbedeutend mit "daß". Der Code-Wert eines Zeichens ergibt sich aus der EUMEL-Code-Tabelle (siehe 5.2.4.1), in der jedem Zeichen eine ganze Zahl zuge­ ordnet ist. BOOL-Denoter: Es gibt nur zwei BOOL-Denoter: TRUE für "wahr" und FALSE für "falsch". 2.3.1.2 LET-Konstrukt für Denoter Neben der Funktion der Abkürzung von Datentypen (siehe 2.6.3) kann das LET- Konstrukt auch für die Namensgebung für Denoter verwandt werden. Die LET-Vereinbarung sieht folgendermaßen aus: #on("i")##on("b")#LET#off("i")##off("b")# Name #on("i")##on("b")#=#off("i")##off("b")# Denoter Mehrere Namensgebungen können durch Komma getrennt werden. ____________________________________________________________________________ .......................... Beispiele: ......................... LET anzahl = 27; LET pi = 3.14159, blank = " "; ____________________________________________________________________________ Der Einsatz von LET-Namen für Denoter hat zwei Vorteile: - feste Werte im Programm sind leicht zu ändern, da nur an einer Stelle des Pro­ gramms der Denoter geändert werden muß (z.B.: In Vereinbarungen von Reihungen (siehe 2.6.1) können LET-Denoter, im Gegensatz zu Konstanten, als Obergrenze angegeben werden. Dieser Wert kann dann auch an anderen Stellen des Programms, z.B. in Schlei­ fen (siehe 2.4.2.5), benutzt werden. Bei Änderung der Reihungsgröße braucht dann nur an einer Stelle des Programms der Wert geändert zu werden.) - der Name gibt zusätzliche Information über die Bedeutung des Denoters. 2.3.2 Zugriffsrecht Von manchen Datenobjekten weiß man, daß sie nur einmal einen Wert erhalten sollen. Sie sollen also nicht verändert werden. Oder man weiß, daß in einem Pro­ grammbereich ein Datenobjekt nicht verändert werden soll. Um ein unbeabsichtigtes Verändern zu verhindern, wird in ELAN dem Datenobjekt ein zusätzlicher Schutz mitgegeben: das Zugriffsrecht oder Accessrecht. In der Deklaration eines Datenobjekts können folgende Accessattribute angegeben werden: - #on("i")##on("b")#VAR #off("i")##off("b")# für lesenden und schreibenden (verändernden) Zugriff - #on("i")##on("b")#CONST#off("i")##off("b")# für nur lesenden Zugriff. 2.3.3 Deklaration Damit man Datenobjekte in einem Programm ansprechen kann, gibt man einem Datenobjekt einen Namen (wie z.B. einen Personennamen, unter der sich eine wirk­ liche Person "verbirgt"). Will man ein Datenobjekt in einem Programm verwenden, so muß man dem Compiler mitteilen, welchen Datentyp und welches Accessrecht das Objekt haben soll. Das dient u.a. dazu, nicht vereinbarte Namen (z.B. verschriebene) vom Compiler entdecken zu lassen. Weiterhin ist aus dem bei der Deklaration ange­ gebenen Datentyp zu entnehmen, wieviel Speicherplatz für das Objekt zur Laufzeit zu reservieren ist. Eine Deklaration oder Vereinbarung besteht aus der Angabe von - Datentyp - Zugriffsrecht ( #on("i")##on("b")#VAR#off("i")##off("b")# oder #on("i")##on("b")#CONST#off("i")##off("b")#) - Name des Datenobjekts. ____________________________________________________________________________ ........................... Beispiel: ......................... INT VAR mein datenobjekt; ____________________________________________________________________________ Verschiedene Datenobjekte mit gleichem Datentyp und Accessrecht dürfen in einer Deklaration angegeben werden; sie werden durch Kommata getrennt. Mehrere Dekla­ rationen werden - genauso wie Anweisungen - durch das Trennzeichen Semikolon voneinander getrennt. ____________________________________________________________________________ .......................... Beispiele: ......................... INT VAR mein wert, dein wert, unser wert; BOOL VAR listen ende; TEXT VAR zeile, wort; ____________________________________________________________________________ 2.3.4 Initialisierung Um mit den vereinbarten Datenobjekten arbeiten zu können, muß man ihnen einen Wert geben. Hat ein Datenobjekt noch keinen Wert erhalten, so sagt man, sein Wert sei undefiniert. Das versehentliche Arbeiten mit undefinierten Werten ist eine beliebte Fehlerquelle. Deshalb wird von Programmierern streng darauf geachtet, diese Fehler­ kuelle zu vermeiden. Eine Wertgebung an ein Datenobjekt kann (muß aber nicht) bereits bei der Deklaration erfolgen. In ELAN wird dies Initialisierung genannt. Für mit CONST vereinbarte Datenobjekte ist die Initialisierung die einzige Möglichkeit, ihnen einen Wert zu geben. Die Initialisierung von Konstanten ist zwingend vorgeschrieben und wird vom Compiler überprüft. Die Initialisierung besteht aus der Angabe von - Datentyp - Zugriffsrecht ( #on("i")##on("b")#VAR#off("i")##off("b")# oder #on("i")##on("b")#CONST#off("i")##off("b")#) - Name des Datenobjekts - Operator #on("i")##on("b")#::#off("i")##off("b")# oder #on("i")##on("b")#:=#off("i")##off("b")# - Wert, den das Datenobjekt erhalten soll (Denoter, Ausdruck). ____________________________________________________________________________ .......................... Beispiele: ......................... INT CONST gewuenschtes gehalt :: 12 000; TEXT VAR zeile :: ""; REAL CONST pi :: 3.14159, zwei pi := 2.0 * pi; BOOL VAR bereits sortiert :: TRUE; ____________________________________________________________________________ #page# 2.4 Programmeinheiten Neben Deklarationen (Vereinbarungen) sind Programmeinheiten die Grundbestandteile von ELAN. Programmeinheiten können sein: #on("b")#- elementare Programmeinheiten #off("b")# - Ausdruck - Zuweisung - Refinementanwendung - Prozeduraufruf #on("b")#- zusammengesetzte Programmeinheiten #off("b")# - Folge - Abfrage - Auswahl - Wiederholung #on("b")#- abstrahierende Programmeinheiten #off("b")# - Refinementbvereinbarung - Prozedurvereinbarung - Operatorvereinbarung - Paketvereinbarung. #page# 2.4.1 Elementare Programmeinheiten 2.4.1.1 Ausdruck Ausdrücke sind eine Zusammenstellung von Datenobjekten (Denoter, VAR- oder CONST-Objekte) und Operatoren. Jeder korrekte Ausdruck liefert einen Wert. Der Typ des Ausdrucks wird bestimmt durch den Typ des Wertes, den der Ausdruck liefert. Operatoren Operatoren werden in ELAN durch ein oder zwei Sonderzeichen oder durch Groß­ buchstaben als Schlüsselwort dargestellt (siehe 2.4.3.3). Als Operanden (also die Datenobjekte, auf die ein Operator "wirken" soll) dürfen VAR- und CONST-Datenobjekte, Denoter oder Ausdrücke verwendet werden. Typ der Operanden und des Resultats eines Operators werden in der Operatorvereinba­ rung festgelegt (siehe 2.4.3.3). Man unterscheidet zwei Arten von Operatoren: #on("b")#- monadische Operatoren #off("b")# Monadischen Operatoren haben nur einen Operanden, der rechts vom Operator­ zeichen geschrieben werden muß. Beispiel: - a NOT x Der '-' - Operator liefert den Wert von a mit umgekehrten Vorzeichen. a muß dabei vom Datentyp INT oder REAL sein. Der Operator 'NOT' realisiert die logische Negation. y muß vom Datentyp BOOL sein. #on("b")#- dyadische Operatoren #off("b")# Dyadische Operatoren haben zwei Operanden. Das Operatorzeichen steht zwi­ schen den beiden Operanden. Beispiele: a + b a - b a * b a DIV b a ** b x < y x <> y x AND y x OR y In den ersten fünf Beispielen werden jeweils die Werte von zwei INT-Objekten a und b addiert (Operatorzeichen: '+'), subtrahiert ('-'), multipliziert ('*'), dividiert (ganzzahlige Division ohne Rest: 'DIV') und potenziert ('**'). Im sechsten und siebten Beispiel werden zwei BOOL-Werte x und y verglichen und im achten und neunten Beispiel die logische Operation 'und' (Operator 'AND') bzw. 'oder' (Operator 'OR') durchgeführt. Priorität von Operatoren Es ist erlaubt, einen Ausdruck wiederum als Operanden zu verwenden. Praktisch bedeutet dies, daß mehrere Operatoren und Datenobjekte zusammen in einem Aus­ druck geschrieben werden dürfen. Beispiele: a + 3 - b * c - a * b Die Reihenfolge der Auswertung kann man durch Angabe von Klammern steuern. Beispiel: (a + b) * (a + b) Es wird jeweils erst 'a + b' ausgewertet und dann erst die Multiplikation durchge­ führt. In ELAN ist es erlaubt, beliebig viel Klammernpaare zu verwenden (Regel: die innerste Klammer wird zuerst ausgeführt). Es ist sogar zulässig, Klammern zu ver­ wenden, wo keine notwendig sind, denn überflüssige Klammernpaare werden überle­ sen. Man muß jedoch beachten, daß Ausdrücke, und damit auch z.B. #on("b")#(a)#off("b")#, immer Accessrecht CONST haben. Beispiel: ((a - b)) * 3 * ((c + d) * (c - d)) Somit können beliebig komplizierte Ausdrücke formuliert werden. Um solche Ausdrücke einfacher zu behandeln und sie so ähnlich schreiben zu kön­ nen, wie man es in der Mathematik gewohnt ist, wird in Programmiersprachen die Reihenfolge der Auswertung von Operatoren festgelegt. In ELAN wurden neun Ebe­ nen, Prioritäten genannt, festgelegt: #on("bold")#Priorität Operatoren #off("bold")# 9 alle monadischen Operatoren 8 ** 7 *, /, DIV, MOD 6 +, - 5 =, <>, <, <=, >, >= 4 AND 3 OR 2 alle übrigen, nicht in dieser Tabelle aufgeführten dyadischen Operatoren 1 := (Die erwähnten Operatoren in der Tabelle werden in der Beschreibung der Standard­ prozeduren und -Operatoren besprochen). Operatoren mit der höchsten Priorität werden zuerst ausgeführt, dann die mit der nächst niedrigeren Priorität usw.. Operatoren mit gleicher Priorität werden von links nach rechts ausgeführt. Dadurch ergibt sich die gewohnte Abarbeitungsfolge wie beim Rechnen. Beispiel: -2 + 3 * 2 ** 3 a) -2 b) 2 ** 3 c) 3 * (2 ** 3) d) ((-2)) + (3 * (2 ** 3)) Wie bereits erwähnt, ist es immer erlaubt, Klammern zu setzen. Ist man sich also über die genaue Abarbeitungsfolge nicht im Klaren, so kann man Klammern verwen­ den. 2.4.1.2 Zuweisung Ein spezieller Operator ist die Zuweisung. Form: Variable #on("i")##on("b")#:=#off("i")##off("b")# Wert Dieser Operator hat immer die geringste Priorität, wird also immer als letzter einer Anweisung ausgeführt. Die Zuweisung wird verwendet, um einer Variablen einen neuen Wert zu geben. Der Operator ':=' liefert kein Resultat (man sagt auch, er liefert keinen Wert) und verlangt als linken Operanden ein VAR-Datenobjekt, an den der Wert des rechten Operanden zugewiesen werden soll). Der Wert des linken Oper­ anden wird also verändert. Der rechte Operand wird nur gelesen. ____________________________________________________________________________ ........................... Beispiel: ......................... a := b; ____________________________________________________________________________ Hier wird der Wert von 'b' der Variablen 'a' zugewiesen. Der vorher vorhandene Wert von 'a' geht dabei verloren. Man sagt auch, der Wert wird überschrieben. Als rechter Operand des ':='-Operators darf auch ein Ausdruck stehen. ____________________________________________________________________________ ........................... Beispiel: ......................... a := b + c; ____________________________________________________________________________ Hier wird das Resultat von 'b + c' an die Variable 'a' zugewiesen. Man beachte dabei die Prioritäten der Operatoren '+' (Priorität 6) und ':=' (Priorität 1): die Addition wird vor der Zuweisung ausgeführt. Die Auswertung von Zuweisungen mit Ausdrücken muß immer so verlaufen, da die Zuweisung stets die niedrigste Priorität aller Operato­ ren hat. Oft kommt es vor, daß ein Objekt auf der linken und rechten Seite des Zuweisungs­ operators erscheint, z.B. wenn ein Wert erhöht werden soll. ____________________________________________________________________________ ........................... Beispiel: ......................... a := a + 1; ____________________________________________________________________________ Hier wird der "alte", aktuelle Wert von 'a' genommen, um '1' erhöht und dem Objekt 'a' zugewiesen. Man beachte, daß hier in einer Anweisung ein Datenobjekt unter­ schiedliche Werte zu unterschiedlichen Zeitpunkten haben kann. 2.4.1.3 Refinementanwendung In ELAN ist es möglich, Namen für Ausdrücke oder eine bzw. mehrere Anweisungen zu vergeben. Das Sprachelement, das diese Namensgebung ermöglicht, heißt Refi­ nement. Die Ausführung eines solchen Namens heißt Refinementanwendung, die Namensgebung heißt Refinementvereinbarung (siehe 2.4.3.1). Die Ausdrücke oder Anweisungen bilden den Refinementrumpf. Ein Refinement kann man in einem Pro­ gramm unter dem Refinementnamen ansprechen. Man kann sich die Ausführung so vorstellen, als würden der Refinementrumpf immer dort eingesetzt, wo der Name des Refinements als Operation benutzt wird. 2.4.1.4 Prozeduraufruf Eine Prozedur ist eine Sammlung von Anweisungen und Daten, die zur Lösung einer bestimmten Aufgabe benötigt werden. Eine Prozedur wird in einer Prozedurvereinba­ rung definiert (siehe 2.4.3.2). Eine solche Prozedur kann man in einem Programm unter einem Namen (eventuell unter Angabe von Parametern) ansprechen. Man spricht dann vom Aufruf einer Prozedur oder einer Prozeduranweisung. Formen des Prozeduraufrufs: - #on("b")#Prozeduren ohne Parameter#off("b")# werden durch den Prozedurnamen angesprochen. ____________________________________________________________________________ ........................... Beispiel: ......................... pause; ____________________________________________________________________________ (Die Prozedur 'pause' wartet bis ein Zeichen eingegeben wird) - #on("b")#Prozeduren mit Parameter#off("b")# werden durch Prozedurnamen #on("i")##on("b")#(#off("i")##off("b")# aktuelle Parameterliste #on("i")##on("b")#)#off("i")##off("b")# aufgerufen. Eine Parameterliste ist entweder ein Datenobjekt oder mehrere durch Kommata getrennte Datenobjekte. ____________________________________________________________________________ ........................... Beispiel: ......................... pause (10); ____________________________________________________________________________ (Mit der Prozedur 'pause (INT CONST zeitgrenze)' kann für eine Zeitdauer von 'zeitgrenze' in Zehntel-Sekunden gewartet werden. Die Wartezeit wird durch Erreichen der Zeitgrenze oder durch Eingabe eines Zeichens abgebrochen) Bei den aktuellen Parametern ist folgendes zu beachten: a) Wird ein VAR-Parameter in der Definition der Prozedur vorgeschrieben, darf kein Ausdruck als aktueller Parameter "übergeben" werden, weil an einen Ausdruck nichts zugewiesen werden kann. Ausdrücke haben - wie bereits erwähnt - das Accessrecht CONST. ____________________________________________________________________________ ........................ Gegenbeispiel: ....................... TEXT VAR text1, text2; text1 := "Dieses Beispiel "; text2 := "Fehlermeldung"; insert char (text1 + text2, "liefert eine", 17); ____________________________________________________________________________ (Die Prozedur 'insert char (TEXT VAR string, TEXT CONST char, INT CONST pos)' fügt das Zeichen 'char' in den Text 'string' an der Position 'pos' ein) b) Wird ein CONST-Parameter verlangt, dann darf in diesem Fall ein Ausdruck als aktueller Parameter geschrieben werden. Aber auch ein VAR-Datenobjekt darf angegeben werden. In diesem Fall wird eine Wandlung des Accessrechts (CONSTing) vorgenommen: der aktuelle Parameter erhält sozusagen für die Zeit der Abarbeitung der Prozedur das Accessrecht CONST. In ELAN sind auch Prozeduren als Parameter erlaubt. Die Prozedur als aktueller Parameter wird in der Parameterliste folgendermaßen angegeben: Resultattyp #on("i")##on("b")#PROC#off("i")##off("b")# #on("i")##on("b")#(#off("i")##off("b")# virtuelle Parameterliste #on("i")##on("b")#)#off("i")##off("b")# Procname Die Angabe des Resultattyps entfällt, wenn es sich nicht um eine wertliefernde Prozedur handelt. Die virtuelle Parameterliste inklusive der Klammern entfällt, falls die Prozedur keine Parameter hat. Die virtuelle Parameterliste beschreibt die Parameter der Parameterprozedur. Es werden Datentyp und Zugriffsrecht eines jeden Parameters angegeben, jedoch ohne Namen. ____________________________________________________________________________ ........................... Beispiel: ......................... wertetabelle (REAL PROC (REAL CONST) sin, untergrenze, obergrenze, schrittweite); (Die Prozedur 'sin' wird an die Prozedur 'wertetabelle' übergeben) ____________________________________________________________________________ 2.4.2 Zusammengesetzte Programmeinheiten 2.4.2.1 Folge Mehrere in einer bestimmten Reihenfolge auszuführende Anweisungen werden als Folge bezeichnet. In ELAN kann man eine oder mehrere Anweisungen in eine Pro­ grammzeile schreiben oder eine Anweisung über mehrere Zeilen. Das setzt jedoch voraus, daß die Anweisungen voneinander getrennt werden. Die Trennung von Anwei­ sungen erfolgt in ELAN durch das Trennsymbol Semikolon. Es bedeutet soviel wie: "führe die nächste Anweisung aus". ____________________________________________________________________________ ........................... Beispiel: ......................... put ("mein"); put (1); put (". Programm") ____________________________________________________________________________ (Die Prozedur 'put' gibt den als Parameter angegebenen Wert auf dem Ausgabegerät aus) 2.4.2.2 Abfrage Mit Abfragen steuert man die bedingte Ausführung von Anweisungen. Abhängig von einer Bedingung wird in zwei verschiedene Programmabschnitte verzweigt. Der formale Aufbau einer Abfrage sieht folgendermaßen aus: #on("i")##on("b")#IF#off("i")##off("b")# Bedingung #on("i")##on("b")#THEN#off("i")##off("b")# Abschnitt #on("i")##on("b")#ELSE#off("i")##off("b")# Abschnitt #on("i")##on("b")#END IF#off("i")##off("b")# Der ELSE-Teil darf dabei auch fehlen. Anstelle von #on("i")##on("b")#END IF#off("i")##off("b")# darf auch die Abkürzung #on("i")##on("b")#FI#off("i")##off("b")# (IF von hinten gelesen) benutzt werden. In folgenden Beispielen wird der Absolutbetrag von 'a' ausgegeben: ____________________________________________________________________________ ........................... Beispiel: ......................... INT VAR a; get (a); IF a < 0 THEN a := -a END IF; put (a) ____________________________________________________________________________ Die Umkehrung des Vorzeichens von a im THEN-Teil wird nur durchgeführt, wenn der BOOLesche Ausdruck ('a < 0') den Wert TRUE liefert. Liefert er den Wert FALSE, wird die Anweisung, die der bedingten Anweisung folgt (nach END IF), ausge­ führt. Das obige Programm kann auch anders geschrieben werden: ____________________________________________________________________________ ........................... Beispiel: ......................... INT VAR a; get (a); IF a < 0 THEN put (-a) ELSE put (a) END IF ____________________________________________________________________________ Der THEN-Teil wird wiederum ausgeführt, wenn die BOOLesche Bedingung erfüllt ist. Liefert sie dagegen FALSE, wird der ELSE-Teil ausgeführt. Die bedingte Anweisung ermöglicht es, abhängig von einer Bedingung eine oder mehrere Anweisungen ausführen zu lassen. Dabei können im THEN- bzw. ELSE- Teil wiederum bedingte Anweisungen enthalten sein. Abfragekette Bei Abfrageketten kann das ELIF-Konstrukt eingesetzt werden. (ELIF ist eine Zu­ sammenziehung der Worte ELSE und IF). Anstatt ____________________________________________________________________________ ........................... Beispiel: ......................... IF bedingung1 THEN aktion1 ELSE IF bedingung2 THEN aktion2 ELSE aktion3 END IF END IF; ____________________________________________________________________________ kann man besser ____________________________________________________________________________ ........................... Beispiel: ......................... IF bedingung1 THEN aktion1 ELIF bedingung2 THEN aktion2 ELSE aktion3 END IF; ____________________________________________________________________________ schreiben. 2.4.2.3 Auswahl Die Auswahl wird benutzt, wenn alternative Anwendungen in Abhängikeit von Werten eines Datenobjekts ausgeführt werden sollen. Der formale Aufbau der Auswahl sieht folgendermaßen aus: #on("i")##on("b")#SELECT#off("i")##off("b")# INT-Ausdruck #on("i")##on("b")#OF#off("i")##off("b")# #on("i")##on("b")#CASE#off("i")##off("b")# 1. Liste von INT-Denotern #on("i")##on("b")#:#off("i")##off("b")# Abschnitt #on("i")##on("b")#CASE#off("i")##off("b")# 2. Liste von INT-Denotern #on("i")##on("b")#:#off("i")##off("b")# Abschnitt . . . #on("i")##on("b")#CASE#off("i")##off("b")# n. Liste von INT-Denotern #on("i")##on("b")#:#off("i")##off("b")# Abschnitt #on("i")##on("b")#OTHERWISE#off("i")##off("b")# Abschnitt #on("i")##on("b")#END SELECT#off("i")##off("b")# Eine Liste von INT-Denotern besteht aus einem oder mehreren durch Kommata ge­ trennten INT-Denotern. Der OTHERWISE-Teil darf auch fehlen. Man sollte ihn jedoch verwenden, um Fehlerfälle abzufangen. ____________________________________________________________________________ ........................... Beispiel: ......................... SELECT monat OF CASE 2: IF schaltjahr THEN tage := 29 ELSE tage := 28 END IF CASE 4, 6, 9, 11: tage := 30 CASE 1, 3, 5, 7, 8, 10 ,12: tage := 31 OTHERWISE kein monat END SELECT; ____________________________________________________________________________ (In diesem Programmausschnitt werden die Tage eines Monats bestimmt) 2.4.2.4 Wertliefernde Abfrage und wertliefernde Auswahl Soll eine Abfrage oder eine Auswahl einen Wert liefern, dann darf der ELSE- bzw. der OTHERWISE-Teil nicht fehlen und alle Zweige müssen einen Wert liefern. ____________________________________________________________________________ ........................... Beispiel: ......................... SELECT monat OF CASE 2: IF schaltjahr THEN 29 ELSE 28 END IF CASE 4, 6, 9, 11: 30 CASE 1, 3, 5, 7, 8, 10 ,12: 31 OTHERWISE kein monat; 0 END SELECT; ____________________________________________________________________________ 2.4.2.5 Wiederholung Die Wiederholung dient zur mehrfachen Ausführung von Anweisungen, meist in Ab­ hängigkeit von einer Bedingung. Darum wird die Wiederholungsanweisung oft auch Schleife genannt und die in ihr enthaltenen Anweisungen Schleifenrumpf. Es gibt verschiedene Schleifentypen: - Endlosschleife - abweisende Schleife - nicht abweisende Schleife - Zählschleife. Endlosschleife Bei der Endlosschleife wird nicht spezifiziert, wann die Schleife beendet werden soll. Form: #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#END REPEAT#off("i")##off("b")# Anstelle von #on("i")##on("b")#REPEAT#off("i")##off("b")# darf die Abkürzung #on("i")##on("b")#REP#off("i")##off("b")# und anstelle von #on("i")##on("b")#END REPEAT#off("i")##off("b")# das Schlüsselwort #on("i")##on("b")#PER#off("i")##off("b")# (REP von hinten gelesen) benutzt werden. ____________________________________________________________________________ ........................... Beispiel: ......................... break; REPEAT fixpoint; pause (18000) END REPEAT ____________________________________________________________________________ Wird dieses Programm in einer Task im SYSUR-Zweig ausgeführt, so führt diese Task Fixpunkte im Abstand von 30 Minuten durch. Abweisende Schleife Bei der abweisenden Schleife wird die Abbruchbedingung an den Anfang der Schleife geschrieben. Form: #on("i")##on("b")#WHILE#off("i")##off("b")# Bedingung #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#END REPEAT#off("i")##off("b")# Bei jedem erneuten Durchlauf der Schleife wird überprüft, ob der BOOLesche Aus­ druck den Wert TRUE liefert. Ist das nicht der Fall, wird die Bearbeitung mit der Anweisung fortgesetzt, die auf das Schleifenende folgt. Die Schleife wird abweisende Schleife genannt, weil der Schleifenrumpf nicht ausgeführt wird, wenn die Bedingung vor Eintritt in die Schleife bereits FALSE liefert. Nicht abweisende Schleife Anders verhält es sich bei der nicht abweisenden Schleife. Bei der nicht abweisenden Schleife wird die Abbruchbedingung an das Ende der Schleife geschrieben. Form: #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#UNTIL#off("i")##off("b")# Bedingung #on("i")##on("b")#END REPEAT#off("i")##off("b")# Hier wird der Schleifenrumpf auf jeden Fall einmal bearbeitet. Am Ende des Rumpfes wird die BOOLesche Bedingung abgefragt. Liefert sie den Wert FALSE, wird die Schleife erneut abgearbeitet. Liefert die Bedingung den Wert TRUE, wird die Schleife abgebrochen und mit der ersten Anweisung hinter der Schleife in der Bearbeitung fortgefahren. Bei den beiden letztgenannten Arten der Wiederholungsanweisung ist es wichtig, daß Elemente der BOOLeschen Bedingung in der Schleife verändert werden, damit das Programm terminieren kann, d.h. die Schleife abgebrochen wird. ____________________________________________________________________________ ........................... Beispiel: ......................... TEXT VAR wort, satz :: ""; REPEAT get (wort); satz CAT wort; satz CAT " " UNTIL wort = "." PER; ____________________________________________________________________________ Dises Programm liest solange Wörter ein und verbindet diese zu einem Satz, bis ein Punkt eingegeben wurde. Zählschleife Zählschleifen werden eingesetzt, wenn die genaue Anzahl der Schleifendurchläufe bekannt ist. Form: #on("i")##on("b")#FOR#off("i")##off("b")# Laufvariable #on("i")##on("b")#FROM#off("i")##off("b")# Anfangswert #on("i")##on("b")#UPTO#off("i")##off("b")# Endwert #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#END REPEAT#off("i")##off("b")# Bei Zählschleifen wird eine Laufvariable verwendet, die die INT-Werte von 'Anfangs­ wert' bis 'Endwert' in Schritten von 1 durchläuft. 'Anfangswert' und 'Endwert' können beliebige INT-Ausdrücke sein. Diese Schleife zählt "aufwärts". Wird anstatt #on("i")##on("b")#UPTO#off("i")##off("b")# das Schlüsselwort #on("i")##on("b")#DOWNTO#off("i")##off("b")# verwendet, wird mit Schritten von 1 "abwärts" gezählt. Form: #on("i")##on("b")#FOR#off("i")##off("b")# Laufvariable #on("i")##on("b")#FROM#off("i")##off("b")# Endwert #on("i")##on("b")#DOWNTO#off("i")##off("b")# Anfangswert #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#END REPEAT#off("i")##off("b")# Die Laufvariable darf in der Schleife nicht verändert werden. Nach dem normalen Schleifenende ist der Wert der Laufvariablen nicht definiert. ____________________________________________________________________________ ........................... Beispiel: ......................... INT VAR summe :: 0, i; FOR i FROM 1 UPTO 100 REPEAT summe INCR i END REPEAT ____________________________________________________________________________ Dieses Programm berechnet die Summe der natürlichen Zahlen von 1 bis 100. Die verschiedenen Schleifenarten können kombiniert werden: #on("i")##on("b")#FOR#off("i")##off("b")# Laufvariable #on("i")##on("b")#FROM#off("i")##off("b")# Anfangswert #on("i")##on("b")#UPTO#off("i")##off("b")# Endwert #on("i")##on("b")#WHILE#off("i")##off("b")# Bedingung #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#END REPEAT#off("i")##off("b")# #on("i")##on("b")#FOR#off("i")##off("b")# Laufvariable #on("i")##on("b")#FROM#off("i")##off("b")# Anfangswert #on("i")##on("b")#UPTO#off("i")##off("b")# Endwert #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#UNTIL#off("i")##off("b")# Bedingung #on("i")##on("b")#END REPEAT#off("i")##off("b")# #on("i")##on("b")#WHILE#off("i")##off("b")# Bedingung #on("i")##on("b")#REPEAT#off("i")##off("b")# Abschnitt #on("i")##on("b")#UNTIL#off("i")##off("b")# Bedingung #on("i")##on("b")#END REPEAT#off("i")##off("b")# #page# 2.4.3 Abstrahierende Programmeinheiten 2.4.3.1 Refinementvereinbarung In ELAN ist es möglich, Namen für Ausdrücke oder eine bzw. mehrere Anweisungen zu vergeben. Das Sprachelement, das diese Namensgebung ermöglicht, heißt Refi­ nement. Die Ausführung eines solchen Namens heißt Refinementanwendung (siehe 2.4.1.3), die Namensgebung heißt Refinementvereinbarung. Die Ausdrücke oder Anweisungen bilden den Refinementrumpf. Werden in einem Programm Refinements benutzt, dann wird der Programmteil bis zum ersten Refinement durch einen Punkt abgeschlossen. Die Refinementvereinba­ rung sieht folgendermaßen aus: Name #on("i")##on("b")#:#off("i")##off("b")# Abschnitt #on("i")##on("b")#.#off("i")##off("b")# ____________________________________________________________________________ ........................... Beispiel: ......................... INT VAR a, b, x; einlesen von a und b; vertauschen von a und b; vertauschte werte ausgeben. einlesen von a und b: get (a); get (b). vertauschen von a und b: x := a; a := b; b := x. vertauschte werte ausgeben: put (a); put (b). ____________________________________________________________________________ Für den Namen 'einlesen von a und b' werden die Anweisungen 'get (a); get (b)' vom ELAN-Compiler eingesetzt. Man kann also die ersten vier Zeilen des Programms als eigentliches Programm ansehen, wobei die Namen durch die betreffenden Anwei­ sungen ersetzt werden. Ein Refinement hat also keinen eigenen Datenbereich, d.h. Vereinbarungen, die in Refinements gemacht werden, gelten auch außerhalb des Refinements. Vorteile der Refinementanwendung Durch die sinnvolle Verwendung von Refinements wird ein Programm im Programm und nicht in einer separaten Beschreibung dokumentiert. Weiterhin kann ein Pro­ gramm "von oben nach unten" ("top down") entwickelt werden: Das obige - zuge­ geben einfache - Beispielprogramm wurde in drei Teile zerlegt und diese durch Namen beschrieben. Bei der Beschreibung von Aktionen durch Namen wird gesagt was gemacht werden soll. Es wird noch nicht beschrieben wie, denn auf dieser Stufe der Programmentwicklung braucht man sich um die Realisierung der Refinements (noch) keine Sorgen zu machen. Das erfolgt erst, wenn das Refinement programmiert werden muß. Dabei können wiederum Refinements verwendet werden usw., bis man auf eine Ebene "heruntergestiegen" ist, bei der eine (jetzt: Teil-) Problemlösung sehr einfach ist und man sie direkt hinschreiben kann. Man beschäftigt sich also an jedem Punkt der Problemlösung nur mit einem Teilaspekt des gesamten Problems. Zudem sieht man - wenn die Refinements einigermaßen vernünftig verwendet werden - dem Programm an, wie die Problemlösung entstanden ist. Die Verwendung von Refinements hat also eine Anzahl von Vorteilen. Refinements ermöglichen: - "top down" - Programmierung - Strukturierung von Programmen und damit effiziente Fehlersuche und gute Wart­ barkeit - Dokumentation im Programmtext. Wertliefernde Refinements Refinements können auch dort verwendet werden, wo ein Wert erwartet wird, z.B. in einem Ausdruck oder einer 'put'-Anweisung. In diesem Fall muß die letzte Anwei­ sung des Refinements einen Wert liefert. ____________________________________________________________________________ ........................... Beispiel: ......................... INT VAR a :: 1, b :: 2, c :: 3; put (resultat). resultat: (a * b + c) ** 3. ____________________________________________________________________________ Man kann auch ein wertlieferndes Refinement mit mehreren Anweisungen schrei­ ben. Allgemeine Regel: Die letzte Anweisung eines Refinements bestimmt, ob es einen Wert liefert - und wenn ja, von welchen Datentyp. 2.4.3.2 Prozedurvereinbarung Eine Prozedur ist eine Sammlung von Anweisungen und Daten, die zur Lösung einer bestimmten Aufgabe benötigt werden. Der formale Aufbau einer Prozedur sieht folgendermaßen aus: #on("i")##on("b")#PROC#off("i")##off("b")# Prozedurname #on("i")##on("b")#:#off("i")##off("b")# Prozedurrumpf #on("i")##on("b")#END PROC#off("i")##off("b")# Prozedurname Der Prozedurrumpf kann Deklarationen, Anweisungen und Refinements enthalten. ____________________________________________________________________________ ........................... Beispiel: ......................... PROC loesche bildschirm ab aktueller cursorposition: out (""4"") END PROC loesche bildschirm ab aktueller cursorposition ____________________________________________________________________________ Verwendung von Prozeduren Prozeduren werden verwendet, wenn - Anweisungen und Datenobjekte unter einem Namen zusammengefaßt werden sollen ("Abstraktion") - gleiche Anweisungen von mehreren Stellen eines Programms verwandt werden sollen (Codereduktion), u.U. mit verschieden Datenobjekten (Parameter) - Datenobjekte nur innerhalb eines Programmteils benötigt werden und diese nicht von dem gesamten Programm angesprochen werden sollen. In den folgenden Programmfragmenten werden zwei Werte vertauscht. In der ersten Lösung wird ein Refinement, in der zweiten eine Prozedur verwandt. ____________________________________________________________________________ ........................... Beispiel: ......................... IF a > b THEN vertausche a und b END IF; put (a); put (b); vertausche a und b. vertausche a und b: INT CONST x :: a; a := b; b := x. ____________________________________________________________________________ ____________________________________________________________________________ ........................... Beispiel: ......................... PROC vertausche a und b: INT CONST x :: a; a := b; b := x END PROC vertausche a und b; IF a > b THEN vertausche a und b END IF; put (a); put (b); vertausche a und b; ____________________________________________________________________________ Beim ersten Hinsehen leisten beide Programme das Gleiche. Es gibt jedoch drei wichtige Unterschiede: 1) Das Refinement 'vertausche a und b' wird zweimal (vom ELAN-Compiler) ein­ gesetzt, d.h. der Code ist zweimal vorhanden. Die Prozedur dagegen ist vom Code nur einmal vorhanden, wird aber zweimal - durch das Aufführen des Prozedur­ namens - aufgerufen. 2) Die Variable 'x' ist in der ersten Programmversion während des gesamten Ablauf des Programms vorhanden, d.h. ihr Speicherplatz ist während dieser Zeit belegt. Solche Datenobjekte nennt man statische Datenobjekte oder auch (aus Gründen, die erst etwas später offensichtlich werden) Paket-Objekte. Das Datenobjekt 'x' der rechten Version dagegen ist nur während der Bearbeitung der Prozedur vor­ handen, sein Speicherplatz wird danach freigegeben. Solche Datenobjekte, die nur kurzfristig Speicher belegen, werden dynamische Datenobjekte genannt. Prozeduren sind also ein Mittel, um die Speicherbelegung zu beeinflussen. 3) Da Refinements keinen eigenen Datenbereich haben, kann die Variable 'x' in der ersten Programmversion - obwohl sie in einem Refinement deklariert wurde - von jeder Stelle des Programms angesprochen werden. Solche Datenobjekte werden globale Datenobjekte genannt. Das Datenobjekt 'x' der Prozedur dagegen kann nur innerhalb der Prozedur angesprochen werden, es ist also ein lokales Datenobjekt der Prozedur. Innerhalb der Prozedur dürfen globale Datenobjekte (also Objekte, die außerhalb von Prozeduren deklariert wurden) auch angespro­ chen werden. Eine Prozedur in ELAN bildet im Gegensatz zu Refinements einen eigenen Gültig­ keitsbereich hinsichtlich Datenobjekten und Refinements, die innerhalb der Pro­ zedur deklariert werden. Prozeduren sind somit ein Mittel, um die in ihr dekla­ rierten Datenobjekte hinsichtlich der Ansprechbarkeit nach Außen "abzuschotten". Prozeduren mit Parametern Prozeduren mit Parametern erlauben es, gleiche Anweisungen mit unterschiedlichen Datenobjekten auszuführen. Form: #on("i")##on("b")#PROC#off("i")##off("b")# Prozedurname #on("i")##on("b")#(#off("i")##off("b")# formale Parameterliste #on("i")##on("b")#)#off("i")##off("b")# #on("i")##on("b")#:#off("i")##off("b")# Prozedurrumpf #on("i")##on("b")#END PROC#off("i")##off("b")# Prozedurnamen Die Parameterliste besteht aus einem oder mehreren durch Kommata getrennten Para­ metern. Ein Parameter wird mit Datentyp, Accessrecht und Namen angegeben. Ähnlich wie bei der Datendeklaration braucht man für aufeinanderfolgende Parameter mit gleichem Datentyp und gleichem Accessrecht die Attribute nur einmal anzugeben. Parameter mit Accessrecht #on("i")##on("b")#CONST#off("i")##off("b")# sind Eingabeparameter, Parameter mit Access­ recht #on("i")##on("b")#VAR#off("i")##off("b")# realisieren Ein-/Ausgabeparameter. ____________________________________________________________________________ ........................... Beispiel: ......................... PROC vertausche (INT VAR a, b): INT VAR x :: a; a := b; b := x END PROC vertausche; INT VAR eins :: 1, zwei :: 2, drei :: 3; vertausche (eins, zwei); vertausche (zwei, drei); vertausche (eins, zwei); put (eins); put (zwei); put (drei) ____________________________________________________________________________ Die Datenobjekte 'a' und 'b' der Prozedur 'vertausche' werden formale Parameter genannt. Sie stehen als Platzhalter für die bei einem Prozeduraufruf einzusetzenden aktuellen Parameter (in obigen Beispiel die Datenobjekte 'eins', 'zwei' und 'drei'). Prozeduren als Parameter Es ist auch möglich, Prozeduren als Parameter zu definieren. Eine Prozedur als Parameter wird folgendermaßen in der Parameterliste spezifiziert: Resultattyp #on("i")##on("b")#PROC#off("i")##off("b")# #on("i")##on("b")#(#off("i")##off("b")# virtuelle Parameterliste #on("i")##on("b")#)#off("i")##off("b")# Prozedurname Die Angabe des Resultattyps entfällt, wenn es sich nicht um eine wertliefernde Proze­ dur handelt. Die virtuelle Parameterliste inklusive der Klammern entfällt, falls die Prozedur keine Parameter hat. Die virtuelle Parameterliste beschreibt die Parame­ ter der Parameterprozedur. Es werden Datentyp und Zugriffsrecht eines jeden Para­ meters angegeben, jedoch ohne Namen. ____________________________________________________________________________ ........................... Beispiel: ......................... PROC wertetabelle (REAL PROC (REAL CONST) funktion, REAL CONST untergrenze, obergrenze, schrittweite): REAL VAR wert; putline ("W E R T E T A B E L L E"); putline ("-----------------------"); wert := untergrenze; REPEAT put (text (wert, 10, 5)); put (text (funktion (wert), 10, 5)); line; wert INCR schrittweite UNTIL wert > obergrenze PER END PROC wertetabelle; (* Prozeduraufruf: *) wertetabelle (REAL PROC (REAL CONST) sin, 0.0, pi, 0.2) ____________________________________________________________________________ Wertliefernde Prozeduren Eine wertliefernde Prozedur sieht folgendermaßen aus: Resultattyp #on("i")##on("b")#PROC#off("i")##off("b")# Prozedurname #on("i")##on("b")#(#off("i")##off("b")# formale Parameterliste #on("i")##on("b")#)#off("i")##off("b")# #on("i")##on("b")#:#off("i")##off("b")# wertliefernder Prozedurrumpf #on("i")##on("b")#END PROC#off("i")##off("b")# Prozedurnamen Die Parameterliste inklusive Klammerung kann fehlen. Der Prozedurrumpf muß einen Wert mit dem in Resultattyp angegeben Datentyp liefern. ____________________________________________________________________________ ........................... Beispiel: ......................... INT PROC max (INT CONST a, b): IF a > b THEN a ELSE b END IF END PROC max; put (max (3, 4)) ____________________________________________________________________________ (In diesem Beispiel wird das Maximum von 'a' und 'b' ermittelt und ausgegeben) #page# 2.4.3.3 Operatorvereinbarung Operatoren können in ELAN ähnlich wie Prozeduren definiert werden. Operatoren müssen einen und können maximal zwei Operatoren besitzen (monadische und dyadi­ sche Operatoren). Form: Resultattyp #on("i")##on("b")#OP#off("i")##off("b")# Opname #on("i")##on("b")#(#off("i")##off("b")# ein oder zwei Parameter #on("i")##on("b")#)#off("i")##off("b")# #on("i")##on("b")#:#off("i")##off("b")# Operatorrumpf #on("i")##on("b")#END OP#off("i")##off("b")# Opname Der Resultattyp wird nur bei wertliefernden Operatoren angegeben. Als Operatornamen sind erlaubt: - ein Sonderzeichen, sofern es nicht als Trennzeichen benutzt wird: ! $ % & ' * + - / < = > ? § ^ ' ~ - eine Kombination von zwei Sonderzeichen. Diese Kombination muß jedoch bereits in ELAN existieren: := <= >= <> ** - ein Schlüsselwort (siehe 2.2.1). Vereinbarung eines monadischen Operators ____________________________________________________________________________ ........................... Beispiel: ......................... INT OP SIGN (REAL CONST argument): IF argument < 0.0 THEN -1 ELIF argument = 0.0 THEN 0 ELSE 1 FI END OP SIGN ____________________________________________________________________________ (Der Operator 'SIGN' liefert abhängig vom Vorzeichen des übergebenen Wertes den INT-Wert -1, 0 oder 1) Vereinbarung eines dyadischen Operators ____________________________________________________________________________ ........................... Beispiel: ......................... TEXT OP * (INT CONST anzahl, TEXT CONST t): INT VAR zaehler :: anzahl; TEXT VAR ergebnis :: ""; WHILE zaehler > 0 REP ergebnis := ergebnis + t; zaehler := zaehler - 1 END REP; ergebnis END OP *; ____________________________________________________________________________ (Der Operator '*' verkettet 'anzahl'- mal den Text 't') 2.4.3.4 Paketvereinbarung Pakete sind in ELAN eine Zusammenfassung von Datenobjekten, Prozeduren, Opera­ toren und Datentypen. Diese bilden den Paketrumpf. Elemente eines Pakets (Prozedu­ ren, Operatoren, Datentypen) können außerhalb des Pakets nur angesprochen werden, wenn sie in der Schnittstelle des Pakets, die auch "interface" genannt wird, aufge­ führt werden. Mit anderen Worten: es können alle Elemente eines Pakets von außen nicht angesprochen werden, sofern sie nicht über die Schnittstelle "nach außen ge­ reicht" werden. Pakete können separat übersetzt werden, so daß der "Zusammen­ bau" eines umfangreichen Programms aus mehreren Paketen möglich ist. Der formale Aufbau eines Pakets sieht folgendermaßen aus: #on("i")##on("b")#PACKET#off("i")##off("b")# Paketname #on("i")##on("b")#DEFINES#off("i")##off("b")# Schnittstelle #on("i")##on("b")#:#off("i")##off("b")# Paketrumpf #on("i")##on("b")#END PACKET#off("i")##off("b")# Paketname In der Schnittstelle werden Prozeduren und Operatoren nur mit ihrem Namen, durch Kommata getrennt, angegeben. Weiterhin können Datentypen und mit CONST verein­ barte Datenobjekte in der Schnittstelle aufgeführt werden, aber keine VAR-Datenob­ jekte, weil diese sonst über Paket-Grenzen hinweg verändert werden könnten. Im Gegensatz zu einer Prozedur kann ein PACKET nicht aufgerufen werden (nur die Elemente der Schnittstelle können benutzt werden). Pakete werden zu folgenden Zwecken eingesetzt: - Spracherweiterung - Schutz vor fehlerhaftem Zugriff auf Datenobjekte - Realisierung von abstrakten Datentypen. Spracherweiterung ____________________________________________________________________________ ........................... Beispiel: ......................... PACKET fuer eine prozedur DEFINES swap: PROC swap (INT VAR a, b): INT CONST x :: a; b := a; a := x END PROC swap END PACKET fuer eine prozedur ____________________________________________________________________________ Dies ist ein Paket, das eine Tausch-Prozedur für INT-Datenobjekte bereitstellt. Das PACKET kann übersetzt und dem ELAN-Compiler bekannt gemacht werden (EUMEL: "insertieren"). Ist das geschehen, kann man 'swap' wie alle anderen Proze­ duren (z.B. 'put', 'get') in einem Programm verwenden. Tatsächlich werden die mei­ sten Prozeduren und Operatoren (aber auch einige Datentypen), die in ELAN zur Verfügung stehen, nicht durch den ELAN-Compiler realisiert, sondern durch solche PACKETs. Um solche Objekte einigermaßen zu standardisieren, wurde in der ELAN-Sprachbeschreibung festgelegt, welche Datentypen, Prozeduren und Operato­ ren in jedem ELAN-System vorhanden sein müssen. Solche Pakete werden Stan­ dard-Pakete genannt. Jeder Installation - aber auch jedem Benutzer - steht es jedoch frei, zu den Standard-Paketen zusätzliche Pakete dem Compiler bekannzu­ geben, und damit den ELAN-Sprachumfang zu erweitern. Schutz vor fehlerhaftem Zugriff auf Datenobjekte ____________________________________________________________________________ ........................... Beispiel: ......................... PACKET stack handling DEFINES push, pop, init stack: LET max = 1000; ROW max INT VAR stack; (* siehe Kapitel Reihung, 2.6.1. *) INT VAR stack pointer; PROC init stack: stack pointer := 0 END PROC init stack; PROC push (INT CONST dazu wert): stack pointer INCR 1; IF stack pointer > max THEN errorstop ("stack overflow") ELSE stack [stack pointer] := dazu wert END IF END PROC push; PROC pop (INT VAR von wert): IF stack pointer = 0 THEN errorstop ("stack empty") ELSE von wert := stack [stack pointer]; stack pointer DECR 1 END IF END PROC pop END PACKET stack handling; ____________________________________________________________________________ Dieses Packet realisiert einen Stack. Den Stack kann man über die Prozeduren 'init stack', 'push' und 'pop' benutzen. #page# ____________________________________________________________________________ ........................... Beispiel: ......................... init stack; werte einlesen und pushen; werte poppen und ausgeben. werte einlesen und pushen: INT VAR anzahl :: 0, wert; REP get (wert); push (wert); anzahl INCR 1 UNTIL ende kriterium END REP. werte poppen und ausgeben: INT VAR i; FOR i FROM 1 UPTO anzahl REP pop (wert); put (wert) END REP. ____________________________________________________________________________ Die Datenobjekte 'stack' und 'stack pointer' haben nur Gültigkeit innerhalb des PACKETs 'stack handling'. Anweisungen wie z.B. ____________________________________________________________________________ ........................... Beispiel: ......................... put (stack [3]); stack [27] := 5 ____________________________________________________________________________ außerhalb des PACKETs 'stack handling' sind also verboten und werden vom ELAN-Compiler entdeckt. Ein PACKET bietet also auch einen gewissen Schutz vor fehlerhafter Verwendung von Programmen und Datenobjekten. Wichtig ist weiterhin, daß die Realisierung des Stacks ohne weiteres geändert werden kann, ohne daß Benutzerprogramme im 'main packet' geändert werden müssen, sofern die Schnittstelle nicht verändert wird. Bei­ spielsweise kann man sich entschließen, den Stack nicht durch eine Reihung, son­ dern durch eine Struktur zu realisieren. Davon bleibt ein Benutzerprogramm unbe­ rührt. Realisierung von abstrakten Datentypen Der Vollständigkeit halber wird folgendes Beispiel hier gezeigt. Wie neue Datentypen definiert werden, wird in Kapitel 2.7.1. erklärt. ____________________________________________________________________________ ........................... Beispiel: ......................... PACKET widerstaende DEFINES WIDERSTAND, REIHE, PARALLEL, :=, get, put: TYPE WIDERSTAND = INT; OP := (WIDERSTAND VAR l, WIDERSTAND CONST r): CONCR (l) := CONCR (r) END OP :=; PROC get (WIDERSTAND VAR w): INT VAR i; get (i); w := WIDERSTAND : (i) END PROC get; PROC put (WIDERSTAND CONST w): put (CONCR (w)) END PROC put; WIDERSTAND OP REIHE (WIDERSTAND CONST l, r): WIDERSTAND : ( CONCR (l) + CONCR (r)) END OP REIHE; WIDERSTAND OP PARALLEL (WIDERSTAND CONST l, r): WIDERSTAND : ((CONCR (l) * CONCR (r)) DIV (CONCR (l) + CONCR (r))) END OP PARALLEL END PACKET widerstaende ____________________________________________________________________________ Dieses Programm realisiert den Datentyp WIDERSTAND und mit den Operationen eine Fachsprache. 2.4.4 Terminatoren für Refinements, Prozeduren und Operatoren Das LEAVE-Konstrukt wird verwendet, um eine benannte Anweisung (Refinement, Prozedur oder Operator) vorzeitig zu verlassen. Es ist auch möglich, geschachtelte Refinements zu verlassen. Form: #on("i")##on("b")#LEAVE#off("i")##off("b")# Name Durch eine (optionale) WITH-Angabe kann auch eine wertliefernde benannte Anwei­ sung verlassen werden. Form: #on("i")##on("b")#LEAVE#off("i")##off("b")# Name #on("i")##on("b")#WITH#off("i")##off("b")# Ausdruck ____________________________________________________________________________ ........................... Beispiel: ......................... INT OP ** (INT CONST basis, exp): IF exp = 0 THEN LEAVE ** WITH 1 ELIF exp < 0 THEN LEAVE ** WITH 0 FI; INT VAR zaehler, ergebnis; ergebnis := basis; FOR zaehler FROM 2 UPTO exp REP ergebnis := ergebnis * basis PER; ergebnis END OP ** ____________________________________________________________________________ (Diese Operation realisiert die Exponentiation für INT-Werte) 2.4.5 Generizität von Prozeduren und Operatoren In ELAN ist es möglich, unterschiedlichen Prozeduren bzw. Operatoren gleiche Namen zu geben. Solche Prozeduren (Operatoren) werden generische Prozeduren (Operatoren) genannt. Die Identifizierung erfolgt durch Anzahl, Reihenfolge und Daten­ typ der Parameter (Operanden). Deshalb werden Prozeduren und Operatoren unter Angabe des Prozedur- bzw. des Operatorkopfes dokumentiert. Beispiele: INT OP MOD (INT CONST l, r) REAL OP MOD (REAL CONST l, r) Der MOD-Operator liefert den Rest einer Division. Er ist sowohl für INT- wie auch für REAL-Datenobjekte definiert. PROC put (INT CONST wert) PROC put (REAL CONST wert) PROC put (TEXT CONST wert) Die put-Prozedur ist für INT-, REAL- und TEXT-Datenobjekte definiert. Priorität von generischen Operatoren Bei der Neudefinition von Operatoren kann man bereits benutzte Sonderzeichen oder Schlüsselwörter benutzen. In diesem Fall bekommt der neudefinierte Operator die gleiche Priorität wie der bereits vorhandene Operator. 2.4.6 Rekursive Prozeduren und Operatoren Alle Prozeduren und Operatoren dürfen in ELAN rekursiv sein. ____________________________________________________________________________ ........................... Beispiel: ......................... INT PROC fakultaet (INT CONST n): IF n > 0 THEN fakultaet (n-1) * n ELSE 1 END IF END PROC fakultaet ____________________________________________________________________________ Die Fakultätsfunktion ist kein gutes Beispiel für eine Rekursion, denn das Programm kann leicht in eine iterative Version umgewandelt werden: ____________________________________________________________________________ ........................... Beispiel: ......................... INT PROC fakultaet (INT CONST n): INT VAR prod :: 1, i; FOR i FROM 2 UPTO n REP prod := prod * i END REP; prod END PROC fakultaet ____________________________________________________________________________ Die Umwandlung von einem rekursiven Programm in ein iteratives ist übrigens immer möglich, jedoch oft nicht so einfach, wie in dem Beispiel der Ackermann-Funktion: ____________________________________________________________________________ ........................... Beispiel: ......................... INT PROC acker (INT CONST m, n): IF m = 0 THEN n + 1 ELIF n = 0 THEN acker (m-1, 0) ELSE acker (m - 1, acker (m, n - 1)) ENDIF END PROC acker ____________________________________________________________________________ Das eigentliche Einsatzgebiet von rekursiven Algorithmen liegt aber bei den 'back­ track'-Verfahren. Diese werden eingesetzt, wenn eine exakte algorithmische Lösung nicht bekannt ist oder nicht gefunden werden kann und man verschiedene Versuche machen muß, um zu einem Ziel (oder Lösung) zu gelangen.