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

Eigene Units, Tonausgabe, Zufallszahlen

11.1. Eigene Units erstellen

Wir haben schon oft auf Bibliotheken (auch Units genannt) wie Crt zurückgegriffen, jedoch müssen wir uns nicht auf vorgefertigte Units beschränken. Sie haben unter Pascal auch die Möglichkeit, eigene zu erstellen. Dies ist einfacher als Sie vielleicht denken.

Units bieten Ihnen die Möglichkeit, Funktionen, Prozeduren, Variablen, Konstanten, Records, usw. allen Quelldateien zur Verfügung zu stellen, die die Unit inkludieren. Nehmen wir die Unit Crt (steht übrigens für Cathode Ray Tube; Bildröhre) als Beispiel her. In ihr werden unzählige Funktionen, Prozeduren und Variablen deklariert und definiert, die zur Datenein- und -Ausgabe benötigt werden.

Falls Sie auch in C oder C++ programmieren, können Sie eine Unit nur bedingt mit einer Headerdatei vergleichen. Während in einer Headerdatei in C/C++ nur Deklarationen stehen, sind Definitionen in Units nicht nur erwünscht, sondern auch Pflicht.

Bei Programmen war es bis jetzt egal (optional), ob eine PROGRAM-Zeile vorhanden war oder nicht. Bei Units hingegen ist das anders. In der ersten Zeile muss UNIT sowie der Name der Unit stehen!

Betrachtet man den Aufbau einer Unit, besteht dieser grob gesagt aus zwei Teilen, einem Deklarations- und einem Definitionsteil. Der Deklarationsteil wird mit dem Schlüsselwort INTERFACE, der Definitionsteil mit IMPLEMENTATION eingeleitet. Das Ende symbolisiert wie bei Programmen ein abschließendes END. .

Aufbau einer Unit:

UNIT Name;

INTERFACE
  Deklarationen
  ...
  ...

IMPLEMENTATION
  Definitionen
  ...
  ...

BEGIN
  Initialisierungen
  ...
  ...
END.

Nachdem eine Unit erstellt wurde, muss diese compiliert werden. Damit das funktioniert, muss die abgespeicherte .pas-Datei denselben Namen wie die Unit tragen. Lazarus (Free Pascal) erstellt aus dem INTERFACE-Teil dann eine .ppu-Datei, aus dem IMPLEMENTATION-Teil eine Objektdatei (Endung .o). Falls Sie mit Turbo Pascal arbeiten, erhalten Sie nach der Compilierung eine .tpu-Datei. Die .tpu-Dateien, die die unterschiedlichen Turbo Pascal - Versionen erstellen, sind untereinander nicht kompatibel. Hinweis: Vor der Compilierung sollten Sie in Turbo Pascal überprüfen, dass als Ausgabeziel Festplatte gewählt wurde.

Eine fertige Unit kann dann per USES-Anweisung in das Programm eingebunden werden.

Das nächste Beispiel zeigt, wie eine sehr simple Unit erstellt und in ein Programm eingebunden werden kann. Dabei lasse ich Ihnen diesmal alle Freiheiten. Das Beispiel ist dazu gedacht, dass Sie es an Ihre Wünschen anpassen. Es existiert eine Prozedur in der Unit, die standardmäßig aufgerufen wird. Sorgen Sie dafür, dass die Prozedur eine sinnvolle Aufgabe übernimmt und erweitern und verändern Sie die Unit nach Ihren Wünschen. Wichtig ist, dass Sie durch die Erstellung der Unit Zeit sparen, denn Sie müssen die Prozedur/Funktion bzw. die Variablen und Konstanten [usw.] nicht immer wieder neu definieren, wenn Sie sie in verschiedenen Programmen benötigen.

UNIT Unit1;
 
INTERFACE
 
  PROCEDURE Prozedur1;
 
IMPLEMENTATION
 
  PROCEDURE Prozedur1;
  BEGIN
    Writeln ('Prozedur1 wurde aufgerufen.');
  END;
 
END.

Das Beispiel demonstriert zugleich, wie Prozeduren (Funktionen) deklariert werden können (kam bisher in keinem Beispiel vor). Dazu schreiben Sie den Prozedur-/Funktionskopf (hier: PROCEDURE Prozedur1;) 1:1 in den INTERFACE-Teil.

Der Initialisierungsblock ist optional und wurde in diesem Beispiel weggelassen. Es wurden keine Variablen deklariert, die man definieren könnte, somit ist der Initialisierungsteil unnötig. Nachdem Sie den Quellcode abgetippt haben, speichern Sie die Datei unter Unit1.pas ab. Der Bezeichner, der nach UNIT steht, muss mit dem Dateinamen übereinstimmen. Wenn Sie einen anderen Namen wählen, müssen Sie das Beispiel (sowie das untere Hauptprogramm) entsprechend anpassen.

Falls Sie Turbo Pascal verwenden, dann überprüfen Sie, ob als Ausgabeziel Festplatte gewählt wurde und korrigieren Sie dies gegebenenfalls. Klicken Sie dazu auf Compiler und Ausgabeziel.

Lassen Sie dann die Unit kompilieren. Lazarus (Free Pascal) erstellt nach dem Compilieren eine .ppu-Datei, Turbo Pascal eine .tpu-Datei, andere Compiler tun Ähnliches. Damit die Unit verwendet werden kann, müssen Sie sie in dasselbe Verzeichnis kopieren, in dem auch die anderen Units liegen, die bei Ihrem Compiler standardmäßig mitgeliefert wurden. Falls Sie Schwierigkeiten haben, die Unit einzubinden, finden Sie im Lazarus-Wiki weitere Tipps.

Das nächste Beispiel inkludiert die Unit und ruft die Prozedur Prozedur1 auf:

PROGRAM Verwende_Unit;
 
USES Crt, Unit1;   (* Unit1 bezeichnet die selbsterstellte Unit! *)
 
BEGIN
  Prozedur1;   (* Prozedur aufrufen, die in der Unit definiert wurde *)
 
  ReadKey;
END.

11.2. Tonausgabe

Achtung: Die in diesem Kapitel vorgestellten Beispiele funktionieren womöglich mit Lazarus (Free Pascal) unter Windows und Linux nicht oder nicht richtig. Testen Sie die Beispiele mit Turbo Pascal 7, wenn Sie die Möglichkeit haben. Sie können dieses Kapitel aber auch einfach überspringen.

Pascal erlaubt es uns, Töne auf einfache Weise auszugeben. Die Töne (man könnte auch Lärm sagen ;-) ) werden über den internen - im Rechner eingebauten - Lausprecher ausgegeben. Zuständig für die Tonausgabe ist die Prozedur Sound. Diese verlangt als Parameter die Frequenz des Tones. Je höher die Frequenz, desto höher ist der Ton.

Beachten Sie, dass ein Mensch nur Töne von etwa 20 Hz bis 20 kHz wahrnehmen kann, eher bis 16 kHz, und das nimmt mit zunehmendem Alter ab. Den Bereich über 20 kHz (> 20.000 Hz) bezeichnet man als Ultraschall-, den Bereich unter 20 Hz als Infraschallbereich. Soviel nur am Rande. Beide Bereiche sind jenseits des Möglichen des eingebauten PC-Brüllers. ;-) Wirklich tiefe Töne (100 Hz sind noch optimistisch) wird er auch nicht wiedergeben können.

Sound (Frequenz);

Wurde Sound einmal aufgerufen, wird der Ton so lange ausgegeben, bis Sie den Rechner neustarten! Um dem Lärm ein - vorzeitiges - Ende zu bereiten, muss der Ton mit NoSound wieder ausgeschaltet werden. NoSound sollten Sie nicht vergessen!

NoSound;

Allerdings, werden Sound und NoSound unmittelbar nacheinander aufgerufen, werden Sie keinen Ton wahrnehmen können, da dieser, ehe Sie ihn hören können, wieder ausgeschaltet wird. Abhilfe schafft hier Delay. Delay wartet mit der Ausführung der nächsten Programmierzeile so lange, wie als Parameter in ms (Millisekunden = Tausendstelsekunden) angegeben wurde.

Delay (ms);

Sie können Delay nicht nur im Zusammenhang mit Tonausgabe verwenden, sondern überall dort, wo Sie eine Verzögerung (Wartezeit) einbauen wollen.

PROGRAM Tonausgabe;
USES Crt;
 
VAR Frequenz, Tonlaenge: Integer;
 
BEGIN
  Write ('Frequenz: ');
  ReadLn (Frequenz);
  Write ('Tonlänge: (Angabe in ms) ');
  ReadLn (Tonlaenge);
 
  Sound (Frequenz);   (* Ton ausgeben *)
  Delay (Tonlaenge);  (* so lange dauert die Ausgabe *)
  NoSound;   (* den Krach wieder ausschalten *)
 
END.

Bei diesem Beispiel kann der Benutzer selbst festlegen, wie lange ein Ton und mit welcher Frequenz er ausgegeben werden soll. Die Angaben für Frequenz und Tonlänge werden in den Variablen Frequenz und Tonlaenge gespeichert. Mit Sound wird anschließend ein Ton ausgegeben, der die in Frequenz gespeicherte Frequenz hat. Damit dieser Ton hörbar ist, kommt Delay zum Einsatz (sonst wäre er gleich wieder weg). Es wird mit der Ausführung der nächsten Programmierzeile so lange gewartet, wie in Tonlaenge in ms abgespeichert wurde. Steht beispielsweise in der Variable Tonlaenge die Zahl 1000, so wird 1 Sekunde (= 1000 ms) gewartet. Mit NoSound wird der Ton wieder ausgeschaltet, denn wir wollen verhindern, dass dieser auch nach dem Beenden des Programmes noch hörbar ist.

11.3. Zufallszahlen

Um in Pascal Zufallszahlen erzeugen zu können, steht die Funktion Random (von engl. random = zufällig) zur Verfügung. Damit Zufallszahlen erzeugt werden können, behilft sich Random der aktuellen Uhrzeit, die sich ja laufend ändert. Weiters existiert die Prozedur Randomize, die den Zufallsgenerator initialisiert. Diese muss vor der Verwendung von Random einmal aufgerufen werden. Die Initalisierung des Zufallsgenerators erfolgt durch einfachen Aufruf der Prozedur Randomize (ohne Parameter).

Randomize;   (* Zufallszahlengenerator initialisieren *)

Anschließend kann mit Random eine Zufallszahl erzeugt werden. Random verlangt als Parameter den Höchstwert der Zufallszahlen. Geben Sie beispielsweise 100 an, können Zufallszahlen im Bereich von 0 bis 99 (0 und 99 mit eingeschlossen) erzeugt werden. Geben Sie 101 an, würden Zahlen erzeugt werden, im Bereich von 0 bis 100 liegen. Um Werte ab 1 zu erhalten, werden wir als Höchstwert den gewünschten Höchstwert minus 1 angeben und nach der Erzeugung 1 hinzuaddieren. Random kann nach der Initalisierung des Zufallszahlengenerators mit Randomize beliebig oft aufgerufen werden, ohne dass eine erneute Initalisierung nötig wird. Random: (Beispiel mit Zuweisung)

Variable := Random (Höchstwert);

Wichtig: Random erzeugt sog. Pseudo-Zufallszahlen. Werte, die von einem Pseudo-Zufallszahlengenerator erzeugt werden, sind zu einem gewissen Maß vorhersehbar. Mehr zu diesem Thema finden Sie auf Wikipedia.

Heute schon Lotto gespielt? Das nächste Programm zeigt, wie 6 zufällige Zahlen im Bereich von 1 bis 45 (1 und 45 mit eingeschlossen) erzeugt werden können. Wichtig ist hierbei, dass keine Zahl erzeugt wird, die bereits vorkam! Um die bereits erzeugten Zahlen abzuspeichern, verwenden wir ein Set. Mit dem IN-Operator überprüfen wir, ob eine erzeugte Zahl im Set schon erhalten ist. Wenn ja, wird eine neue Zahl erzeugt. Wurden 6 verschiedene Zahlen generiert, werden die Zahlen ausgegeben.

PROGRAM Zufallszahlen;
USES Crt;
 
VAR zahlen: ARRAY [1..6] OF Integer;
    bisher: SET OF 1..45;   (* ggf. durch 49 ersetzen *)
    i: Integer;
 
BEGIN
  ClrScr;
  Randomize;   (* Generator initialisieren *)
 
  i := 1;  (* Index; die wievielte Zahl *)
 
  REPEAT
 
    zahlen[i] := (Random(45)+1);   (* eine Zahl erzeugen und zuweisen *)
       (* Deutsche ersetzen hier 45 durch 49 für Lotto "6 aus 49". *)
 
    IF (zahlen[i] IN bisher)   (* dann wurde die Zahl bereits erzeugt *)
      THEN Continue;   (* Schleife noch einmal von vorne ausfuehren; i bleibt gleich! *)
 
    bisher := bisher + [zahlen[i]];   (* Zufallszahl im SET speichern *)
    Inc (i);   (* i erhoehen *)
 
  UNTIL i > 6;
 
  Writeln ('Folgende 6 Zahlen wurden erzeugt:');
  FOR i := 1 TO 6 DO
    Write (zahlen[i], ' ');
 
  ReadKey;
END.

Wir deklarieren ein Array mit 6 Integern, ein Set (Minimalwert 1 und Maximalwert 45 der Zahlen, die im Set gespeichert werden dürfen) sowie die Variable i. Zunächst wird der Zufallsgenerator mit Randomize initalisiert. Dies ist die Voraussetzung dafür, dass Zufallszahlen generiert werden können.

Weiters folgt eine Schleife, die so lange läuft, bis der Zähler einen Wert > 6 erreicht hat. Immer dann, wenn ein Wert erzeugt wurde, der bereits im Set enthalten ist, wird die Schleife erneut aufgerufen und ein neuer Wert erzeugt. Neu ist hier auch Continue. Continue sorgt in einer Schleife (egal welche Art von Schleife!) dafür, dass diese erneut durchlaufen wird! Durch das Continue wird der aktuelle Durchlauf also abgebrochen und zur ersten Zeile im Anweisungsblock der Schleife gesprungen. Die danach folgenden Zeilen werden in dem Durchlauf also übersprungen, darum bleibt i im Beispiel gleich.

Der Zähler i wird also nur dann erhöht, wenn wirklich eine korrekte Zahl erzeugt wurde. Da i erst am Ende des Schleifenblocks mit Inc(i) erhöht (inkrementiert) wird, beträgt der Endwert von i gleich 7.

Vorheriges Kapitel Nächstes Kapitel