|
|
|||||||||||||||||||||
Folge 21.2: Ein Codebuffer |
|||||||||||||||||||||
Schritt 3 - SteuercodeAuf Dauer ist es recht lästig, wenn man den Quellcode der Klasse Test für jede neue Berechnung neu schreiben muss. Ideal wäre es, wenn der Steuercode für die Stackmaschine in einer Textdatei stünde, die vielleicht folgendermaßen aufgebaut wäre:push 4 push 3.14 add push 8 push 2.71 sub mul Dies ist wieder das Beispiel zur Berechnung des Ausdrucks (4 + 3.14) * (8 - 2.71) Was diese sieben Zeilen der Textdatei genau bewirken, müsste Ihnen jetzt eigentlich klar sein. Es handelt sich um Befehle für die Stackmaschine. Ein solcher Befehl hat stets das Format code arg Ein Stackmaschinen-Befehl besteht also aus zwei Komponenten: Dem eigentlichen Befehl, z.B. push, add oder pop, und eventuell einem Argument. Der push-Befehl benötigt z.B. die zu pushende Zahl als Argument, während der add- oder der pop-Befehl kein Argument benötigen. |
|||||||||||||||||||||
|
Siehe auch: |
||||||||||||||||||||
Schritt 4 - Textdateien lesenNun kommt mal wieder etwas völlig Neues - sowohl theoretisch wie aus programmierpraktischer Sicht. Wie kann ein Java-Programm eine Textdatei Zeile für Zeile einlesen und dann auswerten? Wir wollen uns zunächst mit dem ersten Teil dieser Frage beschäftigen: Wie kann eine Textdatei Zeile für Zeile eingelesen und angezeigt werden? Lesen Sie sich dazu bitte den Lexikon-Eintrag "Textdateien einlesen" durch und kommen Sie anschließend wieder hierhin zurück. |
|||||||||||||||||||||
|
|||||||||||||||||||||
Schritt 5 - Steuercode ausführenNachdem Sie jetzt eine Klasse Codebuffer geschrieben haben, die in der Lage ist, Steuercode für eine Stackmaschine zu lesen und zu speichern, wollen wir uns jetzt um den nächsten größeren Schritt in unserem Stackinterpreter-Projekt kümmern. Der Steuercode soll interpretiert werden. Wenn also ein Befehl wie Push 17 erkannt wird, dann soll die Zahl 17 auf den Stack der Stackmaschine gepusht werden. Die Frage ist nur, wer ist eigentlich für die Zusammenarbeit von Stackmaschine und Codebuffer verantwortlich? Vom Projekt-Design her sind mehrere Lösungen denkbar. Wenn Sie interessiert an dieser Frage sind und/oder eine Klausur in Informatik schreiben wollen/müssen, lesen Sie bitte den folgenden Theorieteil. Exkurs für Abiturienten und Klausurleute: Fassen wir jetzt also einmal zusammen (nähere Einzelheiten siehe Exkurs): Wir brauchen eine Klasse Stackinterpreter, die dann Attribute der Klassen Stackmaschine, Codebuffer und - später - Variablenliste hat. Der Stackinterpreter gibt den Befehl zum Einlesen des Stackcodes an das Codebuffer-Objekt weiter. Das Codebuffer-Objekt speichert die Zeilen der Textdatei in einem String-Array (die einfachste Lösung; Sie können das Ganze natürlich auch anderes implementieren, zum Beispiel mit einer dynamischen Liste). Dann holt sich der Interpreter den ersten Befehl aus dem Codebuffer und interpretiert ihn. Handelt es sich zum Beispiel um den Befehl Push 3.14 so ruft der Interpreter das Stackmaschinen-Objekt auf und weist es an, die Zahl 3.14 auf den Stack zu pushen. Das hört sich alles ziemlich kompliziert an, nicht wahr. |
|
||||||||||||||||||||
|
Siehe auch: |
||||||||||||||||||||
Schritt 6 - Analyse von StringsAls Nächstes wollen wir den Stackinterpreter dazu bringen, das zu tun, wozu er hauptsächlich gedacht ist, nämlich die jeweils aktuelle Befehlszeile zu interpretieren. Der Interpreter ruft zunächst den Codebuffer auf, damit dieser die Textdatei in einem internen Speicher zwischenlagert. Ein Anzeigen in der Stackbefehle in der Konsole ist eigentlich überflüssig; später werden wir das System so erweitern, dass die wichtigsten Daten in einem Java-Applet dargestellt werden. Hat der Codebuffer die Textdatei mit den Stackbefehlen gelesen, muss sie "die jeweils aktuelle" Befehlszeile an den Interpreter übergeben. Wir benötigen also eine sondierende Methode, die genau dies macht. Die sondierende Methode könnte zum Beispiel heißen public String naechsterBefehl() Der Interpreter erhält also einen String von dem Codebuffer und muss dann den String analysieren. Wenn der String beispielsweise den Wert "push 3.14" hat, so muss der Interpreter erkennen, dass es sich a) um den push-Befehl handelt und dass b) der Wert 3.14 gepusht werden soll. Für diese Analyse-Arbeit müssen wir uns jetzt ein wenig mit Strings beschäftigen. Lesen Sie dazu bitte den Lexikon-Eintrag zur Klasse String durch. Erkennen des BefehlsEin Befehl wie "push 3.14" liegt zunächst als ein einfacher String vor. Wie kann nun erkannt werden, dass es sich um den push-Befehl handelt und nicht um den add- oder mul-Befehl? Da es nur fünf verschiedene Stackmaschinen-Befehle gibt (zur Zeit jedenfalls), reicht es aus, den ersten Buchstaben des Strings zu analysieren. Handelt es sich dabei um ein "p", so kann nur der push-Befehl gemeint sein. Wenn ein etwas gedankenloser Programmierer allerdings einen Befehl wie "padd" in die Textdatei geschrieben hat, hat er eben Pech gehabt; auch dieser Befehl würde als "push" interpretiert. Vielleicht sollte man also doch nicht nur auf den ersten Buchstaben achten. Ideal hierfür ist die Methode startsWith(String pre). Sie können in Ihren Java-Quelltext also eine if-Abfrage wie if (befehl.startsWith("push")) ...
einbauen. Entsprechend verfahren Sie für die anderen vier Stackmaschinenbefehle add, sub, mul und divi. Richtig interessant wird es aber nur beim push-Befehl, denn hier müssen wir auch noch das Zahlen-Argument aus dem Befehls-String heraus holen und als double-Zahl zurück liefern, damit die Stackmaschine diese Zahl pushen kann. Erkennen des ArgumentsDieses Problem werden wir in zwei Schritten lösen. Zunächst müssen wir den Teilstring "3.14" aus dem String "push 3.14" extrahieren, und anschließend müssen wir den Teilstring "3.14" in die double-Zahl 3.14 übersetzen. Denn die Methode push() der Klasse Stack erwartet ja eine double-Zahl als Parameter und nicht einen String. Herauslösen des ArgumentsDa nicht bekannt ist, welche Zahl als Argument des push-Befehls verwendet wird, kann man den Befehl startsWith() oder das analoge endsWith() natürlich nicht verwenden. Was gibt es sonst noch für interessante String-Befehle? public String substring(int beginIndex) scheint ein solcher Befehl zu sein. Dieser Befehl extrahiert einen Substring aus dem String, und zwar beginnend mit der als Parameter übergebenen Anfangsposition. Wenn wir also die Zeile "push 3.14"
näher untersuchen, stellen wir Folgendes fest:
Die zu extrahierende Zahl beginnt bei Index 5. Also müsste man im Quelltext der entsprechenden Stackmaschinen-Methode schreiben können: argString = befehl.substring(5); Der String argString müsste bei "push 3.14" dann den Wert "3.14" haben. Konvertieren des ArgumentsJetzt haben wir das Argument des push-Befehls extrahiert, es liegt aber immer noch als String vor. Als nächstes müssen wir den String "3.14" (bzw. das jeweilige Argument) in eine Zahl vom Typ double konvertieren (umwandeln). Bei der Suche nach einer geeigneten Konvertierungs-Methode werden wir in der Dokumentation der Klasse String nicht fündig. Versuchen wir es einmal mit der Klasse Double. Diese Klasse stellt wichtige Routinen zum Umgang mit double-Zahlen zur Verfügung, so zum Beispiel auch parseDouble(), welches im Return-Befehl einer sondierenden Methode eingesetzt werden kann. public double gibZahl(String s)
{
return Double.parseDouble(s);
}
|
Die Klasse String (Sun-Dokumentation) Lexikon-Eintrag zur |
||||||||||||||||||||
Schritt 7 - Strategische Überlegungen IIEine strategische Überlegung haben wir ja bereits hinter uns gebracht, als wir verschiedene Möglichkeiten durchspielten, in welchem Verhältnis die Klassen Stackinterpreter, Stackmaschine und Codebuffer zueinander stehen sollen. Ich habe Sie dann ja dazu überredet, die Variante zu wählen, bei der der Stackinterpreter Objekte vom Typ Stackmaschine und Codebuffer HAT. Nun folgt eine weitere strategische Überlegung. Wer bzw. welche Klasse soll eigentlich die Codeanalyse durchführen. Hier gibt es wieder zwei Möglichkeiten. Möglichkeit 1Der Codebuffer gibt den jeweils aktuellen Befehl als einfachen String an den Interpreter zurück, und die Analysearbeit wird im Interpreter geleistet. Möglichkeit 2Der Codebuffer selbst analysiert den Befehlsstring und gibt dann eine Befehlsnummer sowie gegebenenfalls (falls es sich um den push-Befehl handelt) das Argument des Befehls an den Interpreter zurück. Nachdem ich mit meinen Kursen jahrelang die Möglichkeit 1 realisiert habe, möchte ich nun (Mai 2008) zum ersten Mal die zweite Möglichkeit ausprobieren. Das formuliere ich gleich mal als nächste Aufgabe:
|
Siehe auch: |
||||||||||||||||||||
Weiter mit Teil 3 der Folge 21
|
|||||||||||||||||||||
|
Diese HTML-Seite wurde erstellt von Ulrich Helmich am 21. Juli 2006 und sehr stark überarbeitet am 7. Mai 2008. |
|||||||||||||||||||||