Является ли дерево if-else лучшим способом в следующем случае? - PullRequest
3 голосов
/ 28 января 2011

Что я делаю:

Я создаю простой калькулятор на Java, который читает строку, написанную с использованием постфиксной нотации (дляпример: 3 4+).Затем он берет строку и начинает читать ее слева направо.Он сохраняет каждый найденный номер, а затем применяет следующий оператор.Например: 3 4 + -> сохранить 3, сохранить 4, запустить 3 + 4 и сохранить результат.

Что мне нужно помочь с:

Каким образомсимволы должны быть проверены на соответствие заранее заданным операторам (if (c == '/') и т. д.).Какие альтернативы есть в моем случае для дерева if-else, и какое мне следует выбрать, если я хочу иметь возможность добавлять новые операторы с минимальными усилиями (и минимальным снижением производительности).Что обычно считается хорошей практикой?

Ответы [ 6 ]

8 голосов
/ 28 января 2011

Если это калькулятор для людей, перестаньте думать о производительности (как о скорости выполнения).Ни один человек никогда не приблизится к тому, чтобы заметить разницу между if-деревом и любой другой реализацией.

Тем не менее, вы можете попробовать реализовать некоторый класс Operator, который знает, как применять себя к аргументам,а затем используйте хеш от имени оператора (строки типа «+», «-», «*» и т. д.) для соответствующего экземпляра.

5 голосов
/ 28 января 2011

Если вы инкапсулируете свои операции в виде объектов, вы часто можете использовать структуру данных для замены switch -подобного оператора и, надеюсь, просто добавят операции в будущем.

Например, это один из подходовдля инкапсуляции операций как объектов: использование Enum для представления операций:

http://download.oracle.com/javase/1.5.0/docs/guide/language/enums.html

public enum Operation {
  PLUS   { double eval(double x, double y) { return x + y; } },
  MINUS  { double eval(double x, double y) { return x - y; } },
  TIMES  { double eval(double x, double y) { return x * y; } },
  DIVIDE { double eval(double x, double y) { return x / y; } };

  // Do arithmetic op represented by this constant
  abstract double eval(double x, double y);
}

Вы можете расширить этот пример, чтобы связать символ с каждой операцией и предоставить статический метод длянайдите операцию, связанную с символом.[[Меня раздражает смешивание кода пользовательского интерфейса / представления с кодом логики / домена, но вы упомянули, что хотите просто, поэтому, возможно, это нормально для вашей программы.]]

Если вы инкапсулируете свои операции какобъекты, то вы можете рассмотреть структуру данных для замены кода, похожего на переключатель:

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

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

5 голосов
/ 28 января 2011

Я бы не беспокоился о производительности, так как компьютер может анализировать / вычислять данные в 1 миллион раз быстрее, чем вы можете набрать формулу (и это не преувеличение)

То, как я бы подошел,использовать if / else или использовать оператор switch

switch(ch) {
  case '+':

     break;
  case '-':

     break;
  // etc.
}
4 голосов
/ 28 января 2011

То, что вы ищете, это шаблон команды , реализованный картой с Character объектами в качестве ключа и экземплярами, реализующими интерфейс обработки в качестве значений.

2 голосов
/ 28 января 2011

Я бы сделал это, создав суперкласс Operator, который расширяется за одну операцию, следуя примеру (извините за ошибки, я не пытаюсь это сделать).

abstract class Operator {
    String simbol;
    abstract Double calculate(Double firstOperand, Double secondOperand)
}

class Sum extends Operator {
    simbol = "+";
    Double calculate(Double firstOperand, Double secondOperand){
        return firstOperand + secondOperand;
    }    
}

class OperatorRecognizer {
    List<Operator> operators;
    public Operator recognize(String readOperator){
        for(Operator operator : operators){
            if(operator.getSymbol().equals(readOperator)){
                return operator;
            }
        }
    }
}

При чтении строкиЯ бы сделал это следующим образом:

OperatorFactory.recognize(readOperator).calculate(firstOperand, secondOperand);

Таким образом, когда вам нужно добавить одну операцию, вы просто добавляете один класс.

РЕДАКТИРОВАТЬ: LOL хорошо, кажется, другие говорили то же самоевещь, я все равно оставлю это здесь ^^ "

1 голос
/ 28 января 2011

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

Я создам Enum для всех операций.В этом перечислении я также создам функцию-член, которая принимает параметр var args и перечисление операции, и итерационно применяет операцию перечисления к аргументам.

Я также создаю служебный класс String (для обработки ввода)это повторяет все значения перечисления операции и выполняет проверку регулярного выражения для входной строки, чтобы идентифицировать операцию, указанную данной строкой (регулярное выражение, потому что я хочу убедиться в нотации Postfix).Как только он идентифицирует операцию, он делегирует функцию-член enum для выполнения операции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...