3

Implementing a infix to postfix calculator and need to check if an operator has a lower precedence than another. Here's what I have so far:

public enum Operators {

    ADD('+', 2), SUBTRACT('-', 2), MULTIPLY('*', 4), DIVIDE('/', 4);

    private char operator;
    private int precedence;

    Operators(char operator, int precedence) {
        this.operator = operator;
        this.precedence = precedence;
    }

    public char getOperator() {
        return operator;
    }

    public int getPrecedence() {
        return precedence;
    }
}

private static boolean isOperator(char c) {
    return c == Operators.ADD.getOperator() || c == Operators.SUBTRACT.getOperator()
            || c == Operators.MULTIPLY.getOperator() || c == Operators.DIVIDE.getOperator();
}

private static boolean isLowerPrecedence(char ch1, char ch2) {
    // STUCK HERE
}

I've tried a number of different things to check the precedence of the char that is passed in but to no avail. Is there an easy way to compare two values of an enum? Will I have to create a loop?

this_is_cat
  • 138
  • 10
  • 1
    BTW: Why are the methods `isOperator`, `isLowerPrecedence` declared `private`? As of the code in the question they are unused. – LuCio Sep 13 '18 at 19:48
  • @LuCio they are used in my algorithm to convert infix to postfix which I have not posted, would it help to post the lot? – this_is_cat Sep 13 '18 at 19:57
  • I was just curious it they are used at all. Now I know. – LuCio Sep 13 '18 at 19:58

5 Answers5

3

It's easy to compare if you have a method that translates a "operator" char to an enum value.

For example:

static Operators getOperatorForChar(char op) {
    for(Operators val: values())
        if(op == val.operator)
            return val; //return enum type

    return null;
}

And then you can implement your method using:

private static boolean isLowerPrecedence(char ch1, char ch2) {

    //assuming intention is to compare precedence of ch1 to that of ch2
    return getOperatorForChar(ch1).precedence < getOperatorForChar(ch2).precedence;
}
Geetha
  • 18
  • 2
ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • I think there is no need to create a whole new method for this. Could you take a look at [my answer](https://stackoverflow.com/questions/52320567/compare-value-of-enum/52320625#52320625) and tell me what do you think? I appreciate insights from different devs :) Thanks in advance. – lealceldeiro Sep 13 '18 at 19:51
  • 2
    @lealceldeiro I see... but the `valueOf` method provided by `Enum` expects a string literal name, such as `"ADD"`, `"SUBTRACT"`, etc. To convert from `operator` to one of the values, different lookup logic is needed. – ernest_k Sep 13 '18 at 19:54
  • Oh, I see. I was thinking in the OP using this method rather like this: `Operators.isLowerPrecedence('+', '-')`. What you say makes total sense, though. Thanks for the feedback. – lealceldeiro Sep 13 '18 at 19:57
  • Need to remove static keyword from isLowerPrecedence! – LHA Sep 13 '18 at 20:01
  • @Loc Not in this case. But in an alternative design, yes, we could make it an instance method and make it take just **one** `char` parameter (compare current operator to the argument). But it makes sense for this logic to be static, although the method name could be improved. – ernest_k Sep 13 '18 at 20:04
  • @ernest_k: My mistake. I thought getOperatorForChar is an instance method. – LHA Sep 13 '18 at 20:06
  • @ernest_k thanks for your answer, although this works it causes issues in my algorithm for conversion. Time to go back and rethink the design. I wonder would holding the operators and precedence in a map be better? – this_is_cat Sep 13 '18 at 20:27
  • @Pamplemousse9 using a Map would be overkill for such a small example, and you would have to update the map with every new Operators added – Emanuele Giona Sep 13 '18 at 20:51
0

Or, you can compare the precedence like this:

private static boolean isLowerPrecedence(Operators operatorFirst, Operators operatorSecond) {
    if(operatorFirst.getPrecedence() < operatorSecond.getPrecedence()){
        return true;
    } else {
        return false;
    }
}

Of course, it can be written as:

return operatorFirst.getPrecedence() < operatorSecond.getPrecedence();
zlakad
  • 1,314
  • 1
  • 9
  • 16
  • 2
    `if (condition) return true; else return false;` can be simplified to `return condition;`. – Pshemo Sep 13 '18 at 19:40
  • 1
    BTW In OP example `isLowerPrecedence` expects `char` not `Operators` as argument (although it could be nice to have overloaded version like this one). – Pshemo Sep 13 '18 at 19:41
  • @Pshemo , ah, O.K - I know `return(condition)` is a shortcut, but I wanted to write the most readable code as I could. About the `char`s as an arguments, I think (my personal opinion) that's the unnecessary complication in the code. – zlakad Sep 13 '18 at 19:49
0

You can loop the values of enum to match the right operator and compare its precedences:

private static boolean isLowerPrecedence(char ch1, char ch2) {
    Integer first = null;
    Integer second = null;
    for (Operators o: Operators.values()) {
        if (o.getOperator() == ch1) {
            first = o.getPrecedence();
        }
        if (o.getOperator() == ch2) {
            second = o.getPrecedence();
        }
    }
    return (first != null && second !=null && first < second);
}

Returning boolean when the operator has not been found might be confusing. I recommend you to throw an exception in such case instead.

...
if (first == null || second ==null) throw new Exception("Operator not found.");
return first < second;
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
0

By taking a look at this question, you can find out Java handles types comparisons via the Comparable and Comparator interfaces.

Of course, they are meant for more complex situations that this one, but I think you should take them into account so that you can see the proper way to deal with the set of ordering algorithms provided by the standard Java library.

Since you can't override default Enum's compareTo (it is declared as final), you can implement your own Comparator as such:

public class OperatorsComparator implements Comparator<Operators> {

    @Override
    public int compare(Operators o1, Operators o2) {
        return o1.getPrecedence() - o2.getPrecedence();
    }
}

Then you're going to need some kind of way to find the right Operators value from the char you give in:

private static Operators findOperator(char c){
    for(Operators op : Operators.values()){
        if(op.getOperator() == c)
            return op;
    }
    return null;
}

By using a substraction between the two precedences and the previous Operators finder, you can implement your isLowerPrecedence method like this:

public static boolean isLowerPrecedence(char c1, char c2) throws Exception {
    Operators o1 = findOperator(c1);
    Operators o2 = findOperator(c2);
    if(o1 == null || o2 == null)
        throw new Exception("Invalid operators");

    return new OperatorsComparator().compare(o1, o2) <= 0;
}

By comparing precedences this way, you'll get that o1 will be marked as lower precedence even if it has the same precedence as o2, as default behaviour. Beware of the characters you try to use as operator, since you'll need to catch the Exception if anything goes wrong

Execution example:

System.out.println(isLowerPrecedence('+', '-'));
System.out.println(isLowerPrecedence('+', '*'));
System.out.println(isLowerPrecedence('/', '-'));
System.out.println(isLowerPrecedence('/', '*'));
System.out.println(isLowerPrecedence('*', '-'));

prints these messages:

true
true
false
true
false
Emanuele Giona
  • 781
  • 1
  • 8
  • 20
0

You can use EnumLookup helper class proposed in this answer of mine (source code of EnumLookup there).

Upon redesining your Operators enum a little (I strongly suggest using a singular class name), you get:

public enum Operator {

    ADD('+', 2), SUBTRACT('-', 2), MULTIPLY('*', 4), DIVIDE('/', 4);

    private static final EnumLookup<Operator, Character> BY_OPERATOR_CHAR
            = EnumLookup.of(Operator.class, Operator::getOperatorChar, "operator char");

    private final char operatorChar;
    private final int precedence;

    Operator(char operatorChar, int precedence) {
        this.operatorChar = operatorChar;
        this.precedence = precedence;
    }

    public char getOperatorChar() {
        return operatorChar;
    }

    public int getPrecedence() {
        return precedence;
    }

    public static EnumLookup<Operator, Character> byOperatorChar() {
        return BY_OPERATOR_CHAR;
    }
}

private static boolean isOperator(char c) {
    return Operator.byOperatorChar().contains(c);
}

private static boolean isLowerPrecedence(char ch1, char ch2) {
    return Operator.byOperatorChar().get(ch1).getPrecedence() < Operator.byOperatorChar().get(ch2).getPrecedence();
}

The main drawback of this approach is that your char gets boxed into Character, but unless performance is critical for your application, I wouldn't worry about that (readability should be more important).

Tomasz Linkowski
  • 4,386
  • 23
  • 38