Algorithm 具有优先级的等式(表达式)解析器?

Algorithm 具有优先级的等式(表达式)解析器?,algorithm,parsing,equation,Algorithm,Parsing,Equation,我使用一个简单的堆栈算法开发了一个方程解析器,该算法将处理二进制(+、-、|、&、*、/)运算符、一元(!)运算符和括号 然而,使用这个方法,让我的每件事都具有相同的优先级——它是从左到右求值的,不管运算符是什么,尽管可以使用括号强制执行优先级 所以现在“1+11*5”返回60,而不是预期的56 虽然这适用于当前项目,但我希望有一个通用例程,可以用于以后的项目 为清晰起见进行了编辑: 解析具有优先权的方程的好算法是什么 我对一些易于实现的东西感兴趣,我可以自己编写代码来避免可用代码的许可问题 语

我使用一个简单的堆栈算法开发了一个方程解析器,该算法将处理二进制(+、-、|、&、*、/)运算符、一元(!)运算符和括号

然而,使用这个方法,让我的每件事都具有相同的优先级——它是从左到右求值的,不管运算符是什么,尽管可以使用括号强制执行优先级

所以现在“1+11*5”返回60,而不是预期的56

虽然这适用于当前项目,但我希望有一个通用例程,可以用于以后的项目

为清晰起见进行了编辑:

解析具有优先权的方程的好算法是什么

我对一些易于实现的东西感兴趣,我可以自己编写代码来避免可用代码的许可问题

语法:

我不懂这道语法题,这是我亲手写的。这很简单,我不认为需要YACC或野牛。我只需要用诸如“2+3*(42/13)”之类的等式来计算字符串

语言:

我是用C语言做的,但我对算法感兴趣,而不是特定于语言的解决方案。C语言的级别足够低,如果需要,可以很容易地转换为另一种语言

代码示例

我发布了我在上面谈论的内容。项目需求发生了变化,因此我从不需要为性能或空间优化代码,因为它没有被纳入到项目中。它是原始的详细形式,应该易于理解。如果我在操作符优先级方面对它做进一步的研究,我可能会选择它,因为它在简单性方面与程序的其余部分相匹配。但是,如果我在实际项目中使用它,我会选择更紧凑/快速的解析器

相关问题


-Adam

如果你能描述一下你目前用来解析的语法,那会很有帮助。听起来问题可能就在那里

编辑:

您不理解语法问题,并且“您是手工编写的”这一事实很可能解释了为什么您对“1+11*5”形式的表达式(即运算符优先级)有问题。例如,在谷歌上搜索“算术表达式语法”应该会得到一些好的指针。这样的语法不必复杂:

<Exp> ::= <Exp> + <Term> |
          <Exp> - <Term> |
          <Term>

<Term> ::= <Term> * <Factor> |
           <Term> / <Factor> |
           <Factor>

<Factor> ::= x | y | ... |
             ( <Exp> ) |
             - <Factor> |
             <Number>
::=+|
-  |
::=  *  |
/  |
:=x | y ||
(  ) |
-  |
例如,它可以做到这一点,并且可以简单地进行扩充,以处理一些更复杂的表达式(例如,包括函数或幂等)

例如,我建议您看看线程

几乎所有语法/解析的介绍都以算术表达式为例

请注意,使用语法并不意味着使用特定的工具(LaYacc、Bison等)。事实上,您肯定已经在使用以下语法:

<Exp>  :: <Leaf> | <Exp> <Op> <Leaf>

<Op>   :: + | - | * | /

<Leaf> :: <Number> | (<Exp>)
::|
:: + | - | * | /
::  | ()

(或类似的东西)却不知道

有你想使用的语言吗?将允许您从Java的角度执行此操作。Adrian Kuhn在如何用Ruby编写可执行语法方面有着出色的经验;事实上,他的例子几乎就是你的算术表达式例子。

这取决于你希望它的“一般性”程度

如果你想让它变得非常通用,比如能够解析数学函数以及sin(4+5)*cos(7^3),你可能需要一个解析树。

其中,我认为不应该在这里粘贴完整的实现。我建议你去看看一个臭名昭著的“

但是如果您只是想获得优先级支持,那么您可以先将表达式转换为后缀形式,在后缀形式中可以使用一个可以复制和粘贴的算法,或者我认为您可以使用二叉树自己编写代码

当你把它做成后缀形式时,从那时起,它就是小菜一碟了,因为你已经了解了堆栈是如何起作用的。

艰难之路 你想要一个

要获得优先级,您需要递归地思考,例如,使用示例字符串

1+11*5
要手动执行此操作,您必须阅读
1
,然后查看加号并从
11
开始启动一个全新的递归解析“会话”。。。并确保将
11*5
解析为它自己的因子,生成一个具有
1+(11*5)
的解析树

这一切都让人感到很痛苦,即使是试图解释,尤其是在C的无能为力的情况下。请看,在解析11之后,如果*实际上是a+,那么您将不得不放弃尝试创建一个术语,而将
11
本身作为一个因素进行解析。我的头已经爆炸了。使用递归体面策略是可能的,但有更好的方法

简单(正确)的方法 如果您使用像Bison这样的GPL工具,您可能不需要担心许可问题,因为Bison生成的C代码不在GPL范围内(IANAL,但我非常确定GPL工具不会强制生成代码/二进制文件;例如,苹果使用GCC编译像Aperture这样的代码,并且他们在不使用GPL所述代码的情况下销售它)

(或类似物、ANTLR等)

通常有一些示例代码,您可以直接运行bison并获得所需的C代码,以演示此四函数计算器:

查看生成的代码,发现这并不像听起来那么容易。此外,使用像Bison这样的工具的好处是:1)你学到了一些东西(特别是如果你读了《龙之书》并学习了语法),2)你避免了试图重新发明轮子。使用一个真正的解析器生成器工具,您实际上有希望在以后扩展,显示其他peop
$ cc -o parenthesise parenthesise.c
$ ./parenthesise a \* b + c ^ d / e
((((a))*((b)))+(((c)^(d))/((e))))
public class ExpressionParser {

public double eval(String exp){
    int bracketCounter = 0;
    int operatorIndex = -1;

    for(int i=0; i<exp.length(); i++){
        char c = exp.charAt(i);
        if(c == '(') bracketCounter++;
        else if(c == ')') bracketCounter--;
        else if((c == '+' || c == '-') && bracketCounter == 0){
            operatorIndex = i;
            break;
        }
        else if((c == '*' || c == '/') && bracketCounter == 0 && operatorIndex < 0){
            operatorIndex = i;
        }
    }
    if(operatorIndex < 0){
        exp = exp.trim();
        if(exp.charAt(0) == '(' && exp.charAt(exp.length()-1) == ')')
            return eval(exp.substring(1, exp.length()-1));
        else
            return Double.parseDouble(exp);
    }
    else{
        switch(exp.charAt(operatorIndex)){
            case '+':
                return eval(exp.substring(0, operatorIndex)) + eval(exp.substring(operatorIndex+1));
            case '-':
                return eval(exp.substring(0, operatorIndex)) - eval(exp.substring(operatorIndex+1));
            case '*':
                return eval(exp.substring(0, operatorIndex)) * eval(exp.substring(operatorIndex+1));
            case '/':
                return eval(exp.substring(0, operatorIndex)) / eval(exp.substring(operatorIndex+1));
        }
    }
    return 0;
}
number -> [0..9]+
sum -> number | sum "+" sum
expression -> sum
sum -> difference | difference "+" sum
difference -> product | difference "-" product
product -> fraction | fraction "*" product
fraction -> term | fraction "/" term
term -> "(" expression ")" | number
number -> digit+                                                                    
  internal double Compute(string sequence)
    {
        int priority = 0;
        int sequenceCount = sequence.Length;            
        for (int i = 0; i < sequenceCount; i++) {
            char s = sequence[i];                
            if (Char.IsDigit(s)) {
                double value = ParseNextNumber(sequence, i);
                numberStack.Push(value);
                i = i + value.ToString().Length - 1;
            } else if (s == '+' || s == '-' || s == '*' || s == '/')  {                
               Operator op = ParseNextOperator(sequence, i, priority);
                CollapseTop(op, numberStack, operatorStack);
                operatorStack.Push(op);
            } if (s == '(') { priority++; ; continue; }
            else if (s == ')') { priority--; continue; }
        }
        if (priority != 0) { throw new ApplicationException("Parens not balanced"); }
        CollapseTop(new Operator(' ', 0), numberStack, operatorStack);
        if (numberStack.Count == 1 && operatorStack.Count == 0) {
            return numberStack.Pop();
        }
        return 0;
    }    
Calculator c = new Calculator();
double value = c.Compute("89.8+((9*3)+8)+(9*2)+1");
Console.WriteLine(string.Format("The sum of the expression is: {0}", (float)value));
//prints out The sum of the expression is: 143.8