Поиск по блогу

Sunday, October 28, 2012

Exp4j Java-библиотека для разбора формул.

Если вам в Java-программе когда либо понадобится производить разбор формул вида «(A + B) /C» не обязательно избретать собственный велосипед. За вас уже все сделали. Некий Фрэнк Ассег (Frank Asseg) из западной Германии написал небольшую библиотеку весом всего 40 KB, без каких либо дополнительных зависимостей. Она реализует изобретенный Эдсгером Дейкстрой так называемый «Алгоритм сортировочной станции», благодаря чему способна обрабатывать запись математические выражения и функции, записанные в форме, понятной человеку.
Далее приведены примеры использования этой библиотеки.

Вычисление простого выражения:

Calculable calc = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
        .withVariable("x", varX)
        .withVariable("y", varY)
        .build()
double result1=calc.calculate();

Вычисляемое выражение может содержать переменные. Имена переменных могут состоять состоять из символов цифр и символа подчеркивания. При этом они должны начинаться с буквы или символа подчеркивания. Например следующие имена переменных являются правильными: varX, _x1, _var_X_1, а имя переменной 1_var_x не правильное так как не начинается ни с цифры, ни с символа подчеркивания.
Метод withVariable() позволяет определить единственную переменную в выражении:

Calculable calc = new ExpressionBuilder("x * y - 2")
        .withVariableNames("x","y")
        .build();
calc.setVariable("x",1);
calc.setVariable("y",2);
assertTrue(calc.calculate()==0);

С помощью метода withVariableNames() можно определить сразу несколько переменных за один раз:

Calculable calc = new ExpressionBuilder("x * y - 2")
        .withVariable("x",1);
        .withVariable("y",2);
        .build();
assertTrue(calc.calculate()==0);

Библиотека также поддерживает определение польовательских функций. Для этого необходимо реализовать метод applyFunction(double[] values):

CustomFunction fooFunc = new CustomFunction("foo") {
    public double applyFunction(double[] values) {
                return values[0]*Math.E;
        }
};
double varX=12d;
Calculable calc = new ExpressionBuilder("foo(x)")
        .withCustomFunction(fooFunc)
        .withVariable("x",varX)
        .build();
assertTrue(calc.calculate() == Math.E * varX);

Можно определить и функцию с принимющую больше одного аргумента например max(a, b, c) с помощью конструктора CustomFunction(String name, int argc), где 'argc' означает количество аргументов принимаемых функцией аргументов:

CustomFunction custom1 = new CustomFunction("max",3) {
    @Override
    public double applyFunction(double[] values) {
        double max=values[0];
        for (int i=1;i max) {
                max=values[i];
            }
        }
        return max;
    }
};
double varX=Math.E;
Calculable calc = new ExpressionBuilder("max(log(x),sin(x),x)")
        .withVariable("x", varX)
        .withCustomFunction(custom1)
        .build();
assertTrue(varX == calc.calculate());

Пользовательские операторы.

Вы можете расширить абстрактный класс CustomOperator, что бы определить собственную операцию с помощью одного из следующих символов '!,#,§,$,&,;,:,~,<,>,|,='. Учтите, что добавление в CustomOperator исползованный ранее символ переопределяет любую существующую операцию, включая встроенную. Возможно переопределить даже операцию '+'. CustomOperator принимает до 4-х аргументов:


  • Символ, используемый для операции (один из !,#,§,$,&,;,:,~,<,>,|,=);
  • Оператор левоассоциативный (т.е. воздейсвтует на число слева от себя);
  • Приоритет операции;
  • Количество операндов в операции (1 или 2);


CustomOperator factorial = new CustomOperator('!', true, 6, 1) {
        @Override
    double applyOperation(double[] values) {
        double tmp = 1d;
        int steps = 1;
        while (steps < values[0]) {
                tmp = tmp * (++steps);
        }
        return tmp;
    }
};
Calculable calc = new ExpressionBuilder("11!").withOperation(factorial).build();
assertTrue(39916800d == calc.calculate());


Библиотека поддерживает встроенные операторы:

  • Сложение '2 + 2';
  • Вычитание '2 — 2';
  • Умножение '2 * 2';
  • Деление '2 / 2';
  • Возведение в степень '2 ^ 2';
  • Унарный минус или плюс (знаковый оператор) '+2 - (-2)';
  • Остаток от деления '2 % 2';

Приоритет унарного минуса

Приоритет унарных операций при возведении в степень может быть установлен с помощью системного свойства "exp4j.unary.precedence.high". Свойство можно установить с помощью System.setProperty(PROPERTY_UNARY_HIGH_PRECEDENCE, "false"), что бы изменить вычисление такого выражения как -3 ^ 2 с варианта (-3) ^ 2 на -(3 ^ 2):


String expr = "-3^2";
System.setProperty(ExpressionBuilder.PROPERTY_UNARY_HIGH_PRECEDENCE, "false");
Calculable calc = new ExpressionBuilder(expr).build();
assertTrue(-Math.pow(3, 2) == calc.calculate()); // вычислит '-9'

System.clearProperty(ExpressionBuilder.PROPERTY_UNARY_HIGH_PRECEDENCE);
calc = new ExpressionBuilder(expr).build();
assertTrue(Math.pow(-3,2) == calc.calculate()); // вычислит '9'

Встроенные функции

  • abs: абсолютное значение
  • acos: arc-косинус
  • asin: arc-синус
  • atan: arc-тангенс
  • cbrt: корень кубический
  • ceil: ближайшее верхнее целое
  • cos: косинус
  • cosh: hyper-косинус
  • exp: число Эйлера в степени (e^x)
  • floor: ближайшее нижнее целое
  • log: натуральный логарифм (по основанию e)
  • sin: синус
  • sinh: hyper-синус
  • sqrt: корень квадратный
  • tan: тангенс
  • tanh: hyper-тангенс

Более подробную информацию вы можете найти на официальной странице проекта.

No comments:

 
Google+