【问题标题】:Expression evaluation表达式评估
【发布时间】:2011-01-16 19:32:18
【问题描述】:

我正在做一个表情评估程序,就像this。我的问题是我不知道如何处理操作优先级。我使用递归来找到最里面的一对括号,并在找到后解决其中的表达式,如下所示:

Evaluate("2 + (3 * 5)")

将以这种方式重新调用自己:

Evaluate("3 * 5")

现在,由于没有括号,它计算结果并再次调用自己:

Evaluate("2 + 15")

好的,返回值是 17,正如预期的那样。但是如果我打电话给Evaluate("2 + 3 * 5"),结果是:

Evaluate("2 + 3 * 5")
Evaluate("5 * 5")

这显然是错误的。
基本上我正在从左到右解决操作。如何选择必须首先执行的操作?我想在每个操作周围添加几个括号,但看起来不太好。
那么,我需要先解析整个表达式吗?还有其他方法吗?

【问题讨论】:

标签: algorithm math evaluation vb5


【解决方案1】:

这是一篇很好的文章,展示了如何使用 Antlr 和 .net 来做这种事情。

http://www.codeproject.com/KB/recipes/sota_expression_evaluator.aspx

听起来您想手写解析器,但这将为您提供了解如何正确执行此操作所需的一切。

基本上,您通过将表达式定义为一系列可能的操作来实现优先级,其中每个操作都在下一层进行操作。然后按照此序列的顺序对操作的优先级进行编码。

例如一个非常简单的例子,带有 '+' 和 '*'

additiveExpression: multiplicativeExpression '+' multiplicativeExpression
multiplicativeExpression: number '*' number

您的手写递归下降解析器从顶部规则开始并向下工作。

您可以使用 Antlr 来做一个像这样的非常简单的语法,然后查看它生成的代码 - 在这种情况下它会是非常短的代码,因此很容易理解。

如果您的语法变得复杂,我建议您使用 Antlr 之类的工具,因为它消除了解析代码中的大量繁重工作 - 这是已经完成的工作之前几百次,非常机械。它使您可以专注于您想用表达式做的有趣的事情。

【讨论】:

  • 我去看看,但是链接不见了;)
【解决方案2】:

使用波兰表示法可能会有所帮助:http://en.wikipedia.org/wiki/Polish_notation#Computer_programming。此符号允许您不需要括号并帮助您处理操作顺序。

使用 this 的好处是有算法来评估这些类型的表达式。也可以将中缀表达式转换为前缀或后缀表达式。

这是一个如何完成的示例 - 让我们以“2 + 3 * 5”为例:

2 + 3 * 5
b = 3 * 5
    -convert b-
b = * 3 5
2 + b
    -convert expression-
+ 2 b
    -expand b-
+ 2 * 3 5

在我进行这些转换的前几次,我对它们感到非常困惑。如果它也适合你,不要害怕,继续练习。很好的是,您可以找到算法来帮助您进行这种转换,所以这应该会有所帮助。

进行这种转换的最大优点是可以从左到右评估修改后的表达式。算法运行如下:

维护两个堆栈 - 一个用于运算符,一个用于操作数。 从左到右评估:

  1. 如果遇到运算符,请将其压入运算符堆栈
  2. 如果遇到操作数(数字)将其压入操作数堆栈
  3. 为最顶层的运算符(大多数情况下为两个)找到足够的操作数后,查看该运算符并对两个操作数执行该操作。
  4. 将第 3 步的结果压入操作数堆栈。

我把一些事情简单化了,可能遗漏了一些步骤,所以仅以此作为起点。有些细节我已经跳过/不记得很多了。其中一些内容包括二元或一元运算符、如何处理括号、如何处理幂运算等等。

希望这会有所帮助,祝你好运:)

【讨论】:

  • 是的。我认为这将是第二步,因为我应该完全改变我的日常生活。谢谢;)
【解决方案3】:

无论如何你都必须递归。所以即使你看到一个 + 你也必须递归。本质上,您必须将所有没有括号的二元运算符视为有括号。

2 + 3 * 5 实际上是 2 + (3 * 5)。

【讨论】:

  • @BlackBear:不。您必须以一种优先考虑(并考虑关联性)的方式进行解析以产生明确的表示(带括号 everywhere 的中缀会满足这一点,但 RPN 或 AST 更容易,因为无论如何你都必须生成某种中间表示),你可以评估它。有一些算法(如果你坚持使用表达式,调车场非常简单而强大)。
【解决方案4】:

搜索您的最高优先级运算符并首先执行此操作,然后继续。因此,如果您只有 + 和 *,则搜索 * 的实例并将子字符串 aaaa * bbbb 替换为 aaaa * bbbb 的值。一旦没有此类实例,请继续使用 +。

如果给定运算符中的顺序很重要(例如,如果您包含 ^),您将必须决定从哪里开始使用这些运算符。

【讨论】:

  • 谢谢。虽然有更多的技术答案,但我发现你的答案最简单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-28
  • 2010-12-05
  • 2016-07-26
相关资源
最近更新 更多