Skip to content

Instantly share code, notes, and snippets.

@denkspuren
Last active June 10, 2018 16:47
Show Gist options
  • Save denkspuren/b64bfb06b7b54c9c02fd1e867453a12d to your computer and use it in GitHub Desktop.
Save denkspuren/b64bfb06b7b54c9c02fd1e867453a12d to your computer and use it in GitHub Desktop.
Diese Aufgabe verlangt von Ihnen einen Umgang mit Enumerationen, Funktionen, Lambda-Ausdrücken und Strömen
/*
In dieser Aufgabe geht es darum, einen als String gegebenen UPN-Ausdruck in
einen Stapel von Ergebniswerten zu wandeln. Ein Beispiel:
jshell> upn2results.apply("2 3 + 5 4 *", new Stack<Integer>());
$43 ==> [5, 20]
Dieser UPN-Rechner arbeitet ausschließlich mit Ganzzahlen.
Sie werden durch die Aufgabe geführt. Halten Sie sich bitte an alle Angaben.
In dieser Datei sind eine Reihe von `assert`-Anweisungen eingestreut, die den
Code testen.
Ihr Implementierung ist dann vermutlich korrekt, wenn Sie folgende
print-Ausgaben sehen, die nicht von irgendwelchen `assert`-Ausnahmen
durchsetzt sind.
Cool, you got the tokenizer running
Operators ADD and INV are correctly declared! Good job.
Hey, you understood how ZERO works!
I'm impressed, NUM works as expected!
Super, the constructor of Item looks good!
With Item.compute things start to fall into place. GREAT!
Looks like you succeeded. Congratulations!
Rufen Sie die JShell unbedingt mit `jshell -R-ea` auf, ansonsten werden die
`assert`-Anweisungen nicht ausgeführt!
Füllen Sie die nachfolgenden Code-Fragmente aus. Wenn Sie Ihre Arbeit mit
dieser Datei beginnen, kommentieren Sie alles nachfolgende aus, weil die
Code-Fragmente so kein gültiger Java-Code sind und zu Abbrüchen und
Inkonsistenzen führen. Bisweilen hilft es auch, ein `/reset` in der JShell
einzugeben.
*/
/*
Implementieren Sie die Funktion `tokenize`, die einen als Zeichenkette
gegebenen UPN-Ausdruck in seine Bestandteile zerlegt, d.h. die Leerzeichen
trennen Zahlen und Operatoren.
jshell> tokenize.apply("4 5 * 7 /").collect(Collectors.toList())
$49 ==> [4, 5, *, 7, /]
jshell> tokenize.apply("4 5 * 7 /").forEachOrdered(System.out::println)
4
5
*
7
Die Implementierung ist ein Einzeiler, d.h. sie benötigt ein(!) Semikolon.
*/
Function<String,Stream<String>> tokenize = ...;
assert tokenize.apply(" Hello World! ").collect(Collectors.toList()).equals(List.of("Hello","World!"));
assert tokenize.apply(" 2 4 + 5 4 * * ").collect(Collectors.toList()).equals(List.of("2","4","+","5","4","*","*"));
assert tokenize.apply("").collect(Collectors.toList()).equals(List.of(""));
System.out.println("Cool, you got the tokenizer running");
/*
Erstellen Sie einen Aufzählungstyp `Operator`, der die vorhandenen Operatoren
zum Rechnen abbildet. Es soll die zweiwertigen Operatoren `ADD`, `SUB`, `MUL`,
`DIV` und `MOD` geben, den einwertigen Operator `INV` und die nullwertigen
Operatoren `ZERO` und `NUM`.
Die Abkürzungen sind übliche Mnemonics (`ADD` steht z.B. für die Addition,
`INV` für die Invertierung, d.h. für den Vorzeichenwechsel). `ZERO` ist ein
Operator, der eine Null repräsentiert. `NUM` ist ein besonderer Operator, der
für eine Zahl steht.
Für die Operatoren soll jeweil angegeben werden, durch welche Zeichenfolge sie
repräsentiert werden (Variablenname `repr`), wieviele Argumente der Operator
benötigt (Variablennamen `arity`) und wie der Operator als Operation rechnet,
wenn die Argumente als Array gegeben sind (eine Funktion mit dem
Variablennamen `calculation).
Neben dem Konstruktor gibt es eine statische Funktion namens `getOperator`,
die zu einer Zeichenkette (der Repräsentation eines Operators) den passenden
Operator heraussucht.
Eine sorgfältige Auswertung der `assert`-Anweisungen leitet Sie durch die
Implementierung.
*/
enum Operator {
ADD("+",2, args -> args[1] + args[0]),
...
Operator(String repr, int arity, ...) {
...
}
static Operator getOperator(String s) {
...
}
}
assert Operator.getOperator("+") != null;
assert Operator.getOperator("+").name().equals("ADD");
assert Operator.ADD.arity == 2;
assert Operator.ADD.calculation.apply(new Integer[] {2,3}) == 5;
assert Operator.getOperator("~") != null;
assert Operator.getOperator("~").name().equals("INV");
assert Operator.INV.arity == 1;
assert Operator.INV.calculation.apply(new Integer[] {1}) == -1;
System.out.println("Operators ADD and INV are correctly declared! Good job.")
assert Operator.getOperator("ZERO") != null;
assert Operator.getOperator("ZERO").name().equals("ZERO");
assert Operator.ZERO.arity == 0;
assert Operator.ZERO.calculation.apply(new Integer[0]) == 0;
System.out.println("Hey, you understood how ZERO works!");
assert Operator.getOperator("") != null;
assert Operator.getOperator("").name().equals("NUM");
assert Operator.ZERO.arity == 0;
assert Operator.ZERO.calculation.apply(new Integer[0]) instanceof Integer;
System.out.println("I'm impressed, NUM works as expected!");
/*
Der durch den `tokenizer` generierte Zeichenkettenstrom soll später
in einem ersten Durchlauf abgebildet werden auf einen Strom von Items.
Dafür wird die Klasse `Item` benötigt.
Die Klasse `Item` bekommt im Konstruktor eine Zeichenkette geliefert und
legt intern im erzeugten Objekt in der Variable `operator` den Operator
ab und -- wenn der Operator ein ´NUM` ist -- den Ganzzahlwert der
Zeichenkette in einer Variable namens `value` ab.
Die Methode `compute` erwartet einen Stapel mit Ganzzahlen, wendet die
zum Operator gespeicherte Rechenoperation des Items an (außer bei NUM,
da wird der Zahlenwert auf den Stapel gepusht) und legt das Ergebnis
auf den Stapel ab.
Im Rumpf der Referenzimplementierung kommen insgesamt 11 Semikolons zum
Einsatz.
*/
class Item {
...
Item(String s) throws NumberFormatException {
...
}
Stack<Integer> compute(Stack<Integer> stack) {
...
}
}
// Ignorieren Sie die Funktion: helper function for testing purposes
Function<List<Integer>,Stack<Integer>> stackify = lst -> {
Stack<Integer> stack = new Stack<>();
lst.stream().forEachOrdered(stack::push);
return stack;
}
assert new Item("ZERO").operator == Operator.ZERO;
assert new Item("234").operator == Operator.NUM;
assert new Item("0").value == 0;
assert new Item("-10").value == -10;
System.out.println("Super, the constructor of Item looks good!");
assert new Item("ZERO").compute(new Stack<Integer>()).equals(List.of(0));
assert new Item("123").compute(new Stack<Integer>()).equals(List.of(123));
assert new Item("/").compute(stackify.apply(List.of(2,12,3))).equals(List.of(2,4));
assert new Item("~").compute(stackify.apply(List.of(7,14))).equals(List.of(7,-14));
System.out.println("With Item.compute things start to fall into place. GREAT!");
/*
Sie haben es fast geschafft! Es fehlt nur noch die Umsetzung der Funktion
`upn2results`.
Der Funktion wird ein UPN-Ausdruck als Zeichenkette und ein Stapel übergeben.
Zurück kommt ein Stapel, auf dem die Ergebnisse der Auswertung zusätzlich
abgelegt sind.
Die Referenzimplementierung kommt mit zwei Semikolons aus!
*/
... upn2results = ...;
assert upn2results.apply("2 3 +", new Stack<Integer>()).equals(List.of(5)); // pop() == 5;
assert upn2results.apply("2 3 -", new Stack<Integer>()).equals(List.of(-1)); // pop() == -1;
assert upn2results.apply("2 3 + 4 * 2 3 -", new Stack<Integer>()).equals(List.of(20,-1));
assert upn2results.apply("2 3 + 4 * 2 3 ~ +", new Stack<Integer>()).equals(List.of(20,-1));
assert upn2results.apply("ZERO ZERO + ZERO *", new Stack<Integer>()).equals(List.of(0));
System.out.println("Looks like you succeeded. Congratulations!");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment