Pascal-Tutorial (Vorwort, Installation, Kapitel 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)

Eigene Datentypen, Mengen und Strukturen

7.1. Eigene Datentypen

Wir haben in Pascal auch die Möglichkeit, einen alternativen Bezeichner für ein Array, eine Menge oder einen bestehenden Datentyp festzulegen. Dazu lernen wir einen neuen Block kennen: den TYPE-Block. Dieser steht oberhalb von CONST und VAR (falls diese vorhanden sind), jedoch unterhalb von USES (falls vorhanden).

Um einen Alternativbezeichner festzulegen, muss dieser wie folgt definiert werden:

TYPE Bezeichner = Typ/Array/Menge/...;

Sie können damit statt Integer z.B. Int als Bezeichner definieren, also einen Alias festlegen. Der TYPE-Block müsste dazu wie folgt aussehen:

TYPE Int = Integer;

Wollen wir nun die Variable i vom Typ Integer deklarieren, wäre dann ebenfalls möglich:

VAR i: Int;

Achten Sie darauf, dass Sie = als Operator im TYPE-Block verwenden, und nicht den Zuweisungsoperator := !

TYPE hat jedoch auch einen weitaus praktischeren Zweck als den eben gezeigten. So benötigt man TYPE zur Definition von Strukturen (Records). Eine wirkliche Arbeitsersparnis ergibt sich, wenn man TYPE verwendet, um Arrays zu definieren. Erfolgt dies in einer Unit, kann man den Alternativbezeichner des definierten Arrays in allen Programmdateien verwenden, in die die Unit eingebunden wird.

7.2. Mengen und Strukturen

Zunächst noch zwei kleine Ergänzungen: Die erste betrifft die Variablendeklaration und deren Typen. Sie können den Wertebereich auf einfache Weise begrenzen. Das bedeutet, dass Sie beispielsweise bestimmen können, dass der Zahlenbereich lediglich von 1 bis 100 reicht. Die Variable dieses Typs ist dann nur imstande, Werte abzuspeichern, die innerhalb dieses Bereichs (1 bis 100) liegen. Die Variablendefinition hierfür sieht wie folgt aus:

VAR Bezeichner: Von..Bis;

Beispielsweise:

VAR i: 1..50;

Die Variable i darf dann nur Werte von 1 bis 50 annehmen. Wird versucht, der Variable einen höheren oder niedrigeren Wert zuzuweisen, egal ob durch Zuweisung oder durch den Benutzer bei einer Eingabe, meldet der Compiler einen Fehler.

Die zweite Ergänzung betrifft Aufzählungstypen. Manche Dinge lassen sich schlecht in Zahlen ausdrücken. Farben zum Beispiel. Hier wäre es angenehmer, stünde im Quellcode rot statt 4. Eine Möglichkeit wäre natürlich, für 4 eine Konstante mit dem Namen rot zu definieren:

CONST rot = 4;

Nun könnten wir überall im Quellcode rot anstelle von 4 verwenden. Benötigen wir jedoch für jeden der 16 Standard-Farben einen deutschen Bezeichner, wäre diese Methode mit ziemlich viel Arbeit verbunden.

Abhilfe schaffen hier sog. Aufzählungstypen. Aufzählungstypen müssen explizit vom Programmierer definiert werden. Das bedeutet, dass im TYPE-Definitionsblock des Programmes der Aufzählungstyp definiert werden muss, bevor eine Variable dieses definierten Typs verwendet werden kann. Die Syntax der Aufzählungstypen sieht wie folgt aus:

TYPE Bezeichner = (Name1, Name2, Name3, ...);

Ein Beispiel dazu:

TYPE Farben = (Schwarz, Blau, Gruen,
               Blaugruen, Rot, Rotblau,
               Braun, Hellgrau, Dunkelblau,
               Hellgruen, Helles_Blaugruen, Hellrot,
               Helles_Rotblau, Gelb, Weiss);

Nun ist es noch nötig, eine Variable vom Aufzählungstyp Farben zu deklarieren. Nennen wir diese Variable aktuelleFarbe:

VAR aktuelleFarbe: Farben;

Es wäre auch möglich gewesen, wie folgt vorzugehen:

VAR aktuellefarbe: (Schwarz, Blau, Gruen, Blaugruen, Rot,
                    Rotblau, Braun, Hellgrau, Dunkelblau,
                    Hellgruen, Helles_Blaugruen, Hellrot, Helles_Rotblau,
                    Gelb, Weiss); 

Von letzterem würde ich eher abraten. Einerseits leidet darunter die Übersicht im Programm, andererseits könnte man, ginge man wie im ersten Fall vor, den Typ Farben mehrmals (in mehreren Quellcodedateien) verwenden, würde man diesen in einer Unit definieren (mehr zu Units in Kapitel 11!). Also Übersicht, Wiederverwendbarkeit und nicht zu vergessen: Ihre Tipparbeit!

Haben wir eine Variable eines Aufzählungstyps deklariert, so können wir dieser einen Wert zuweisen. Der zulässige Wertebereich sind alle definierten Bezeichner. Hätten wir nur Eins, Zwei und Drei definiert, wäre es unzulässig, würde man der Variable den Wert Vier oder XYZ zuweisen. Korrekt wäre im oberen Farbbeispiel z.B.:

aktuelleFarbe := Blau;

Eine Variable von einem Aufzählungstyp speichert die Werte intern als Dezimalzahlen. Nehmen wir noch einmal das Beispiel mit den Farben her: Der erste definierte Bezeichner wird intern als 0 abgespeichert. Der zweite als 1, der dritte mit 2 usw. Im Farbenbeispiel wird Hellgrau somit intern als 7 gespeichert. Da Blau den Wert 1 hat, speichert in der oberen Zuweisung die Variable aktuelleFarbe intern den Wert 1.

Was Sie allerdings nicht können, ist, einer Integer-Variable den Wert eines Aufzählungstypens zu übergeben. Trotz der dezimalen Speicherung der Aufzählungstypen werden diese nicht mit Integer-Typen gleichgestellt. Folgendes wäre also FALSCH:

TextColor (aktuelleFarbe);    (* FALSCH! *)

Um dennoch den dezimalen Wert einer solchen Variable zu erhalten, gibt es die Funktion Ord (Abkürzung für Ordinal). Um hier das Thema Funktionen kurz vorweg zu nehmen: Eine Funktion liefert immer einen Wert zurück, in diesem Fall den ordinalen Wert des Aufzählungstyps. Ordinale Variablen sind abzählbar, was bedeutet, dass diese einen festgelegten Wertebereich haben. ShortInt zum Beispiel: Man weiß, dass der niedrigste Wert -128 ist, der höchste 127. Alles andere ist ungültig und kann nicht gespeichert werden. Den zurückgelieferten Wert kann man direkt an eine Funktion oder Prozedur (mehr dazu in Kapitel 8) übergeben - wie im nächsten Beispiel.

Noch einmal, aber diesmal richtig:

TextColor (Ord(aktuelleFarbe));    (* RICHTIG! *)

Ord ermittelt den Dezimalwert von aktuelleFarbe. Dieser Wert (im Beispiel der Wert 1) wird an TextColor übergeben, also quasi an der Stelle "eingesetzt". TextColor ändert dann die Farbe korrekt zu Blau.

Ein vollständiges Beispiel hierzu dürfte nicht nötig sein. Mittlerweile müssten Sie imstande sein, das eben Gelernte selbst umzusetzen. Was nicht heißen soll, dass keine Anwendungsbeispiele mehr folgen.

Um wieder zum eigentlichen Thema zurückzukommen ...
Nehmen wir an, der Benutzer soll eine Eingabe tätigen. Gültig sind die Zeichen a bis z sowie X und Y (nur diese beiden Großbuchstaben sind erlaubt). Nun stehen wir vor einem Problem. Zwar könnten wir eine CASE-Anweisung verwenden, um die Eingabe zu überprüfen, das wäre jedoch unnötige Arbeit. Es gibt einen einfacheren Weg. Dieser besteht darin, eine Menge (auch als SET bezeichnet) zu definieren.

Diese Menge kann nur von einem ordinalen Typ sein. Üblicherweise gibt man - wie etwas weiter oben gezeigt - den Bereich direkt an. Zunächst muss eine Menge definiert werden. Entweder man gibt diese direkt bei der Variablendefinition an (im VAR-Teil) oder man definiert einen Typ im TYPE-Definitionsbereich. Im Folgenden sehen Sie die Syntax für die Definition von Mengen:

VAR Bezeichner: SET OF Bereich/Typ;

Zum Beispiel:

VAR Menge1: SET OF 1..5;

Die Menge Menge1 ist nun imstande, die Werte 1 bis 5 zu speichern. Das bedeutet jedoch, dass nicht nur ein Wert gespeichert werden kann, sondern mehrere! In diesem Fall können max. 5 Werte abgespeichert werden. Allgemein sind max. 256 Werte möglich. Wir können der Menge die Werte 1, 2, 3, 4 und 5 zuweisen. Zum Beispiel kann die Menge nur den Wert 1, sie kann aber ebenso die Werte 3 und 5, aber auch alle Werte enthalten. Auch gar keinen Wert zu speichern, ist zulässig.

Ist die Menge definiert, kann überprüft werden, ob ein Wert IN der Menge enthalten ist. Hierzu gibt es den IN-Operator. Sehen Sie sich einmal das nächste Beispiel an. Der Benutzer wird aufgefordert, ein Zeichen einzugeben. Zulässig sind nur die Zeichen a, b, c, k, x, y und z (Kleinbuchstaben). Das Programm überprüft, ob der eingegebene Wert in der Menge enthalten ist und zeigt je nach Ergebnis einen anderen Text am Bildschirm an.

PROGRAM Beispiel_SET;
USES Crt;
 
TYPE Wertebereich = SET OF 'a'..'z';
 
VAR Menge: Wertebereich;
    Eingabe: Char;
 
BEGIN
  ClrScr;
 
  Menge := ['a','b','c','k','x','y','z'];     (* Werte muessen in eckigen Klammern stehen! *)
 
  Write ('Ihre Eingabe: ');
  ReadLn (Eingabe);
 
  IF Eingabe IN Menge THEN
    WriteLn ('Die Eingabe war zulaessig.')
  ELSE
    WriteLn ('Die Eingabe war unzulaessig!');
 
  ReadKey;
END.

Wie Sie es von den bisherigen Beispielen mittlerweile gewöhnt sein müssten, ist auch dieses Beispiel einfach gehalten. Zwar ist es nicht besonders sinnvoll, beschränkt sich jedoch auf das Wesentliche und verwirrt nicht mit unnötigen Nebensächlichkeiten.

In diesem Beispiel habe ich im TYPE-Definitionsblock den Mengentyp Wertebereich definiert. Die Variable Menge wird in der darauffolgenden Zeile von diesem Typ deklariert. In der 8. Programmierzeile definieren wir die Variable Menge. Wir bestimmen, welche Werte in der Menge enthalten sind. In der späteren Überprüfung (siehe 11. Zeile) testen wir, ob der Wert der Variable Eingabe (die Eingabe vom Benutzer) in der Menge enthalten ist.

Soviel zu den Mengen. In Pascal haben Sie aber auch die Möglichkeit, Records (Strukturen) festzulegen. (Für C-Programmierer: In C entspricht das den mit struct definierten Strukturen.) Sie möchten beispielsweise die Daten (sagen wir einmal Name, Adresse und Telefonnummer) einer Person abspeichern.

Hier wäre eine Struktur eine wunderbare Lösung für dieses Problem. Generell besteht eine Struktur aus mehreren Variablen. Für dieses Beispiel definieren wir die Struktur Daten, bestehend aus 3 Variablen. Diese 3 Variablen haben die Bezeichner Name, Adresse und Telefonnummer und sind alle drei vom Typ String. Eine Struktur ist ein selbstdefinierter Typ, und wird auch im TYPE-Block definiert.

Die Struktur ist zunächst nur abstrakt definiert, quasi ein Modell, eine Schablone. Damit Sie die Struktur auch tatsächlich verwenden können, muss von dem Typ (der definierten Struktur) eine Instanz erstellt werden. Eine Instanz erstellen Sie, indem Sie eine Variable vom Typ der selbstdefinierten Struktur deklarieren. Ein Struktur wird folgendermaßen vereinbart:

TYPE Bezeichner = RECORD
    Variable1: Typ;
    Variable2: Typ;
    Variable3: Typ;
    ...
  END;

Zum Beispiel:

TYPE Daten = RECORD
    Name: String[50];      (* Name darf ein String mit max. 50 Zeichen sein *)
    Adresse: String[255];
    Telefonnummer: String[15];
  END;

Die Struktur wäre damit erstellt. Fehlt noch die Instanz im VAR-Bereich:

VAR Datensatz: Daten;

Nach folgendem Schema greifen Sie dann darauf zu:

... Instanz.Variable ...    (* wichtig ist hier nur . als Operator *)

Um nun in der deklarierten Struktur Datensatz den Namen Claudia abzuspeichern, müsste man Folgendes tun:

Datensatz.Name := 'Claudia';

Abschließend hierzu noch ein vollständiges Beispiel:

PROGRAM Telefonbuch;
USES Crt;
 
TYPE Daten = RECORD
    Name: String[50];
    Adresse: String[255];
    Telefonnummer: String[15];
  END;
 
VAR Datensatz: ARRAY [1..100] OF Daten;
    i: 0..100;
 
BEGIN
  ClrScr;
 
  REPEAT   (* Schleifenbeginn *)
 
    Write ('Welchen Eintrag verwenden: (1-100; 0 = Programmende) ');
    ReadLn (i);
 
    IF i = 0 THEN Exit;    (* Exit beendet das Programm *)
 
        (* Wenn etwas nicht definiert wurde, Daten einlesen *)
    IF (Datensatz[i].Name = '') OR (Datensatz[i].Adresse = '') OR (Datensatz[i].Telefonnummer = '') THEN
    BEGIN
      WriteLn ('Eintrag ',i);
      Write ('Name: ');
      ReadLn (Datensatz[i].Name);
      WriteLn ('Adresse:');
      ReadLn (Datensatz[i].Adresse);
      Write ('Telefonnummer: ');
      ReadLn (Datensatz[i].Telefonnummer);
      WriteLn;
    END
    ELSE   (* sonst Daten anzeigen *)
    BEGIN
      WriteLn ('Name: ',Datensatz[i].Name);
      WriteLn ('Adresse:');
      WriteLn (Datensatz[i].Adresse);
      WriteLn ('Telefonnummer: ',Datensatz[i].Telefonnummer);
    END;
 
  UNTIL i = 0;
 
  ReadKey;
END.

Das Beispiel kann zu 100 Personen deren Name, Telefonnummer und Adresse speichern und wieder abzurufen. Nur gehen die Daten verloren, wenn das Programm beendet wird. In Kapitel 9 werden wir das Beispiel so ausbauen, dass die Daten in einer Datei gespeichert werden.

Vorheriges Kapitel Nächstes Kapitel