查看调车场算法并将字符串转换为反向波兰符号。
如果您还通过一次读取一个字符而不是仅仅跳转到运算符来将数字检测与符号检测分开,这将有所帮助。
之后,执行计算就相当容易了,因为输入的排序方式使得基于堆栈的计算器易于实现。
是的,你可以做一个成熟的递归下降解析器;但是,对于相对简单的代数表达式来说,它们是多余的。
--- 编辑是因为我发现工具与技术存在争议---
我看到很多人提到 lexx 和 yacc。它们是很棒的工具,但对于您需要的东西来说也太过分了。这就像说要更换栅栏上的木板时要开一家木工店。您不需要学习另一种语言来处理您的数学语言,而 lexx 和 yacc 将要求您学习它们的领域特定配置语言来为您的领域特定语言构建解析器。对于一个简单的、已解决的问题,有很多语言。
如果您想构建数据的数学树,Lexx 和 Yacc 非常棒;但是,RPN 是您的数据的数学树,存储在两个基本节点类型(数据)和(操作)的列表中。
(a)(b)(c) // three "data nodes"
(a)(b)(+) // two "data nodes" and one operation node.
这允许人们使用基于堆栈的机器(非常容易实现)。机器有一个“指令集”
if (x is data) { push X onto the top of the stack }
if (x is operation) {
pop the operation's required parameters.
pass them to the operation.
push the result on the stack
}
所以假设您有一个“+”运算符,表达式 3 + 5 + 6 将转换为 RPN 3 5 + 6 +,并且堆栈看起来像(在处理期间)
<empty>
(3)
(3)(5)
(8)
(8)(6)
(14)
转换为 RPN 的主要原因不是因为它是必要的,而是因为它使您的程序的其余部分更容易实现。想象一下 3 + 5 * 7 转换为 RPN,你有 3 5 7 * + 这意味着你不必对评估“机器”语言做任何特别的事情。请注意,(3 + 5) * 7 转换为 RPN 3 5 + 7 *。简而言之,您可以通过处理输入数据来减少歧义,从而降低评估引擎的复杂性。
Lexx 和 Yacc 提供了很多“可配置性”来让你完成同样的事情;但是,您不会在此处配置 lexx 和 yacc 来做任何特别的事情,因此可配置的界面不会给您带来太多好处。当您只需要一个开关时,这就像拥有一个多选旋钮。
现在,如果您想构建 any 类型的解析器,而您不知道 any 类型的未来可能添加的规则;然后肯定选择 lexx 和 yacc。但是一个简单的代数表达式解析器太“解决”了一个问题,无法引入大炮。使用分流算法并将您的问题分解为
simple scanner to identify the main types of "tokens", NUMBER, +, -, /, *, (, and )
shunting algorithim to convert the "list" of tokens into a RPN list
a stack to hold the "memory" of the evaluation "machine"
the "machine" pull / push / evaluate loop
这具有能够独立开发各个部分的额外好处,因此您可以在整个框架到位的情况下验证每个步骤。 Lexx 和 Yacc 提倡将您的代码添加到他们的框架中,因此除非您采取其他措施,否则您可能必须将整个事情作为一个大块进行调试。