Deutsch English
Home
About tdbengine
Newsletter
Download
Helpware
Forum
Chat
Documentation
Basic Course
Basics
Programmierumgebung
CGI Aufbereitung
EASY Programmierung
Standard-Bibliothek
Die Datenbank
HTML-Formulare
Function reference
HOWTO...?
Projects
Links
Benchmarks
Bug Reporting
Support request
 
Home    Overview    Search    Impressum    Contact    Members
Lesson 4: Grundlagen der EASY-Programmierung

In den ersten Folgen des Kurses wurden Sie mit den Grundlagen der CGI-Schnittstelle und den Werkzeugen der tdbengine bekannt gemacht. Ausserdem wurde die Aufbereitung der CGI-Variablen durch die tdbengine vorgestellt.

Ab dieser Folge gibt es nun einen Crash-Kurs "EASY-Programmierung". Wir beschränken uns dabei auf die zum Schreiben von CGI-Programmen wesentlichen Aspekte. So bleiben VDP-Anwendungen hier aussen vor. Der Kurs selbst ist wiederum in zwei Folgen aufgeteilt: In dieser Folge lernen Sie die Grundkonzepte von EASY kennen, in der nächsten werden die wichtigsten Funktionen der Standard-Bibliothek vorgestellt.

EASY heisst die Programmiersprache der tdbengine. Es ist handelt sich um eine "historisch gewachsene" Sprache, so dass sich nicht alle Sprachkonzepte so unmittelbar erschließen, als wenn sie am Konzept-Reißbrett entstanden wäre. Auf der anderen Seite sind im Laufe der Jahre so viele praktische Aspekte in die Entwicklung eingeflossen, dass EASY nun zu Recht als "praktisches und extrem schnelles Entwicklungswerkzeug" bezeichnet werden kann.

Das Grundkonzept
EASY ist ein prozedurale Programmiersprache. Das bedeutet, dass zur Untergliederung und Strukturierung einer Aufgabe eigene Prozeduren bzw. Funktionen geschrieben werden können. Im Falle der CGI-Programmierung muss sogar eine eigene Funktion geschrieben werden, nämlich die Hauptfunktion "Main".

EASY ist ein modulare Sprache. Das wiederum bedeutet, dass ein Programm Funktionen aus anderen Programmen importieren bzw. in solche exportieren kann. Damit können Bibliotheken erstellt werden, die dann von anderen Programmen genutzt werden.

EASY als modulare und prozedurale Programmiersprache fällt damit in gleiche Kathegorie wie C, Pascal oder Modula. Gegenüber diesen Sprachen fehlt vor allem die Möglichkeit, eigene Datentypen zu definieren. EASY stellt nur einfache Variablen vom Typ "Zeichenkette" und "Zahl" sowie mehrdimensionale Felder von diesen Typen zur Verfügung. Ausserdem gibt es keine Zeiger-Typen. Diesen Mangel teilt EASY zum Beispiel mit Java.

EASY hat aber gegenüber den oben genannten Sprachen einen wesentlichen Vorteil: Da seine Wurzeln in einer Script-Sprache für flexible Datenbankjobs liegen, sind eine Reihe von Datenbankfunktionalitäten in die Sprache übernommen worden.

Die Grundstruktur
Ein CGI-Programm muss die Prozedur "Main" enthalten. Wir wollen also als Erstes erläutern, wie man eine Prozedur schreibt. Hier die allgemeine Form:

PROCEDUREProzedurname
  Anweisungen
ENDPROC

Die hier fettgedruckten Wörter PROCEDURE und ENDPROC sind sogenannte reservierte Wörter. Eine syntaktische Besonderheit von EASY besteht darin, dass alle reservierten Wörter in einer eigenen Zeile stehen müssen, und dass die Zeilen damit beginnen müssen, wobei (führende) Leerzeichen ignoriert werden. Die Groß/Kleinschreibung wird für die reservierten Wörter ignoriert, wir können also genauso "procedure" oder "Procedure" schreiben.

Hier die wichtigsten syntaktischen Grundregeln eines EASY-Programms:

  • Reservierte Wörter sind nur am Anfang einer Zeile erlaubt *)
  • Leerzeilen werden überlesen
  • Leerzeichen und Tabs werden überlesen und dienen nur als Trennezeichen zwischen Bezeichnern
  • Kommentare im Programmtext beginnen mit zwei aufeinanderfolgenden Punkten oder zweier Slashes // oder über mehrere Zeilen hinweg mit /* ... Kommentar ... */)
*) Diese Regel wurde mit der Version 6.2.3 weitgehend aufgehoben. Lesen Sie bitte hierzu die tdbengine news vom 4.6.2000. Ein übersichtlicher Programmierstil setzt jedoch in den meisten Fällen das freiwillige Einhalten dieser Regel voraus.

Und hier gleich noch der Fundamentaltipp des Programmierens: Schreiben Sie ein Programm immer so, dass Sie eine Chance haben, es auch nach Jahren noch zu verstehen.

Die kursivgedruckten Wörter "Prozedurname" und "Anweisungen" sind Platzhalter für die Angaben des Programmierers.

Bei "Prozedurname" handelt es sich um den Platzhalter für einen sogenannten Bezeichner, also einen vom Programmierer vergebenen Namen. Dabei sind sämtliche Buchstaben (inklusive Umlaute), alle Ziffern sowie Binde- und Unterstrich zugelassen. Das erste Zeichen muss jedoch ein Buchstabe sein. Die maximale Länge eines Bezeichners beträgt 255 Zeichen.

Gültige Bezeichner:
Main
Meine_erste_Prozedur
Firmen-Name
A1234567890

Ungültige Bezeichner:
_Hallo                beginnt nicht mit einem Buchstaben
Erste Prozedur   enthält ein Leerzeichen
A/B                   enthält ein illegales Zeichen

Wichtig: Bei allen selbstdefinierten Bezeichnern wird Groß/Kleinschreibung unterschieden! "HALLO" und "Hallo" und "hallo" sind deshalb drei verschiedene Bezeichner.

Tipp: Verzichten Sie darauf, die reservierten Wörter von EASY (wie PROCEDURE) als Bezeichner zu verwenden. Es ist zwar nicht direkt verboten, aber sie können solche Bezeichner nur sehr eingeschänkt verwenden.

Kommen wir zum Platzhalter "Anweisungen". EASY kennt folgende Anweisungen, die in der Folge alle (mit Ausnahme der SUB-Anweisung) ausführlich besprochen werden:

  • (arithmetischer) Ausdruck Funktions- und Prozeduraufruf, Zuweisung
  • bedingte Anweisung IF ... THEN ... ELSIF ...THEN ... ELSE ... END
  • WHILE-Schleife WHILE ... DO ... END
  • REPEAT-Schleife REPEAT ... UNTIL
  • RETURN-Anweisung RETURN Wert
  • SUB-Anweisung SUB ... ENDSUB (dynamischer Datenbankzugriff)

Das war's auch schon. Und damit, so fragen Sie vielleicht erstaunt, soll man richtige Programme schreiben können? Sie werden sehen: Man kann - und wie!

Variablen
Jedes Programm, das etwas mehr tut, als "Hallo Welt" auf dem Bildschirm auszugeben, braucht ein "Gedächtnis", also Speicher, um Zwischenergebnisse aufzubewahren. EASY stellt dafür sogenannte Variablen zur Verfügung. Dabei gilt die Regel, dass jede Variable vor der ersten Verwendung beim System angemeldet werden muss. Die Anmeldung wird einer Variablen-Deklaration erledigt:

VAR Variablenname : Typ

Bei "Variablenname" handelt es sich wieder um einen Bezeichner, der nach den obigen Regeln gebildet werden muss.

Als "Typ" kommen nur folgende in Frage:

STRING und CHAR für Zeichen(ketten)
REAL, INTEGER und BYTE für Zahlen

Hinweis: Das stimmt nicht ganz, es gibt noch weitere Datentypen: TBROWSER, OBJECT, MARKS und TBITS. TBROWSER und OBJECT werden ausschließlich in VDP-Projekten eingesetzt. TBITS und MARKS spielen erst in den Kursen für Fortgeschrittene eine Rolle.

Man kann auch mehrere Variablen gleichen Typs in einer Variablen-Deklaration erledigen. Hier werden die Variablennamen - durch Komma getrennt - einfach hintereinander geschrieben.

Beispiel:

VAR Eingabe, Ausgabe : STRING
VAR Summe : REAL

Tipps:

Verwenden Sie "selbstdokumentierende" Variablennamen. Sie erleichtern das Programmieren ungemein.

Deklarieren Sie alle in einer Prozedur benötigten Variablen gleich am Anfang. Das schafft Übersicht.

Lokalität
EASY berücksichtigt auch das Lokalitäts-Prinzip: Eine Variable, die innerhalb einer Prozedur deklariert wird, ist nur in dieser Prozedur bekannt (lokale Variable). Eine Variable, die ausserhalb einer Prozedur deklariert wird, ist überall (also auch in Prozeduren) bekannt (globale Variable). EASY greift immer auf die "lokalste" Variable zu. Wenn also eine lokale und eine globale Variable mit gleichem Namen vorliegen, so wird grundsätzlich auf die lokale zugegegriffen.
Felder
Von beiden Typen können auch mehrdimensionale Felder deklariert werden. Dabei werden die maximalen Indizes (durch Komma getrennt) in eckigen Klammern direkt nach der Typbezeichnung angegeben. Auf die einzelnen Feldelemente wird über den Feldnamen und den aktuellen Index zugegriffen. Der Startindex ist immer "0".

Beispiele:

Zahlen : REAL[10]

Damit wird unter dem Namen Zahlen ein eindimesionales Feld (Vektor) mit folgenden Elementen angelegt:

Zahlen[0]
Zahlen[1]
...
Zahlen[10]

Wörter : STRING[1,20]

Hier handelt es sich um ein zweidimensionales STRING-Feld. Der erste Index kann die Werte 0 und 1 annehmen, der zweite die Zahlen von 0 bis 20. Auf die Feldelemente kann so zugegriffen werden:

Wörter[0,0]:="Hans"
Wörter[0,1]:="Huber"
Wörter[0,10]:="Martina"
Wörter[1,10]:="Müller"

Arithmetische Ausdrücke
Bei der Ausführung eines Programms, also zu dessen Laufzeit, werden Audrücke berechnet. Häufig ist das Ergebnis der Berechnung völlig uninteressant (und fällt demnach auch einfach unter den Tisch). Es sind in diesen Fällen die sogenannten "Seiteneffekte" der Berechnung, denen das Augenmerk gilt. So liefert die Berechnung der Funktion CgiWriteLn("Hallo Welt") den Wert 0. Als Seiteneffekt der Funktionsberechnung wird aber die Zeichenkette "Hallo Welt" in die Standardausgabe geschrieben - und das genau wollen wir an dieser Stelle ja auch.

Konstanten

Im einfachsten Fall ist ein arithmetischer Ausdruck eine Konstante. Das ist eine Größe, die ihren Wert nicht ändert, beispielsweise die Zahl 123 oder die Zeichenkette "Hallo"

EASY kennt folgende Konstanten:

Zeichenketten:
"abc" beliebige Zeichenfolge in doppelten Anführungszeichen
'abc' beliebige Zeichenfolge in einfachen Anführungszeichen

Zahlen:
123 natürliche Zahl
-123 (negative) ganze Zahl
123.456 Dezimalbruch (immer mit Dezimalpunkt)
-123.456 negativer Dezimalbruch
12:35 Zeitangabe
11.08.2000 Datumsangabe
11.08.2004_12:34:59
Unix-Timestamp-angabe

Variablen und Funktionen

Überall dort, wo eine Konstante stehen kann, darf auch ein Variablenname oder ein Funktionsname stehen. Wenn eine Funktion Parameter benötigt (dazu später mehr), so werden diese in runden Klammern nach dem Funktionsnamen angegeben.

Somit ist cgiwriteln("hello world") ein (einfacher) arithmetischer Ausdruck.

Zusammengesetzte arithmetische Ausdrücke

Aus diesen einfachen Komponenten können mit den sogenannten "arithmetischen" Operation (=Rechenzeichen) kompexe Ausdücke gebildet werden.

Für Zahlen gibt es folgende Operatoren:
+ Addition 1+2 = 3
- Subtraktion 12-4 = 8
* Multiplikation 3*6 = 18
/ Division 1/4 = 0.25
DIV Division ohne Rest 9 DIV 4 = 2
MOD Rest einer (ganzzahligen) Division 9 MOD 4 = 1
B_AND bitweises AND zweier ganzer Zahlen 3 B_AND 6 = 2
B_OR bitweises OR zweier ganzer Zahlen 3 B_OR 6 = 7
B_NOT binäres Komplement einer ganzen Zahl B_NOT 0 = -1 (=x0ffffffff)

Für Zeichenketten gibt es nur zwei Operatoren
+ Verkettung zweier Zeichenketten "abc"+"def" = "abcdef"
[p,n] (nachgestellter) Teilstring-Operator "abcdef"[3,2] = "cd"

Hinweis zum Teilstring-Operator: Hier werden in eckigen Klammern zwei Parameter angegeben. Der erste bestimmt die Startposition, der zweite die Anzahl der Zeichen. Fehlt der zweite Parameter, so wird hier 1 angenommen. Zeigt der Teilstring-Operator ausserhalb des Strings (auf den er angewandt wird) so wird eine leere Zeichenkette geliefert.

Für beide Typen gibt es noch den Zuweisungsoperator ":=". Dieser nimmt eine gewisse Sonderstellung ein, denn links vom Operator muss immer ein Variablenname stehen, rechts davon darf ein beliebiger Ausdruck sein:
Variablenname:=Ausdruck

Die Berechnung eines Ausdrucks mit dem Zuweisungsoperator ergibt den Wert des (Teil-)Ausdrucks auf der rechten Seite des Operators. Als Seiteneffekt erhält die Variable genau diesen Wert zugewiesen.

Wichtig ist hier festzuhalten, dass in EASY die Wertzuweisung an eine Variable ein Ausdruck ist, der selbst wiederum den zugewiesenen Wert repräsentiert. Dieses Verhalten unterscheidet sich von den Zuweisungen in Pascal oder BASIC und entspricht der Zuweisung in C und C++.

Damit sind beispielsweise auch gleichzeitige Zuweisungen an mehrere Variablen möglich:

Variable1:=Variable2:=Variable3:=1+2+3+4

Abkürzungen

Seit tdbengine 6.2.3 sind noch folgende Abkürzungen zugelassen:

Wenn "i"eine Variable vom Typ REAL,BYTE oder INTEGER ist, können folgende Abkürzungen verwendet werden:
i:=i+1 zu i++
i:=i-1 zu i--

Klammern

Anstelle einer Konstanten (oder Variablen oder Funktion) darf in einem Ausdruck auch ein in runde Klammern eingeschlossener Ausdruck stehen.

Das ist die "akademische" Definition des sonst üblichen Hinweises, dass in einem Ausdruck zur Auflösung von Mehrdeutigkeiten runde Klammern eingesetzt werden dürfen.

Beispiele für arithmetische Ausdrücke:

1+2*(3-4)

"Hans"+(" - " + "Huber")[4,2]

Prioritäten

Bei allen arithmetischen Ausdrücken gilt die Regel: Punkt vor Strich. Das sollte hier genügen. Bei Unsicherheit verwenden Sie einfach Klammern.

Beispiel

2+3*4 => 2+12 => 16
(2+3)*4 => 5*4  => 20

Mit dem Werkzeug "Variablen-Deklaration" und der Anweisung "arithmetischer Ausdruck" können wir schon erste Programme schreiben.

Als erstes wollen wir eine kleine Addier-Maschine schaffen, bei der wir die beiden zu addierenden Zahlen gleich in die URL eintragen:http://localhost/cgi-tdb/summe.prg?x=10&y=32

Ein erster (naiver) Versuch könnte etwa so aussehen:

PROCEDURE Main
VARDEF x,y : STRING
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  x:=GetQueryString('x')
  y:=GetQueryString('y')
  CGIWriteLn('x = '+x+'<br>')
  CGIWriteLn('y = '+y+'<br>')
  CGIWriteLn('x + y = '+x+y+'<br>')
ENDPROC

Wenn Sie dieses Programm laufen lassen, so werden Sie sehen, dass es nicht das gewünschte Resultat liefert:

x = 10
y = 32
x + y = 1032

Der Fehler liegt darin, dass wir mit Zeichenketten (STRING) gerechnet haben. Die Funktion GetQueryString() liefert immer Zeichenketten, und keine Zahlen. Es gibt aber zwei Funktionen, mit denen die beiden Typen umgewandelt werden können: Val() uns Str()

val(Zeichenkette) -> Zahl
str(Zahl) -> String

Damit können wir unser Programm so abändern:

PROCEDURE Main
VARDEF x,y : REAL
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  x:=Val(GetQueryString('x'))
  y:=Val(GetQueryString('y'))
  CGIWriteLn('x = '+str(x)+'<br>')
  CGIWriteLn('y = '+str(y)+'<br>')
  CGIWriteLn('x + y = '+str(x+y)+'<br>')
ENDPROC

Jetzt stimmt das Ergebnis:

x = 10
y = 32
x + y = 42


Logische Ausdrücke
Neben den arithmetischen Ausdrücken spielen in der Programmierung die logischen Ausdrücke eine besondere Rolle. Ein logischer Ausdruck kann immer nur einen der beiden Werte "wahr" und "falsch" annehmen. Die einfachste Form eines logischen Ausdrucks ist ein Vergleich, zu dem EASY eine ganze Reihe von Vergleichsoperatoren zur Verfügung stellt.

Vergleichsoperatoren

Vergleichsoperatoren für Zahlen:
= gleich 1=1 (wahr)   1=2 (falsch)
# ungleich 1#1 (falsch)  1#2 (wahr)
> größer 1>1 (falsch) 1>2 (falsch) 2>1 (wahr)
>= größer oder gleich 1>=1 (wahr) 1>=2 (falsch)  2>=1 (wahr)
< kleiner 1<1 (falsch)  1<2 (wahr)  2<1 (falsch)
<= kleiner oder gleich 1<=1 (wahr)  1<=2 (wahr)  2<=1 (falsch)

Vergleichsoperatoren für Zeichenketten:

= gleich "adam"="adam" (wahr) "adam"="eva" (falsch)
# ungleich "adam"#"adam" (falsch) "adam"#"eva" (wahr)
< kleiner "adam"<"adam" (falsch) "adam"="eva" (wahr)
<= kleiner oder gleich "adam"<="adam" (wahr) "adam"<="eva" (wahr)
> größer "adam">"adam" (falsch) "adam">"eva" (falsch)
>= größer oder gleich "adam">="adam" (wahr) "adam">="eva" (falsch)
like Mustervergleich "adam" like "a*m" (wahr)

Hinweise:

Bei den Vergleichsoperatoren für Zahlen ist darauf zu achten, dass rechnerintern immer eine Berechnung mit doppelter Genauigkeit erfolgt. Trotzdem müssen viele Ergebnisse gerundet werden, weil zu ihrer Darstellung eigentlich ein unendlicher Speicherbereich zur Verfügung stehen müsste (beispielsweise bei Brüchen wie 1/3). Deshalb sind Prüfungen auf Gleichheit immer mit Vorsicht zu genießen, wenn die einzelnen Operanden Ergebnis von Berechnungen sind und Nachkommastellen aufweisen.

Die Vergleichsoperatoren für Zeichenetten (mit Ausnahme von '=', '#' und 'like') setzen eine Ordnung auf Zeichenketten voraus. Dabei wird als ,kleiner' betrachtet, was im Lexikon vorher steht (lexikalische Ordnung).

Der Vergleichsoperator 'like' erlaubt Suchmuster mit den Jokern '?' und '*'. Dabei steht das Fragezeichen für genau ein beliebiges Zeichen, der Stern für beliebig viele (also auch gar kein) Zeichen. Zudem wird bei diesem Vergleich die Groß-/Kleinschreibung ignoriert.

Links und rechts von den Vergleichsoperatoren können beliebige arithmetische Ausdrücke des jeweiligen Typs stehen.

Logische Operatoren

Doch damit nicht genug. Aus einzelnen Vergleichen können mit den logischen Operatoren wiederum zusammengesetzte komplexe logische Ausdrücke gebildet werden:
AND logisches UND wahr AND wahr -> wahr, sonst immer falsch
OR logisches ODER falsch OR falsch -> falsch, sonst immer wahr
NOT logisches NICHT NOT wahr -> falsch, NOT falsch -> wahr

Bei den logischen Operatoren ist darauf zu achten, dass NOT stärker bindet als AND, und AND wiederum stärker bindet als OR. Im Zweifelsfall können Sie auch hier runde Klammern verwenden, um Klarheit zu schaffen.

Abkürzungen

EASY erlaubt bei den logischen Ausdrücken eine Reihe von Abkürzungen:

Ist x für ein arithmetischer Ausdruck, so gilt

x steht als Abkürzung für x # 0 bei Zahlen
x steht als Abkürzung für x # '' bei Zeichenketten

An Stelle von dem Wort AND kann auch das Komma geschrieben werden.

Häufig wird geprüft, ob ein numerischer Ausdruck einen von mehreren Werten annimmt:

x=Wert_1 OR x=Wert_2 OR x=Wert_3 ...

Ein solcher logischer Ausdruck kann abgekürzt werden zu

x IN [Wert_1, Wert_2, Wert_3 ... ]

Ähnliches gilt auch für Zeichenketten-Ausdrücke, nur dass hier der Vergleichsoperator LIKE verwendet wird:

x LIKE Muster_1 OR x LIKE Muster_2 OR x LIKE Muster_3 ...

kann abgekürzt werden zu

x IN [Muster_1, Muster_2, Muster_3 ... ]

Die bedingte Anweisung
Jetzt ist es wieder an der Zeit, einen Blick auf die restlichen Anweisungen zu werfen. Wie bereits gesehen, werden durch arithmetische Ausdrücke Aktionen ausgeführt wie Werte berechnet oder Zeichenketten ausgegeben. Über arithmetische Ausdrücke wird also das Programm veranlasst, irgendetwas zu tun.

Wir wollen nun die bedingte Anweisung betrachten, denn damit ist es möglich, dem Programm mitzuteilen, unter welchen Bedingungen es etwas tun soll und unter welchen etwas ganz anderes.

Die einfachste Form der bedingten Anweisung hat folgende Form:

IF logischer Ausdruck THEN Anweisungen END

Zur Laufzeit des Programm wird der logische Ausdruck ausgerechnet, und wenn das Ergebnis "wahr" ist, werden die dem THEN folgenden Anweisungen ausgeführt. Andernfalls wird der Programmfluss mit der ersten Anweisung nach dem END fortgesetzt.

Beispiel:

PROCEDURE Main
  VARDEF name : STRING
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  name:=GetQueryString('name')
  IF name<>'' THEN
    CGIWriteLn('Guten Morgen '+name)
  END
ENDPROC

Dieses kleine Programm schaut nach, ob in der URL ein Name übergeben wurde. Im positiven Fall (name<>'') gibt es eine Begrüßungsmeldung aus. Für localhost/cgi-tdb/test.prg?name=Hans lautet die Begrüßung demnach: "Guten Morgen Hans".

Nun wollen wir das Programm so abändern, dass für den Fall, dass kein Name angegeben wird, die Standardbegrüßung "Hallo unbekannte Welt" erfolgt. Mit dem bisher bekannten Werkzeugen können wir das so erledigen:

PROCEDURE Main
VARDEF name : STRING
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  name:=GetQueryString('name')
  IF name<>'' THEN
    CGIWriteLn('Guten Morgen '+name)
  END
  IF NOT name<>''THEN
    CGIWriteLn('Hallo unbekante Welt')
  END
ENDPROC

Das funktioniert zwar, sieht aber nicht besonders elegant aus. Zudem müssen in jedem Fall beide logischen Ausdrücke berechnet werden:

name<>'' UND NOT name<>''

Für diesen Zweck gibt es eine Erweiterung der bedingten Anweisung:

IF logischer Ausdruck THEN
  Anweisungen_1
ELSE
  Anweisungen_2
END

Auch hier wird der logische Ausdruck berechnet. Wenn er "wahr" liefert, so werden die Anweisungen_1 ausgeführt. Liefert er aber "falsch", so werden die Aweisungen_2 ausgeführt. Damit kann unser Begrüßungsprogramm vereinfacht werden:

PROCEDURE Main
VARDEF name : STRING
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  name:=GetQueryString('name')
  IF name<>'' THEN
    CGIWriteLn('Guten Morgen '+name)
  ELSE
    CGIWriteLn('Hallo unbekante Welt')
  END
ENDPROC

Und schon steigen unsere Ansprüche. Wir könnten ja jetzt auf die Idee kommen, dass das Programm den User "Hans" ganz besonders herzlich begrüßt, während es bei den anderen die Standardfloskeln aus dem obigen Beispiel verwendet. Wir können dieses Probem bereits lösen, wenn wir erkennen, dass in der Definition der bedingten Anweisung wiederum Anweisungen vorkommen, die bedingte Anweisung mithin beliebig verschachtelt werden kann. Zur besseren Übersicht verwenden wird das Prinzip der Texteinrückung, bei der jede Verschachtelung immer weiter eingerückt wird:

PROCEDURE Main
VARDEF name : STRING
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  IF name='Hans' THEN
    CGIWriteLn('Einen besonders guten Morgen lieber Hans')
  ELSE
    IF name<>'' THEN
      CGIWriteLn('Guten Morgen '+name)
    ELSE
      CGIWriteLn('Hallo unbekante Welt')
    END
  END
ENDPROC

Wenn wir nun mehrere Anwender mit einer jeweils eigenen Floskel begrüßen wollen, wird die Verschachtelung schnell tief und unübersichtlich. Aber auch hier bietet EASY Abhilfe. Für den Fall, dass im ELSE-Zweig einer bedingten Anweisung wiederum genau eine bedingte Anweisung steht, kann dafür einen ELSIF-Zweig (ohne Verschachtelung) verwenden:

IF logischer Ausdruck_1 THEN
  Anweisungen_1
ELSIF logischer Ausdruck_2 THEN
  Anweisungen_2
ELSIF logischer Ausdruck_3 THEN
  Anweisungen_3
...
ELSE
Anweisungen_sonst
END

Das Proramm prüft zunächst den logischen Ausdruck_1. Nur wenn dieser den Wert "falsch" ergibt, wird der logische Ausdruck_2 berechnet, im "falsch"-Fall der logische Ausdruck_3 usw. Die Anweisungen_sonst nach dem ELSE werden nur ausgeführt, wenn sich bis dahin kein "wahr" ergeben hat, ansonsten werden diejenigen Anweisungen ausgeführt, die zu dem Abschnit gehören, dessen logischer Ausdruck "wahr" ergeben hat. Das klingt kompliziert, ist es aber nicht. Unser Programm sieht demnach so aus:

PROCEDURE Main
VARDEF name : STRING
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  name:=GetQueryString('name')
  IF name='Hans' THEN
    CGIWriteLn('Einen besonders guten Morgen lieber Hans')
  ELSIF name<>'' THEN
    CGIWriteLn('Guten Morgen '+name)
  ELSE
    CGIWriteLn('Hallo unbekante Welt')
  END
ENDPROC

Ein wichtiger Hinweis: Die Syntax der bedingten Anweisung hat sich seit Version 6.2.3 geändert. In diesem Kurs wird die neue erweiterte Syntax besprochen. Wenn Sie diesen Kurs mit einer älteren Version durchführen, müssen Sie das "THEN" am Ende der IF-Zeilen entfernen.

Die WHILE-Anweisung
Die WHILE-Anweisung hat folgende Form:


WHILE logischer Ausdruck DO
  Anweisungen
END

Das Programm macht dabei zur Laufzeit folgendes: Zuächst wird der logische Ausdruck berechnet, ergibt er wahr, so werden die Anweisungen ausgeführt, andernfalls wird das Programm mit der ersten Anweisung nach dem END fortgesetzt. Es reagiert also zunächst genauso wie bei der einfachen bedingten Anweisungen. Im Gegensatz dazu erfolgt jedoch nach der Ausführung der Anweisungen ein Rücksprung zur WHILE-Zeile, und das Spiel beginnt wieder von vorne.

Bei der WHILE-Anweisung handelt es sich also um eine Programmschleife. Und weil die Bedingung bereits am Anfang der Schleife geprüft wird, spricht man auch von einer abweisenden Schleife, die niemals durchlaufen (abgewiesen) wird, wenn der logische Ausdruck bereits vor dem Eintritt den Wert "falsch" ergibt.

Wir wollen zur Übung ein kleines Programm schreiben, das einen via URL übergebenen Namen in seine Einzelbuchstaben zerlegt. Um die Länge einer Zeichenkette zu bestimmen, greifen wir auf die Standardfunktion Length() zurück:

length(S : STRING) -> Anzahl der Buchstaben in S

PROCEDURE Main
VARDEF name : STRING
VARDEF i : REAL
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  name:=GetQueryString('name')
  CGIWriteLn(ToHTML(name)+' buchstabiert man so:<br>')
  i:=0
  WHILE i:=i+1<=Length(name) DO
    CGIWriteLn(ToHTML(name[i])+'<br>')
  END
ENDPROC

Bei Schleifen müssen Sie aufpassen: Eine Endlosschleife ist schneller programmiert als man denkt! Endloschleifen sind solche, welche niemals terminieren, also beendet werden. Hier ein Beispiel aus der Praxis:

VARDEF x : REAL x:=FirstRec(database)
WHILE x>0 DO
  CGIWriteLn(ToHTML(GetField(database,'Firma'))+'<br>')
  x:=NextRec(database)
END

Wenn hier beispielweise über physikalische Satznummern auf Tabelle "database" zugegriffen wird, ändert sich im Schleifendurchlauf die Variable x nicht mehr, und die Schleife wird endlos laufen. Hier hat der Programmierer schlicht das Lesen des zugehörigen Datensatzes vergessen:

WHILE x>0 DO
  ReadRec(database,x)
  CGIWriteLn(ToHTML(GetField(database,'Firma'))+'<br>')
  x:=NextRec(database)
END

Manchmal wird die Abbruchbeding erst beim Schleifendurchlauf erstmals definiert. Eine solche Schleife muss also mindestens einmal ausgeführt werden. Für diesen Fall gibt es die REPEAT-Anweisung.

Die REPEAT-Anweisung
Die REPEAT-Anweisung hat folgende Struktur:

REPEAT
  Anweisungen
UNTIL logischer Ausdruck

Hier macht das Programm folgendes: Zunächst werden die Anweisungen ausgeführt. Dann wird der logische Ausdruck berechnet. Liefert er das Ergebnis "wahr", so wird mit der ersten Anweisung nach dem logischen Ausdruck fortgefahren. Andernfalls, also wenn das Ergebnis "falsch" ergibt, springt das Programm zurüch zu REPEAT und nimmt dort seine Arbeit wieder auf.

Hier handelt es sich also wiederum um eine Programm-Schleife. Und weil diese Schleife mindestens einmal durchlaufen wird, spricht man auch von einer nicht abweisenden Schleife.

Die RETURN-Anweisung
Bleibt von der obigen Liste noch die RETURN-Anweisung zu besprechen. In einer normalen Prozedur veranlasst diese Anweisung das Programm, die Prozedur sofort zu verlassen. Das kann durchaus sinnvoll sein, wenn beispielsweise irgendwelche Bedingungen nicht erfüllt sind.

Beispiel:

PROCEDURE Main
VAR name : STRING
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  name:=GetQueryString('name')
  IF name='' THEN
    CGIWriteLn('Sie haben keinen Namen angegeben')
    RETURN
  END
  CGIWriteLn('Guten Morgen '+name)
ENDPROC

In diesem Fall sorgt die RETURN-Anweisung dafür, dass das Programm nicht unnötig verschachtelt ist.

Wichtiger freilich ist der Einsatz der RETURN-Anweisung in einer sogenannten Funktionsprozedur, kurz auch Funktion genannt. Hier lautet die Syntax:

RETURN arithmetischer Ausdruck

Was ist nun eine Funktionsprozedur bzw. eine Funktion? Einfach gesagt, ist das eine Prozedur, die ein Ergebnis liefert. Und dieses Ergebnis kann in jedem arithmetischen Ausdruck verwendet werden, genauso wie jede Standardfunktion.

Zunächst muss man bei der Deklaration der Prozedur den Typ angeben. Dieser wird ganz einfach durch einen Doppelpunkt an den Prozedurnamen angefügt:

PROCEDURE Prozedurname : TYP

Als Typ kommen hierbei REAL, INTEGER, BYTE, CHAR und STRING in Frage.

Damit können wir bereits erste parameterlose Funktionen schreiben. Hier ein Beispiel, das das aktuelle Datum (Systemdatum) als Zeichenkette der Form "Tag Monatsname Jahr" liefert:

Wir verwenden dabei folgende Standardfunktionen:

today() -> das aktuelle Datum als Zahl (Tage seit Christi Geburt)
day() -> extrahiert die Tageszahl aus einem Datum
month() -> extrahiert die Monatszahl
year() -> extrahiert die Jahreszahl

PROCEDURE SystemDatum : STRING
VAR tag, Monat, jahr : INTEGER
VAR resultat : STRING
  tag:=Day(Today)
  Monat:=Month(Today)
  jahr:=Year(Today)
  resultat:=Str(tag)+' '
  IF Monat=1 THEN
    resultat:=resultat+'Januar'
  ELSIF Monat=2
    resultat:=resultat+'Februar'
  ELSIF Monat=3
    resultat:=resultat+'März'
  ELSIF Monat=4
    resultat:=resultat+'April'
  ELSIF Monat=5
    resultat:=resultat+'Mai'
  ELSIF Monat=6
    resultat:=resultat+'Juni'
  ELSIF Monat=7
    resultat:=resultat+'Juli'
  ELSIF Monat=8
    resultat:=resultat+'August'
  ELSIF Monat=9
    resultat:=resultat+'September'
  ELSIF Monat=10
    resultat:=resultat+'Oktober'
  ELSIF Monat=11
    resultat:=resultat+'November'
  ELSIF Monat=12
    resultat:=resultat+'Dezember'
  END
  RETURN resultat+' '+str(jahr)
ENDPROC


PROCEDURE Main
// zum Testen der obigen Funktion
  CGIWriteLn('content-type: text/html')
  CGIWriteLn('')
  CGIWriteLn(SystemDatum)
ENDPROC

Parameterlisten für Prozeduren
Solche einfachen Prozeduren und/oder Funktionen ohne Parameter dürften aber eher die Ausnahme sein. Es ist jedoch ganz einfach, Parameter an Prozeduren/Funktionen zu übergeben. Dazu muss nur eine Parameterliste im Prozedurkopf (so heisst die Zeile mit dem PROCEDURE-Kommando) angegeben werden:

PROCEDUREProzedurnameParameterliste : Typ

Die Parameterliste ist entweder leer (diesen Fall hatten wir ja schon), oder sie hat folgende Struktur:

(Bezeichner1 : Typ1; Bezeichner2 : Typ2; ...)

Bei den Bezeichnern handelt es sich wiederum um von Ihnen frei vergebene Namen. Diese Namen sind nur innerhalb der folgenden Prozedur/Funktion bekannt. Sie haben dort genau den selben Status wie innerhalb der Prozedur deklarierte Variablen vom angegeben Typ. Im Unterschied dazu werden diese Variablen beim späteren Aufruf der Prozedur/Funktion mit demjenigen Wert vorbelegt (initialisiert), der dann anstelle des jeweiligen Bezeichners in der Parameterliste angegeben wird.

Dazu ein kleines Beispiel: Wir wollen eine Funktion schreiben die den Durchschnitt zweier Zahlen berechnet.

PROCEDURE Durchschnitt(x : REAL; y : REAL) : REAL
  RETURN (x+y)/2
ENDPROC

Innerhalb der Prozedur sind die Werte "x" und "y" genauso sichtbar, als wenn sie dort erst definiert werden. Sie können diesen (Pseudo-)Variablen auch Werte zuweisen, sie an andere Funktionen übergeben und alles tun, was Sie mit Variablen tun dürfen.

Wird dann irgendwo später im Programm diese Funktion aufgerufen, etwa mit

Durchschnitt(12-4,25)

so wird zunächst der Ausdruck 12-4 (=8) berechnet und der (Pseudo-)Variablen x zugewiesen, dann wird der Wert 25 der (Pseudo-)Variablen y zugewiesen. Erst im Anschluss daran wird das Programm der Prozedur ausgeführt:

RETURN (8+25)/2

Wichtig ist hier folgendes: Entscheidend für die Zuweisung der berechneten Werte bei einem Prozedur/Funktions-Aufruf ist allein die Reihenfolge in der Parameterliste.

Diese Art der Parameter, die Werte von aussen in eine Prozedur/Funktion liefern, nennt man Wert-Parameter (call by value). Es gibt noch weitere Arten, die aber in diesem Kurs nicht besprochen werden.

Abkürzungen:

Wieder gibt es eine Abkürzungsform innerhalb einer Parameterliste:

p1 : Typ; p2 : Typ; ... kann abgekürzt werden zu
p1, p2, ... : Typ wenn es sich dabei um den gleichen Typ handelt.

So könnten wir auch die obige Funktion folgendermaßen schreiben:

PROCEDURE Durchschnitt(x, y : REAL) : REAL
  RETURN (x+y)/2
ENDPROC

Zusammenfassung
In dieser Folge haben Sie die wesentlichen Programmierwerkzeuge von EASY kennengelernt. Sie können nun eigene Prozeduren und Funktionen schreiben. Sie kennen Variablen, Parameter, Anweisungen. In der nächsten Folge werden wir einen intensiven Blick in die Standardbibliothek der tdbengine werfen und uns an das erste Datenbankprojekt wagen.
Aufgaben:
  1. Schreiben Sie ein Programm, das einen Namen in der Form buchstabiert:
    >> Werner buchstabiert man W-e-r-n-e-r <<

    Bedenken Sie, dass nach dem letzten Buchstaben kein Bindestrich kommt.

  2. Jemand behauptet, die WHILE-Anweisung ist überflüssig, weil man sie durch eine Kombination aus IF- und REPEAT-Anweisung ersetzen kann. Stimmt das? Wie könnte diese Kombination aussehen?
  3. Kann man die REPEAT-Anweisung durch eine Kombination aus den anderen Anweisungen ersetzen


tdbengine Anwendungen im Web:

Open-Source Web CMS


Open-Source Bug-Tracking


Free wiki hosting

Open-Source Wiki-System

Kostenloses Foren-Hosting

Diät mit tdbengine 8-)

tdbengine chat
irc.tdbengine.org
#tdbengine

   Copyright © 2003-2004 tdb Software Service GmbH
   Alle rechte vorbehalten. / All rights reserved
   Last changed: 05.05.2004
{Fehler für :execmacro{execmacro="sessionspy"}


ranking-charts.de

Programmers Heaven - Where programmers go!