Algorithm 具有优先级的等式(表达式)解析器?
我使用一个简单的堆栈算法开发了一个方程解析器,该算法将处理二进制(+、-、|、&、*、/)运算符、一元(!)运算符和括号 然而,使用这个方法,让我的每件事都具有相同的优先级——它是从左到右求值的,不管运算符是什么,尽管可以使用括号强制执行优先级 所以现在“1+11*5”返回60,而不是预期的56 虽然这适用于当前项目,但我希望有一个通用例程,可以用于以后的项目 为清晰起见进行了编辑: 解析具有优先权的方程的好算法是什么 我对一些易于实现的东西感兴趣,我可以自己编写代码来避免可用代码的许可问题 语法: 我不懂这道语法题,这是我亲手写的。这很简单,我不认为需要YACC或野牛。我只需要用诸如“2+3*(42/13)”之类的等式来计算字符串 语言: 我是用C语言做的,但我对算法感兴趣,而不是特定于语言的解决方案。C语言的级别足够低,如果需要,可以很容易地转换为另一种语言 代码示例 我发布了我在上面谈论的内容。项目需求发生了变化,因此我从不需要为性能或空间优化代码,因为它没有被纳入到项目中。它是原始的详细形式,应该易于理解。如果我在操作符优先级方面对它做进一步的研究,我可能会选择它,因为它在简单性方面与程序的其余部分相匹配。但是,如果我在实际项目中使用它,我会选择更紧凑/快速的解析器 相关问题Algorithm 具有优先级的等式(表达式)解析器?,algorithm,parsing,equation,Algorithm,Parsing,Equation,我使用一个简单的堆栈算法开发了一个方程解析器,该算法将处理二进制(+、-、|、&、*、/)运算符、一元(!)运算符和括号 然而,使用这个方法,让我的每件事都具有相同的优先级——它是从左到右求值的,不管运算符是什么,尽管可以使用括号强制执行优先级 所以现在“1+11*5”返回60,而不是预期的56 虽然这适用于当前项目,但我希望有一个通用例程,可以用于以后的项目 为清晰起见进行了编辑: 解析具有优先权的方程的好算法是什么 我对一些易于实现的东西感兴趣,我可以自己编写代码来避免可用代码的许可问题 语
-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