方程(表达式)解析器具有优先级?

15 浏览
0 Comments

方程(表达式)解析器具有优先级?

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

然而,使用这种方法会导致所有元素具有相同的优先级 - 它是从左到右进行评估的,不考虑运算符,尽管可以使用括号来强制执行优先级。

所以现在"1+11*5"返回60,而不是人们可能期望的56。

虽然这对于当前项目来说是合适的,但我希望有一个通用的程序,可以在以后的项目中使用。

编辑以提高清晰度:

有没有一个好的算法用于解析具有优先级的方程?

我对一个简单易实现和理解的算法很感兴趣,这样我就可以自己编写代码,避免使用可用代码时出现许可问题。

语法:

我不明白语法问题 - 我是手写的。它足够简单,我不认为需要使用YACC或Bison。我只需要计算带有方程的字符串,例如"2+3 * (42/13)"。

语言:

我在C语言中进行这个项目,但我对算法感兴趣,而不是特定于语言的解决方案。如果需要,C语言足够底层,可以很容易转换为其他语言。

代码示例:

我发布了上面提到的简单表达式解析器的测试代码。由于项目需求发生变化,因此我从未需要对代码进行性能或空间优化,因为它没有被纳入项目中。它以原始冗长的形式呈现,并且应该很容易理解。如果我在运算符优先级方面做任何进一步的工作,我可能会选择"宏hack",因为它与程序的其余部分相匹配。但是,如果我在实际项目中使用它,我将选择更紧凑/快速的解析器。

相关问题:

Smart design of a math parser?

-Adam

0
0 Comments

表达式解析器是一种常见的计算机科学问题,它的出现源于对表达式求值和处理的需求。表达式解析器可以将一个数学或逻辑表达式转换成计算机可以理解和处理的形式。在解析表达式时,需要考虑运算符的优先级和结合性,以确保正确的计算顺序。为了解决这个问题,有几种不同的方法被提出。

作者提到了几种不同的方法来解析表达式,包括递归下降识别、逆波兰算法、经典解法和优先级爬升。这些方法都有自己的特点和适用场景。

递归下降识别是一种基于递归的解析方法,它通过递归地调用子程序来解析表达式。这种方法简单直观,但在处理复杂的表达式时可能会变得冗长和难以维护。

逆波兰算法,也称为逆波兰表达式,是一种使用后缀表示法的解析方法。它通过将运算符放在操作数的后面来表示运算顺序,从而避免了括号和优先级的问题。逆波兰算法的主要优点是计算顺序清晰,但其表达形式不够直观,不易于人类阅读和理解。

经典解法是一种基于栈的解析方法,它使用运算符栈和操作数栈来解析表达式。经典解法通过维护运算符的优先级和结合性来确定计算顺序,但实现起来比较复杂。

优先级爬升是一种基于运算符优先级的解析方法,它通过逐步提升运算符的优先级来解析表达式。这种方法简单直观,并且可以很好地处理运算符的优先级和结合性。

以上这些方法都有各自的优缺点,选择哪种方法取决于具体的需求和应用场景。在给出的链接中,作者提供了对每种方法的详细解释和伪代码实现,为读者提供了很好的参考和指导。然而,由于链接已经失效,所以如果能对每种方法进行简要概括和说明,以便在链接失效时仍然能够获取到有用的信息,那将更好。

在解析表达式时,根据运算符的优先级和结合性选择合适的解析方法是非常重要的。不同的方法适用于不同的情况,选择合适的方法可以提高解析效率和准确性。无论选择哪种方法,理解其原理和实现方式都是非常重要的。

0
0 Comments

问题的出现的原因是想要实现一个具有优先级的方程(表达式)解析器,以便正确解析包含运算符的表达式。采用手动递归下降解析的方法,需要考虑递归和优先级的关系,这会导致代码复杂且容易出错。

解决方法是使用现有的解析工具,如Bison、ANTLR等。这些工具可以自动生成解析器的C代码,省去了手动编写复杂的解析逻辑的过程。使用这些工具的优点是可以提高开发效率,避免重复造轮子,并且有助于扩展和维护解析器。

对于小型简单的解释器,使用Flex/Bison可能会过于复杂,可以考虑使用Shunting Yard算法或手动编写递归下降解析器。然而,如果语言的功能需要增加,这些简化的方法可能会变得不够灵活和可扩展。因此,根据具体情况选择合适的解析方法。

在众多解析工具中,作者推荐了Haskell编程语言中的Parsec库作为解析器的最佳工具。Parsec库是一个递归下降解析器库,可以与Haskell代码一起编译,提供了简洁而强大的解析能力。作者还提到,将解析语言嵌入到非Haskell语言中可能会导致语法冗余和代码不够简洁。

总结起来,解析器的设计和实现是一个复杂且容易出错的过程。使用现有的解析工具可以提高开发效率,避免重复工作,并且能够应对解析需求的扩展和维护。在选择解析工具时,根据具体情况选择合适的工具,并根据需求考虑解析方法的复杂度和灵活性。

0
0 Comments

表达式(方程)解析器带有优先级是一个常见的问题,出现这个问题的原因是为了正确地计算表达式中不同运算符的优先级。解决这个问题的方法是使用逆波兰表达式和"Shunting Yard"算法。

"Shunting Yard"算法是解决这个问题的合适工具。该算法的基本思想是,当从左到右扫描字符串时,只有当后面的运算符的优先级较低(或相等)时,才对当前运算符进行求值。具体来说,对于表达式1 + 2 * 3 + 4,算法的处理步骤如下:

1. 查看1 + 2,暂不计算。

2. 继续查看1 + 2 * 3,仍然不计算。

3. 现在查看1 + 2 * 3 + 4,根据下一个运算符的优先级,确定需要先计算2 * 3。

为了实现这个算法,需要使用两个栈:一个用于存储数字,另一个用于存储运算符。将数字依次推入数字栈,每次将新的运算符与栈顶的运算符进行比较,如果栈顶的运算符优先级较高,则将其从运算符栈中弹出,同时从数字栈中弹出相应的操作数,执行运算并将结果推入数字栈。然后,再次比较栈顶的运算符。

继续以示例为例,算法的执行过程如下:

数字栈(N):[ ]

运算符栈(Ops):[ ]

1. 读取1。N = [1],Ops = [ ]

2. 读取+。N = [1],Ops = [+]

3. 读取2。N = [1 2],Ops = [+]

4. 读取*。N = [1 2],Ops = [+ *]

5. 读取3。N = [1 2 3],Ops = [+ *]

6. 读取+。N = [1 2 3],Ops = [+ *]

- 弹出3、2,并计算2 * 3,将结果6推入N。N = [1 6],Ops = [+]

- +是左结合的,因此弹出1、6,并执行加法。N = [7],Ops = []

- 最后将[+]推入运算符栈。N = [7],Ops = [+]

7. 读取4。N = [7 4],Ops = [+]

8. 输入已经结束,现在可以清空栈了。得到结果11。

这个算法并不复杂,它不需要使用任何语法或解析器生成器。实际上,只需要一个栈即可,只要能够查看栈顶的第二个元素而不弹出栈顶元素即可。这实际上与LR解析器生成器(如bison)的工作原理相同。

除了使用两个栈,还可以使用一个交替存储数字和运算符的单个栈。这实际上就是LR解析器生成器所做的。

"Shunting Yard"算法的一个简化版本可以在这里找到:andreinc.net/2010/10/05/…,其中提供了Java和Python的实现。

感谢提到左结合的重要性。对于嵌套的三元运算符,如何解析复杂的表达式一直是一个困扰。我意识到,'?'和':'运算符必须具有相同的优先级。如果我们将'?'解释为右结合,':'解释为左结合,那么这个算法就可以很好地处理它们。另外,只有当两个运算符都是左结合时,才能合并两个运算符。

总之,"Shunting Yard"算法是一种解析表达式的有效方法,它可以根据运算符的优先级正确计算表达式的值。通过使用栈来存储数字和运算符,并根据优先级进行比较和计算,可以实现这个算法。这种方法不需要使用任何语法或解析器生成器,并且可以轻松添加对括号的支持。

0