Have char[] operator = // " " or "<" or "<=" etc
if(op.length == 1) {
switch (op[0]) {
case ' ':
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() b.doubleValue();
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() b.longValue();
}
break;
case '-':
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() - b.doubleValue();
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() - b.longValue();
}
break;
case '*':
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() * b.doubleValue();
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() * b.longValue();
}
break;
case '^':
if (a instanceof Double || b instanceof Double) {
result = Double.valueOf(Math.pow(a.doubleValue(), b.doubleValue()));
} else if (a instanceof Long && b instanceof Long) {
result = (long) Math.pow(a.longValue(), b.longValue());
}
break;
case '/':
if (b.doubleValue() == 0) {
throw new UnsupportedOperationException("Cannot divide by zero");
}
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() / b.doubleValue();
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() / b.longValue();
}
break;
case '>':
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() > b.doubleValue()?1:0;
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() > b.longValue()?1:0;
}
break;
case '<':
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() < b.doubleValue()?1:0;
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() < b.longValue()?1:0;
}
break;
}
} else if (op.length == 2) {
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
switch (op.toString()) {
case ">=":
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() >= b.doubleValue()?1:0;
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() >= b.longValue()?1:0;
}
break;
case "<=":
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() <= b.doubleValue()?1:0;
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() <= b.longValue()?1:0;
}
break;
case "==":
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() == b.doubleValue()?1:0;
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() == b.longValue()?1:0;
}
break;
}
}
Wanted to avoid many comparison operations.
I don't see a solution to covert string to literal I mean convert to working operator(single char operator or two char operator).
Can it be achieved without using any lib?
CodePudding user response:
Convert each one of these into BiFunctions.
if (a instanceof Double || b instanceof Double) {
result = a.doubleValue() b.doubleValue();
} else if (a instanceof Long && b instanceof Long) {
result = a.longValue() b.longValue();
}
And put them into a map using the operator as key. Your big if-else-switch statement becomes:
result = operatorMap.get(op.toString()).apply(a, b);
CodePudding user response:
As always, avoid repetition by moving common code into methods. The struggle seems to be with the passing of the actual operations as arguments. One solution could be
switch(operator.length == 1? operator[0]: operator[0] << 16 | operator[1])
{
case ' ': result = perform(a, b, Double::sum, Long::sum); break;
case '-': result = perform(a, b, (d1, d2) -> d1 - d2, (l1, l2) -> l1 - l2); break;
case '*': result = perform(a, b, (d1, d2) -> d1 * d2, (l1, l2) -> l1 * l2); break;
case '/': result = perform(a, b, (d1, d2) -> d1 / check(d2),(l1, l2) -> l1 / l2);break;
case '%': result = perform(a, b, (d1, d2) -> d1 % check(d2),(l1, l2) -> l1 % l2);break;
case '^': result = perform(a, b, Math::pow, (l1, l2) -> (long)Math.pow(l1, l2)); break;
case '>':
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
result = performCmp(a, b, (d1, d2) -> d1 > d2, (l1, l2) -> l1 > l2);
break;
case '<':
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
result = performCmp(a, b, (d1, d2) -> d1 < d2, (l1, l2) -> l1 < l2);
break;
case '<' << 16 | '=':
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
result = performCmp(a, b, (d1, d2) -> d1 <= d2, (l1, l2) -> l1 <= l2);
break;
case '>' << 16 | '=':
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
result = performCmp(a, b, (d1, d2) -> d1 >= d2, (l1, l2) -> l1 >= l2);
break;
case '=' << 16 | '=':
resultDecorator.setTypeOfexpresion(EvaluatorConstants.Boolean);
result = performCmp(a, b, (d1, d2) -> d1 == d2, (l1, l2) -> l1 == l2);
break;
default: throw new ArithmeticException("unknown operator " new String(operator));
}
static Number perform(
Number a, Number b, DoubleBinaryOperator dOp, LongBinaryOperator lOp)
{
return a instanceof Double || b instanceof Double?
dOp.applyAsDouble(a.doubleValue(), b.doubleValue()):
lOp.applyAsLong(a.longValue(), b.longValue());
}
@FunctionalInterface interface BinaryDoublePredicate
{
boolean test(double d1, double d2);
}
@FunctionalInterface interface BinaryLongPredicate
{
boolean test(long d1, long d2);
}
static Number performCmp(
Number a, Number b, BinaryDoublePredicate dOp, BinaryLongPredicate lOp)
{
return (a instanceof Double || b instanceof Double?
dOp.test(a.doubleValue(), b.doubleValue()):
lOp.test(a.longValue(), b.longValue()))? 1: 0;
}
static double check(double d2)
{
if(d2 == 0) throw new ArithmeticException("/ by zero");
return d2;
}
Note that when using the better suited ArithmeticException
instead of UnsupportedOperationException
for division by zero, you get it for free when performing /
or %
on long
. Only for the double
operation, an explicit check is needed when you want to get an exception rather than NaN
.
The code above also handles the one-char and two-char values with a single switch
statement as an int
can easily contain two chars. The only drawback is that it would handle "\0 "
like " "
but such input should be ruled out by the preceding processing operation already.
CodePudding user response:
You could use a table of binary operators. For instance, the following class could be used to model one of these binary operators:
public class Operator2<T1 extends Number, T2 extends Number> {
private final Class<T1> t1;
private final Class<T2> t2;
private final BiFunction<T1,T2,?> op;
public Operator2(
Class<T1> t1, Class<T2> t2, BiFunction<T1,T2,?> op) {
this.t1 = t1;
this.t2 = t2;
this.op = op;
}
public boolean canApplyTo(Number a, Number b) {
return canConvert(a.getClass(), t1) && canConvert(b.getClass(), t2);
}
public Object apply(Number a, Number b) {
return op.apply((T1)convert(a, t1), (T2)convert(b, t2));
}
private static boolean canConvert(
Class<? extends Number> s, Class<? extends Number> t) {
if (s == t) {
return true;
}
if (t == Double.class) {
return true;
}
return false;
}
private static Number convert(Number v, Class<? extends Number> t) {
if (v.getClass() == t) {
return v;
}
if (t == Double.class) {
return v.doubleValue();
}
return 0L;
}
}
The next class is a table of binary operators:
public class Operator2Table {
private final Map<String,List<Operator2<?,?>>> map = new HashMap<>();
public Operator2Table() {
register(" ", Long.class, Long.class, (a,b) -> a b);
register(" ", Double.class, Double.class, (a,b) -> a b);
// ...
}
public Object apply(String s, Number a, Number b) {
List<Operator2<?,?>> list = map.get(s);
if (list == null) {
throw new IllegalArgumentException("Unknown operator " s);
}
for (Operator2<?,?> op: list) {
if (op.canApplyTo(a, b)) {
return op.apply(a, b);
}
}
throw new IllegalArgumentException("Cannot apply operator " s
" to given argument");
}
private <T1 extends Number,T2 extends Number>
void register(String s, Class<T1> t1, Class<T2> t2, BiFunction<T1,T2,?> op) {
List<Operator2<?,?>> list = map.get(s);
if (list == null) {
list = new ArrayList<>();
map.put(s, list);
}
list.add(new Operator2<>(t1, t2, op));
}
}
Note that it is important that you register the Long
version of a given operator before the Double
version because the Double
version can also be applied to two Long
s whereas the Long
version cannot be applied to two Double
s.
These tables are very flexible, and you could easily extend the code to support Int
s, Short
s and Float
s.