Сравнить значение перечисления

Реализация инфикса в калькуляторе постфиксов и необходимость проверки того, имеет ли оператор более низкий приоритет, чем другой. Вот что у меня есть на данный момент:

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
}

Я пробовал несколько разных вещей, чтобы проверить приоритет переданного символа, но безрезультатно. Есть ли простой способ сравнить два значения перечисления? Придется ли мне создавать петлю?

Кстати: Почему методы isOperator, isLowerPrecedence объявлены как private? Что касается кода в вопросе, они не используются.

LuCio 13.09.2018 21:48

@LuCio они используются в моем алгоритме для преобразования инфикса в постфикс, который я не публиковал, поможет ли опубликовать лот?

this_is_cat 13.09.2018 21:57

Мне просто было любопытно, они вообще используются. Теперь я знаю.

LuCio 13.09.2018 21:58
3
3
183
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Это легко сравнить, если у вас есть метод, который переводит "оператор" char в значение перечисления.

Например:

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

    return null;
}

А затем вы можете реализовать свой метод, используя:

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;
}

Я думаю, что нет необходимости создавать для этого совершенно новый метод. Не могли бы вы взглянуть на мой ответ и сказать мне, что вы думаете? Я ценю идеи от разных разработчиков :) Заранее спасибо.

lealceldeiro 13.09.2018 21:51

@lealceldeiro Я вижу ... но метод valueOf, предоставляемый Enum, ожидает строкового литерала, такого как "ADD", "SUBTRACT" и т. д. Для преобразования из operator в одно из значений требуется другая логика поиска.

ernest_k 13.09.2018 21:54

О, я вижу. Я думал в OP, используя этот метод, примерно так: Operators.isLowerPrecedence('+', '-'). Однако то, что вы говорите, имеет смысл. Спасибо за ответ.

lealceldeiro 13.09.2018 21:57

Необходимо удалить ключевое слово static из isLowerPrecedence!

Loc 13.09.2018 22:01

@Loc Не в этом случае. Но в альтернативном дизайне, да, мы могли бы сделать его методом экземпляра и заставить его принимать только параметр одинchar (сравните текущий оператор с аргументом). Но имеет смысл сделать эту логику статической, хотя имя метода можно улучшить.

ernest_k 13.09.2018 22:04

@ernest_k: Моя ошибка. Я думал, что getOperatorForChar - это метод экземпляра.

Loc 13.09.2018 22:06

@ernest_k благодарит за ваш ответ, хотя это работает, но вызывает проблемы в моем алгоритме преобразования. Пора вернуться и переосмыслить дизайн. Интересно, будет ли лучше хранить операторы и приоритет на карте?

this_is_cat 13.09.2018 22:27

@ Pamplemousse9 использование карты было бы излишним для такого небольшого примера, и вам придется обновлять карту с каждым добавлением новых операторов

Emanuele Giona 13.09.2018 22:51

Или вы можете сравнить приоритет следующим образом:

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

Конечно, это можно записать так:

return operatorFirst.getPrecedence() < operatorSecond.getPrecedence();
if (condition) return true; else return false; можно упростить до return condition;.
Pshemo 13.09.2018 21:40

BTW В примере OP isLowerPrecedence ожидает, что char, а не Operators в качестве аргумента (хотя было бы неплохо иметь перегруженную версию, подобную этой).

Pshemo 13.09.2018 21:41

@Pshemo, ах, хорошо - я знаю, что return(condition) - это ярлык, но я хотел написать максимально читаемый код. Что касается char в качестве аргумента, я думаю (мое личное мнение), что это ненужное усложнение кода.

zlakad 13.09.2018 21:49

Вы можете зациклить значения enum, чтобы они соответствовали правильному оператору и сравнивали его приоритеты:

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);
}

Возврат boolean, когда оператор не найден, может сбивать с толку. Я рекомендую вам вместо этого выбросить исключение.

...
if (first == null || second ==null) throw new Exception("Operator not found.");
return first < second;

Взглянув на этот вопрос, вы можете узнать, что Java обрабатывает сравнения типов через интерфейсы Сопоставимый и Компаратор.

Конечно, они предназначены для более сложных ситуаций, чем эта, но я думаю, вы должны принять их во внимание, чтобы увидеть правильный способ работы с набором алгоритмов упорядочивания, предоставляемым стандартной библиотекой Java.

Поскольку вы не можете переопределить сравнить с Enum по умолчанию (он объявлен как окончательный), вы можете реализовать свой собственный Comparator как таковой:

public class OperatorsComparator implements Comparator<Operators> {

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

Тогда вам понадобится какой-то способ найти правильное значение Operators из char, которое вы дадите:

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

Используя вычитание между двумя приоритетами и предыдущим средством поиска Operators, вы можете реализовать свой метод isLowerPrecedence следующим образом:

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;
}

Сравнивая таким образом приоритеты, вы увидите, что o1 будет помечен как более низкий приоритет, даже если он имеет тот же приоритет, что и o2, в качестве поведения по умолчанию. Остерегайтесь символов, которые вы пытаетесь использовать в качестве оператора, так как вам нужно будет поймать Exception, если что-то пойдет не так.

Пример выполнения:

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

печатает эти сообщения:

true
true
false
true
false

Вы можете использовать вспомогательный класс EnumLookup, предложенный в этот мой ответ (исходный код EnumLookup там).

Немного переработав перечисление Operators (я настоятельно рекомендую использовать единственное имя класса), вы получите:

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();
}

Основным недостатком этого подхода является то, что ваш char получает в штучной упаковке в Character, но, если производительность не критична для вашего приложения, я бы не стал беспокоиться об этом (читаемость должна быть более важной).

Другие вопросы по теме