【问题标题】:parsing of mathematical expressions解析数学表达式
【发布时间】:2011-02-25 07:08:09
【问题描述】:

(在 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*/

【问题讨论】:

  • 你真的需要更好地解释自己。
  • @Matsemann 是的,这是您重新标记问题时的预期行为。坏标签就是坏标签,不管帖子有多旧。

标签: c algorithm parsing data-structures


【解决方案1】:

查看这篇文章: http://blog.barvinograd.com/2011/03/online-function-grapher-formula-parser-part-2/ 它使用 ANTLR 库来解析数学表达式,这个专门使用 JavaScript 输出,但 ANTLR 有许多输出,例如 Java、Ruby、C++、C#,您应该能够将帖子中的语法用于任何输出语言。

【讨论】:

    【解决方案2】:

    可能最简单的方法是使用像 Lua 或 Python 这样的嵌入式语言,这两种语言的解释器都是用 C 编写的。不幸的是,如果你走 Lua 路线,你必须将二进制操作转换为函数调用,在这种情况下,使用 Python 可能更容易。所以我会沿着这条路走。

    如果您只想将结果输出到控制台,这真的很简单,您甚至不必深入研究 Python 嵌入。既然如此,那么您只需要在 Python 中编写单行程序即可输出该值。

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

    exec "import math;A=<vala>;B=<valb>;C=<valc>;D=<vald>;print <formula>".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('^') 你必须删除 @ 987654323@并使用**供电。

    我对C的了解还不够多,无法告诉你如何在C中生成这个字符串,但是当你有了之后,你可以使用下面的程序来运行它:

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

    您可以在此处查找有关在 C 中嵌入 Python 的更多信息:Python Extension and Embedding Documentation

    如果您需要在程序中使用计算结果,可以通过 Python 读取该值,但您必须自己阅读。

    【讨论】:

    • 嘿。我认为这是作弊;-)。在 Python 中执行此操作肯定比在 C 中更容易,但在 C 中实现表达式评估器既有趣又有趣(嗯,它可以;我从未在 C 中实现过,但我在 C++ 中实现了,并且 600几行代码之后,我对如何评估表达式有了更多了解:-))。
    • 如果目标是学习表达式评估,我同意;否则,如果这只是一个中间步骤,这既更容易,也更不容易出错。您可以将其与使用标准库功能进行比较。当你被要求自己写时使用它是作弊;否则,这很有意义。 :) 但是,是的,我同意这可能是我还没有进行过的有趣的学习经历。
    • 一个有效的观点:由于 OP 没有说明他的目的是什么,这是一个解决方案的好主意 (+1)。
    【解决方案3】:

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

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

    expr => <function_identifier> ( stmt )
            ( stmt )
            <variable_identifier>
            <numerical_constant>
    
    stmt => expr <operator> stmt
    

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

    有一些工具可以帮助您完成此操作(yaccbisonantlr 等),但您也可以手动完成。有很多方法可以做到这一点,但它们都有一个共同点——堆栈。处理这样的语言需要一种称为下推自动机的东西,这只是一种奇特的说法,可以根据新输入、当前状态和堆栈的顶部项目做出决策。它可以做出的决定包括推送、弹出、更改状态和组合(将2+3 转换为5 是一种组合形式)。合并通常称为产生式,因为它会产生结果。

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

    您需要做的另一件事是写下构成您的语言的不同类型的单词和运算符。这些单词和运算符称为词位,代表您的语言的标记。我用语法&lt;like_this&gt; 表示这些标记,除了代表它们自己的括号。

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

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

    还有一些工具可以为此生成代码。 lex 是其中之一,它与解析器生成程序 yacc 高度集成,但由于您正在努力学习,您也可以用 C 编写自己的分词器/词法分析代码。

    在你完成所有这些之后(这可能会花费你相当长的时间),你需要让你的解析器构建一个树来表示输入的表达式和语法。在表达式评估的简单情况下(例如编写一个简单的命令行计算器程序),您可以让解析器在处理输入时评估公式,但是对于您的情况,据我了解,您需要制作一棵树(或反向波兰表示,但我认为树更容易)。

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

    【讨论】:

      【解决方案4】:

      此外,您应该查看您在 SO 上的帖子以及有关二叉树的其他帖子。使用树结构实现这一点。遍历作为中缀进行评估。对树的问题有一些很好的答案。

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

      【讨论】:

        【解决方案5】:

        您需要编写lexical analyzer 来标记输入(将其分解为其组成部分——运算符、标点符号、标识符等)。不可避免地,您最终会得到一些标记序列。

        之后,有多种方法可以评估输入。执行此操作的最简单方法之一是使用 shunting yard algorithm 将表达式转换为后缀(后缀表达式的评估很简单,大写 E)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-07
          相关资源
          最近更新 更多