解析数学表达式

(在c90)(linux)

输入:

sqrt(2 - sin(3*A/B)^2.5) + 0.5*(C*~(D) + 3.11 +B) a b /*there are values for a,b,c,d */ c d 

输入:

 cos(2 - asin(3*A/B)^2.5) +cos(0.5*(C*~(D)) + 3.11 +B) a b /*there are values for a,b,c,d */ c d 

输入:

 sqrt(2 - sin(3*A/B)^2.5)/(0.5*(C*~(D)) + sin(3.11) +ln(B)) /*max lenght of formula is 250 characters*/ a b /*there are values for a,b,c,d */ c /*each variable with set of floating numbers*/ d 

正如您所看到的,输入中的中缀公式取决于用户。 我的程序将采用公式和n元组值。 然后计算a,b,c和d的每个值的结果。 如果你想知道我在说;程序的结果是图表。 / 有时,我想我会把输入和存储在字符串中。 然后另一个想法出现“我应该在结构中存储公式”,但我不知道如何在结构的基础上构建代码。 /

实际上,我不知道如何在程序代码中存储公式,以便我可以完成我的工作。 能给我看看么?

 /* a,b,c,d is letters cos,sin,sqrt,ln is function*/ 

您需要编写一个词法分析器来标记输入(将其分解为其组成部分 – 运算符,标点符号,标识符等)。 不可避免地,你最终会得到一些令牌序列。

之后,有许多方法可以评估输入。 最简单的方法之一是使用分流码算法将表达式转换为后缀(后缀表达式的评估是大写E的Easy)。

您应该查找“抽象语法树”和“表达式树”以及“词法分析”,“语法”,“解析”和“编译器理论”。 阅读文本输入并从中获取意义对于大多数事情来说是非常困难的(尽管我们经常尝试确保我们有简单的输入)。

生成解析器的第一步是记下输入语言的语法。 在这种情况下,您的输入语言是一些数学表达式,因此您可以执行以下操作:

 expr =>  ( stmt ) ( stmt )   stmt => expr  stmt 

(我在几年内没有写过像这样的语法{查找BNFEBNF },所以我可能会做出一些其他人会指出的明显错误)这可能会变得更复杂,具体取决于你如何处理运算符优先级(乘法和加法和减法类型之前的设备),但这种情况下的语法点是帮助你编写一个解析器。

有一些工具可以帮助你做到这一点( yaccbisonantlr等),但你也可以手工完成。 有很多方法可以做到这一点,但它们都有一个共同点 – 堆栈。 处理诸如此类的语言需要一种称为下推自动机的东西,这只是一种奇特的方式,可以根据新输入,当前状态和堆栈的顶级项目来做出决策。 它可以做出的决定包括推动,弹出,改变状态和组合(将2+3变为5是一种组合forms)。 组合通常被称为生产,因为它产生结果。

在各种常见类型的解析器中,您几乎肯定会从一个递归的解析器开始。 它们通常直接用通用编程语言编写,例如C.这种类型的解析器由几个(通常很多)相互调用的函数组成,它们最终使用系统堆栈作为下推自动机堆栈。

您需要做的另一件事是记下构成您的语言的不同类型的单词和运算符。 这些单词和运算符称为lexemes,代表您的语言标记。 我在语法表示了这些标记,除了代表它们的括号。

您很可能希望用一组正则表达式来描述您的词位。 如果你使用grepsedawkperl ,你应该熟悉这些。 它们是描述所谓的常规语言的一种方式,可以通过称为有限状态自动机的东西进行处理。 这只是一种奇特的说法,它是一个程序,可以通过仅考虑其当前状态和下一个输入(输入的下一个字符)来决定改变状态。 例如,您的词汇描述的一部分可能是:

 [AZ] variable-identifier sqrt function-identifier log function-identifier [0-9]+ unsigned-literal + operator - operator 

还有一些工具可以为此生成代码。 lex是其中之一,它与解析器生成程序yacc高度集成,但是既然你正在尝试学习,你也可以在C中编写自己的tokenizer / lexical分析代码。

完成所有这些操作后(可能需要一段时间),您需要让解析器构建一个树来表示输入的表达式和语法。 在表达式评估的简单情况下(比如编写一个简单的命令行计算器程序),您可以让解析器在处理输入时评估公式,但对于您的情况,据我所知,您需要创建一个树(或者反向波兰语表示,但在我看来树木更容易)。

然后,在读取变量的值后,可以遍历树并计算实际数。

可能最简单的事情是使用Lua或Python之类的嵌入式语言,因为解释器都是用C编写的。不幸的是,如果你使用Lua路由,你必须将二进制操作转换为函数调用,其中如果使用Python可能更容易。 所以我会走那条路。

如果您只想将结果输出到控制台,这非常简单,您甚至不必深入研究Python嵌入。 从那以后,你只需要在Python中编写一个单行程序来输出值。

以下是您可以使用的Python代码:

 exec "import math;A=;B=;C=;D=;print ".replace("^", "**").replace("log","math.log").replace("ln", "math.log").replace("sin","math.sin").replace("sqrt", "math.sqrt").replace("cos","math.cos") 

请注意,替换是在Python中完成的,因为我很确定在Python中而不是C中更容易实现。另请注意,如果要使用xor(’^’),则必须删除.replace("^","**")并使用**进行供电。

我不知道足够的C能够告诉你如何在C中生成这个字符串,但是在你拥有之后,你可以使用以下程序来运行它:

 #include  int main(int argc, char* argv[]) { char* progstr = "..."; Py_Initialize(); PyRun_SimpleString(progstr); Py_Finalize(); return 0; } 

您可以在此处查找有关在C中嵌入Python的更多信息: Python扩展和嵌入文档

如果你需要在程序中使用计算结果,有办法从Python中读取这个值,但你必须自己阅读它们。

此外,您应该查看您的post到SO和其他有关二叉树的post。 使用树结构实现它。 遍历作为中缀进行评估。 对树问题有一些很好的答案。

如果你需要存储它(对于文件中的持久性),我建议使用XML。 解析XML应该让你真正体会到你的任务是多么容易。

看看这篇文章: http : //blog.barvinograd.com/2011/03/online-function-grapher-formula-parser-part-2/它使用ANTLR库来解析数学表达式,这个特别使用JavaScript输出但是ANTLR有许多输出,如Java,Ruby,C ++,C#,你应该能够在post中使用任何输出语言的语法。