我试图写一个Java例程来计算数学表达式从字符串值,如:

"5 + 3" "10-4 * 5" "(1 + 10) * 3"

我想避免很多如果-then-else语句。 我该怎么做呢?


当前回答

我已经使用迭代解析和分流码算法,我真的很喜欢开发表达式求值器,你可以在这里找到所有的代码

https://github.com/nagaraj200788/JavaExpressionEvaluator

有73个测试用例,甚至工作于大整数,大小数

支持所有关系,算术表达式和两者的组合。 甚至支持三元运算符。

增加了增强,以支持有符号的数字,如-100+89,这是有趣的,详细信息请检查TokenReader.isUnaryOperator()方法,我已经更新了上面链接中的代码

其他回答

本文讨论了各种方法。以下是文中提到的两种关键方法:

Apache的JEXL

允许脚本包含对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);

使用JDK中嵌入的javascript引擎:

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

还有一个选择:https://github.com/stefanhaustein/expressionparser

我已经实现了一个简单而灵活的选项,以允许两者:

即时处理(Calculator.java, SetDemo.java) 构建和处理解析树(TreeBuilder.java)

上面链接的TreeBuilder是进行符号推导的CAS演示包的一部分。还有一个BASIC解释器的例子,我已经开始使用它来构建一个TypeScript解释器。

这实际上是对@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();
}

这样怎么样:

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和。net开源库,叫做mXparser。我将给出几个例子,让大家对语法有一些感觉,如需进一步指导,请访问项目网站(特别是教程部分)。

https://mathparser.org/

https://mathparser.org/mxparser-tutorial/

https://mathparser.org/api/

举几个例子

一个简单的开始

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

最近发现的-如果你想尝试语法(并查看高级用例),你可以下载由mXparser支持的标量计算器应用程序。