【问题标题】:How to evaluate a mathematical expression using strings and arrays in C [closed]如何在 C 中使用字符串和数组评估数学表达式 [关闭]
【发布时间】:2014-10-26 14:36:41
【问题描述】:

我想评估存储为字符串的数学表达式,例如。 "1+3/4*3".

我的想法是:

  • 读入字符串存储到数组中,
  • 将运算符提取到一个临时数组并根据运算的数学顺序进行排序。
  • 将值提取到一个临时数组,然后使用正确的操作计算表达式。

我的逻辑听起来对吗?如果是这样,您能否给我举个例子,即使只是像3+1-3 这样的基本版本。

【问题讨论】:

  • 选择 lexx 和 yacc。如果试图重新发明轮子,请阅读有关树木的信息。
  • 你为什么要问?在什么情况下?哪里来的表达?请编辑您的问题以改进它!
  • 请看一下,可能是这个链接,expression-evaluation,可能会有所帮助:-) 我不能投票(我的限制已经过去了),否则肯定会投票赞成这个问题,因为对我来说,这个问题似乎是真实的,正如它所解释的那样,以某种方式来说,OP 的想法是什么:-)
  • @nIcEcOw:你为什么要投票?我相信“没有足够的研究努力”的反对票绝对值得。
  • @BasileStarynkevitch:我真的不知道OP是怎么想的,但在我看来,如果有什么问题摆在我面前,我总是想先自己努力( 虽然 OP 从未在这方面工作过 :(),但我可能需要一些时间才能真正将其置于工作状态,但我仍然不会失去希望。但如果我仍然做不到,是的,我会尝试找到答案,而不是为所有让我感到困惑的事情,而是一小部分。虽然你说得太对了,但这似乎是 OP 的第一个支持 :-)

标签: c arrays string evaluation


【解决方案1】:

查看调车场算法并将字符串转换为反向波兰符号。

如果您还通过一次读取一个字符而不是仅仅跳转到运算符来将数字检测与符号检测分开,这将有所帮助。

之后,执行计算就相当容易了,因为输入的排序方式使得基于堆栈的计算器易于实现。

是的,你可以做一个成熟的递归下降解析器;但是,对于相对简单的代数表达式来说,它们是多余的。

--- 编辑是因为我发现工具与技术存在争议---

我看到很多人提到 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 提倡将您的代码添加到他们的框架中,因此除非您采取其他措施,否则您可能必须将整个事情作为一个大块进行调试。

【讨论】:

  • 无需转换为反向波兰符号!
  • 如果你想要理智和轻松地处理优先级,实际上转换为 RPN 比尝试在修复中进行计算要容易得多。
  • 这非常有帮助,非常感谢:)
【解决方案2】:

您可以使用标准的recursive descent parsing 技术编写自己的表达式parser 和求值器(或interpreter)(然后您可以显式表示您的AST)。您可以使用标准的GNU flex lexing 和GNU bison 解析技术,例如infix calc example of Bison (几乎完全处理您的示例)!或者,按照Edwin Buck's answer 的建议,使用shunting-yard algorithm

(所以不,你原来的逻辑看起来不对!)

您可能想要一些更强大的表达式,例如包含变量(如2*x+3)或更复杂的东西(条件、循环、定义的函数)。您很快就会需要Turing-complete 语言。

那么你应该考虑在你的程序中嵌入一些解​​释器;像例如Lua 或 GNU guile。这可能是最好的方法(避免重新发明轮子,并为高级用户提供一些他可能已经熟悉的脚本语言,并找到相关教程)。

另请阅读Domain Specific LanguagesJIT compilation(例如使用libjit

另请参阅this related question,了解如何在 C++ 中计算表达式。

【讨论】:

  • 实际上,我很喜欢这两个答案中的输入,所以想保留该评论,作为书签,让我在被允许的情况下回来为所有人投票:-)。但你的答案很深刻,在如此小的空间内提出了如此多的知识。似乎需要相当长的时间才能真正访问每个链接,并了解每件事的作用,尽管我对某事只有一点想法。 但知识很少总是危险的,他们说。所以肯定会一一介绍:-)
猜你喜欢
  • 2022-01-17
  • 2012-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多