Skip to content

Commit 8c77964

Browse files
committed
week4 Evaluator lahendused
1 parent 51019df commit 8c77964

9 files changed

Lines changed: 421 additions & 0 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package week4.baselangs.bool;
2+
3+
import week4.baselangs.bool.ast.*;
4+
5+
import java.util.Set;
6+
7+
public class BoolEvaluator {
8+
9+
// Väärtustada tõeväärtusavaldis, kui ette antud on tõeste muutujate hulk.
10+
public static boolean eval(BoolNode node, Set<Character> tv) {
11+
return switch (node) {
12+
case BoolVar(char name) -> tv.contains(name);
13+
case BoolOr(BoolNode left, BoolNode right) -> eval(left, tv) || eval(right, tv);
14+
case BoolImp(BoolNode antedecent, BoolNode consequent) -> !eval(antedecent, tv) || eval(consequent, tv);
15+
case BoolNot(BoolNode exp) -> !eval(exp, tv);
16+
};
17+
}
18+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package week4.baselangs.expr;
2+
3+
import week4.baselangs.expr.ast.*;
4+
5+
import java.util.Collections;
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
9+
public class ExprEvaluator {
10+
11+
// Esiteks implementeeri ExprNode alamklassides eval meetodid õigesti.
12+
// Nende testimiseks on ExprEvaluatorTest-is testEvalNodeMethod.
13+
14+
// Mõnikord aga me ei taha/saa muuta AST klasse.
15+
// Seega vaatame mitmeid alternatiive, kuidas AST-i töödelda.
16+
17+
// Klassikaline viis on kasutada instanceof-i.
18+
19+
/**
20+
* Väärtustab avaldise.
21+
*/
22+
public static int evalInstanceof(ExprNode node) {
23+
// Paranda siin üks vigane juht.
24+
if (node instanceof ExprNum num) {
25+
return num.value();
26+
} else if (node instanceof ExprNeg neg) {
27+
return -evalInstanceof(neg.expr());
28+
} else if (node instanceof ExprAdd add) {
29+
return evalInstanceof(add.left()) + evalInstanceof(add.right());
30+
} else if (node instanceof ExprDiv div) {
31+
return evalInstanceof(div.numerator()) / evalInstanceof(div.denominator());
32+
} else {
33+
// Siia ei peaks kunagi jõudma (v.a. null), aga kompilaator seda ei tea.
34+
throw new IllegalArgumentException();
35+
}
36+
}
37+
// (Tegelikult oleme siin kasutanud juba Java 16 instanceof mustri võimalust. Ilma selleta oleks veel kohmakam.)
38+
// Seejärel proovi evalInstanceof meetodis ka IntelliJ soovitusi rakendada.
39+
40+
41+
// Kui neid soovitusi omavahel kombineerida, siis saame kasutada uuemaid Java 21 võimalusi:
42+
// kirjed (record-id), mustrisobitus (pattern matching) ja kinnised (sealed) liidesed/klassid.
43+
44+
/**
45+
* Väärtustab avaldise.
46+
* Kasutab uuemaid Java 21 võimalusi.
47+
*/
48+
public static int eval(ExprNode node) {
49+
// Paranda siin ka vigane juht.
50+
return switch (node) {
51+
// Kirjete puhul saab kasutada mustrisobitust, et panna vastavad väljad kohe lokaalsetesse muutujatesse.
52+
case ExprNum(int value) -> value;
53+
case ExprNeg(ExprNode expr) -> -eval(expr);
54+
case ExprAdd(ExprNode left, ExprNode right) -> eval(left) + eval(right);
55+
case ExprDiv(ExprNode numerator, ExprNode denominator) -> eval(numerator) / eval(denominator);
56+
// Kuna ExprNode on kinnine, siis kompilaator teab, et default juhtu pole vaja.
57+
};
58+
}
59+
60+
// Nagu näha, siis need võimalused oluliselt lihtsustavad avaldispuude defineerimist ja nende väärtustamist.
61+
62+
63+
/**
64+
* Kogub kokku kõik tippudes esinevad arvud.
65+
*/
66+
public static Set<Integer> getAllValues(ExprNode node) {
67+
return switch (node) {
68+
case ExprNum(int value) -> Collections.singleton(value);
69+
case ExprNeg(ExprNode expr) -> getAllValues(expr);
70+
case ExprAdd(ExprNode left, ExprNode right) -> {
71+
Set<Integer> result = new HashSet<>();
72+
result.addAll(getAllValues(left));
73+
result.addAll(getAllValues(right));
74+
yield result;
75+
}
76+
case ExprDiv(ExprNode numerator, ExprNode denominator) -> {
77+
Set<Integer> result = new HashSet<>();
78+
result.addAll(getAllValues(numerator));
79+
result.addAll(getAllValues(denominator));
80+
yield result;
81+
}
82+
};
83+
}
84+
85+
86+
/**
87+
* Kogub kokku kõik tippudes esinevad arvud.
88+
* Väldib koodi kordusi kasutades abiklassi isendit ja imperatiivset stiili.
89+
*/
90+
public static Set<Integer> getAllValuesImperative(ExprNode node) {
91+
ValueCollector valueCollector = new ValueCollector();
92+
valueCollector.addAllValues(node);
93+
return valueCollector.result;
94+
}
95+
96+
private static class ValueCollector {
97+
private final Set<Integer> result = new HashSet<>();
98+
99+
private void addAllValues(ExprNode node) {
100+
switch (node) {
101+
case ExprNum(int value) -> result.add(value);
102+
case ExprNeg(ExprNode expr) -> addAllValues(expr);
103+
case ExprAdd(ExprNode left, ExprNode right) -> {
104+
addAllValues(left);
105+
addAllValues(right);
106+
}
107+
case ExprDiv(ExprNode numerator, ExprNode denominator) -> {
108+
addAllValues(numerator);
109+
addAllValues(denominator);
110+
}
111+
}
112+
}
113+
}
114+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package week4.baselangs.expr;
2+
3+
import week4.baselangs.expr.ast.*;
4+
5+
import java.util.Collections;
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
9+
public class ExprVisitorEvaluator {
10+
11+
// Nüüd proovime ExprEvaluator-i meetodeid kirjutada külastaja disainimustri (visitor design pattern) abil.
12+
// Üldiselt meie AST-idel visitor-e pole - Expr keel on erand.
13+
// Siiski, tutvume ka visitor-i kasutamisega juba nüüd, sest hiljem ANTLR-i juures on see möödapääsmatu.
14+
15+
/**
16+
* Väärtustab avaldise.
17+
*/
18+
public static int eval(ExprNode node) {
19+
ExprVisitor<Integer> visitor = new ExprVisitor<>() {
20+
@Override
21+
public Integer visit(ExprNum num) {
22+
return num.value();
23+
}
24+
25+
@Override
26+
public Integer visit(ExprNeg neg) {
27+
return -visit(neg.expr());
28+
}
29+
30+
@Override
31+
public Integer visit(ExprAdd add) {
32+
return visit(add.left()) + visit(add.right());
33+
}
34+
35+
@Override
36+
public Integer visit(ExprDiv div) {
37+
return visit(div.numerator()) / visit(div.denominator());
38+
}
39+
};
40+
// Hakka siin null-i asemele kirjutama "new ExprVisitor<Integer>()" ning lase IntelliJ-l see lõpuni kirjutada ja meetodid genereerida.
41+
// Seejärel asenda genereeritud implementatsioonid õigetega.
42+
43+
// NB! Vaata, et kasutad rekursiivsete eval kutsete asemel visit kutseid.
44+
// Vastasel juhul hiilid visitorist mööda ja ikka kasutad tavalist rekursiooni.
45+
46+
// Olles defineerinud uue (anonüümse) visitor klassi, käivitame selle lõpuks antud node argumendi peal.
47+
return node.accept(visitor);
48+
}
49+
50+
// Võrdle seda eval meetodit ExprEvaluator-i eval meetodiga.
51+
52+
53+
/**
54+
* Kogub kokku kõik tippudes esinevad arvud.
55+
*/
56+
public static Set<Integer> getAllValues(ExprNode node) {
57+
ExprVisitor<Set<Integer>> visitor = new ExprVisitor<>() {
58+
@Override
59+
protected Set<Integer> visit(ExprNum num) {
60+
return Collections.singleton(num.value());
61+
}
62+
63+
@Override
64+
protected Set<Integer> visit(ExprNeg neg) {
65+
return visit(neg.expr());
66+
}
67+
68+
@Override
69+
protected Set<Integer> visit(ExprAdd add) {
70+
Set<Integer> result = new HashSet<>();
71+
result.addAll(visit(add.left()));
72+
result.addAll(visit(add.right()));
73+
return result;
74+
}
75+
76+
@Override
77+
protected Set<Integer> visit(ExprDiv div) {
78+
Set<Integer> result = new HashSet<>();
79+
result.addAll(visit(div.numerator()));
80+
result.addAll(visit(div.denominator()));
81+
return result;
82+
}
83+
};
84+
return node.accept(visitor);
85+
}
86+
87+
88+
/**
89+
* Kogub kokku kõik tippudes esinevad arvud.
90+
* Väldib koodi kordusi kasutades BaseVisitor-i agregeerimist.
91+
*/
92+
public static Set<Integer> getAllValuesAggregate(ExprNode node) {
93+
94+
ExprVisitor<Set<Integer>> visitor = new ExprVisitor.BaseVisitor<>() {
95+
// Vaata BaseVisitor-i definitsiooni ja implementeeri siin kaks meetodit.
96+
@Override
97+
protected Set<Integer> visit(ExprNum num) {
98+
return Collections.singleton(num.value());
99+
}
100+
101+
@Override
102+
protected Set<Integer> aggregateResult(Set<Integer> aggregate, Set<Integer> nextResult) {
103+
Set<Integer> result = new HashSet<>();
104+
result.addAll(aggregate);
105+
result.addAll(nextResult);
106+
return result;
107+
}
108+
};
109+
110+
return node.accept(visitor);
111+
}
112+
113+
// Pane tähele, et BaseVisitor-ile analoogilist lähenemist ExprEvaluator-is polnud.
114+
// Seega, kuigi visitor-id võivad tihti olla pigem kohmakad, võib neil mõnikord siiski olla eeliseid.
115+
116+
117+
/**
118+
* Kogub kokku kõik tippudes esinevad arvud.
119+
* Väldib koodi kordusi kasutades BaseVisitor-i ja imperatiivset stiili.
120+
*/
121+
public static Set<Integer> getAllValuesImperative(ExprNode node) {
122+
Set<Integer> result = new HashSet<>();
123+
124+
ExprVisitor<Void> visitor = new ExprVisitor.BaseVisitor<>() {
125+
@Override
126+
protected Void visit(ExprNum num) {
127+
result.add(num.value());
128+
return null; // Geneerikus kasutatav Void (mitte void!) tüüp nõuab tagastust.
129+
}
130+
};
131+
132+
node.accept(visitor);
133+
return result;
134+
}
135+
136+
// Võrdle seda getAllValuesImperative meetodit ExprEvaluator-i getAllValuesImperative meetodiga.
137+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package week4.baselangs.expr.ast;
2+
3+
public record ExprAdd(ExprNode left, ExprNode right) implements ExprNode {
4+
5+
@Override
6+
public <T> T accept(ExprVisitor<T> visitor) {
7+
return visitor.visit(this);
8+
}
9+
10+
@Override
11+
public int eval() {
12+
return left.eval() + right.eval();
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package week4.baselangs.expr.ast;
2+
3+
public record ExprDiv(ExprNode numerator, ExprNode denominator) implements ExprNode {
4+
5+
@Override
6+
public <T> T accept(ExprVisitor<T> visitor) {
7+
return visitor.visit(this);
8+
}
9+
10+
@Override
11+
public int eval() {
12+
return numerator.eval() / denominator.eval();
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package week4.baselangs.expr.ast;
2+
3+
public record ExprNeg(ExprNode expr) implements ExprNode {
4+
5+
@Override
6+
public <T> T accept(ExprVisitor<T> visitor) {
7+
return visitor.visit(this);
8+
}
9+
10+
@Override
11+
public int eval() {
12+
return -expr.eval();
13+
}
14+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package week4.baselangs.expr.ast;
2+
3+
// See klass on kirje (record), mistõttu konstruktor, get meetodid (ilma get eesliiteta), equals, hashCode ja toString genereeritakse automaatselt kompilaatori poolt.
4+
// See on võimalik alates Java 16-st.
5+
public record ExprNum(int value) implements ExprNode {
6+
7+
@Override
8+
public <T> T accept(ExprVisitor<T> visitor) {
9+
return visitor.visit(this);
10+
}
11+
12+
@Override
13+
public int eval() {
14+
return value;
15+
}
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package week4.baselangs.rnd;
2+
3+
import week4.baselangs.rnd.ast.*;
4+
5+
import java.util.function.BooleanSupplier;
6+
7+
public class RndEvaluator {
8+
// Väärtusta antud avaldist vasakult paremale, kasutades ettantud münti.
9+
public static int eval(RndNode node, BooleanSupplier coin) {
10+
return switch (node) {
11+
case RndNum(int num) -> num;
12+
case RndNeg(RndNode neg) -> -eval(neg, coin);
13+
case RndAdd(RndNode left, RndNode right) -> eval(left, coin) + eval(right, coin);
14+
case RndFlip(RndNode left, RndNode right) -> eval(coin.getAsBoolean() ? left : right, coin);
15+
};
16+
}
17+
}

0 commit comments

Comments
 (0)