Home > database >  Java Convert two char Operator String to working Operator?
Java Convert two char Operator String to working Operator?

Time:10-22

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 Longs whereas the Long version cannot be applied to two Doubles.

These tables are very flexible, and you could easily extend the code to support Ints, Shorts and Floats.

  • Related