Programmieren mit Delphi 2005

Die Seite wird erstellt Till Metz
 
WEITER LESEN
Programmieren mit Delphi 2005

1. Teil: Eine Einführung in die Programmiersprache
         an Hand von Konsolenanwendungen

2. Teil: Nutzung der graphischen Programmentwick-
          lungsumgebung zur Erstellung von
                Windows-Programmen

        Entwurf eines Textes als Arbeitsgrundlage
              für den Informatik-Unterricht
                  in einem 11. Schuljahr

         2. Halbjahr des Schuljahres 2007/2008
                         (B.Spier)
Seite 2                                                              Programmieren mit Delphi 2005

Inhalt:

Teil 1: Grundlagen der Programmiersprache PASCAL                                          Seite 4

 1. Hallo-Welt-Programm                                                                   Seite 4
   (writeln, write, readln)

 2. Programm VarDemo                                                                      Seite 6
   (integer, real, string als Variablentypen; readln zum Einlesen)

 3. Programm If_Then_Demo                                                                 Seite 8
   (if-then-else)

 4. Programm QuadGl1                                                                      Seite 9
   (Wertzuweisung; Funktionen verwenden; begin-end für Anweisungsblöcke)

 5. Programm QuadGl1a                                                                   Seite 10
   (Verschachtelte if-then-else-Struktur, Warnhinweis)

 6. Programm QuadGl2                                                                     Seite 11
   (repeat-until-Schleife)

 7. Programm GGT1                                                                       Seite 12
   (while-Schleife)

 8. Programm Fakultaet                                                                  Seite 13
   (for-Schleife)

 9. Programm GGT3                                                                       Seite 14
   (function)

10. Programm GGT4                                                                       Seite 15
   (unit)

11. Programm KGV1                                                                       Seite 16
   (compilierte unit verwenden)

12. Programm „Berechnungen“                                                             Seite 18
   (procedure, boolean, char, case)

13. Records                                                                             Seite 19
   (type, record)

14. Klassen                                                                             Seite 20
   (class, create, free)
Programmieren mit Delphi 2005                                                                    Seite 3

Teil 2: Windows-Programme mit Delphi2005                                                       Seite 21

15. Hallo-Welt-Programm für Windows                                                            Seite 21
   (Schalter verwenden, showmessage)

16. Programm Gaskosten                                                                         Seite 24
   (Ein- und Ausgabe über TEdit und TLabel; Verwendung von Konstanten)

17. Programm Lottozahlengenerator                                                              Seite 27
   (array, random und randomize für Zufallszahlen)

18. Programm Lotto2                                                                            Seite 29
   (speichern von Daten: TSaveDialog, TOpenDialog)

19. Programm Lotto3                                                                            Seite 31
   (Menü verwenden)

20. Werteparameter, Variablenparameter                                                         Seite 32
   (var: ein wenig Theorie zur Übergabe von Daten an Methoden)

21. Programm Gaskosten2                                                                        Seite 34
   (ein weiteres Fenster für Einstellungen, ShowModal)

22. Rekursive Programmierung                                                                   Seite 36
   (eine Funktion ruft sich selbst auf!)

23. Weitere Komponenten                                                                        Seite 37
   (TCheckBox, TRadiobutton, TGroupBox, TRadioGroup)

24. Schlussbemerkungen                                                                         Seite 39

Lösungen der Aufgaben                                                                          Seite 40

Übersicht zur Wiederholung, Tipps und Hinweise                                                 Seite 44
   (Programmstrukturen-Übersicht, Arbeiten mit Strings, Tipps zur IDE, nützliche Funktionen)

Stichwortverzeichnis                                                                           Seite 46
Seite 4                                                            Programmieren mit Delphi 2005

Programmieren mit Delphi 2005

Teil 1: Grundlagen der Programmiersprache PASCAL
PASCAL ist eine Programmiersprache, die für stark strukturierten Quelltext von Niklaus Wirth
entwickelt wurde. Mit zunehmenden Fähigkeiten von Computern wurden die Ansprüche der
Programmierer größer und Pascal entsprechend erweitert. Aus Turbo-Pascal für DOS entwickelte
Borland ein Pascal für Windows (Win3.1), Klassen wurden als Datentyp eingeführt (um Prozeduren
und Variablen in einem Typ zusammen zu fassen), mit Delphi 1 kam die erste grafische Pascal-
Entwicklungsoberfläche für Windows3.1 auf den Markt, mit Delphi 2 die für Windows95, die
Weiterentwicklungen ab Delphi 3 passten sich der Weiterentwicklung der Microsoft Betriebssysteme
an. Was blieb, war die Syntax des Programmquelltextes, dessen Sprache wegen der vielen
Erweiterungen kein reines Pascal mehr ist. Die grafische Entwicklungsumgebung von Delphi erzeugt
viele Zeilen des Quelltextes automatisch. Um den Aufbau der Quelltexte besser zu verstehen,
beginnen wir mit Programmen ohne grafische Oberfläche.

1. Hallo-Welt – ein Minimalprogramm zum Einstieg
Unser erstes Programm soll nur den Text „Hallo Welt!“ ausgeben. Wir wählen über das Menü Datei-
neu-weitere den Projekttyp Konsolenanwendung.
Erster Schritt nach Beginnen eines neuen Projekts sollte immer das Speichern des
Projekts in einem eigens für dieses Projekt erstellten Ordner sein!
Für den Dateinamen gibt es Einschränkungen:
Der Dateiname hat die Rolle eines Pascal-Bezeichners und unterliegt damit denselben Ein-
schränkungen wie sie z.B. auch für Variablennamen gelten: Es dürfen nur Buchstaben, Ziffern und
der Unterstrich ( _ ) verwendet werden, das erste Zeichen darf keine Ziffer sein. Sonderzeichen
(Leerzeichen, Bindestrich, Umlaute, ß, $, § usw.) sind nicht erlaubt. Die Pascal-Syntax unterscheidet
nicht zwischen Groß- und Kleinbuchstaben.
Wenn wir unser erstes Projekt als „hello_word“ speichern, lautet der vorgegebene Quelltext:
                                program hello_world;
Von diesem Text löschen wir
die hier überflüssige uses-     {$APPTYPE CONSOLE}
Anweisung und den TODO-
Kommentar und fügen statt       uses
dessen zwischen begin und         SysUtils;
end. zwei eigene Programm-
                                begin
zeilen ein, um folgendes Er-
                                 { TODO -oUser -cConsole Main : Hier Code einfügen }
gebnis zu erhalten:
                                end.

 1:   program hello_world;
 2:
                                      Zeile 1: Programmname
 3:   {$APPTYPE CONSOLE}              Zeile 3: Compileranweisung „erzeuge Konsolenanwendung“
 4:                                   Zeilen 5 und 8: Zwischen begin und end. steht immer der
 5:   begin                             Code des (Haupt-)Programms.
 6:     writeln('Hallo Welt!');       Zeile 6: Textausgabe.
 7:     readln;
 8:   end.                            Zeile 7: Warten auf die Eingabetaste, mit der dann das Pro-
 9:                                     gramm (und damit das Konsolenfenster) beendet wird.

Man beachte: Jede Anweisung endet mit Semikolon! (Auch Zeile 1 endet damit.)
Programmieren mit Delphi 2005                                                                 Seite 5

Bei Konsolenanwendungen sind writeln und write die typischen Ausgabe-Befehle. Sie unter-
scheiden sich darin, dass writeln (= write line) den Cursor im Textfenster in die nächste Zeile an den
Anfang setzt, während write den Cursor hinter dem zuletzt ausgegebenen Zeichen belässt. readln
ist eigentlich zum Einlesen einer Eingabe gedacht (siehe nächstes Programm). Es dient hier aber
dazu, auf die Betätigung der Eingabetaste zu warten, bevor das Programm beendet wird.
    Zum Starten eines Programms aus der integrierten Delphi-Entwicklungsumgebung (oder
integrated development engine, kurz IDE) ist es am einfachsten, F9 zu tippen, es kann aber genau so
der grüne Start-Schalter geklickt werden. Dabei geschieht folgendes:
Zunächst wird der Quelltext compiliert, d.h. es wird eine ausführbare Datei erzeugt. Sie trägt den
Namen des Programms mit der Endung .exe, hier also hello_world.exe. Falls das Projekt (und damit
der Quelltext) bereits gespeichert wurde, gelangt diese Datei in denselben Ordner wie das Projekt.
Dass der Programmierer wissen sollte, wo hello_world.exe erzeugt wird, ist ein weiterer Grund, jedes
neue Projekt als allererstes zu speichern! Nachdem die ausführbare Datei erstellt ist, wird sie
aufgerufen. Windows erstellt dann ein Konsolenfenster, in welchem Text ausgegeben werden kann
bzw. in dem Eingaben erfolgen können. In unserem Beispiel sieht das so aus:

Es gibt mehrere Möglichkeiten, das Programm auch ohne die IDE aufzurufen:
➔ Auswahl der Datei in einem Explorer-Fenster, dort die Datei doppelt anklicken
➔ Start – Ausführen, dort „J:\Text\Informatik\2007-2008\Pascal\hello_world.exe“ eingeben (der Pfad
    zur .exe-Datei muss individuell angepasst werden)
➔ Start – Programme – Zubehör – Eingabeaufforderung wählen, dort entweder
    J:\Text\Informatik\2007-2008\Pascal\hello_world.exe eingeben
    oder in den Ordner der Datei wechseln und dann nur hello_world eingeben. Die entsprechenden
    DOS-Befehle dazu zeigt die folgende Abbildung (zuerst die Laufwerksauswahl, dann „cd“ für
    „change directory“, dann der Programmaufruf, bei dem „.exe“ auch fehlen darf):

Bei der Verwendung der letzten Variante kann das Programm sogar noch kürzer sein: Die readln-
Zeile kann fehlen, weil dieses Fenster nach Programmablauf geöffnet bleibt. Es kann mit der Maus
geschlossen werden, es kann aber auch mit exit verlassen werden (ggfs. vorher mit der Eingabetaste
das Programm hello_world beenden!).
Aufgabe 1:
    Nach dem Speichern des Projekts (in dem eigens dafür vorgesehenen Ordner) werden mehrere
    Dateien für das Projekt angelegt. Eine von ihnen enthält den Quelltext, andere enthalten
    Konfigurationseinstellungen. Wie viele Dateien sind es, welche der Dateien enthält den
    Quelltext? (Auf die Dateiendung, also den Dateityp kommt es an!)
Lösen Sie die Aufgaben möglichst selbstständig. Vergleichen Sie erst dann mit den am Ende des Dokuments
aufgeführten Lösungen, dort finden Sie oft auch noch ergänzende Informationen zum Thema!
Seite 6                                                          Programmieren mit Delphi 2005

2. Programm VarDemo: Verwendung von Variablen
Pascal verlangt, dass jede Variable vor ihrer Verwendung dem System bekannt sein muss, bei der
Deklaration wird zugleich der Typ festgelegt. Während bei anderen Programmiersprachen nach a=7
die Variable a als Ganzzahl, als Dezimalzahl (mit anschließender Zuweisung a=a+0.5 erhält a den
Wert 7.5) oder als Zeichenkette (mit anschließender Zuweisung a=a+"Zwerge" enthält a den Text
"7Zwerge") interpretiert werden kann, ist Pascal streng und eindeutig. Das mag umständlich
erscheinen, ist aber bei der Vermeidung von Missverständnissen und Fehlern hilfreich.
In diesem Programm wollen wir uns mit 3 Variablentypen vertraut machen.
Wir rufen dazu den Menüpunkt Datei-Neu-weitere und wieder die Konsolenanwendung auf. Dann
speichern wir das Projekt im Ordner für unsere Delphi-Projekte, wobei wir für dieses Projekt einen
neuen Ordner mit dem Namen „ Variablen“ (oder „VarDemo“) einrichten. Auch wenn es bei
Konsolenanwendungen, die nur eine einzige Quelltextdatei besitzen, keinen Unterschied zwischen
„Projekt speichern unter“, „Speichern unter“ (aktuelle Datei), „Speichern“ (wenn noch nicht
gespeichert wurde, wird daraus „Speichern unter“) gibt, sollte man sich in Hinblick auf umfang-
reichere Projekte daran gewöhnen, stets zuerst „PROJEKT SPEICHERN UNTER“ zu wählen. In
diesem Fall wählen wir den Dateinamen VarDemo.bdsproj (.bdsproj wird automatisch angehängt,
wenn keine Extension angegeben wird). Dann bearbeiten wir den Quelltext, bis er wie folgt lautet:
  1:   program VarDemo;
  2:
  3:   {$APPTYPE CONSOLE}
  4:
  5:   var
  6:     a: integer;
  7:     b: real;
  8:     c: string;
  9:   begin
 10:     write('Bitte eine ganze Zahl eingeben: ');
 11:     readln(a);
 12:     write('Bitte eine Dezimalzahl (wie 12.75) eingeben: ');
 13:     readln(b);
 14:     write('Bitte eine Zeichenkette eingeben: ');
 15:     readln(c);
 16:     writeln; // Erzeugt eine Leerzeile.
 17:     // Die eingegebenen Daten werden zur Kontrolle wieder ausgegeben.
 18:     // Dieses Demo-Programm zeigt, wie mit Variablen gearbeitet werden kann:
 19:     writeln('Ihre ganze Zahl ist die Haelfte von ',2*a);
 20:     writeln('Ihre Dezimalzahl ist das Dreifache von ',b/3);
 21:     writeln('Und die Zeichenkette ist ','"'+c+'"');
 22:     readln; // Programm erst nach Tippen der Eingabetaste beenden
 23:   end.
 24:
(Die Zeilennummern werden von der IDE angezeigt, sie werden natürlich nicht eingegeben!)
Wenn das Programm fehlerfrei eingetippt wurde, kann der Programmablauf etwa folgendes Bild
bringen:
Programmieren mit Delphi 2005                                                                  Seite 7

Tipp: Der Quelltext dieses Programms ist schon etwas länger. Um die Syntax zu prüfen, kann die
Tastenkombination Strg-F9 verwendet werden: Sie compiliert das Programm, ohne es zu starten.
Daher muss man im Falle fehlerfreien Quelltextes keine Eingabe vornehmen, um das Programm zu
beenden. Im Fehlerfall läuft dagegen Strg-F9 und F9 auf dasselbe hinaus: Der Fehler wird unten im
Editorfenster angezeigt, eine neue Exe-Datei wird nicht erzeugt.
Erläuterungen zu den verwendeten Variablentypen:
INTEGER – ganze Zahlen (positiv oder negativ):
Für integer-Variablen werden 4 Bytes an Speicherplatz reserviert, das sind 32 Bit. Ein Bit wird für das
Vorzeichen benötigt, es bleiben also 31 Bit für den Betrag. Die größte integer-Zahl lautet in dualer
Darstellung (=Zweiersystem) 01111111 11111111 11111111 11111111, was dezimal 2147483647 = 231–1 ist.
Wenn man im Dualsystem zu 11111111 11111111 11111111 11111111 die Zahl 1 addiert, wird daraus 0,
falls kein weiteres Bit zum Speichern des Übertrags verfügbar ist. Die Zahl –1 wird daher dual als
11111111 11111111 11111111 11111111 gespeichert, 11111111 11111111 11111111 11111110 stellt entsprechend
–2 dar. Die negative integer-Zahl mit dem größten Betrag ist – 2147483648, die dual als
10000000 00000000 00000000 00000000 gespeichert wird.
Früher wurden für integer-Variablen nur 2 Bytes verwendet, entsprechend war der Zahlbereich
kleiner und reichte nur von –32768 bis +32767. Möchte man aus Kompatibilitätsgründen 2-Byte-
integer-Zahlen verwenden, muss man als Typ smallint verwenden. Seltener wird man sich auf ein
einziges Byte begnügen wollen, es ist aber mit shortint möglich. Wenn zukünftige Prozessoren
intern mit 8-Byte-Registern rechnen, kann in späteren Compilern der Typ integer eine 8-Byte-Zahl
bedeuten. Der Typ integer wird daher als „generischer Typ“ bezeichnet, seine Verwendung nutzt die
Prozessormöglichkeiten optimal aus. Er ist derzeit mit dem Typ LongInt (ebenfalls 4 Byte) identisch,
LongInt ist und bleibt aber auf 4 Byte festgelegt (da weiß man, was man hat).
Wer jetzt schon mit 8 Byte großen Integer-Zahlen rechnen muss, kann den Typ Int64 verwenden.
Neben diesen Integer-Typen gibt es auch noch vorzeichenlose Ganzzahl-Typen:
byte (1 Byte, mögliche Werte von 0 bis 255), word (2 Bytes, Werte von 0 bis 65535), longword (4
Bytes, auch als uint = unsigned integer oder dword = doubleword bezeichnet) von 0 bis
4294967295=232–1, der generische Typ Cardinal ist derzeit mit longword identisch.

REAL – „reelle“ Zahlen, auch Fließkomma-Zahlen genannt (3,75·107 · 2000 = 7500·107 = 7,5·1010;
das dezimale Trennzeichen „fließt“ beim Rechnen):
real ist ebenfalls ein generischer Typ, der Zahlen in 8 Bytes speichert. Was in einer dezimalen
Darstellung der Zehnerexponent ist, ist in der dualen Darstellung (die ein Computer verwendet) der
Zweierexponent (0,0625 ist dual 1·2–4). Auf eine genaue Darstellung, wie viele Bits für den
Exponenten und wie viele für die Mantisse genutzt werden, wird hier verzichtet. Real ist derzeit mit
dem nicht-generischen Typ double („doppelte“ Genauigkeit gegenüber real bei früheren Compilern)
identisch. Wegen der endlichen Stellenzahl stellt real natürlich keine wirklichen reellen Zahlen dar,
                                                                    1
sondern nur brauchbare Näherungen. Selbst der einfache Bruch 5 ist im Dualsystem periodisch
                                               1
und muss gerundet werden, vergleichbar mit     3   im Dezimalsystem.

STRING – Zeichenketten (engl. Kette = string):
Strings sind ein komplizierter Variablentyp, weil der Speicherplatzbedarf von der Länge des Strings
abhängt. Pascal reservierte früher für Strings als Standard 256 Bytes: Byte 0 für die Länge, Bytes 1
bis 255 für die Zeichen – es konnten aber auch Strings kürzerer Länge vereinbart werden. Inzwischen
werden Strings dynamisch verwaltet und können bis zu 4GB lang sein. Die Länge ist nicht mehr
Bestandteil des Strings, sie kann mit der Funktion Length ermittelt werden. Unter Delphi ist das
Arbeiten mit Strings z.T. einfacher als bei anderen Programmiersprachen, bei denen das mit 0
codierte Zeichen stets das Ende eines Strings darstellt und für jeden String vor seiner Verwendung
Länge+1 Bytes an Speicherplatz reserviert werden müssen.
Seite 8                                                            Programmieren mit Delphi 2005

Stringkonstanten (z.B. 'Hallo Welt!' im ersten Programm) sind durch einzelne Hochkommas
eingeschlossen (nicht durch "). Wenn der String ein solches als Zeichen enthalten soll, muss es 2-fach
gesetzt werden, z.B. 'Nimm''s leicht'.
Im Beispiel VarDemo.exe wurde das Wort „Hälfte“ als „Haelfte“ geschrieben. Der Grund dafür ist,
dass Umlaute (und ß) unter dem ersten Microsoft-Betriebssystem MS-DOS anders codiert wurden als
später unter Windows. Das Fenster „Eingabeaufforderung“ (auch DOS-Fenster oder Konsolenfenster
genannt) verwendet die DOS-Codierung. Da wir uns nur in der Einstiegsphase mit Kon-
solenanwendungen befassen, wird hier nicht auf mögliche Abhilfen eingegangen.

3. Programm If_Then_Demo
Hier lernen wir die Programmierung von Fallunterscheidungen kennen. Wir beginnen also ein neues
Projekt – dazu gehört auch das Speichern des neuen Projekts in einem eigens dafür einzurichtenden
Ordner (siehe vorige Kapitel; Vorschlag: Ordner If_Then_Demo, Projektname ebenfalls).
Das Programm soll den Anwender fragen, wer die Sprache Pascal entwickelt hat und die Antwort
bewerten. Die if-then-else-Anweisung wird wie jede andere Anweisung mit Semikolon abge-
schlossen, der else-Teil ist optional (er darf fehlen). Die Syntax lautet also:
       if  then
         ;
bzw.
       if  then
         
       else
         ;
Bitte Beachten: Vor dem else steht kein Semikolon, weil sonst die if-Anweisung bereits
abgeschlossen wäre. (Ein häufiger Fehler, der aber leicht zu korrigieren ist, wenn man ihn an der
Fehlermeldung erkennt.)
Sie können versuchen, mit diesen Informationen das Programm zunächst selbst zu erstellen. Lesen
Sie bitte aber auch dann den folgenden Quelltext, um die Konvention des Einrückens von Textzeilen
zu lernen, mit der Quelltexte übersichtlich werden sollen!
   1: program If_Then_Demo;
   2: {$APPTYPE CONSOLE}
   3:
   4: var
   5:   antwort: string;
   6: begin
   7:   writeln('Wer entwickelte die Sprache PASCAL?');
   8:   readln(antwort);
   9:   writeln;
  10:
  11:// Jetzt die Antwort prüfen:
  12:   if antwort = 'Wirth' then
  13:     writeln('Bravo, das ist richtig!')
  14:   else
  15:     writeln('Leider falsch, es ist Wirth.');
  16:
  17:{ Die folgenden Programmzeilen werden wieder
  18: unabhängig von der Antwort ausgeführt. }
  19:   writeln;
  20:   writeln('Es hat Ihnen hoffentlich Spass gemacht.');
  21:   readln;
  22: end.
Programmieren mit Delphi 2005                                                               Seite 9

4. Programm QuadGl1: Wertzuweisung, Anweisungsblock
Das Programm soll uns die Lösungen einer quadratischen Gleichung x 2 p⋅xq=0 berechnen.
Wir speichern das neue Projekt in einem neuen Ordner QuadGl1, und so nennen wir auch das darin
gespeicherte Projekt.

                                                                           
                                                                               2
Bei der Berechnung der Lösung nach der Lösungsformel                   p      p
                                                               x 1,2=− ±         −q kann der Fall
                                                                       2      2
eintreten, dass die Zahl unter der Wurzel (der Radikand) negativ ist. Damit das Programm dann nicht
mit einer Fehlermeldung abbricht, wird zuvor das Vorzeichen des Radikanden getestet.
Wenn es zwei Lösungen gibt, sollen beide in je einer Zeile ausgegeben werden. Weil die Syntax der
if-then-else-Anweisung zwischen then und else nur eine einzige Anweisung erwartet, müssen die
beiden writeln-Anweisungen mit begin und end zu einem Anweisungsblock zusammen gefasst
werden (end hier ohne Semikolon, weil ein else folgt). Für gute Übersichtlichkeit auch in langen
Quelltexten werden begin und zugehöriges end gleich weit eingerückt.
Hier nun der Quelltext:
   1:   program QuadGl1;
   2:   {$APPTYPE CONSOLE}
   3:   var
   4:     p, q, Radikand, x1, x2: real;
   5:   begin
   6:     writeln('x^2 + px + q = 0');
   7:     write('p? ');
   8:     readln(p);
   9:     write('q? ');
  10:     readln(q);
  11:     Radikand := Sqr(p/2)-q;
  12:     if Radikand >= 0 then
  13:       begin
  14:         x1 := -p/2 + Sqrt(Radikand);
  15:         x2 := -p/2 – Sqrt(Radikand);
  16:         writeln('x1 = ',x1);
  17:         writeln('x2 = ',x2);
  18:       end
  19:     else // Für ö wird ALT-0148 eingegeben!
  20:       writeln('Die Gleichung hat keine L”sung.');
  21:     readln;
  22:   end.

Beachten Sie die folgenden Erläuterungen:
Zeile 4: Mehrere Variablen desselben Typs können in einer gemeinsamen Anweisung deklariert
   werden, sie werden dabei durch Komma getrennt.
Zeile 11: Wertzuweisungen an eine Variable werden in Pascal mit := formuliert, das Gleichheits-
   zeichen ohne Doppelpunkt wird dagegen beim Vergleich verwendet
   (if a=0 then ...).
   Sqr ist eine Funktion. Funktionen geben wie in der Mathematik einen Wert zurück, der meistens
   von einem oder mehreren übergebenen Werten abhängt. Hier wird als Wert -p/2 übergeben, Sqr
   ist die Quadratfunktion (=square). Man hätte Zeile 11 auch als
      Radikand := (p/2)*(p/2)-q; oder
      Radikand := p*p/4-q; formulieren können, ohne die Quadratfunktion zu verwenden.
Zeile 12: Vergleichsoperatoren sind , =, >= (größer oder gleich),
Seite 10                                                          Programmieren mit Delphi 2005

   an Variable x1 und x2 macht das Programm übersichtlich, ist aber nicht zwingend erforderlich.
   Zeile 16 könnte daher auch auch lauten:
           writeln('x1 = ',-p/2 + Sqrt(Radikand));
   Mit entsprechender Zeile 17 wären die Zeilen 14 und 15 dann überflüssig.
Zeile 20 stellt den Sonst-Fall mit einer einzigen Anweisung dar. Wenn auch der Sonst-Fall aus
   mehreren Anweisungen besteht, wird auch hier ein Block mit begin und end verwendet, in
   diesem Fall mit abschließendem Semikolon.

5. Programm QuadGl1a: Verschachtelte if-Anweisungen
Das vorige Programm soll hier geringfügig geändert werden, denn man kann den Fall mit nur einer
einzigen Lösung (x1 und x2 fallen zusammen) gesondert behandeln. Er tritt ein, wenn der Radikand
Null ist, deshalb werden die Zeilen 12 bis 20 von QuadGl1 durch folgende Zeilen ersetzt:
   12:     if Radikand = 0 then
   13:       writeln('Einzige L”sung ist x = ',-p/2)
   14:     else
   15:       if Radikand > 0 then
   16:          begin
   17:            writeln('1. L”sung: x1 = ', -p/2 + Sqrt(Radikand));
   18:            writeln('2. L”sung: x2 = ', -p/2 – Sqrt(Radikand));
   19:          end
   20:       else
   21:          writeln('Die Gleichung hat keine L”sung.');
(Dies kann ausprobiert werden, ohne das Programm unter einem neuen Namen zu speichern – oder
speichern Sie das Projekt ausnahmsweise im selben Ordner wie QuadGl1 als QuadGl1a.)
     WARNUNG!
     Auf keinen Fall sollten Sie im Quelltext in der ersten Zeile den Programm-Namen QuadGl1 in
     QuadGl1a ändern! Der Bezeichner, der auf das Schlüsselwort program folgt, muss immer mit
     dem Dateinamen (ohne Endung .dpr) übereinstimmen!
     Grundsätzlich gilt die Regel, dass automatisch erzeugter Quelltext nie geändert werden
     darf, wenn Sie sich Ärger ersparen wollen! (Kommentare dürfen natürlich gelöscht werden.
     Bisheriges Löschen von „uses SysUtils“ war eine absolute Ausnahme.)
Hier besteht der Sonst-Fall der ersten if-Anweisung aus einer weiteren if-Anweisung.
Zum Testen aller 3 Fälle wurde QuadGl1a in folgender Abbildung drei mal nacheinander in einem
Eingabeaufforderungsfenster aufgerufen:
Programmieren mit Delphi 2005                                                            Seite 11

Aufgabe 2                          program aufgabe2; {$APPTYPE CONSOLE}
   Pit hat ein Programm            var AmPCsitzt: string;
   geschrieben. Ermitteln Sie        Ergebnis1, Ergebnis2: string;
   an Hand seines Quellcodes,      begin
   welcher Text ausgegeben
                                     write('Geben Sie bitte Ihren Namen ein: ');
   wird, wenn der Name Moni
                                     readln(AmPCsitzt);
   oder der Name Pit ein-
   gegeben wird. Lassen Sie        if AmPCsitzt = 'Pit' then begin
   sich dabei nicht durch die        Ergebnis1 := 'Am PC sitzt ein Programmierer';
   unsinnigen Einrückungen in           Ergebnis2 := 'mit hervorragenden Kenntnissen!';
   die Irre leiten!                end else
                                      Ergebnis1 := 'Am PC sitzt eine hübsche Person';
                                      Ergebnis2 := 'ohne Ahnung von Pascal.';
                                   writeln(Ergebnis1);
                                   writeln(Ergebnis2);
                                   readln;
                                   end.

6. Programm QuadGl2: Repeat-Schleife
Wenn ein Programmabschnitt mehrfach nacheinander ausgeführt werden soll, spricht man von einer
Programmschleife. In den Anfängen elektronischer Rechenanlagen war eine Schleife sehr konkret:
Der Lochstreifen mit dem Programmcode wurde zu einer Schleife geformt und zusammen geklebt!
Syntax der Repeat-Schleife: repeat  until ;
Wir erweitern zunächst das   1:   program QuadGl2; {$APPTYPE CONSOLE}
Programm Quadgl1 durch       2:   uses SysUtils;
eine repeat-Schleife, um     3:   var
                             4:     p, q, Radikand, x1, x2: real;
mehrere quadratische Glei-
                             5:     s: string; // erhält erste Eingabe
chungen nacheinander lösen   6:   begin
zu können. Dazu öffnen wir   7:     writeln('x^2 + px + q = 0, Ende mit Leer-Eingabe');
das Projekt Quadgl1 (Menü    8:     repeat
Datei, Projekt öffnen) und   9:        write('p = ');
speichern es anschließend   10:        readln(s); // String für p oder leerer String
im neuen Ordner Quadgl2     11:        if s  '' then // Mit '' den Block überspringen!
unter dem Namen QuadGl2.    12:          begin
                            13:            p := StrToFloat(s);// Typumwandlung
Beachten Sie, dass repeat 14:              write('q = ');
und until bereits Anfang 15:               readln(q);
und Ende der Schleife ein- 16:             Radikand := Sqr(p/2)-q;
deutig kennzeichnen, meh- 17:              if Radikand >= 0 then
                            18:              begin
rere Anweisungen dazwi- 19:                     x1 := -p/2 + Sqrt(Radikand);
schen müssen daher nicht 20:                    x2 := -p/2 - Sqrt(Radikand);
durch begin und end zu 21:                      writeln('x1 = ',x1);
einem Anweisungsblock zu- 22:                   writeln('x2 = ',x2);
sammengefasst werden. Um 23:                 end
die Schleife verlassen zu 24:              else
                            25:              writeln('Die Gleichung hat keine L”sung.');
können, wurde hier eine
                            26:            writeln; // Leerzeile vor jeder neuen Aufgabe
Prüfung auf die leere Ein- 27:           end; // Ende des 'if s  '' – Blocks.
gabe (ein String mit 0 28:          until s=''; // repeat-Ende: Keine Wdh. bei s=''
Zeichen) anstelle von einer 29:   end.
Zahl für p vorgesehen. In
Zeile 13 wird deshalb zur Typumwandlung die Funktion StrToFloat benötigt, die versucht, aus
einer Zeichenkette eine real-Zahl zu erzeugen, denn die Zeichenkette im String s kann nicht der zum
Rechnen benötigten real-Variablen p zugewiesen werden. Diese Funktion StrToFloat steht aber nur
Seite 12                                                           Programmieren mit Delphi 2005

zur Verfügung, wenn die Unit SysUtils in das Programm eingebunden wird. Deshalb wird hier vor der
Variablendeklaration die Anweisung uses SysUtils; benötigt. (Weiteres zum Thema Units im
Kapitel 10.)
Beachten Sie auch hier wieder die Einrückungen, mit der die Programmstruktur verdeutlicht wurde:
repeat steht an gleicher Position wie das zugehörige until!
Um aus dem Quelltext von Quadgl1 den für Quadgl2 zu erzeugen, müssen Sie einige Zeilen ergänzen.
Für das Einrücken von vorhandenen Zeilen können Sie mehrere Zeilen markieren, dann Strg gedrückt
halten und k i nacheinander tippen (falls zu weit eingerückt: Strg und k u), eventuell mehrfach.
Aufgabe 3
  Erklären Sie, warum in QuadGl2 kein readln vor dem abschließenden end. steht.

7. Programm GGT1: While-Schleife
Hier wird ein Programm vorgestellt, das den größten gemeinsamen Teiler (GGT) zweier natürlicher
Zahlen berechnet. Das Verfahren basiert auf folgendem Gedanken: Es sei a größer als b. Wenn x der
GGT von a und b ist, dann ist x auch Teiler von a–b, b kann sogar mehrmals subtrahiert werden,
solange das Ergebnis positiv bleibt: Man gelangt so zum Divisionsrest der Division a:b. Der GGT(a,
b) ist also auch der GGT(b, Divisionsrest von a:b). Diese Überlegung kann man so oft anwenden, bis
der Divisionsrest 0 ist. Dann ist der Teiler (b) der GGT. Beispiel: x = GGT(51, 15): 51:15 hat den
Rest 6, also ist x = GGT(15, 6). 15:6 hat den Rest 3, also gilt x =GGT(6, 3). 6:3 hat den Rest 0, also
ist x = 3.
Syntax der While-Schleife: while  do ;
Das Programm GGT1:
   1:   program GGT1; {$APPTYPE CONSOLE}
   2:   uses SysUtils;
   3:   var a, b, c, d, h: cardinal;
   4:   begin
   5:     writeln('Berechnung des GGT zweier Zahlen');
   6:     // ---------Eingabe----------------
   7:     write('a='); read(a);
   8:     write('b='); readln(b);
   9:     // ---------Berechnung-------------
  10:     c:= a; // c, d erhalten die Startwerte a, b.
  11:     d:= b;
  12:     while d > 0 do
  13:        begin
  14:          c := c mod d; // Divisionsrest nach c
  15:    // Inhalte von c, d tauschen, damit d die kleinere Zahl enthält:
  16:          h := c; // Wert aus c in Hilfsvariable h merken
  17:          c := d; // c erhält den Wert von d
  18:          d := h; // d erhält den Wert von h, also den alten c-Wert.
  19:        end;
  20:     // -------Ergebnis ausgeben--------
  21:     writeln('Der GGT von ',a,' und ',b,' ist ',c);
  22:     readln;
  23:   end.
Erläuterungen:
Zeile 14: Der Operator mod berechnet den Divisionsrest (mod steht für modulo). Wenn c kleiner als d
   ist, ist der Rest von c:d die Zahl c selbst. In diesem Fall wird im Schleifendurchlauf keine neue
   Zahl berechnet, es werden dann nur die Inhalte der Variablen c und d getauscht.
Zeilen 16, 17, 18: Zum Tauschen zweier Werte wird immer eine Hilfsvariable benötigt!
Zeile 12: Die While-Schleife hat im Gegensatz zur Repeat-Scheife eine Anfangsabfrage. Unter
   Umständen wird die Schleife keinmal durchlaufen, während eine Repeat-Schleife wegen der End-
   abfrage immer mindestens einmal durchlaufen wird. Falls Sie mit dem Programm GGT(7, 0)
   berechnen, wird die Schleife nicht durchlaufen, es kann sofort der GGT 7 ausgegeben werden.
Programmieren mit Delphi 2005                                                                 Seite 13

   Wenn Sie dagegen den GGT(0, 7) berechnen, wird in Zeile 14 der Rest von 0:7 berechnet, er ist 0,
   es wird getauscht, also ist d=0 und c=7, ein weiterer Schleifendurchlauf erfolgt nicht.
Arbeitsauftrag
  Erweitern Sie das Programm GGT1 so, dass wie bei QuadGl2 wiederholt Rechnungen durchgeführt
  werden können, bis eine leere Eingabe anstelle der ersten Zahl eingegeben wird. Speichern Sie
  dieses erweiterte Programm als GGT2. (Zur Typumwandlung benötigen Sie hier die Funktion StrToInt,
  um aus einem String eine Ganzzahl zu erhalten!)

8. Programm Fakultaet: For-Schleife
In verschiedenen Bereichen der Mathematik spielt ein Produkt der Form 1·2·3·4·...·n eine wichtige
Rolle (Kombinatorik, Analysis). Die Funktion, die jeder natürlichen Zahl n das Produkt 1·2·3·4·...·n
zuordnet, heißt Fakultät. Man schreibt dafür n!; so bedeutet z.B. 3!=1·2·3=6. Gleichungen aus der
Wahrscheinlichkeitsrechnung behalten ihre Gültigkeit auch bei n=0, wenn man 0!=1 festsetzt. Dann
ist 1!=0!·1, 2!=1!·2, 3!=2!·3 usw.
Für den Fall, dass von vorn herein feststeht, wie oft eine Schleife durchlaufen werden soll, ist die For-
Schleife vorgesehen, sie wird auch Zählschleife genannt.
Syntax der For-Schleife:
for  :=  to  do ;
Statt einer einzigen Anweisung wird man häufig einen Anweisungsblock verwenden.
Wenn die Schleifenvariable sich vom Anfangswert mit den Schleifendurchläufen auf den Endwert
verkleinern soll, muss to durch downto ersetzt werden.
   1:   program Fakultaet;
   2:   {$APPTYPE CONSOLE}
   3:   var
   4:     Ergebnis: int64;
   5:     n, i: integer;
   6:   begin // ä mit ALT-0132
   7:     writeln('Berechnung von Fakult„ten');
   8:     write('n = '); readln(n);
   9:     Ergebnis := 1;
  10:     for i := 2 to n do
  11:        Ergebnis := Ergebnis * i;
  16:     writeln(n,'! = ',Ergebnis);
  17:     readln;
  18:   end.
Erläuterungen:
Zeile 10: Weil der Schleifenvariablen i ein Wert zugewiesen wird, steht auch hier :=.
   Wenn für n die Zahl 1 (oder 0) eingegeben wurde, ist der Startwert 2 größer als der Endwert n. In
   diesem Fall wird die Schleife nicht durchlaufen. (Bei n=2 wird die Schleife einmal durchlaufen.)
   Innerhalb der Schleife wird die Schleifenvariable am Ende jedes Durchlaufs um 1 erhöht. Wenn
   ihr Wert den Endwert überschreitet, wird die Schleife verlassen und das Programm in diesem Fall
   mit Zeile 16 fortgesetzt. Man könnte vermuten, dass i dann den Wert n+1 besitzt – aber der
   Compiler verwendet i in einer Weise, dass er das nicht garantiert. Wird i ab Zeile 16 ohne neue
   Wertzuweisung ausgelesen, gibt Delphi beim Compilieren die Warnung aus, dass der Wert von i
   nach dem Schleifendurchlauf undefiniert sein kann.
   Der Schleifenvariablen darf innerhalb der Schleife kein neuer Wert zugewiesen werden.
Zeile 16: Das Ergebnis kann falsch sein! Siehe Aufgabe 4.
Aufgabe 4
   Erweitern Sie das Programm Fakultaet so, dass Fakultäten berechnet werden können, bis anstelle
   von einer Zahleingabe eine leere Eingabe erfolgt (wie bei QuadGl2). Speichern Sie dieses erweiterte
   Programm als Fakultaet2. Testen Sie, bis zu welchem n sich n! mit dem Programm korrekt berechnen
   lässt! Warum wurde der Typ int64 verwendet? (Sie können auch den Typ cardinal oder real testen.)
Seite 14                                                           Programmieren mit Delphi 2005

9. Programm GGT3: Funktionen
Wir öffnen das Projekt GGT1 und speichern es in einem neuen Ordner mit dem Namen GGT3 als
Projekt GGT3. Das Programm soll besser strukturiert werden, indem Ein- und Ausgabe von der
Berechnung getrennt wird: Die Berechnung wird in eine Funktion ausgelagert, der wir den Namen
ggt geben. Das Hauptprogramm behält Ein- und Ausgabe und verwendet diese Funktion:
  1:   program GGT3; {$APPTYPE CONSOLE}
  2:
  3:   var a, b: cardinal;
  4:
  5:   function ggt(x, y: cardinal): cardinal;
  6:   var h: cardinal;
  7:   begin
  8:     while y > 0 do
  9:        begin
 10:          x := x mod y; // Divisionsrest nach x
 11:          // Inhalte von x, y tauschen, damit y die kleinere Zahl enthält:
 12:          h := x;
 13:          x := y;
 14:          y := h;
 15:        end;
 16:     result := x;
 17:   end;
 18:   // ---------- HAUPTPROGRAMM ---------------
 19:   begin
 20:     writeln('Berechnung des GGT zweier Zahlen');
 21:     // ---------Eingabe----------------
 22:     write('a='); read(a);
 23:     write('b='); readln(b);
 24:     // ---------Ausgabe----------------
 25:     writeln('Der GGT von ',a,' und ',b,' ist ',ggt(a,b));
 26:     readln;
 27:   end.

Erläuterungen:
Zeile 5: Die Deklaration einer Funktion beginnt mit dem Schlüsselwort function. Es folgt der Name
   der Funktion, der eindeutig sein muss (er darf z.B. nicht mit dem Programmnamen überein-
   stimmen – ein Grund, weshalb das Programm GGT3 und nicht GGT heißt!). In runden Klammern
   stehen hinter dem Programmnamen die Werteparameter mit ihrer Typbezeichnung, die an die
   Funktion übergeben werden. Die strenge Syntaxprüfung von Pascal würde es nicht zulassen, dass
   an die Funktion ggt Zeichenketten oder real-Werte übergeben werden. Mit Aufruf der Funktion
   werden die Variablen x und y als lokale Variable neu angelegt, welche die beim Aufruf über-
   mittelten Werte erhalten (auf lokale Variable hat das Hauptprogramm keinen Zugriff). Zuletzt
   muss noch angegeben werden, von welchem Typ der Rückgabewert der Funktion sein soll. Bei
   einer Wertzuweisung wie z.B. a := ggt(48,124) muss a eine Variable sein, die zum Typ
   cardinal kompatibel ist.
Zeile 6: Innerhalb der Funktion wird hier die Hilfsvariable h beim Tausch benötigt. Sie ist ebenfalls
   eine lokale Variable.
Zeilen 8 bis 15: Hier wird wie in GGT1 der GGT aus den Werten berechnet, die in x und y stehen.
   (Die Werte von a und b im Hauptprogramm bleiben unverändert.)
Zeile 16: Result ist eine lokale Variable, die das System in jeder Funktion für den Rückgabewert
   vorsieht. Mit dem Abschluss der Funktion wird der Speicherplatz für die Variablen h, x, y wieder
   freigegeben.
Zeile 25: Das Hauptprogramm ist jetzt sehr einfach. Zur Berechnung des GGT werden an die
   Funktion ggt die beiden Zahlen übergeben, für die der GGT berechnet werden soll. Das Ergebnis
Programmieren mit Delphi 2005                                                                    Seite 15

   wird mit writeln ausgegeben (es hätte auch zuvor einer Variablen zugewiesen werden können).
Aufgabe 5
   Gibt es Sie einen Konflikt, wenn in der Funktion ggt (Zeilen 5 bis 16) x und y durch a und b ersetzt
   werden ? Überprüfen Sie Ihre Vermutung (oder Ihr Wissen) durch entsprechende Änderung des
   Programms! Welche Werte a, b gibt Zeile 25 dann mit writeln aus? (Erklärung?)

10. Programm GGT4: Units
Wir (öffnen und) speichern das Projekt GGT3 im neuen
Ordner GGT4 unter dem Namen GGT4. Dann wählen
wir den Menüpunkt Datei/Neu/Unit-Delphi_für_Win32.
Delphi geht sofort davon aus, dass wir diese Unit auch
verwenden wollen und ergänzt im Quelltext von
GGT4.dpr den Text uses Unit1 in 'Unit1.pas'.
Falls Sie sich davon überzeugen möchten, wechseln Sie im Quelltext-Editor zur Registerkarte GGT4.
Wechseln Sie dann wieder zurück zur Registerkarte Unit1.
  unit math1;         Wir speichern die neue Unit (Speichern unter – nicht Projekt speichern
                      unter...!) im Ordner GGT4 (er wird auch schon vorgeschlagen) unter dem
  interface           Namen math1.pas (math1 genügt, .pas wird dann automatisch angefügt). In
                      beiden Quelltextdateien wird Unit1 automatisch in math1 geändert. Die neue
  implementation Unit enthält dann den abgebildeten Quelltext.
                      Jetzt soll die Funktion ggt in diese Unit verlegt werden. Dazu markieren wir sie
  end.
                      in GGT4.dpr, schneiden sie aus (mit Strg-x) und fügen sie in math1.pas
zwischen implementation und end. ein (Strg-v). Zwischen interface und implementation
muss die Funktionsdeklaration zusätzlich erscheinen. Unser Projekt besteht jetzt aus diesen
Quelltextdateien:
 program GGT4; {$APPTYPE CONSOLE}
 uses
   math1 in 'math1.pas';
 var a, b: cardinal;
 begin
   writeln('Berechnung des GGT zweier Zahlen');
   write('a='); read(a);
   write('b='); readln(b);
   writeln('Der GGT von ',a,' und ', b,' ist ',ggt(a,b));
   readln;                             unit math1;
 end.
                                                  interface
Neben der übersichtlicheren Struktur bietet
die Aufteilung des Quelltextes in mehrere         function ggt(x, y: cardinal): cardinal;
Dateien den zusätzlichen Vorteil, dass ein
                                                  implementation
Projekt von verschiedenen Programmierern
erstellt werden kann. Der Ersteller des Haupt-    function ggt(x, y: cardinal): cardinal;
programms braucht nicht zu wissen, wie man        var h: cardinal;
den GGT berechnet. Er muss nur den                begin
Interface-Abschnitt kennen, um zu wissen,           while y > 0 do
was ihm die Unit zur Verfügung stellt. Den             begin
Programmierer der Unit math1 braucht es                  x := x mod y;
                                                         h := x; x := y; y := h;
nicht zu interessieren, wer in welchen                 end;
Programmen seine Unit einsetzt, er kann             result := x;
sogar den Implementation-Abschnitt geheim         end;
halten! Er speichert dann den Quelltext bis
zum Schlüsselwort implementation in               end.
Seite 16                                                          Programmieren mit Delphi 2005

einer separaten Text-Datei, die üblicherweise die unit math1;
Endung .int (für Interface) trägt.                 { Datei math1.int zum Weitergeben
Neben dieser Datei muss er dann die compilierte       zusammen mit math1.dcu }
Unit weitergeben, die in unserem Fall math1.dcu
heißt. Ein Programmierer, der diese beiden interface
Dateien erhält, kann der int-Datei entnehmen,
dass durch Einbindung von math1.dcu eine { Die Funktion berechnet den größten
Funktion ggt verfügbar wird und welche Para-          gemeinsamen Teiler zweier Zahlen }
                                                   function ggt(x, y: cardinal): cardinal;
meter sie erwartet.
Zur Verwendung von math1.dcu muss entweder implementation
in den Delphi-Umgebungsoptionen ein Pfad zu
dieser Datei ergänzt werden oder die Datei muss sich im selben Ordner wie die Projektdatei befinden.
Delphi2005 stellt in der freien Version von vielen Units nur die dcu-Dateien zur Verfügung,
SysUtils.dcu haben wir bereits verwendet. Mag sein, dass bei gekauften Versionen auch int-Dateien
mitgeliefert werden, bei Delphi4 waren seinerzeit viele dabei.
Da sich das kleinste gemeinsame Vielfache zweier Zahlen (KGV) sehr einfach mit dem GGT
berechnen lässt, erweitern wir unsere Unit math1 um diese Funktion.
Es gilt KGV(a,b) = a·b:GGT(a,b). Damit keine zu große Zahl entsteht, ist es sicherer, mit der
Division zu beginnen. Die Division mit dem Operator „/“ liefert ein Ergebnis von Typ real, selbst
dann, wenn die Division aufgeht. Um im Ganzzahlbereich zu bleiben, verwenden wir den Operator
div, der eine Ganzzahl-Division ausführt und einen eventuellen Rest unberücksichtigt lässt. Der
Quelltext von math1 lautet dann:
unit math1;
                                                    Statt u und v hätte bei der Funktion kgv genauso
interface                                           gut wieder x und y verwendet werden können.
function kgv(u, v: cardinal): cardinal;
function ggt(x, y: cardinal): cardinal;
                                                    Diese Unit werden wir im folgenden Programm
                                                    verwenden. Damit die dcu-Datei die Funktion
implementation                                      kgv enthält, müssen wir das Projekt nach dieser
                                                    Ergänzung erneut compilieren (Strg-F9 oder
function ggt(x, y: cardinal): cardinal;             F9)!
var h: cardinal;
begin
  while y > 0 do                                    Merke:
     begin                                          Ein Programm, dass eine Unit verwendet, kann
       x := x mod y;                                nur auf die Bezeichner in dieser Unit zugreifen,
       h := x; x := y; y := h;
     end;                                           die im Abschnitt zwischen interface und
  result := x;                                      implementation genannt sind (Funktionen,
end;                                                Variablendeklarationen, Konstantendefinitionen,
                                                    Typdeklarationen, Prozeduren).
function kgv(u, v: cardinal): cardinal;
begin
  result := (u div ggt(u,v))*v;
end;

end.

11. Programm KGV1: Compilierte Unit verwenden
Richten Sie einen neuen Ordner KGV ein und kopieren Sie hierhin die Datei math1.dcu, die Sie bei
der Bearbeitung des vorigen Kapitels erstellt haben. Beginnen Sie eine neue Konsolenanwendung und
speichern Sie dieses Projekt unter dem Namen KGV1 ebenfalls in diesen Ordner. Das Programm
kann jetzt auf die Unit math1 zugreifen, ohne den Quelltext der Unit zu verwenden:
Programmieren mit Delphi 2005                                                                  Seite 17

  program KGV1; {$APPTYPE CONSOLE}

  uses
    math1; { ohne Angabe des Pfades zur pas-Datei,
             math1.dcu sollte sich im Ordner dieses Projekts befinden! }

  var a, b: cardinal;

  begin
    writeln('Berechnung des KGV zweier Zahlen');
    write('a=');
    read(a);
    write('b=');
    readln(b);
    writeln('Das KGV von ',a,' und ', b,' ist ',kgv(a,b));
    readln;
  end.

Wenn Sie den Quelltext von math1 innerhalb dieses Projekts bearbeiten möchten, müssen Sie die
uses-Anweisung vollständig entfernen und den Menüpunkt Projekt/Dem_Projekt_hinzufügen wählen.
Wählen Sie die Datei math1.pas aus dem Ordner GGT4. Dabei wird die uses-Anweisung automatisch
neu erzeugt, so dass das Projekt anschließend wieder compiliert werden kann. Sie können auf diese
Weise den Quelltext einer Unit in mehreren Projekten gemeinsam verwenden. Sie können math1.pas
in den Ordner KGV übertragen, indem Sie im Editor den math1-Quelltext anzeigen und ihn über
Datei/Speichern_unter nach KGV speichern. (Das mag sinnvoll sein, bevor man alle Quelltextdateien
eines Projekts auf einen anderen Rechner kopiert.)
Ergänzende Information zum Thema Units: Es gibt eine Unit, die stets eingebunden wird, ohne dass
sie im Quelltext erwähnt ist, und zwar die Unit System. Sie enthält u.a. die Funktionen Sqr und
Sqrt, die wir im Programm QuadGl1 verwendet haben.

Aufgabe 6
  Warum darf der Programmbezeichner hinter dem Schlüsselwort program im Editor nicht geändert
  werden? Warum kann ein Projekt nicht als Bücherei.bdsproj gespeichert werden?

Aufgabe 7
  Wann steht hinter dem Schlüsselwort end ein Punkt, wann ein Semikolon, wann keines dieser
  Zeichen?

Aufgabe 8
  Worin unterscheiden sich die Schleifentypen For-Schleife, While-Schleife, Repeat-Schleife?

Aufgabe 9
  Berechnen Sie im Kopf: a) 47 div 4, b) 47 mod 4, c) 47/4.

Aufgabe 10
  Length(s) liefert die Zeichenanzahl in einem String s. s[i] liefert das i-te Zeichen. Schreiben Sie
  eine Funktion rueckwaerts, an die ein String übergeben werden kann und die diesen String mit
  umgekehrter Zeichenreihenfolge zurück gibt. Ein Hauptprogramm soll diese Funktion verwenden.

Aufgabe 11
  In der Unit math1.pas werde im Interface-Abschnitt zusätzlich eine Variable a vom Typ String
  deklariert. Prüfen Sie, ob das vor oder hinter den Funktionen oder an beliebiger Stelle möglich ist. Wie
  kann nun ein Hauptprogramm, das eine globale Variable mit demselben Namen a, aber vom Typ
  integer verwendet, zwischen beiden Variablen unterscheiden? (Vergl. Lösung zu Aufgabe 5)
Seite 18                                                           Programmieren mit Delphi 2005

Aufgabe 12
  a, b und c seien String-Variablen. Welchen Wert enthält c, wenn folgender Code ausgeführt wird?
  a:='12'; b:='28'; c:=a+b;
  Können Strings auch durch - (minus) verknüpft werden?
  (Was ändert sich, wenn a, b, c integer-Variablen sind?)

12. Programm „Berechnungen“: Prozeduren, Typen boolean und char,
    Case-Anweisung
Eine Prozedur ist ein in sich abgeschlossener Programm-Abschnitt, der im Gegensatz zu einer
Funktion keinen Rückgabewert liefert. Wie bei einer Funktion können aber Werte an die Prozedur
übergeben werden. Oberbegriff für Prozeduren und Funktionen ist der Begriff Methode.
                                                Dieses Programm lässt den Anwender
 1:   program Berechnungen;
 2:
                                                wählen, ob er GGT, KGV oder eine Fakul-
 3:   {$APPTYPE CONSOLE}                        tät berechnen möchte. Bevor eine Methode
 4:                                             verwendet werden kann, muss sie bekannt
 5:   uses                                      sein – in Pascal bedeutet das, sie muss vor
 6:     SysUtils, math1 in '..\GGT4\math1.pas'; dem Hauptprogramm im Quelltext er-
 7:                                             scheinen. Da GGT- und KGV-Berechnung
 8:   // Prozeduren ----------------------      Gemeinsamkeiten aufweisen, wurde hier
 9:   procedure GGT_KGV(ggt_Suche: boolean);
10:   var a, b: integer;
                                                eine Prozedur für beides geschrieben. Der
11:   begin                                     Parameter ggt_Suche ist vom Typ
12:     writeln('Bitte 2 Zahlen eingeben:');    boolean: er kann genau einen von zwei
13:     readln(a,b);                            Werten annehmen: TRUE oder FALSE. Das
14:     if ggt_Suche then                       Hauptprogramm (bei dem hier zur Hervor-
15:        writeln('GGT=',ggt(a,b))
                                                hebung Großbuchstaben verwendet wur-
16:     else
17:        writeln('KGV=',kgv(a,b));            den) übergibt an die Prozedur GGT_KGV
18:   end;                                      den Wert true, wenn ein GGT berechnet
19:                                             werden soll und den Wert false, wenn ein
20:   procedure BerechneFaku;                   KGV gewünscht wird (Zeilen 36, 37).
21:   begin                                     Diese Zeilen befinden sich in einer Case-
22:   // ...Code für Eingabe, Rechn., Ausgabe   Anweisung:
23:   end;
24:                                                       Die Case-Anweisung vereinfacht oft eine
25:   // Beginn des Hauptprogramms -------                Fallunterscheidung für mehr als 2 Fälle.
26:   VAR C: CHAR;                                        Voraussetzung ist aber, dass die Fallunter-
27:   BEGIN                                               scheidung sich auf eine Variable bezieht,
28:     REPEAT
29:        WRITELN('Verschiedene Berechnungen');
                                                          bei der zu jedem Wert der Nachfolger oder
30:        WRITELN('Waehlen Sie aus:');                   Vorgänger eindeutig ist. Zum Beispiel sind
31:        WRITELN('1 - GGT');                            integer-Zahlen möglich, aber keine real-
32:        WRITELN('2 - KGV');                            Zahlen, auch keine Strings.
33:        WRITELN('3 - Fakultaet');                      Der hier verwendete Typ CHAR ist ver-
34:        READLN(C); // C enthält 1.Zeichen              gleichbar mit dem Typ Byte (Zahlen von 0
35:        CASE C OF // der Eingabe
                                                          bis 255), nur dass CHAR (Character =
36:        '1': GGT_KGV(true);
37:        '2': GGT_KGV(false);                           Zeichen) diese Werte als Zeichen interpre-
38:        '3': BerechneFaku;                             tiert. 65 bis 90 sind z.B. die Werte für A bis
39:        ELSE                                           Z, 97 bis 122 die für a bis z, Werte unter
40:          WRITELN('ungueltig');                        32 stellen „Steuercodes“ dar.
41:        END;                                           Der Wert 13 steht für einen Zeilenumbruch
42:        WRITELN;                                       bzw. die Eingabetaste, siehe Zeile 43.
43:     UNTIL c=#13;
44:   END.
                                                          (Ein String kann als eine Liste von CHAR-
                                                          Werten aufgefasst werden.)
Programmieren mit Delphi 2005                                                              Seite 19

Ergänzungen zur Case-Anweisung:
Wenn ein Fall in der Case-Anweisung aus mehreren Anweisungen besteht, müssen diese mit begin
und end zu einen Block zusammengefasst werden. Der ELSE-Fall tritt hier ein, wenn z.B. ein
Buchstabe oder eine 4 eingegeben wurde. Ein ELSE-Fall ist in der Case-Anweisung optional, die
Zeilen 39 und 40 dürften von der Syntax her auch fehlen. Die Case-Anweisung wird mit END
abgeschlossen. Beachten Sie: Hier darf vor dem ELSE ein Semikolon stehen! (Es darf aber auch –
wie vor einem END – fehlen.)                 CASE C OF
Wenn bei verschiedenen Werten der Entschei- '1','g','G': GGT_KGV(true);
dungsvariablen derselbe Code ausgeführt '2','k','K': GGT_KGV(false);
werden soll, können vor dem Doppelpunkt '4'..'9','0': writeln('ungültige Ziffer');
mehrere Werte angegeben werden (siehe Ab- END;
bildung). '4'..'9' steht für die Auflistung
aller Zeichen von 4 bis 9.
13. Records: Definition von Typen
Für viele Anwendungen reichen die Typen integer, real, boolean, string, char (usw.) nicht aus. Pascal
erlaubt deshalb auf vielfältige Weise die Definition eigener Variablentypen.
Nehmen wir als Beispiel ein Programm, das den Fahrzeugbestand eines Autohändlers verwalten soll.
Ein Kfz hat dann Eigenschaften wie Hersteller, Fahr- type
zeugtyp, Baujahr, gefahrene km, Farbe, Stahlschiebedach          TFahrzeug = record
(SSD). Alle diese Eigenschaften lassen sich zu einem                Hersteller,
neuen Typ zusammenfassen. Üblich ist es, bei der Defi-              Fahrzeugtyp: string;
nition von Typen den Bezeichner mit „T“ zu beginnen, um             Baujahr: integer;
                                                                    gefahrene_km: integer;
den Typ „Fahrzeug“ leicht von dem einzelnen Objekt
                                                                    Farbe: string;
„Fahrzeug“ unterscheiden zu können. Die Typdeklaration              mit_SSD: boolean;
lautet dann wie nebenstehend.                                    end;
Das Schlüsselwort type leitet den Abschnitt für Typ-
deklarationen ein. Typen werden definiert durch „ =“. Mit record ... end;
wird ein Typ mit mehreren Datenfeldern festgelegt. Gleiche Feldtypen (Hersteller, Fahrzeugtyp)
können durch Komma getrennt aufgelistet werden.
 var                                                     Das Beispiel links zeigt die Syntax für den
    Kfz1, Kfz2: TFahrzeug;                               Zugriff auf die Felder von TFahrzeug.
begin                                                 Records werden auch in grafischen Anwen-
  Kfz1.Hersteller := 'DKW';
  Kfz1.Fahrzeugtyp := '1000S';                        dungen häufig eingesetzt. Ein Punkt ist z.B. in
  Kfz1.Baujahr := 1961;                               der unit Windows durch
  Kfz1.gefahrene_km := 174000;                        TPoint = record x, y: integer; end;
  Kfz1.Farbe := 'blau/weiß';                          deklariert.
  Kfz1.mit_SSD := false;                              Funktionen, die einen Record zurückgeben,
// Eine with-Anweisung erspart "Kfz2.":               können auf diese Weise gleich einen ganzen
  with Kfz2 do                                        Satz von zusammengehörenden Werten zu-
    begin
      Hersteller := 'NSU';
                                                      rückgeben (z.B. beide Koordinaten eines
      Fahrzeugtyp := 'RO-80';                         Punktes).
      Baujahr := 1965;                                Aufgabe 13:
      gefahrene_km := 189255;                         Entwerfen Sie eine Typdeklaration für Perso-
      Farbe := 'grün-metallic';                       nen. Sie soll mindestens je 1 Feld des Typs
      mit_SSD := false;                               String, boolean, TDateTime und integer ent-
    end;                                              halten. (Informationen zu TDateTime finden Sie
// usw.
                                                      in der Delphi-Hilfe.)
Wie wär's in Aufgabe 13 mit einem Feld für die Blutgruppe? Dafür können Sie einen eigenen Typ
deklarieren: TBlutgruppe = (A,B,AB,O);. (Die Blutgruppe 0 wird durch den Buchstaben O reprä-
sentiert, weil die Zahl 0 kein Bezeichner sein kann.)
Seite 20                                                               Programmieren mit Delphi 2005

14. Vom Record zur Klasse
Bevor wir zur Programmierung von Windows-Anwendungen übergehen, an dieser Stelle ein wenig
Theorie zum Begriff „Klasse“, denn jedes Windows-Fenster, das wir dann verwenden werden, ist die
Instanz einer Klasse, die wiederum ein Nachfolger der Klasse TForm ist.
Während Records nur Datenfelder zusammenfassen, kann type
eine Klasse zusätzlich auch Funktionen und Prozeduren    TKfz = class
enthalten. TFahrzeug aus dem vorigen Kapitel kann damit    Hersteller,
eine Funktion zur Berechnung des Fahrzeug-Alters erhal-    Fahrzeugtyp: string;
                                                           Baujahr,
ten! Die spezielle Methode für diese Berechnung wird nur
                                                           gefahrene_km: integer;
für Fahrzeuge benötigt, also soll sie auch Bestandteil     Farbe: string;
dieser Klasse sein (Kapselung).                            mit_SSD: boolean;
Mit der Typdeklaration ist zunächst nur festgelegt, dass       function Alter: integer;
                                                             end;
TKfz eine Funktion Alter besitzt, es fehlt noch der Code
für die Berechnung. Üblicherweise ist der Typ in einem function TKfz.Alter: integer;
Interface-Abschnitt einer Unit definiert, der Code für die var y,m,d: word;
Funktion Alter befindet sich dann im Implementation- begin
Abschnitt.                                                   DecodeDate(Date,y,m,d);
                                                                  Result := y-Baujahr;
Die Funktion Alter weist einige Besonderheiten gegenüber end;
bisher angesprochenen Funktionen auf: Weil sie Teil der Klasse
TKfz ist, muss TKfz. dem Bezeichner Alter vorangestellt werden. Außerdem braucht diese Funktion
keinen Parameter: Sie kann das Alter aus dem aktuellen Tagesdatum (dieses liefert die Funktion Date aus der
Unit SysUtils) und dem Baujahr berechnen. Die Methode DecodeDate zerlegt das mit Date ermittelte Datum
in Jahr, Monat und Tag und weist diese Werte den Variablen y, m, d zu – DecodeDate ist damit eine
Prozedur, die Werte in den übergebenen Parametern ändert, anders als dies bei der Funktion ggt auf Seite 14
der Fall war (Werte-Parameter, Variablen-Parameter: Siehe Kapitel 20 auf Seite 32).
Klassen erlauben nicht nur die Einbindung von Funktionen und Prozeduren, sie bieten zusätzlich die
Möglichkeit der „Vererbung“: Weitere Klassen können von bestehenden abgeleitet werden und
erhalten so die Eigenschaften der „Vorfahren“, die erweitert oder abgeändert werden können. Klassen
sind damit ein grundlegendes Konstrukt der modernen Programmierung.
Ein Nachfahre von TKfz könnte zum Beispiel TLKW type
sein. Dieser Typ kann dann zusätzlich die Anzahl der  TLKW = class(TKfz)
Achsen enthalten. Alles andere erbt TLKW von TKfz,      Achsenanzahl: integer;
die Berechnung des Alters braucht nicht neu geschrie- end;
ben zu werden.
                                                                 var
Wegen einer wesentlich komplizierteren Speicherplatzver-     LKW1: TLKW;
waltung von Objekten können einzelne Fahrzeuge nicht         a: integer;
mehr so einfach wie auf Seite 19 verwendet werden: Mit begin
einer Create-Methode, die jede Klasse besitzt, wird der // Instanz erzeugen
Speicherplatz für das betreffende Objekt (oft auch als       LKW1 := TLKW.Create;
                                                            // Instanz verwenden
Instanz der Klasse bezeichnet) reserviert. Dieser Speicher-
                                                             LKW1.Hersteller:='Scania';
platz muss aber auch wieder frei gegeben werden, wenn das    LKW1.Baujahr := 2005;
Objekt nicht mehr benötigt wird. Das geschieht mit der       LKW1.Achsenanzahl := 3;
Methode Free, die jede Instanz einer Klasse besitzt.         a := LKW1.Alter;
                                                                    ... // (weiterer Code)
Die Beschreibung des Begriffs „Klasse“ ist hier bei weitem
nicht erschöpfend ausgeführt, sollte zunächst aber für das  LKW1.Free;
Verständnis der folgenden Kapitel ausreichen. Wir ver- end.
wenden in dieser Einführung nur die Klassen, die Delphi für
unsere Windows-Anwendungen automatisch erstellt. Eine Vertiefung mit selbst erstellten Klassen ist
eine der möglichen Fortsetzungen.
Programmieren mit Delphi 2005                                                                Seite 21

Teil 2: Windows-Programme mit Delphi2005

15. Hallo-Welt-Programm für Windows
Wir beginnen ein neues Projekt über den Menüpunkt Datei-Neu-VCL-Formularanwendung – Delphi für
Win32. Wählen Sie dann (wie immer) als erstes „ Projekt speichern unter ...“ und folgen Sie genau
dieser Anleitung:
● Richten Sie einen neuen Ordner HalloWelt2 ein, wechseln Sie in diesen. Der vorgeschlagene
    Dateiname ist jetzt nicht Projekt1.bdsproj, sondern Unit1.pas. Das liegt daran, dass dieses Projekt
    2 Quelltextdateien enthält. Beim Speichern eines Projekts werden zuerst immer die noch nicht
    gespeicherten Units gespeichert. Der vorgeschlagenen Name kann in diesem Schritt beibehalten
    werden.
● Als nächstes wählen Sie den Dateinamen für das
    Projekt. Ändern Sie den Vorschlag Projekt1.bdsproj
    in HalloWelt.
● Starten Sie das Programm. Sie sollten ein Fenster
    ähnlich dem abgebildeten sehen.
● Beenden Sie das Programm durch Schließen des
    Fensters.
Jetzt sehen wir uns die Quelltexte an.
Unit1 enthält den folgenden Text:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs;

type
  TForm1 = class(TForm)
                                               program HalloWelt;
  private
     { Private-Deklarationen }
  public                                       uses
     { Public-Deklarationen }                    Forms,
                                                 Unit1 in 'Unit1.pas' {Form1};
  end;
                                               {$R *.res}
var
  Form1: TForm1;
                                               begin
implementation                                   Application.Initialize;
                                                 Application.CreateForm(TForm1, Form1);
{$R *.dfm}                                       Application.Run;
                                               end.
end.

Delphi bindet vorsorglich viele Units ein, die meistens auch gebraucht werden. Dann wird die Klasse
TForm1 als Nachfolger von TForm festgelegt (TForm ist der Unit Forms definiert). private und
public brauchen uns hier noch nicht zu interessieren. Form1 ist als globale Variable vom Typ
TForm1 deklariert. {$R * dfm} ist eine Compiler-Anweisung. Sie bindet die „Resourcendatei“
Unit1.dfm beim Compilieren ein. Unit1.dfm enthält Eigenschaften des Fensters wie Position, Größe,
Positionen von Steuerelementen (Schalter, Texte, Eingabefelder, sobald wir solche einfügen, usw.).
Der zweite abgebildete Text zeigt den Inhalt von HalloWelt.dpr, dem Hauptprogramm. Das Haupt-
programm bleibt in der Regel so kurz – nur bei Erstellung weiterer Fenster kommt je Fenster eine
Zeile hinzu. Um den Hauptprogramm-Text anzuzeigen wählen Sie im Menü Projekt den Punkt
Sie können auch lesen