Informatik II – Übung 4 Gruppe 3 Leyna Sadamori leyna.sadamori@inf.ethz.ch
Debriefing Übung 3
U3A1: Programmverifikation Schleifeninvariante: z + u ∙ j − i ∙ j Mit dem Hoare-Kalkül lässt sich die partielle(!) Korrektheit beweisen Zusätzlich muss noch die Terminierung bewiesen werden
U3A2: Strings und StringBuffer Aneinanderreihung Strings: String + String + ... StringBuffer: StringBuffer.append().append() ... Mögliche Ausgabe: Starting encryption (using Strings) Done - Duration: 3732 ms. Starting decryption (using StringBuffers) Done - Duration: 73 ms. Decryption successful :-)
U3A2: Caesar-Chiffre Konstanter Offset, dadurch sehr einfach zu “berechnen” Addition bzw. Subtraktion in ASCII Zeichensatz Der primitive Datentyp char entspricht dem numerischen Wert eines ASCII Zeichens
U3A4: Syntaxchecker Erweiterung des Syntaxdiagramms für Bäume um den leeren (Teil-)Baum
U3A4: Syntaxchecker Zeichenweise Überprüfung mittels Syntaxchecker für Baum, Nachfolger und Knoten Herausforderung: Aktuelle Position im String Einfache Lösung: Globale Variable Globale Variablen normalerweise als Instanzvariablen Achtung bei Klassenvariablen! (static) Eleganter: Jeder Syntaxchecker gibt zurück, wie weit er im String vorangeschritten ist (oder wirft eine Exception bei Fehler)
U3A4: Baum private static int parseTree(String kd, int offset) throws ParseException { if (kd.charAt(offset) == '-') return offset + 1; offset = parseNode(kd, offset); if (offset == kd.length()) return offset; if (kd.charAt(offset) == '(') { offset++; offset = parseSubtree(kd, offset); if (kd.charAt(offset) == ')') { } else throw new ParseException("expected ')'", offset); }
U3A4: Teilbaum private static int parseSubtree(String kd, int offset) throws ParseException { offset = parseTree(kd, offset); while (kd.charAt(offset) == ',') { offset++; } return offset;
U3A4: Knoten private static int parseNode(String kd, int offset) throws ParseException { char c = kd.charAt(offset); if (Character.isUpperCase(c)) return offset + 1; else throw new ParseException(String.format("'%c' is not a valid node name", c), offset); }
U3A4: Parser A(-,B(C),-,D(E(-,F(G,H)),I)) public static void parse(String kd) throws ParseException { try { int offset = parseTree(kd, 0); if (offset != kd.length()) throw new ParseException("Garbage at the end of the tree", offset); } catch (IndexOutOfBoundsException e) { throw new ParseException("Unexpected end of string.", kd.length()); }
Briefing Übung 4
Stack Stack: Funktioniert wie ein Stapel Wo werden Stacks verwendet? Speicher nach dem LIFO (last-in-first-out) Prinzip Wo werden Stacks verwendet? Z.B. beim Call Stack (Aufrufstapel)
Call Stack (Aufrufstapel) Jeder Funkstionsaufruf erhält eigenen “Context” Eigene Parameter (Funktionsargumente) Lokale Variablen Rücksprungadresse und Rückgabewerte Je nach Implementierung (verfügbare “Features”) sehr komplex Beispiel: siehe Demo Impedance.add Vereinfachtes Beispiel
Call Stack Beispiel Impedance z3 = Impedance.add("z3", z1, z2); double real = z1.getReal() + z2.getReal(); getReal double real double imag String name = “z3” Impedance z1 = #1 Impedance z2 = #2 add local Variables function arguments
Call Stack Beispiel Impedance z3 = Impedance.add("z3", z1, z2); double real = z1.getReal() + z2.getReal(); double imag = z1.getImag() + z2.getImag(); getImag double real double imag String name = “z3” Impedance z1 = #1 Impedance z2 = #2 add local Variables function arguments
Call Stack Beispiel Impedance z3 = Impedance.add("z3", z1, z2); double real = z1.getReal() + z2.getReal(); double imag = z1.getImag() + z2.getImag(); return new Impedance(name, real, imag); String name = “z3” double real = 50.0 double imag = 20.0 Impedance double real double imag String name = “z3” Impedance z1 = #1 Impedance z2 = #2 add local Variables function arguments
U4A1: Stack U4A1a: Die Variable size gibt die aktuelle Größe des Stacks an, nicht seine Kapazität U4A1b: Konvertierung von int zu String mittels Verwendung von StringBuffer und der Methode append(String str) static String Integer.toString(int i)
U4A1: Stack U4A1c: Allgemeine Hinweise: Einfache Variante: ? Fortgeschrittene Variante: Verwendung von Allgemeine Hinweise: static Funktion: Klassenfunktion, benötigt keine Erzeugung von Objekten Beachtet die Beschreibung in der Java API static int[] Arrays.copyOf(int[] original, int newLength)
U4A1d: Stack push() pop(), peek() Kontrolliert, ob die Kapazität des Stacks ausreichend ist. Ansonsten grow() pop(), peek() Kontrolliert, ob überhaupt etwas auf dem Stack liegt.
U4A2: Ackermann Funktion rekursive Definition wächst extrem schnell A(3,3) = 61 A(4,2) = 265536 − 3 !! Wilhelm Ackermann (1986 – 1962, Deutchland)
U4A2a Schreibt die Ackermann Funktion um: A(0, m) = m +1 A(n, 0) = A(n-1, 1) A(n, m) = A(n-1, A(n, m-1)) Schreibt die Aufrufe ausführlicher auf, als im Beispiel Schreibt anschließend nur die Aufrufe von A auf (wie im Beispiel) Beim Aufschreiben auf die Einrückung achten
U4A2b-c Aufgabenteil b) kann zusammen mit c) gelöst werden Pseudocode: Wenn die Implementierung von c) korrekt ist und ausreichend kommentiert ist, zählt b) auch als gelöst. Es muss nicht zusätzlich Pseudocode geschrieben werden. Wer Aufgabenteil c) nicht oder nur teilweise lösen kann, sollte den Pseudocode von Aufgabenteil b) schreiben. Pseudocode: Keine sprachspezifische Syntax Pseudocode ist selbsterklärend Grundlage für Kommentare
Push Start A(1,1) A(1,1) A(1,0) A(0, 1) <- 2 A(0, 2) <- 3 Iteration m = 1 m = 0 m = 1 m = 2 Pop size > 2? No. n = 1 n = 0 n = 0 n = 1 Push n == 0? m + 1 No. n - 1 Push m == 0? No. 1 n - 1 Push m = 0 m = 1 else n n = 0 m - 1 m = 1 n = 1 m = 2 n = 0 m = 3 n = 1 Pop Stack End A(1,1) = 3
U4A2: Weitere Überlegungen for- oder while Schleife? Kann man vorhersagen, wie tief die Rekursion geht? Keine “saubere Trennung” zwischen Funktionsargumenten und Rückgabewerten Rückgabewert einer Ackermann-Funktion entspricht m Deshalb die Reihenfolge der Argumente n, m
U4A3: Bytecode Bevor ihr den Code disassembliert, müsst ihr den Code erst kompilieren Hinweis für Linux und Mac Nutzer: Verwendet den >> Operator in der Konsole, um die Ausgabe direkt in eine Datei zu schreiben Beispiel: javap -c RecursiveAckermann >> output.txt
U4A3: Bytecode Befehle iload_<n> lädt die lokale Variable aus Position <n> aus dem Speicher aload_<n> lädt eine Referenz aus Position <n> aus dem Speicher ifne <l> springt zu Zeile <l>, wenn der Vergleich mit Null negativ ausfällt invokevirtual ruft eine neue Instanz der Methode auf Beachtet die Dokumentation in http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5
Noch mehr Theorie
Call Stacks – Erweiterte Themen Rekursive Funktionen Je nach Rekursionstiefe wenig effizient (und begrenzt! Stack Overflow) Debugging: Stack Trace Der Call Stack wird “zurückgespult” (unwinding)
…viel Spass!