Programmieren mit Delphi 2005
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
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⋅xq=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