评估没有分隔符的算术表达式

时间:2014-04-26 13:35:32

标签: java algorithm

我想创建一个计算算术表达式的方法,例如((2+100)*5)*7,不包含操作数和运算符之间的空格 如果有空格,我会使用split方法,将" "作为分隔符。

有谁能帮我找到解决这类问题的算法?

3 个答案:

答案 0 :(得分:1)

变式1:

您可以使用javascript引擎来评估表达式。

private static final ScriptEngine engine = new ScriptEngineManager()。getEngineByName(" JavaScript");

public static String eval(String expression){
    if(expression == null){
        return "NULL";
    }
    String js_parsable_expression = expression
            .replaceAll("\\((\\-?\\d+)\\)\\^(\\-?\\d+)", "(Math.pow($1,$2))")
            .replaceAll("(\\d+)\\^(\\-?\\d+)", "Math.pow($1,$2)");
    try{
        return engine.eval(js_parsable_expression).toString();
    }catch(javax.script.ScriptException ex){
        return "NULL";
    }
}

请注意,您必须将所有^转换为Math.pow才能获得权力。

变式2

您可以使用Shunting-yard alghoritm

例如:

  

输入:3 + 4

  • 将3添加到输出队列(每当读取一个数字时,它都会添加到输出中)
  • 将+(或其ID)推送到操作员堆栈
  • 将4添加到输出队列
  • 读完表达式后,将操作符从堆栈中弹出并将它们添加到输出中。
  • 在这种情况下,只有一个," +"。
  

输出:3 4 +

我的算法实现(抱歉命名错误):

private static final TreeMap<Character, Integer> operatorsOrder= new TreeMap<Character, Integer>();
static
{
    operatorsOrder.put('(', 0);
    operatorsOrder.put(')', 0);  
    operatorsOrder.put('+', 1);
    operatorsOrder.put('-', 1);
    operatorsOrder.put('*', 2);
    operatorsOrder.put('/', 2);
    operatorsOrder.put('%', 2);
    operatorsOrder.put('s', 3);
    operatorsOrder.put('l', 3);
}

private void evalButtonActionPerformed(java.awt.event.ActionEvent evt)                                           
{ 
    final Stack<Character> operatori  = new Stack<Character>();
    final Stack<Double> valori = new Stack<Double>();

    String expresie = exprTf.getText();
    expresie = expresie.replaceAll("sqrt", "s");
    expresie = expresie.replaceAll("ln", "l");

    final StringBuilder builder = new StringBuilder();
    final char[] expresieChar = expresie.toCharArray();

    for(int i = 0; i<expresieChar.length; i++)
    {
        char ch = expresieChar[i];
        if (!operatorsOrder.containsKey(ch))
        {
            if(Character.isDigit(ch))
            {
                while (Character.isDigit(ch) || ch == '.')
                {
                    builder.append(ch);
                    if(++i <expresieChar.length)
                        ch = expresieChar[i];
                    else
                        break;
                }
                --i;
                valori.push(Double.parseDouble(builder.toString()));
                builder.delete(0, builder.capacity());
            }
            continue;
        }

        while (true)
        {
            if (operatori.isEmpty() || ch == '(' || (operatorsOrder.get(ch) > operatorsOrder.get(operatori.peek())))
            {
                operatori.push(ch);
                break;
            }

            final char op = operatori.pop();

            if (op == '(')
            {
                if(ch == ')')
                    break;
            }
            else if(op == 's' || op == 'l')
            {
                final double val1 = valori.pop();
                valori.push(eval(op, val1, 0));
            }
            else
            {
                final double val2 = valori.pop();
                final double val1 = valori.pop();
                valori.push(eval(op, val1, val2));
            }
        }
    }

    while (!operatori.isEmpty())
    {
        final char op = operatori.pop();
        if(op == 's' || op == 'l')
        {
            final double val1 = valori.pop();
            valori.push(eval(op, val1, 0));
        }
        else
        {
            final double val2 = valori.pop();
            final double val1 = valori.pop();
            valori.push(eval(op, val1, val2));
        }
    }
    resultLabel.setText(String.valueOf(valori.pop()));
    if(!operatori.isEmpty())
        System.out.println("There are operators left.");
    if(!valori.isEmpty())
        System.out.println("There are operands left.");
    }
}                                      

public static double eval(char op, double val1, double val2)
{
    switch (op)
    {
        case '+':
            return val1 + val2;
        case '-':
            return val1 - val2;
        case '/':
            return val1 / val2;
        case '*':
            return val1 * val2;
        case '%':
            return val1 % val2;
        case 's':
            return Math.sqrt(val1);
        case 'l':
            return Math.log(val1);
        default:
            throw new RuntimeException("Operator is invalid.");
    }
}

要解决no delimiter问题,我将字符串表达式转换为字符数组并迭代它。在每一步我:

  • 检查它是否是有效的运营商
  • 如果是,请将其添加到堆栈
  • 如果不是,请检查它是否是有效数字
  • 如果是,请检查是否有更多数字并为该数字构建字符串
  • 如果不是,请建立号码

答案 1 :(得分:0)

您可以使用Javascript来评估整个表达式:

ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
String exp = "((2+100)*5)*7";
System.out.println(engine.eval(exp));

答案 2 :(得分:0)

一种很好的方法是使用一些解析器生成器来创建输入文本的解析器。在java中,有几个很好的解析器。我建议使用ANTLR,因为您提供的示例在其一些基本教程中可用。关于这方面的更多细节还有另外一个问题:Parsing an arithmetic expression and building a tree from it in Java

正如所指出的,接受的答案有一个死链接。有一些Five minute introduction to ANTLR 3可用

的变体