)
1. [A] (String ist final). Die Klasse String wurde im JDK als final deklariert und kann daher nicht abgeleitet werden. Das schränkt ihre Anwendbarkeit und insbesondere ihre Erweiterbarkeit stark ein. Überlegen Sie sich, auf welche Weise ohne Ableitung neue String-Methoden entwickelt werden können und zeigen Sie deren Vor- und Nachteile auf.
2. [A] (Übergabe von Strings an Methoden).
Strings werden in Java als Objekte dargestellt und damit als
Parameter einer Methode per Referenz an diese übergeben. Demzufolge
würde man erwarten, daß eine innerhalb der Methode ausgeführte
Zuweisung an einen String-Parameter nach Ende des Methodenaufrufs den
aktuellen Parameter verändert hätte und somit auch außerhalb der
Methode sichtbar wäre. Tatsächlich ist dies keineswegs der Fall,
sondern Änderungen an String-Argumenten, die innerhalb der Methode
vorgenommen werden, bleiben lokal und sind außerhalb von ihr
unsichtbar. Erklären Sie dieses Verhalten und begründen Sie, warum
es vollkommen korrekt ist.
Aufg1102.java
3. [A] (String und StringBuffer). Kapitel 11 stellt sowohl die Klasse String als auch StringBuffer vor. Überlegen Sie, wann es sinnvoller ist, einen String zu verwenden und wann man einen StringBuffer einsetzen sollte. Begründen Sie Ihre Überlegungen.
4. [B] (Worte in einem String zählen).
Schreiben Sie eine Methode wcnt, die ein Argument vom Typ
String akzeptiert und die Anzahl der darin enthaltenen Worte
zählt. Ein Wort soll dabei eine zusammenhängende Folge von Zeichen
sein, die keine Leerzeichen, Tabulatoren oder Zeilenschaltungen
enthält.
Aufg1104.java
5. [B] (Die B-Sprache). Ebenso wie Informatiker erfinden auch Kinder gerne spezielle "Sprachen", die sich lustig anhören und deren Verständnis nur Eingeweihten zugänglich ist. Das im englischsprachigen Raum bekannte "Pig-Latin" ähnelt dabei der hierzulande bekannten "B-Sprache", der wir diese Aufgabe widmen wollen.
Die B-Sprache funktioniert so, daß an alle zusammenhängenden Folgen von Vokalen innerhalb eines Wortes ein "b" plus die Wiederholung der Vokalfolge angehängt wird. Aus dem Satz "Meine Mutter ißt gerne Fisch" wird so beispielsweise "Meibeinebe Mubutteber ibißt gebernebe Fibisch". Nach einiger Übung lernt man problemlos, die B-Sprache fließend zu sprechen und zu verstehen.
Schreiben Sie ein Programm, das eingegebene Sätze in die
B-Sprache übersetzt. Falls Sie noch genügend Zeit haben, realisieren
sie auch die umgekehrte Übersetzung.
Aufg1105.java
6. [B] (Englischer Plural). Zu einem englischen Substantiv kann sehr leicht der Plural gebildet werden, wenn man die folgenden Regeln anwendet:
7. [B] (Ersetzen eines Teilstrings). Obwohl die Klasse String in Java eine ganze Reihe nützlicher Methoden bietet, ist sie doch nicht ohne Schwachstellen. So fehlt in der aktuellen Version des JDK beispielsweise eine Methode, die es erlaubt, jedes Auftreten eines vorgegeben Teilstrings gegen einen anderen auszutauschen. Zwar gibt es die Methode replace, diese erlaubt es aber lediglich, ein einzelnes Zeichen gegen ein anderes auszutauschen.
Schreiben Sie eine Methode replaceString, die in einem
gegebenen String alle Vorkommen eines Strings s1
gegen einen String s2 austauscht. s1 und
s2 dürfen dabei beliebig (auch unterschiedlich) lang sein.
Aufg1107.java
8. [B] (Ganzzahlen formatieren). Zwar ist es in JAVA sehr leicht, eine Zahl oder einen String auf der Konsole auszugeben, aber C-Programmierer, die in JAVA programmieren, vermissen schmerzlich die Formatierungsmöglichkeiten, die ihnen die Familie der printf-Ausgabefunktionen in C zur Verfügung gestellt hat. Schreiben Sie eine Methode formattedInt, die zwei Argumente vom Typ int akzeptiert und einen formatierten String zurückgibt. Das erste Argument bezeichnet die in einen String zu konvertierende Zahl und das zweite die Breite des Feldes, in dem die Zahl rechtsbündig plaziert werden soll. So führt beispielsweise ein Aufruf von formattedInt(173,5) zur Rückgabe des Strings " 173" und ein Aufruf von formattedInt(-22055,8) liefert " -22055".
9. [B] (Kalender ausdrucken). Schreiben Sie ein Programm, das ein Kalenderblatt für einen vorgegebenen Monat ausdruckt. Tag, Monat und Jahr sollen dabei als Kommandozeilenargumente an das Programm übergeben werden. Die Darstellung des 14.9.97 könnte beispielsweise so aussehen:
14. September 1997
----------------------------
Mo Di Mi Do Fr Sa So
1 2 3 4 5 6 7
8 9 10 11 12 13 >14<
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Aufg1109.java
10. [B] (Zufallszahlen). Schreiben Sie eine statische Methode randomInt, die gleichverteilte ganzzahlige Zufallszahlen innerhalb eines vorgegebene Wertebereichs liefert. Obere und untere Grenze sollen als Argument an die Methode übergeben werden. So soll beispielsweise jeder Aufruf von randomInt(4,10) ein Zufallszahl im Bereich von 4 bis 10 liefern. Alle Zahlen in diesem Bereich sollen gleich wahrscheinlich sein.
11. [B] (Linksbündige Leerzeichen entfernen). Schreiben Sie eine Methode ltrim, die einen String als Argument akzeptiert, daraus die linksbündigen Leerzeichen entfernt und den so modifizierten String an den Aufrufer zurückgibt.
12. [B] (Nach wiederholten Zeichenketten suchen). Schreiben Sie eine Methode lookupPairs, die einen beliebigen String auf Teilzeichenketten untersucht, die mehr als einmal darin vorkommen. Der Rückgabestring soll jede dieser Zeichenketten genau einmal ausgeben. Die einzelnen Elemente sollen dabei durch ein Leerzeichen voneinander getrennt werden. Schätzen Sie ab, welches Laufzeitverhalten Ihr Verfahren hat. Ist es O(n*n) oder sogar noch besser?
13. [B] (Performance von String und StringBuffer). Um an eine bestehende Zeichenkette eine weitere anzuhängen, gibt es zwei Möglichkeiten:
14. [B] (Einfache Verschlüsselung). Eine einfache Methode, Texte zu verschlüsseln, besteht darin, jedes einzelne Zeichen gegen ein anderes auszutauschen. Angenommen, im Text kommen nur die Großbuchstaben 'A' bis 'Z' vor, dann könnte man ein Array von 26 Elementen verwenden, in dem die 26 Großbuchstaben in beliebiger Anordnung vorkommen. Das Verschlüsseln würde nun so funktionieren, daß jeder Buchstabe durch das Zeichen ersetzt wird, das in der Tabelle an seiner eigentlichen Position steht. Schreiben Sie ein Programm, das einen Text auf diese Weise ver- und wieder entschlüsseln kann.
15. [B] (Punkte beim Scrabble). In dem Kreuzworträtselspiel Scrabble werden die von den Spielern gefundenen Worte auf der Basis der darin enthaltenen Buchstaben bewertet. Jeder Buchstabe besitzt eine feste Punktzahl entsprechend der folgenden Liste:
16. [B] (Wortpalindrome).
Ein Palindrom ist ein Wort, das vorwärts und rückwärts
gelesen dasselbe ergibt (abgesehen von Unterschieden in Groß-
und Kleinschreibung). Beispiele für Palindrome sind "Uhu",
"neben", "Ebbe", "nennen", "Renner" oder "Rotor". Schreiben Sie
eine Methode isPalindrome, die ermittelt, ob
ein als Argument übergebener String ein Palindrom ist.
Aufg1116.java
17. [B] (Satzpalindrome). Neben Wort-Palindromen gibt es auch ganze Sätze, die ein Palindrom ergeben, wenn man Interpunktion und Groß-/Kleinschreibung ignoriert. Beispiele dafür sind "Ein Neger mit Gazelle zagt im Regen nie", "Madam, I'm Adam" oder "A man, a plan, a canal: panama". Schreiben Sie eine Methode isSentencePalindrome, die genau dann true zurückgibt, wenn der übergebene String ein Satz-Palindrom ist. Falls Sie bei der Aufgabe zu den Wortpalindromen einen iterativen Lösungsansatz gewählt haben, versuchen Sie nun einen rekursiven und umgekehrt.
18. [B] (Einfache Wildcardsuche). Die Methode indexOf der Klasse String kann dazu verwendet werden, das erste Auftreten eines Strings innerhalb eines anderen Strings zu suchen. Implementieren Sie eine erweiterte Methode wildIndexOf, bei der auch das Wildcard "?" im Suchstring enthalten sein darf. Ein "?" steht dabei für das Auftreten eines einzelnen beliebigen Zeichens an dieser Stelle. Würden Sie beispielsweise mit dem Suchausdruck "t?r" in der Zeichenkette "Der Ball ist rund" suchen, so würde die Methode 11 zurückgeben. Falls der Suchbegriff nicht gefunden wurde, sollte -1 zurückgegeben werden.
19. [B] (Wortanfänge in Großbuchstaben konvertieren). Schreiben Sie eine Methode wordsToUpperCase, die in einem String den ersten Buchstaben in jedem Wort in Großschrift konvertiert.
20. [B] (Zeichen in einem String sortieren).
Angenommen, ein String besteht nur aus den Großbuchstaben
"A" bis "Z". Schreiben Sie eine Methode, die die Zeichen in dem
String in aufsteigender Reihenfolge sortiert. Aus "HALLO" soll also
beispielsweise "AHLLO" und aus "GURKENSUPPE" soll "EEGKNPPRSUU"
werden. Kennen Sie ein Verfahren mit linearer Laufzeit?
Aufg1120.java
21. [B] (Buchstaben auf amerikanischen Telefonen). Bei amerikanischen Telefonen sind neben Zahlen auch Buchstaben auf den Tasten aufgedruckt (s. folgende Abbildung). Da somit jedem Wort eindeutig die zugehörige Telefonnummer zugeordnet werden kann (umgekehr stimmt dies nicht), bietet sich die Möglichkeit, anstelle von Nummern leichter merkbare mnemonische Kürzel im Kopf zu behalten:
+---+ +---+ +---+ ! 1 ! ! 2 ! ! 3 ! ! ! !ABC! !DEF! +---+ +---+ +---+ +---+ +---+ +---+ ! 4 ! ! 5 ! ! 6 ! !GHI! !JKL! !MNO! +---+ +---+ +---+ +---+ +---+ +---+ ! 7 ! ! 8 ! ! 9 ! !PRS! !TUV! !WXY! +---+ +---+ +---+ +---+ +---+ +---+ ! ! ! 0 ! ! ! ! ! ! ! ! ! +---+ +---+ +---+So wäre beispielsweise die Nummer 1-800-JAVA sehr leicht zu behalten, wenn es der Anschluß der JAVA-Serviceabteilung der Firma SUN wäre (die Vorwahl 1-800 ist das Gegenstück zur deutschen 0130, bezeichnet also eine Telefonnummer, die für den Anrufer kostenlos ist). In Wirklichkeit entspricht das Wort JAVA der Nummer 5282, die der Anrufer wählt, wenn er "JAVA" eingibt.
Bei der Vergabe dieser Kürzel kann allerdings oftmals die Nummer nicht frei gewählt werden, sondern es muß versucht werden, zu einer vorgegebenen Nummer einen leicht zu merkenden Begriff zu finden. Ihre Aufgabe ist es nun, dafür ein Programm zu schreiben. Das Programm erwartet eine numerische Telefonnummer als Eingabe und gibt dann alle Worte aus, die auf der Telefontastatur der vorgegebenen Nummer entsprechen.
22. [B] (Ein Problem rückwärts bearbeiten). Manchmal ist es einfacher ein Problem zu lösen, wenn die zu bearbeitende Datei oder der zu bearbeitende String rückwärts gelesen wird. Eine vormals sehr schwierige Lösung kann dadurch möglicherweise eine überraschende Wendung erfahren. Versuchen Sie diesen Ansatz am Beispiel der folgenden Aufgabenstellung.
Schreiben Sie ein Programm, das aus einem gültigen Java-Ausdruck die Namen und Position der darin enthaltenen Methodenaufrufe findet und als Array von Strings mit angehängter Position zurückgibt. Lautet der Ausdruck beispielsweise
showStatus("Zeile "+3*(i+line (x))+pack(line(y))+"/"+x*(pack((z))));
so soll Ihr Programm ein Array mit den folgenden 5 Strings
zurückgeben:
"showStatus,0" "line,25" "pack,35" "line,40" "pack,56"Versuchen Sie einen Lösungsansatz, bei dem der String rückwärts gelesen wird. Dabei sollte bei jeder öffnenden Klammer solange nach links weitergelesen werden, bis das erste Zeichen auftaucht, das weder ein Leerzeichen, Tabulator oder eine Zeilenschaltung ist. Handelt es sich bei diesem Zeichen um ein in Bezeichnern erlaubtes Zeichen (also um einen Buchstaben, eine Zahl oder einen Unterstrich), so wurde ein Methodenaufruf gefunden. Handelt es sich dagegen um ein Trennzeichen wie "*" oder "+", so wurde kein Methodenaufruf gefunden. Nehmen Sie der Einfachheit halber an, daß nur Methoden aus der aktuellen Klasse verwendet werden, also keine qualifizierten Namen mit darin enthaltenen Punkte vorkommen.
23. [B] Lottotip abgeben. Seit einiger Zeit können beim Lotto auch Zufallswetten abgegeben werden. Dabei werden die 6 Kreuze nicht mehr vom Spieler gemacht, sondern ein Zufallszahlengenerator wählt den vermeintlich glückbringenden Tip aus den 49 Zahlen aus.
Schreiben Sie ein Programm, das in der Lage ist, einen Tip für das
Lotto 6 aus 49 abzugeben. Selbstverständlich muß der Tip gültig sein
(darf also keine doppelten Zahlen enthalten), und die
Wahrscheinlichkeit für das Auftreten jeder beliebigen
Zahlenkombination sollte gleich sein.
Aufg1123.java
24. [B] Stack zur Prüfung korrekter Klammerung. Ein Stack wird häufig da verwendet, wo rekursive Strukturen, also Schachtelungen, untersucht werden müssen. Eine typische Anwendung besteht darin, die korrekte Klammerung von arithmetischen Ausdrücken zu prüfen, also festzustellen, ob zu jeder öffnenden Klammer eine zugehörige schließende Klammer existiert.
Schreiben Sie ein Programm, das einen als String vorgegebenen Ausdruck auf korrekte Klammerung untersucht, indem jede öffnende Klammer auf dem Stack abgelegt und bei Auftreten einer schließenden Klammer wieder entfernt wird. Der Ausdruck ist korrekt geklammert, wenn niemals versucht wird, eine Klammer vom leeren Stack zu entfernen und nach Ende des Ausdrucks keine Klammern mehr auf dem Stack sind.
25. [C] (Römische Zahlen). Lange bevor in Mitteleuropa die positionsorientierten Zahlensysteme in Mode kamen, gab es bereits das System der römischen Zahlen. Dieses basierte auf einem Alphabet der folgenden Ziffern:
| Ziffer | Wert |
|---|---|
| I | 1 |
| V | 5 |
| X | 10 |
| L | 50 |
| C | 100 |
| D | 500 |
| M | 1000 |
Jede römische Zahl setzt sich aus einer Folge römischer Ziffern zusammen. Der Wert der Zahl ergibt sich durch einfaches Addieren der Werte der einzelnen Ziffern. Einzige Ausnahme: steht eine kleinere vor einer größeren Ziffer, so wird der Wert der kleineren Ziffer nicht zum Ergebnis addiert, sondern von ihm subtrahiert. So ergibt beispielsweise die Ziffer MCMXCVII den Wert 1000 - 100 + 1000 - 10 + 100 + 5 + 1 + 1 = 1997.
Bei der Konstruktion einer römischen Ziffer zu einem gegebenen Wert sind einige Bildungsregeln (sozusagen eine sehr frühe Form eines Style-Guides) zu beachten:
26. [C] Blocksatz. Java-Strings unterliegen keinen prinzipiellen Längenbeschränkungen und sind daher auch dafür geeignet, kleinere Textdateien komplett im Hauptspeicher halten und bearbeiten zu können. Die Trennung der einzelnen Zeilen erfolgt ebenso wie in der externen Datei dadurch, daß Zeilenschaltungen eingefügt werden. Eine typische Anwendung bestehtnun darin, einen mehrzeiligen Textstring so zu formatieren, daß er im Blocksatz dargestellt wird. Dabei wird jede Zeile innerhalb einer vorgegebenen Zeilenlänge so dargestellt, daß der darin enthaltenen Text sowohl links- als auch rechtsbündig dargestellt wird. Die zum Auffüllen erforderlichen Leerzeichen werden dabei so zwischen den Worten eingefügt, daß das Ergebnis möglichst harmonisch aussieht. Ist eine Zeile zu lang, so wird an einer Wortgrenze ein geeigneter Zeilenumbruch eingefügt. Die letzte Zeile des Textes bleibt unformatiert.
Schreiben Sie eine Methode leftRightAlign, die einen String und ein int als Argument erwartet. Der übergebene String soll dabei entsprechend der im zweiten Argument angegebenen Zeilenbreite im Blocksatz formatiert und an den Aufrufer zurückgegeben werden.
Soll beispielsweise der vorige Absatz im Blocksatz mit 40 Zeichen ausgegeben werden, so könnte dies etwa so aussehen:
Schreiben Sie eine Methode leftRightAlign, die einen String und ein int als Argument erwartet. Der übergebene String soll dabei entsprechend der im zweiten Argument angegebenen Zeilenbreite im Blocksatz formatiert und an den Aufrufer zurückgegeben werden.
Aufg1126.java
27. [C] (Galgenraten). In der Schule wurde oft das Spiel "Galgenraten" gespielt, bei dem es darum ging, einen vom Mitspieler ausgesuchten Begriff mit möglichst wenigen Versuchen zu erraten. Die Spielregeln waren dabei wie folgt:
3
------------- -------------
| / | | / |5
|/ O |/4 O6
| -+- | -+-8
| | | |7
| / \ 2| 9/ \10
| |
| |
| |
---------------------- ----------------------
1
Schreiben Sie eine textorientierte Implementierung des Spiels
"Galgenraten". Das Programm ist ihr Gegner, es wählt auf der
Basis eines kleinen Katalogs ein Wort aus, das Sie erraten
sollen. Nach jedem Rateversuch, den Sie unternehmen, zeigt
das Programm sowohl den Zustand des teilweise erratenen Wortes
als auch des Galgens an. Das Spiel ist beendet, wenn entweder
das Wort erraten, oder der Galgen komplett gezeichnet ist.
28. [C] (Gewichtete Zufallszahlen). Entwickeln Sie eine Klasse WRandom, die in der Lage ist, gewichtete ganzzahlige Zufallszahlen zu produzieren. Im Gegensatz zu den gleichverteilten Zufallszahlen soll die Klasse die Möglichkeit bieten, Zufallszahlen zu produzieren, die mit unterschiedlicher Wahrscheinlichkeit auftreten. Die Klasse soll zwei Methoden addWeightedRange und nextNumber besitzen, die wie folgt verwendet werden:
29. [C] (du). Auf UNIX-Anlagen gibt es ein Programm Programm du, das den Platzbedarf eines Verzeichnisses durch Summierung der Größe aller darin enthaltenen Dateien ermittelt. Dabei werden rekursiv auch Unterverzeichnisse und die darin enthaltenen Dateien und Unterverzeichnisse usw. berücksichtigt. Schreiben Sie ein Programm, das den Namen eines Verzeichnisses über die Kommandozeile übergeben bekommt und analog zu du den Platzbedarf für dieses Verzeichnis ermittelt.
30. [C] (Eine Nachricht entschlüsseln). Die verschlüsselte Nachricht
"rle gnagon ermer aisae mongase rue usnuhigas tnraumas anwrchta, frsd an eich is eaisam batt zu aisam usgahauanas usgaziafan vanwrsdalt."
soll entschlüsselt werden. Über den Text und seine Verschlüsselung ist folgendes bekannt:
Die fünf häufigsten Buchstaben des Wortes "hoechstgeschwindigkeit" sind beispielsweise (h, e, i, c, s). Werden diese zu (i, h, s, e, c) vertauscht, so ergibt sich die Verschlüsselung "ioheictghceiwsndsgkhst".
Schreiben Sie ein Programm, das Sie beim Entschlüsseln der
Nachricht unterstützt. Es soll die häufigsten Buchstaben ermitteln
und die Möglichkeit bieten, diese interaktiv im Dialog zu vertauschen.
Das jeweilige Entschlüsselungsergebnis soll auf dem Bildschirm
angezeigt werden.
Aufg1130.java
31. [C] (Master Mind). Ein ebenfalls sehr bekanntes und oft gespieltes Spiel ist MasterMind (auch unter dem Namen Superhirn bekannt). Obwohl die Regeln sehr leicht zu erlernen sind, bietet es eine Vielzahl von taktischen Varianten.
32. [C] (Implementierung einer Klasse IntSet). Verwenden Sie die Klasse Vector dazu, eine Klasse IntSet zu implementieren, die eine Menge von Ganzzahlen darstellt. Im Gegensatz zu einem normalen Vektor darf in einer Menge natürlich kein Element mehrfach vorkommen, was durch die Eingaberoutinen sichgestellt werden muß. Implementieren Sie die folgenden Methoden:
33. [C] (Bearbeiten von ini-Dateien). Während der Versionen 3.0 bis 3.11 waren ini-Dateien das wichtigste Instrument zur Konfiguration von Programmen unter MS-Windows. Mit Windows 95 haben sie durch die Einführung der Registry (einer Art hierarchischer Datenbank für Konfigurationseinträge) an Bedeutung verloren, werden aber trotzdem noch von verschiedenen Programmen verwendet. Das Format der ini-Dateien kann wie folgt beschrieben werden:
[boot] system.drv=system.drv drivers=mmsystem.dll power.drv user.exe=user.exe gdi.exe=gdi.exe sound.drv=mmsound.drv dibeng.drv=dibeng.dll comm.drv=comm.drv shell=Explorer.exe [386Enh] ebios=*ebios device=*vshare device=*dynapage device=*vcd device=*vpd [mci] cdaudio=mcicda.drv sequencer=mciseq.drv waveaudio=mciwave.drvDas Windows-API stellt die Funktion GetProfileString und GetPrivateProfileString zur Verfügung, um einen beliebigen Eintrag aus einer ini-Datei zu lesen. Sie haben nun die Aufgabe, eine etwas vereinfachte Version dieser Funktion sowie eine Methode zum Schreiben eines Konfigurationseintrags zu programmieren. Erstellen Sie dazu eine Klasse IniFile, und implementieren Sie die folgenden Methoden:
34. [C] (Hexdump). Schreiben Sie ein Programm, das eine als Argument in der Kommandozeile übergebene Datei als Hexdump ausgibt. Soll beispielsweise eine Datei mit dem Inhalt
The hex dump output should look like thisals Hexdump ausgegegen werden, so ist die Ausgabe des Programmes:
0000 54 68 65 20 68 65 78 20-64 75 6D 70 20 6F 75 74 The hex dump out 0010 70 75 74 0D 0A 73 68 6F-75 6C 64 20 6C 6F 6F 6B put..should look 0020 20 6C 69 6B 65 20 74 68-69 73 2E 0D 0A like this..Der Inhalt der Datei wird dabei zeilenweise zu jeweils 16 Byte mit ihrem hexadezimalen Bytewert und als ASCII-Text (nicht-druckbare Zeichen werden als "." dargestellt) ausgegeben. Die erste Spalte enthält die Adresse des ersten Bytes der Ausgabezeile, sie wird nach jeder Zeile um 16 Bytes weitergezählt.
Aufg1134.java
35. [C] (LineNumberWriter). Kapitel 13 erklärt, wie eigene Writer-Klassen angelegt und als Filter verwendet werden können. Entwickeln Sie eine Klasse LineNumberWriter, die als Ausgabefilter verwendet werden kann, um die Ausgabe eines Writers mit Zeilennummern zu versehen. Entwerfen Sie auch ein Beispielprogramm, an dem Sie die Funktionsfähigkeit Ihrer Klasse demonstrieren.
36. [D] (Eliza). Eines der ersten und ältesten Programme, das sich mit dem Thema Künstliche Intelligenz auseinandersetzte, war Eliza von Joseph Weizenbaum. Seine Aufgabe war es, mit Hilfe einer geschickten Fragetechnik die Rolle eines Psychiaters in einem Arzt-/Patientendialog zu übernehmen. Eliza hat mittlerweile Kultstatus und ist eine berühmt gewordene Inkarnation des Turing-Tests, bei dem es darum geht, einen menschlichen Gesprächspartner geschickt darüber im Unklaren zu lassen, ob sein Gesprächspartner ein Mensch oder eine Maschine ist.
Interessanterweise kann man eine brauchbare Implementierung von Eliza bereits mit relativ einfachen Mitteln erstellen. Das Programm beginnt den Dialog mit der Frage "Guten Tag. Bitte beschreiben Sie mir Ihr Problem". Die Antwort des Anwenders muß ein einfacher Satz sein, der nach folgenden Regeln beantwortet wird:
37. [D] CPU-Emulation mit MINIPU. Jeder Programmierer weiß, daß die von ihm produzierten Programme in die Maschinensprache des Prozessors übersetzt werden müssen, um von diesem ausgeführt werden zu können. Nur wenige wissen allerdings, wie man einen Prozessor in Assembler oder gar Maschinensprache programmiert. Wir wollen uns in dieser Aufgabe damit beschäftigen, eine kleine Prozessorsimulation und die zugehörigen Werkzeuge zu entwickeln.
Unser Prozessor, den wir im folgenden MINIPU nennen wollen, hat einen sehr einfachen Aufbau. Er besitzt ein einziges universell verwendbares Register (den Akkumulator ACC) und 4096 16 Bit breite Speicherworte, die für Programmcode und Daten verwendet werden können. Weiterhin gibt es einen ebenfalls 4096 Byte großen Stack, der Rücksprungadressen und Akkumulatorinhalte aufnehmen kann. Der Stack wächst von unten nach oben. Er wird über den Stackpointer SP referenziert, der auf das nächste freie Element zeigt. Unmittelbar nach der Initialisierung von MINIPU enthält SP den Wert 0.
Jedes Befehlswort ist 16 Bit lang. Die ersten 4 Bit kodieren den Befehlstyp, die übrigen 12 Bit bleiben entweder unbenutzt oder werden als Adresse verwendet. Der Programmablauf wird durch den Programmzähler PC gesteuert, der jeweils auf den nächsten auszuführenden Befehl zeigt. Die Ausführung eines MINIPU- Programms beginnt immer an Adresse 0. Soll am Anfang des Programms ein zusammenhängender Bereich für Daten reserviert werden, so muß als erster Befehl ein Sprung an den Anfang des Code-Bereichs verwendet werden.
Die folgende Tabelle gibt einen Überblick über den Befehlsvorrat von MINIPU:
| Mnemo | Codierung | Bedeutung |
|---|---|---|
| HLT | 0000 ------------ | Anhalten des Programms |
| LDA | 0001 xxxxxxxxxxxx | Laden des Akkumulators mit dem Inhalt der Speicheradresse (ACC := (x)) |
| STA | 0010 xxxxxxxxxxxx | Speichern des Akkumulatorinhalts an der angegebenen Adresse ((x) := ACC) |
| ADD | 0011 xxxxxxxxxxxx | Addieren eines Speicherworts zum Akkumulator (ACC := ACC + (x)) |
| SUB | 0100 xxxxxxxxxxxx | Subtrahieren eines Speicherworts vom Akkumulator (ACC := ACC - (x)) |
| MUL | 0101 xxxxxxxxxxxx | Multiplizieren eines Speicherwortsmit dem Akkumulator (ACC := ACC * (x)) |
| DIV | 0110 xxxxxxxxxxxx | Dividieren des Akkumulators durch ein Speicherwort (ACC := ACC / (x)) |
| JMP | 0111 xxxxxxxxxxxx | Sprung zur Adresse x (PC := x) |
| JZE | 1000 xxxxxxxxxxxx | Sprung zur Adresse x falls der Akkumulator 0 enthält (PC := x falls ACC == 0) |
| JGT | 1001 xxxxxxxxxxxx | Sprung zur Adresse x falls der Akkumulator einen Wert größer 0 enthält (PC := x falls ACC > 0) |
| PSH | 1010 ------------ | Den Akkumulatorinhalt oben auf dem Stack ablegen ((SP) := ACC; SP := SP + 1) |
| POP | 1011 ------------ | Das oberste Element vom Stack entfernen und im Akkumulator ablegen (SP := SP - 1; ACC := (SP)) |
| JSR | 1100 xxxxxxxxxxxx | Den Inhalt des Programmzählers oben auf dem Stack ablegen ((SP) := PC; SP := SP + 1) |
| RET | 1101 ------------ | Das oberste Element vom Stack entfernen und es als Programmzähler verwenden (SP := SP - 1; PC := (SP)) |
| INP | 1110 xxxxxxxxxxxx | Ein 16-Bit-Wort als Dezimalzahl über die Tastatur einlesen und im Akkumulator ablegen. An Adresse x steht ein String, der zur Beschriftung der Eingabe verwendet werden kann. |
| OUT | 1111 xxxxxxxxxxxx | Den Inhalt des Akkumulators als Dezimalzahl auf dem Bildschirm ausgeben. An Adresse x steht ein String, der zur Beschriftung der Ausgabe verwendet werden kann. |
Ihre erste Aufgabe besteht nun darin, einen Emulator für den MINIPU- Prozessor zu schreiben. Schreiben Sie dazu eine geeignete Klasse MiniPU, die alle benötigten Datentrukturen und Funktionen implementiert. Die Methode startProgram soll dabei den Interpreter starten und das als Array übergebene Programm ausführen. Führen Sie den Programmlauf als separaten Thread aus und ermöglichen Sie es, ihn mit Hilfe eines Aufrufs von stopProgram zu unterbrechen. Integrieren Sie den Emulator in eine Testumgebung, die das interaktive Starten und Stoppen erlaubt und die Möglichkeit bietet, ein MINIPU-Maschinenprogramm von der Festplatte in den Speicher zu lesen.
Da die Programmierung in Maschinencode sehr aufwendig ist, sollten Sie weiterhin einen Assembler für MINIPU-Code entwickeln. Ein Assembler ist ein Programm, das mnemonische Befehlscodes in MINIPU-Maschinencode übersetzt. Er soll beispielsweise das folgende Programm, das in MINIPU-Assembler geschrieben wurde, in die interne Maschinendarstellung übersetzen:
0000 JMP 0005 ;Sprung an den Programmanfang 0001 DW 0002 ;Konstante 2 0002 DW 'Za' ;String "Zahl " 0003 DW 'hl' 0004 DW ' \0' 0005 INP 0002 ;Zahl einlesen 0006 MUL 0001 ;Mit 2 multiplizieren 0007 OUT 0002 ;Zahl ausgeben 0008 HLT ;ProgrammendeStatten Sie den Assembler auch mit Fähigkeiten zum Lesen und Schreiben aus einer Datei aus, um die erstellten Programme auf Festplatte ablegen zu können. Erweitern Sie die Klasse MiniPU zusätzlich um die Fähigkeit, MINIPU-Programme aus einer Datei zu lesen und auszuführen.
38. [D] (Vokabeltrainer). Eine einfache und wirksame Methode, Vokabeln zu lernen, besteht darin, das Karteikastenprinzip anzuwenden. Dabei steht jede Vokabel auf einer Karteikarte, die sich in einem von vier Karteikästen befindet. Auf der Rückseite der jeweiligen Karte steht ihre Übersetzung. Zu Beginn des Lernvorgangs befinden sich alle Karten in Kasten eins. Nun werden die Karten aus diesem Kasten nacheinander abgefragt. Bei einer richtigen Antwort landet die betreffende Karte in Kasten zwei, bei einer falschen Antwort verbleibt sie in Kasten eins. Nach dem ersten Durchlauf befinden sich also alle Karten, die richtig übersetzt wurden, in Kasten zwei, während alle fehlerhaften in Kasten eins geblieben sind.
Nach dem gleichen Prinzip werden nun in weiteren Durchgängen die vier Karteikästen mehrfach durchgearbeitet. Immer, wenn eine Vokabel richtig übersetzt wurde, wandert sie in den nächsten Kasten, andernfalls geht es einen Kasten zurück. Befinden sich alle Karteikarten in Kasten vier, so wurden jede mindestens dreimal nacheinander richtig übersetzt und man kann davon ausgehen, daß das Erlernte "sitzt".
Wichtig für den Lernerfolg ist eine geschickte Strategie für die Reihenfolge, in der die Kästen bearbeitet werden. Bei insgesamt 10 Durchgängen wären etwa die Kastenfolgen 1-2-3-1-2-3-1-4-2-1 oder 1-1-2-1-3-2-1-2-3-4 denkbar. Schreiben Sie ein Programm, das es ermöglicht, Vokabeln mit Hilfe des Karteikastenprinzips zu lernen. Speichern Sie die zu lernenden Vokabeln in einer Textdatei, die beim Aufruf des Programms geladen und beim Beenden mit den veränderten Kasteninformationen zurückgeschrieben wird. Das Programm soll folgende Features bieten:
39. [D] (edlin). Einer der ersten Texteditoren, die unter MS-DOS zur Verfügung standen, war EDLIN. Wegen seiner zeilenorientierten Arbeitsweise und der umständlichen Bedienung war er bei vielen Anwendern gefürchtet. Seit der Version 5.0 ist er aus dem Betriebssystem verschwunden und wurde durch den leistungsfähigeren Texteditor EDIT ersetzt.
Ihre Aufgabe besteht nun darin, EDLIN wieder zum Leben zu erwecken und eine stark vereinfachte Version nachzuprogrammieren. Da es an dieser Stelle zu aufwendig wäre, alle Details der echten EDLIN-Implementierung zu implementieren, gehen Sie entsprechend der folgenden, vereinfachten Spezifikation vor:
*Nach der Eingabe eines Kommandos und dem Drücken der ENTER-Taste wird das Kommando interpretiert und entweder ausgeführt oder mit einer Fehlermeldung abgelehnt. Anschließend wird wieder die Kommandozeile angezeigt und EDLIN ist zur Aufnahme eines neuen Kommandos bereit.
1: #includeVor jeder Zeile steht die Zeilennummer und die aktuelle Zeile (die zuletzt editierte) ist mit einem Stern markiert.2: 3:*void main(void) 4: { 5: printf("Hello, World\n"); 6: }
Nach der Eingabe des I-Kommandos bleibt EDLIN im Eingabemodus und erlaubt es, mehrere Zeilen einzugeben. Soll der Einfügemodus beendet werden, so ist in einer leeren Zeile ein alleinstehender Punkt einzugeben (im echten EDLIN wurde die Eingabe durch STRG+C beendet).