package lambdasinaction.chap14;
import java.util.function.Function;
import java.util.function.Supplier;
public class PatternMatching {
public static void main(String[] args) {
simplify();
Expr e = new BinOp("+", new Number(5), new BinOp("*", new Number(3), new Number(4)));
Integer result = evaluate(e);
System.out.println(e + " = " + result);
}
private static void simplify() {
TriFunction<String, Expr, Expr, Expr> binopcase =
(opname, left, right) -> {
if ("+".equals(opname)) {
if (left instanceof Number && ((Number) left).val == 0) {
return right;
}
if (right instanceof Number && ((Number) right).val == 0) {
return left;
}
}
if ("*".equals(opname)) {
if (left instanceof Number && ((Number) left).val == 1) {
return right;
}
if (right instanceof Number && ((Number) right).val == 1) {
return left;
}
}
return new BinOp(opname, left, right);
};
Function<Integer, Expr> numcase = val -> new Number(val);
Supplier<Expr> defaultcase = () -> new Number(0);
Expr e = new BinOp("+", new Number(5), new Number(0));
Expr match = patternMatchExpr(e, binopcase, numcase, defaultcase);
if (match instanceof Number) {
System.out.println("Number: " + match);
} else if (match instanceof BinOp) {
System.out.println("BinOp: " + match);
}
}
private static Integer evaluate(Expr e) {
Function<Integer, Integer> numcase = val -> val;
Supplier<Integer> defaultcase = () -> 0;
TriFunction<String, Expr, Expr, Integer> binopcase =
(opname, left, right) -> {
if ("+".equals(opname)) {
if (left instanceof Number && right instanceof Number) {
return ((Number) left).val + ((Number) right).val;
}
if (right instanceof Number && left instanceof BinOp) {
return ((Number) right).val + evaluate((BinOp) left);
}
if (left instanceof Number && right instanceof BinOp) {
return ((Number) left).val + evaluate((BinOp) right);
}
if (left instanceof BinOp && right instanceof BinOp) {
return evaluate((BinOp) left) + evaluate((BinOp) right);
}
}
if ("*".equals(opname)) {
if (left instanceof Number && right instanceof Number) {
return ((Number) left).val * ((Number) right).val;
}
if (right instanceof Number && left instanceof BinOp) {
return ((Number) right).val * evaluate((BinOp) left);
}
if (left instanceof Number && right instanceof BinOp) {
return ((Number) left).val * evaluate((BinOp) right);
}
if (left instanceof BinOp && right instanceof BinOp) {
return evaluate((BinOp) left) * evaluate((BinOp) right);
}
}
return defaultcase.get();
};
return patternMatchExpr(e, binopcase, numcase, defaultcase);
}
static class Expr {
}
static class Number extends Expr {
int val;
public Number(int val) {
this.val = val;
}
@Override
public String toString() {
return "" + val;
}
}
static class BinOp extends Expr {
String opname;
Expr left, right;
public BinOp(String opname, Expr left, Expr right) {
this.opname = opname;
this.left = left;
this.right = right;
}
@Override
public String toString() {
return "(" + left + " " + opname + " " + right + ")";
}
}
static <T> T MyIf(boolean b, Supplier<T> truecase, Supplier<T> falsecase) {
return b ? truecase.get() : falsecase.get();
}
static interface TriFunction<S, T, U, R> {
R apply(S s, T t, U u);
}
static <T> T patternMatchExpr(Expr e,
TriFunction<String, Expr, Expr, T> binopcase,
Function<Integer, T> numcase, Supplier<T> defaultcase) {
if (e instanceof BinOp) {
return binopcase.apply(((BinOp) e).opname, ((BinOp) e).left, ((BinOp) e).right);
} else if (e instanceof Number) {
return numcase.apply(((Number) e).val);
} else {
return defaultcase.get();
}
}
}