Einführung in die Programmiersprache Pascal Teil 5

In den vorangegangenen vier Teilen dieses Kurses wurden hauptsächlich Befehle besprochen, die auch in Sprachen wie FORTRAN oder BASIC vorhanden sind oder nachgebildet werden können. Nun wird es jedoch interessanter, denn die in diesem Teil besprochene Vereinbarungen von Datentypen sind in diesen Sprachen nicht vorhanden. Es ist deshalb an der Zeit für Programmierer dieser Sprachen, in diesen Kurs einzusteigen.

Bisher wurden nur die Datentypen REAL, INTEGER, CHAR und *BOOLEAN verwendet. Dies sind die in Pascal vordefinierten Standard-Datentypen. Pascal stellt dem Benutzer jedoch noch eine Vielzahl von weiteren Typen zur Verfügung. Einen Überblick über diese Typen und ihre Zugehörigkeit gibt die Grafik (Bild 1).

BILD 1: Standardfunktionen der einfachen Datentypen

1. Standard-Datentypen

Zur Veranschaulichung werden die auf die Standardtypen anwendbaren Funktionen in einer Tabelle dargestellt. Dabei werden auch die möglichen Datentypen für Argument und Ergebnis angegeben (siehe Bild 2).

Die Funktion PRED(x) liefert den Vorgänger des Zeichens x bezüglich der ASCII-Tabelle. SUCC(x) ist dementsprechend die Nachfolgefunktion. Außerdem gibt es noch die Standardfunktion ODD(x). Sie liefert den Wert TRUE, wenn das Argument eine ungerade Zahl ist.

PRED('H') = 'G'
SUCC('A') = 'B'
ODD(2) = false

EXP(x) ist die Potenzfunktion zur Basis e. LN(x) ist deren Umkehrfunktion, der Logarithmus von x zur Basis e.

Als Ergänzung sei noch erwähnt, daß der Datentyp BOOLEAN außer den gebräuchlichen Vergleichsoperationen >, <, = usw. noch die Operationen AND, OR und NOT erlaubt.

AND (logisches UND)
OR (logisches ODER)
NOT (logisches NICHT)

2. Benutzerdefinierte Datentypen

Für bestimmte Anwendungen ist es nützlich, eigene Datentypen definieren zu können. Dadurch lassen sich zum einen manche Fehler leichter aufspüren und zum anderen kann man Übersicht in die Programm bringen, weil der selbstdefinierte Datentyp seinen Verwendungszweck sichtbar macht.

Das Definieren von Datentypen geschieht im Vereinbarungsteil und dort direkt vor der Variablenvereinbarung. Das Schlüsselwort ist TYPE.

In Pascal gibt es zwei Arten von definierbaren Datentypen, der Aufzähltyp und der Teilbereichstyp. Der Aufzähltyp entsteht einfach durch Aufzählung der Elemente:

TYPE TAGE =
(MO,DI,MI,DN,FR,SA,SO);

Nun können beliebige Variablen mit diesem Typ definiert werden, z. B.:

VAR TAG : TAGE;

Diese Variable läßt sich nicht einfach mit READ einiesen oder mit WRITE ausgeben. Möglich jedoch Zuweisungen und Vergleichsoperationen:

TAG: = mi;  
if TAG < sa then write('werktag')  
if TAG in [mo,di,mi,do,fr] then...

Jedes Element des Types WOCHENTAG hat einen Stellenwert bezüglich der Funktion ORD, innerhalb der Aufzählung. Das erste Element (hier: MO) hat den Wort 0:

ORD(mo) = 0 
ORD(so) = 6

Durch die somit klare Reihenfolge der Elemente dieses Datentyps kann er zur Schleifensteuerung eingesetzt werden:

for TAG: = mo to mi do...

Es ist möglich, einen Teil eines Standard- oder eines Aufzähltyps als neuen Datentyp zu definieren. Diesen nennt man deshalb Teilbereichstyp. Es ist z. B. dann sinnvoll, diesen Typ einzusetzen, wenn man damit den Wertebereich einer Variablen eingrenzt:

TYPE ZIFFER = 1 ... 9;
VAR NUMMER : ZIFFER;

Die Variable NUMMER kann somit nur Werte von 1 bis 9 annehmen. Wird ihr ein anderer Wert zugewiesen, so kommt es zu einer Fehlermeldung.

BILD 2: DATENTYPEN

TYPE
TAGE = (MO,DI,MI,DN,FR,SA,SO);
WERKTAGE = MO ... FR;
WOCHENENDE = SA ... SO;

Bei der Definition von Teilbereichstypen ist zu beachten, daß sich die Typenmengen nicht überschneiden (!) oder außerhalb des Bereichs liegen.

Anmerkung: die Standard-Datentypen sind lediglich vordefinierte Typen, sie lassen sich wie folgt erklären:

TYPE INTEGER = -32768 ... 32767;
BOOLEAN = (true,false);

Zur Veranschaulichung der soeben besprochenen Typen ist das Programm DEMO (Listing 1) gedacht. Dabei wird auch eine Möglichkeit der Ausgabe von Elementen dieser Typen gezeigt, die nur indirekt erfolgen kann und hier mit einer CASE-Anweisung realisiert wird.

3. Strukturierte Datentypen

Zu den strukturierten Datentypen zählen ARRAY (Feld), STRING (Zeichenkette), SET (Menge), FILE (Datei) und RECORD (Verbund).

Der Typ SET wird benutzt, um eine Menge zu definieren. Die Form ist:

Bezeichner = SET of Menge

z. B.:

TYPE tage = (mo,di,mi,dn,fr,sa,so); 
VAR arbeitstag : SET of tage;

oder:

VAR arbeitstag :
SET of (mo,di,mi,dn,sa,so);

(Anmerkung: die beiden vorherigen Vereinbarungen definieren eine Variable 'arbeitstag’ kann die Werte einer Menge von Wochentagen annehmen. Übergeben werden sie ihr mit der Anweisung:

arbeitstag:=[mo,di,mi,do,fr] oder arbeitstag: = [mo ... fr]
Struktogramm zu Listing 1
program DEMO;
	{ Definition von Aufzaehl- & Unterbereichstypen }

type sprachen=(assembler,fortran,cobo1,basic,
				logo,pascal,c,modula2); 
	hprog = assembler .. modula2;

var hprogsprache : hprog;

procedure ausgabe(a:sprachen); 
	begin
		write(ord(a),') ');
		case a of
			assembler	: writeln('assembler'); 
			basic		: writeln('basic');
			c			: writeln('c');
			cobol		: writeln('cobol');
			fortran		: writeln('fortran');
			logo		: writeln('logo');
			modula2		: writeln('modula-2');
			pascal		: writeln('pascal');
		end
	end;	{ of AUSGABE }

begin	{ Hauptprogramm	}

	writeln('hoehere PROGRAMMIERSPRACHEN;'); 
	for hprogsprache:=fortran to modula2 do 
		AUSGABE(hprogsprache); writeln;
	writeln('MASCHINENSPRACHE;');
	AUSGABE(assembler)

end.

Listing 1

Die Variable ’arbeitstag’ ist somit eine Untermenge von 'tage’. Bei diesem Datentyp besteht eine Ähnlichkeit zum Aufzähl- und Unterbereichstyp. So zum Beispiel die Einschränkung, daß man die Variablen nicht direkt einiesen und ausgeben kann.

Um die Anwendung des Typs SET zu zeigen, wird er dem normalen Aufzählund Unterbereichstyp gegenübergestellt. Die Programme WOCHENTAGl und WOCHENTAG2 (Listing 2 & 3) liefern das gleiche Ergebnis, sie sind jedoch mit verschiedenen Datentypen aufgebaut.

program WOCHENTAG1;

type tage = (mo,di,mi,dn,fr,sa,so);

var tag : tage;

begin

	for tag:=mo to so do 
		begin
			writeln(ord(tag)+1);
			if tag in [mo..fr] then writeln('Wochentag')
							else writeln('Wochenende')
		end
end.

Listing 2

Als Operatoren mit Mengen sind die üblichen mathematischen Mengenoperationen erlaubt:

a+b Vereinigung
(ergibt die Menge aller Elemente von a und b)

a-b Differenz
(alle Elemente von a, die nicht in b enthalten sind)

a*b Schnittmenge
(alle Elemente die in a und b enthalten sind)

a< = b Teilmenge
(ist wahr, wenn a in b enthalten ist)

a> =b Teilmenge
(ist wahr, wenn b in a enthalten ist)

a=b Gleichheit
(ist wahr, wenn a und b gleich sind)

a IN b
(ist wahr, wenn a ein Element von b ist)

program WOCHENTAG2;
		{ SET Demo }

type tage = (mo,di,mi,dn,fr,sa,so);

var arbeitstag : set of tage; 
	tag : tage;

begin
	arbeitstag:=[mo..fr]; 
	for tag:=mo to so do 
		begin
			writeln(ord(tag)+1);
			if tag in arbeitstag then writeln('arbeitstag')
							else writeln('Wochenende')
		end
end.

Listing 3

ARRAY

Wenn man für eine Aufgabe eine feste Anzahl von Elementen des gleichen Typs benötigt, so kann man sie als Feld (ARRAY) anlegen:

Bezeichner: ARRAY[...] of Datentyp

z. B.:

TYPE feld = array[1...10] of integer; VAR zahlen : feld;

bzw.:

VAR zahlen : array[1...10] of integer;

Damit ist ein Feld namens 'zahlen’ festgelegt, das 10 Elemente enthält. Die Elemente sind vom Datentyp INTEGER und einzeln aufrufbar:

	zahl[1] := 1986 
	zahl[2*x-1] := 5

Hierbei wird dem ersten Element des Feldes der Wert 1986 zugewiesen. Werte von anderen Datentypen würden bei diesem Beispiel zu einer Fehlermeldung führen.

Als Index bei der Array-Definition sind alle Grundtypen außer REAL zugelassen:

	array ['a'...'z'] of real

Als Datentyp für das Array sind alle Grundtypen zugelassen. Außerdem ist es möglich, ein ARRAY[] OF ARRAY[] zu definieren:

TYPE horizontal = array[1...10] of integer;
VAR feld : array[1...5] of horizontal; 

bzw.

VAR feld : array [1...5] of array[1...10] of integer;

bzw.:

VAR feld: array[1...5,1...10] of integer;

Ein so definiertes Feld nennt man zweidimensional. Eine Zuweisung zu diesem Feld hat dann folgende Form:

	feld[1,1] := 9

Um Felder speicherplatzeffizient anzulegen, gibt es in PASCAL den Typ PACKED ARRAY. Für ihn gelten weitgehend die gleichen Vereinbarungen wie für das normale ARRAY.

Ein Beispiel für die Verwendung eines ARRAYS zeigt das Programm HORNER, das im dritten Teil dieses Kurses besprochen wurde. Weiter unten wird im Programm ADRESSDATEI ebenfalls ein Feld benutzt.

STRING

Der Datentyp STRING entspricht im wesentlichen einem PACKED ARRAY[1...N] OF CHAR. Er ist in STANDARD-PASCAL nicht definiert, jedoch in den meisten Pascal-Versionen vorhanden.

Bei den meisten Pascal-Compilern hat eine als STRING definierte Variable eine Maximallänge von 80 Zeichen. Die Lange kann jedoch im Bereich von 1 bis 255 liegen. Dazu wird definiert:

name : STRINGfx]

In UCSD-PASCAL sind zur String-Manipulation folgende Funktionen vorgesehen:

LENGTH
gibt die aktuelle Länge des Strings an

POS
Position eines Strings in einem String

STR
Umwandlung einer Integer-Zahl in einen String

COPY
Ausgabe eines Stringteils

DELETE
Löschen von Zeichen aus einem String

INSERT
Einfügen eines Strings in einen String

CONCAT
Stringaddition

Diese Befehle sind bis auf STR auch im ST-PASCAL von ATARI enthalten und dort oder in einem entsprechenden Handbuch nachzulesen (Syntax usw.).

RECORD

Ein RECORD (Verbund) ist die Zusammenfassung mehrerer Elemente, die auf irgend eine Art und Weise zusammengehören. Die Elemente müssen nicht vom selben Datentyp sein. Der RECORD hat die Form:

TYPE Recordname = RECORD 
	Element 1 : Datentyp; 
	Element 2 : Datentyp;
	..
	Element n : Datentyp 
			END;

Ein charakteristisches Beispiel ist eine Adressdatei (siehe Listing 4). Unter dem Namen 'ADRESSE’ werden alle Variablen zusammengefaßt und deklariert:

TYPE ADRESSE = RECORD
		name : string[10]; 
		vorname : string[10];
		Strasse : string[20]; 
		plz : integer; 
		ort : string[15]; 
		telefon : string[10];
			END;

Die Variablen können von jedem Typ sein, also auch RECORDS und ARRAYS. Damit ist z. B. ein ARRAY of RECORDS möglich.

Den mit der TYPE-Anweisung festgelegten RECORD kann man nun als Datentyp für ein Feld verwenden:

	VAR FREUNDE : ARRAY [1...100] of ADRESSE;

Damit wird ein Datenfeld mit 100 Elementen vom Typ ADRESSE festgelegt. Auf die einzelnen Elemente des Datenfeldes bzw. RECORDS kann nun einzeln zugegriffen werden:

	FREUNDE[1].NAME: = 'schneider'; 
	FREUNDE[1].VORNAME: = 'harald';

Dabei muß als erstes der Name des Feldes angegeben werden (FREUNDE) und dann in Klammern der Index. Nach einem Punkt folgen dann die Variablen des RECORDS. Wird ein RECORD geschachtelt, so folgt nach einem weiteren Punkt die Variable dieses RECORDS:

	FREUNDE[1].NAME.VOR: = 'harald':

Das Programm ADRESSDATEI (Listing) zeigt einige Besonderheiten und Vereinfachungen des RECORD-Befehls.

Die Anweisung WITH erlaubt es, den Namen des RECORDS beim Aufrufen wegzulassen:

WITH FREUNDE[nummer] DO 
	begin 
		NAME.VOR:= ...
		...

anstelle von:

FREUNDE[nummer].NAME. VOR: =...

Diese Vereinfachung ist vor allem bei großen RECORDS sehr nützlich. Die WITH-Anweisung kann auch verschachtelt werden. Sie hat dann folgende Form:

WITH a DO 
	WITH b DO 
		WITH c DO
			...

oder verkürzt:

WITH a,b,c DO ...

Das Programm ADRESSDATEI (Listing 4) zeigt die Verwendung des RECORD-Befehls. Alle RECORDS werden unter TYPE deklariert. Die RECORDS ’datum’ und ’name’ sind ein Unterverbund von ’adresse’. An diesem Beispiel sieht man auch den Unterschied, ob man einen RECORD direkt verschachtelt (’name’) oder ihn, als Datentyp einer RECORD-Variable, extern definiert (’datum’).

Die zweite Methode (extern) ist unübersichtlicher, außerdem tritt bei ihr ein zusätzlicher Bezeichner (’datum’) auf, der bei der anderen Methode nicht benötigt wird. Ihr ist jedoch dann der Vorzug zu geben, wenn der Datentyp ’datum’ mehrmals auftritt.

In der Variablendeklaration wird ein Feld ('freunde’) mit hundert Elementen angelegt, dessen Datentyp für RECORD ’adresse’ ist. Mit dieser Vereinbarung ist das Gerüst für die Adressdatei angelegt. Man kann es natürlich noch beliebig erweitern und verändern, das Prinzip bleibt jedoch das gleiche.

Die Dateneingabe wird von einer Prozedur erledigt. Dabei ist den Variablen der READLN-Anweisungen besondere Aufmerksamkeit zu schenken. Das Hauptprogramm ist nur für die Eingabe der Adressen vorgesehen. Es kann aber leicht erweitert werden. Die Eingabe der Adressen kann durch Eingabe von ’*’ als Vornamen beendet werden, ansonsten liest das Programm 100 Adressen ein.

Bei der Eingabe bricht das Programm ab, wenn man im Feld ’plz’ oder beim Datum zu große Zahlen oder Buchstaben eingibt.

So, das war’s. Und nun viel Erfolg beim Probieren dieser recht komplexen Befehle.

Der nächste Teil des Kurses beschäftigt sich mit weiteren Datentypen. Das Hauptgewicht liegt dabei bei dem letzten strukturierten Typ FILE. (MN)

STRUKTOGRAMM zu Listing 4
program ADRESSDATEI;

TYPE datum = RECORD
				monat : 1..12; 
				tag : 1..31; 
				jahr : 1986..2000;
			END;

adresse = RECORD
			name : RECORD
					vor : string[10]; 
					nach : string[10];
				END;
			strasse : string[20]; 
			plz : integer; 
			ort : string[15]; 
			geburtsdatum : datum;
			telefon : string[10];
		END;

VAR freunde : array[1..100] of adresse; 
	nummer : integer;

procedure eingabe (nummer : integer); 
 begin
	with freunde[nummer] do 
	 begin
		write('vorname: '); readln(name.vor); 
		write('nachname: '); readln(name.nach);
		write('strasse: '); readln(strasse); 
		write('plz: ');		readln(plz);
		write('ort: ');		readln(ort);
		writeln('geburtsdatum:');
		write('tag: ');		readln(geburtsdatum.tag);
		write('monat: ');	readln(geburtsdatum.monat); 
		write('jahr: ');	readln(geburtsdatum.jahr);
		write('telefon: '); readln(telefon);
	end;
 end;

begin

	writeln('ADRESSDATEI');
	writeln('-----------');
	repeat
		nummer:=nummer+1;
		writeln(‘Adresse Nummer:',nummer); 
		eingabe(nummer)
	until freunde[nummer].name.vor='*'; 
	writeln('ENDE der Eingabe')

end.

Listing 4



Aus: ST-Computer 06 / 1986, Seite 19

Links

Copyright-Bestimmungen: siehe Über diese Seite