Я пытаюсь написать подпрограмму Java для оценки простых математических выражений из String
значений, таких как:
"5+3"
"10-40"
"10*3"
Я хочу избегать множества утверждений if-then-else. Как я могу это сделать?
Я пытаюсь написать подпрограмму Java для оценки простых математических выражений из String
значений, таких как:
"5+3"
"10-40"
"10*3"
Я хочу избегать множества утверждений if-then-else. Как я могу это сделать?
Ответы:
С JDK1.6 вы можете использовать встроенный движок Javascript.
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Test {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+2";
System.out.println(engine.eval(foo));
}
}
return (Double) engine.eval(foo);
new javax.script.ScriptEngineManager().getEngineByName("JavaScript") .eval("var f = new java.io.FileWriter('hello.txt'); f.write('UNLIMITED POWER!'); f.close();");
- записывает файл через JavaScript в (по умолчанию) текущую директорию программы
Я написал этот eval
метод для арифметических выражений, чтобы ответить на этот вопрос. Это делает сложение, вычитание, умножение, деление, возведение в степень (используя ^
символ), и несколько основных функций, как sqrt
. Он поддерживает группировку с использованием (
... )
, и он корректно определяет правила приоритета операторов и ассоциативности .
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
return x;
}
}.parse();
}
Пример:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
Выход: 7,5 (что правильно)
Анализатор является анализатором рекурсивного спуска , поэтому внутренне использует отдельные методы синтаксического анализа для каждого уровня приоритета оператора в своей грамматике. Я держал его коротким , так что легко изменить, но вот некоторые идеи , которые вы могли бы хотеть , чтобы расширить его:
Переменные:
Бит синтаксического анализатора, который читает имена для функций, можно легко изменить для обработки пользовательских переменных, просматривая имена в таблице переменных, передаваемой eval
методу, например a Map<String,Double> variables
.
Отдельная компиляция и оценка:
Что если, добавив поддержку переменных, вы захотите вычислять одно и то же выражение миллионы раз с измененными переменными, не анализируя его каждый раз? Это возможно. Сначала определите интерфейс, который будет использоваться для оценки предварительно скомпилированного выражения:
@FunctionalInterface
interface Expression {
double eval();
}
Теперь измените все методы, которые возвращают double
s, поэтому вместо этого они возвращают экземпляр этого интерфейса. Лямбда-синтаксис Java 8 отлично подходит для этого. Пример одного из измененных методов:
Expression parseExpression() {
Expression x = parseTerm();
for (;;) {
if (eat('+')) { // addition
Expression a = x, b = parseTerm();
x = (() -> a.eval() + b.eval());
} else if (eat('-')) { // subtraction
Expression a = x, b = parseTerm();
x = (() -> a.eval() - b.eval());
} else {
return x;
}
}
}
Это создает рекурсивное дерево Expression
объектов, представляющих скомпилированное выражение ( абстрактное синтаксическое дерево ). Затем вы можете скомпилировать его один раз и повторно оценить с различными значениями:
public static void main(String[] args) {
Map<String,Double> variables = new HashMap<>();
Expression exp = parse("x^2 - x + 2", variables);
for (double x = -20; x <= +20; x++) {
variables.put("x", x);
System.out.println(x + " => " + exp.eval());
}
}
Различные типы данных:
Вместо этого double
вы можете изменить оценщик, чтобы использовать что-то более мощное, например BigDecimal
класс, реализующий комплексные числа или рациональные числа (дроби). Вы даже можете использовать Object
, позволяя смешивать типы данных в выражениях, как настоящий язык программирования. :)
Весь код в этом ответе опубликован в открытом доступе . Радоваться, веселиться!
double x = parseTerm();
оценивает левый оператор, после этого for (;;) {...}
оценивает последовательные операции фактического уровня заказа (сложение, вычитание). Та же логика есть и в методе parseTerm. ParseFactor не имеет следующего уровня, поэтому существуют только оценки методов / переменных или, в случае парантеза, - оценка подвыражения. boolean eat(int charToEat)
Проверка метода равенство текущего символа курсора с характером charToEat, если равно возвращение истинный и перемещает курсор к следующему символу, я использую имя «принять» для него.
Для моего университетского проекта я искал парсер / оценщик, поддерживающий как базовые формулы, так и более сложные уравнения (особенно итерированные операторы). Я нашел очень хорошую библиотеку с открытым исходным кодом для JAVA и .NET, которая называется mXparser. Я приведу несколько примеров, чтобы немного разобраться в синтаксисе, для дальнейших инструкций, пожалуйста, посетите веб-сайт проекта (особенно раздел учебника).
https://mathparser.org/mxparser-tutorial/
И несколько примеров
1 - Простая фурмула
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - Определяемые пользователем аргументы и константы
Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()
3 - Пользовательские функции
Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()
4 - Итерация
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
Найден недавно - если вы хотите попробовать синтаксис (и посмотреть расширенный вариант использования), вы можете скачать приложение Scalar Calculator , которое работает на mXparser.
С уважением
ЗДЕСЬ есть еще одна библиотека с открытым исходным кодом на GitHub под названием EvalEx.
В отличие от движка JavaScript эта библиотека ориентирована только на оценку математических выражений. Кроме того, библиотека расширяема и поддерживает использование логических операторов, а также скобок.
Вы также можете попробовать интерпретатор BeanShell :
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
Вы можете легко оценить выражения, если ваше Java-приложение уже обращается к базе данных, без использования других JAR-файлов.
Некоторые базы данных требуют, чтобы вы использовали фиктивную таблицу (например, «двойную» таблицу Oracle), а другие позволят вам оценивать выражения, не «выбирая» из какой-либо таблицы.
Например, в Sql Server или Sqlite
select (((12.10 +12.0))/ 233.0) amount
и в Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
Преимущество использования БД в том, что вы можете вычислять много выражений одновременно. Кроме того, большинство БД позволит вам использовать очень сложные выражения, а также будет иметь ряд дополнительных функций, которые можно вызывать по мере необходимости.
Однако производительность может снизиться, если многие отдельные выражения необходимо оценивать по отдельности, особенно если БД находится на сетевом сервере.
Следующее в некоторой степени решает проблему производительности, используя базу данных Sqlite в памяти.
Вот полный рабочий пример на Java
Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();
Конечно, вы можете расширить приведенный выше код для одновременной обработки нескольких вычислений.
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
В этой статье рассматриваются различные подходы. Вот 2 ключевых подхода, упомянутых в статье:
Позволяет для сценариев, которые включают ссылки на объекты Java.
// Create or retrieve a JexlEngine
JexlEngine jexl = new JexlEngine();
// Create an expression object
String jexlExp = "foo.innerFoo.bar()";
Expression e = jexl.createExpression( jexlExp );
// Create a context and add data
JexlContext jctx = new MapContext();
jctx.set("foo", new Foo() );
// Now evaluate the expression, getting the result
Object o = e.evaluate(jctx);
private static void jsEvalWithVariable()
{
List<String> namesList = new ArrayList<String>();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
jsEngine.put("namesListKey", namesList);
System.out.println("Executing in script environment...");
try
{
jsEngine.eval("var x;" +
"var names = namesListKey.toArray();" +
"for(x in names) {" +
" println(names[x]);" +
"}" +
"namesListKey.add(\"Dana\");");
}
catch (ScriptException ex)
{
ex.printStackTrace();
}
}
Другой способ - использовать Spring Expression Language или SpEL, который делает намного больше вместе с оценкой математических выражений, поэтому может быть немного излишним. Вам не нужно использовать Spring Framework, чтобы использовать эту библиотеку выражений, поскольку она автономна. Копирование примеров из документации SpEL:
ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0
Прочитайте более краткие примеры SpEL здесь и полные документы здесь
если мы собираемся реализовать это, то мы можем использовать следующий алгоритм:
Пока еще есть токены для чтения,
1.1 Получить следующий токен 1.2 Если токен:
1.2.1 Число: поместите его в стек значений.
1.2.2 Переменная: получите ее значение и поместите в стек значений.
1.2.3 Левая скобка: вставьте ее в стек оператора.
1.2.4 Правая скобка:
1 While the thing on top of the operator stack is not a
left parenthesis,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Pop the left parenthesis from the operator stack, and discard it.
1.2.5 Оператор (назовите это thisOp):
1 While the operator stack is not empty, and the top thing on the
operator stack has the same or greater precedence as thisOp,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Push thisOp onto the operator stack.
Пока стек операторов не пуст, 1 вытолкните оператор из стека операторов. 2 Дважды вытолкните стек значений, получив два операнда. 3 Примените оператор к операндам в правильном порядке. 4 Вставьте результат в стек значений.
На этом этапе стек операторов должен быть пустым, а в стеке значений должно быть только одно значение, что является конечным результатом.
Это еще одна интересная альтернатива https://github.com/Shy-Ta/expression-evaluator-demo
Использование очень простое и выполняет свою работу, например:
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
Я думаю, что когда бы вы ни делали это, это будет включать в себя много условных утверждений. Но для отдельных операций, как в ваших примерах, вы можете ограничить его до 4, если операторы с чем-то вроде
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
Это становится намного сложнее, когда вы хотите иметь дело с несколькими операциями, такими как «4 + 5 * 6».
Если вы пытаетесь построить калькулятор, я бы предпочел передавать каждую часть вычисления отдельно (каждый номер или оператор), а не как одну строку.
Уже слишком поздно, чтобы ответить, но я столкнулся с той же ситуацией, чтобы оценить выражение в Java, это может помочь кому-то
MVEL
Выполняет оценку выражений во время выполнения, мы можем написать Java-код, String
чтобы оценить его в этом.
String expressionStr = "x+y";
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("x", 10);
vars.put("y", 20);
ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
Object result = MVEL.executeExpression(statement, vars);
Вы можете взглянуть на платформу Symja :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
Обратите внимание, что определенно более сложные выражения могут быть оценены:
// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2
Попробуйте следующий пример кода, используя Javascript движок JDK1.6 с обработкой внедрения кода.
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
try {
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Object eval(String input) throws Exception{
try {
if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
throw new Exception("Invalid expression : " + input );
}
return engine.eval(input);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
Это фактически дополняет ответ, данный @Boann. Имеется небольшая ошибка, которая приводит к тому, что «-2 ^ 2» дает ошибочный результат -4.0. Проблемой для этого является тот момент, когда возведение в степень оценивается по-своему. Просто переместите возведение в блок parseTerm (), и все будет в порядке. Посмотрите на приведенный ниже ответ , который @ Boann слегка изменил. Модификация есть в комментариях.
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
//if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation -> This is causing a bit of problem
return x;
}
}.parse();
}
-2^2 = -4
на самом деле нормально, а не ошибка. Это группируется как -(2^2)
. Попробуйте это на Desmos, например. Ваш код на самом деле вносит несколько ошибок. Во-первых, ^
больше не группируются справа налево. Другими словами, 2^3^2
предполагается, что сгруппировать как, 2^(3^2)
потому что ^
это право-ассоциативно, но ваши изменения делают это сгруппировать как (2^3)^2
. Второе - это то, что ^
должен иметь более высокий приоритет, чем *
и /
, но ваши модификации относятся к нему одинаково. Смотрите ideone.com/iN2mMa .
package ExpressionCalculator.expressioncalculator;
import java.text.DecimalFormat;
import java.util.Scanner;
public class ExpressionCalculator {
private static String addSpaces(String exp){
//Add space padding to operands.
//https://regex101.com/r/sJ9gM7/73
exp = exp.replaceAll("(?<=[0-9()])[\\/]", " / ");
exp = exp.replaceAll("(?<=[0-9()])[\\^]", " ^ ");
exp = exp.replaceAll("(?<=[0-9()])[\\*]", " * ");
exp = exp.replaceAll("(?<=[0-9()])[+]", " + ");
exp = exp.replaceAll("(?<=[0-9()])[-]", " - ");
//Keep replacing double spaces with single spaces until your string is properly formatted
/*while(exp.indexOf(" ") != -1){
exp = exp.replace(" ", " ");
}*/
exp = exp.replaceAll(" {2,}", " ");
return exp;
}
public static Double evaluate(String expr){
DecimalFormat df = new DecimalFormat("#.####");
//Format the expression properly before performing operations
String expression = addSpaces(expr);
try {
//We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and
//subtraction will be processed in following order
int indexClose = expression.indexOf(")");
int indexOpen = -1;
if (indexClose != -1) {
String substring = expression.substring(0, indexClose);
indexOpen = substring.lastIndexOf("(");
substring = substring.substring(indexOpen + 1).trim();
if(indexOpen != -1 && indexClose != -1) {
Double result = evaluate(substring);
expression = expression.substring(0, indexOpen).trim() + " " + result + " " + expression.substring(indexClose + 1).trim();
return evaluate(expression.trim());
}
}
String operation = "";
if(expression.indexOf(" / ") != -1){
operation = "/";
}else if(expression.indexOf(" ^ ") != -1){
operation = "^";
} else if(expression.indexOf(" * ") != -1){
operation = "*";
} else if(expression.indexOf(" + ") != -1){
operation = "+";
} else if(expression.indexOf(" - ") != -1){ //Avoid negative numbers
operation = "-";
} else{
return Double.parseDouble(expression);
}
int index = expression.indexOf(operation);
if(index != -1){
indexOpen = expression.lastIndexOf(" ", index - 2);
indexOpen = (indexOpen == -1)?0:indexOpen;
indexClose = expression.indexOf(" ", index + 2);
indexClose = (indexClose == -1)?expression.length():indexClose;
if(indexOpen != -1 && indexClose != -1) {
Double lhs = Double.parseDouble(expression.substring(indexOpen, index));
Double rhs = Double.parseDouble(expression.substring(index + 2, indexClose));
Double result = null;
switch (operation){
case "/":
//Prevent divide by 0 exception.
if(rhs == 0){
return null;
}
result = lhs / rhs;
break;
case "^":
result = Math.pow(lhs, rhs);
break;
case "*":
result = lhs * rhs;
break;
case "-":
result = lhs - rhs;
break;
case "+":
result = lhs + rhs;
break;
default:
break;
}
if(indexClose == expression.length()){
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose);
}else{
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose + 1);
}
return Double.valueOf(df.format(evaluate(expression.trim())));
}
}
}catch(Exception exp){
exp.printStackTrace();
}
return 0.0;
}
public static void main(String args[]){
Scanner scanner = new Scanner(System.in);
System.out.print("Enter an Mathematical Expression to Evaluate: ");
String input = scanner.nextLine();
System.out.println(evaluate(input));
}
}
Как насчет чего-то вроде этого:
String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
if(st.charAt(i)=='+')
{
result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
System.out.print(result);
}
}
и сделайте то же самое для каждого другого математического оператора соответственно ..
Можно преобразовать любую строку выражения в инфиксной записи в постфиксную запись с помощью алгоритма шунтирования ярда Джикстры . Результат алгоритма может затем служить входом для постфиксного алгоритма. с возвращением результата выражения.
Я написал статью об этом здесь, с реализацией в Java
Еще один вариант: https://github.com/stefanhaustein/expressionparser
Я реализовал это, чтобы иметь простой, но гибкий вариант, позволяющий:
TreeBuilder, связанный выше, является частью демонстрационного пакета CAS, который выполняет символическое создание . Существует также пример интерпретатора BASIC, и я начал создавать интерпретатор TypeScript, используя его.
Класс Java, который может оценивать математические выражения:
package test;
public class Calculator {
public static Double calculate(String expression){
if (expression == null || expression.length() == 0) {
return null;
}
return calc(expression.replace(" ", ""));
}
public static Double calc(String expression) {
if (expression.startsWith("(") && expression.endsWith(")")) {
return calc(expression.substring(1, expression.length() - 1));
}
String[] containerArr = new String[]{expression};
double leftVal = getNextOperand(containerArr);
expression = containerArr[0];
if (expression.length() == 0) {
return leftVal;
}
char operator = expression.charAt(0);
expression = expression.substring(1);
while (operator == '*' || operator == '/') {
containerArr[0] = expression;
double rightVal = getNextOperand(containerArr);
expression = containerArr[0];
if (operator == '*') {
leftVal = leftVal * rightVal;
} else {
leftVal = leftVal / rightVal;
}
if (expression.length() > 0) {
operator = expression.charAt(0);
expression = expression.substring(1);
} else {
return leftVal;
}
}
if (operator == '+') {
return leftVal + calc(expression);
} else {
return leftVal - calc(expression);
}
}
private static double getNextOperand(String[] exp){
double res;
if (exp[0].startsWith("(")) {
int open = 1;
int i = 1;
while (open != 0) {
if (exp[0].charAt(i) == '(') {
open++;
} else if (exp[0].charAt(i) == ')') {
open--;
}
i++;
}
res = calc(exp[0].substring(1, i - 1));
exp[0] = exp[0].substring(i);
} else {
int i = 1;
if (exp[0].charAt(0) == '-') {
i++;
}
while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
i++;
}
res = Double.parseDouble(exp[0].substring(0, i));
exp[0] = exp[0].substring(i);
}
return res;
}
private static boolean isNumber(int c) {
int zero = (int) '0';
int nine = (int) '9';
return (c >= zero && c <= nine) || c =='.';
}
public static void main(String[] args) {
System.out.println(calculate("(((( -6 )))) * 9 * -1"));
System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));
}
}
Внешняя библиотека, такая как RHINO или NASHORN, может быть использована для запуска JavaScript. И javascript может оценить простую формулу без разбора строки. Не влияет на производительность, если код написан хорошо. Ниже приведен пример с RHINO -
public class RhinoApp {
private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";
public void runJavaScript() {
Context jsCx = Context.enter();
Context.getCurrentContext().setOptimizationLevel(-1);
ScriptableObject scope = jsCx.initStandardObjects();
Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
Context.exit();
System.out.println(result);
}
import java.util.*;
public class check {
int ans;
String str="7 + 5";
StringTokenizer st=new StringTokenizer(str);
int v1=Integer.parseInt(st.nextToken());
String op=st.nextToken();
int v2=Integer.parseInt(st.nextToken());
if(op.equals("+")) { ans= v1 + v2; }
if(op.equals("-")) { ans= v1 - v2; }
//.........
}