如何解析涉及括号的数学表达式

Rob*_* P. 9 .net math parsing

这不是学校作业或任何事情,但我意识到这是一个主要的学术问题.但是,我一直在努力做的是解析"数学"文本并提出答案.

例如 - 我可以弄清楚如何解析'5 + 5'或'3*5' - 但是当我尝试正确地将操作链接在一起时我失败了.

(5 + 5)*3

这大多只是让我烦恼,我无法弄明白.如果有人能指出我的方向,我真的很感激.

编辑 感谢您的所有快速回复.对不起,我没有更好地解释.

首先 - 我没有使用正则表达式.我也知道已有的库可以作为字符串使用数学表达式并返回正确的值.所以,我主要是看着这个,因为,遗憾的是,我并没有"明白".

第二 - 我尝试过的事情(可能是误入歧途),但我在计算'('和')'并首先评估最深的项目.在简单的例子中,这有效; 但我的代码不漂亮,更复杂的东西崩溃.当我'计算'最低级别时,我正在修改字符串.

所以...(5 + 5)*3

会变成10*3

然后评估为30

但它感觉"错了".

我希望这有助于澄清事情.我肯定会查看提供的链接.

Mat*_*nen 9

在使用一个简单的图形应用程序时,我使用这个算法(相当容易理解并且对于像这样的简单数学表达式非常有效)首先将表达式转换为RPN,然后计算结果.RPN对于不同的变量值执行起来非常好而且快速.

当然,语言解析是一个非常广泛的主题,还有很多其他方法可以解决它(以及它的预制工具)

  • @shoosh:实际上,函数调用可以很容易地实现为一元运算符(尽管维基百科页面似乎忽略了它们,但可以扩展算法以将它们考虑在内)。对于多个参数,您可以引入一个二进制逗号运算符将值打包在一起。 (2认同)
  • @shoosh:不,如果您认为“f(x,y)”是应用于单个参数的f,即“(x,y)”,那么您会没事的。您可以将此参数视为一个元组,由“,”运算符创建(正如 Matti 提到的)。 (2认同)

And*_*tus 6

这是您想要的简单(朴素的运算符优先级)语法。

expression = 
    term
    | expression "+" term
    | expression "-" term .
term = 
    factor
    | term "*" factor
    | term "/" factor .
factor = 
    number
    | "(" expression ")" .
Run Code Online (Sandbox Code Playgroud)

当您处理“因子”时,您只需检查下一个标记是数字还是“(”,如果它是“(”,那么您再次解析“表达式”,当表达式返回时,您检查下一个标记是否是“)”。您可以通过使用outref参数将 [calculated|read] 值向上冒泡到父级,或者构建表达式树。

这是 EBNF 中的相同内容:

expression = 
    term
    { "+" term | "-" term  } .

term = 
    factor
    { "*" factor | "/" factor }.

factor = 
    number
    | "(" expression ")" .
Run Code Online (Sandbox Code Playgroud)


And*_*tus 6

@Rising Star [我希望将其添加为评论,但格式化失败]

这可能看起来有悖常理,但二叉树既简单又灵活。在这种情况下,节点可以是常量(数字)或运算符。当您决定使用控制流和函数等元素扩展语言时,二叉树会让生活变得更轻松。

例子:

((3 + 4 - 1) * 5 + 6 * -7) / 2

                  '/'
                /     \
              +        2
           /     \
         *         *
       /   \     /   \
      -     5   6     -7
    /   \
   +     1
 /   \
3     4
Run Code Online (Sandbox Code Playgroud)

在上述情况下,扫描仪已被编程为读取“-”后跟一系列数字作为单个数字,因此“-7”作为“数字”标记的值组件返回。'-' 后跟空格作为“减号”标记返回。这使得解析器更容易编写。它在您想要“-(x * y)”的情况下失败,但您可以轻松地将表达式更改为“0 - exp”


gbe*_*nce 5

对于任何在这篇文章发表九年后看到这个问题的人:如果你不想重新发明轮子,那么那里有很多奇特的数学解析器。

有一个是我几年前用 Java 写的,它支持算术运算、方程求解、微分、积分、基本统计、函数/公式定义、绘图等。

它被称为ParserNG并且是免费的。

评估表达式非常简单:

    MathExpression expr = new MathExpression("(34+32)-44/(8+9(3+2))-22"); 
    System.out.println("result: " + expr.solve());

    result: 43.16981132075472
Run Code Online (Sandbox Code Playgroud)

或者使用变量并计算简单的表达式:

 MathExpression expr = new MathExpression("r=3;P=2*pi*r;"); 
System.out.println("result: " + expr.getValue("P"));
Run Code Online (Sandbox Code Playgroud)

或者使用函数:

MathExpression expr = new MathExpression("f(x)=39*sin(x^2)+x^3*cos(x);f(3)"); 
System.out.println("result: " + expr.solve());

result: -10.65717648378352
Run Code Online (Sandbox Code Playgroud)

或者评估给定点的导数(注意: 它在幕后进行符号微分(而不是数值),因此精度不受数值近似误差的限制):

MathExpression expr = new MathExpression("f(x)=x^3*ln(x); diff(f,3,1)"); 
System.out.println("result: " + expr.solve());

 result: 38.66253179403897
Run Code Online (Sandbox Code Playgroud)

x^3 * ln(x)在 x=3 处微分一次。目前可以区分的次数为1。

或对于数值积分:

MathExpression expr = new MathExpression("f(x)=2*x; intg(f,1,3)"); 
System.out.println("result: " + expr.solve());

result: 7.999999999998261... approx: 8
Run Code Online (Sandbox Code Playgroud)

该解析器速度相当快,并且具有许多其他功能。

免责声明:ParserNG 是我创作的。