设计数学解析器最聪明的方法是什么?我的意思是一个函数,它接受一个数学字符串(如:“2 + 3/2 +(2 * 5)”)并返回计算值?我在VB6很久以前写过一个,但它最终变得臃肿而且不太便携(或者说很聪明......)。一般的想法,伪造的代码或真正的代码表示赞赏。
答案 0 :(得分:84)
一个相当不错的方法将涉及两个步骤。第一步涉及converting the expression from infix to postfix(例如,通过Dijkstra's shunting yard)表示法。完成后,编写postfix evaluator非常简单。
答案 1 :(得分:13)
我写了几篇关于设计数学解析器的博客文章。有关introduction,grammars和sample implementation written in Ruby的一般test suite基本知识。也许你会发现这些材料很有用。
答案 2 :(得分:6)
你有几种方法。您可以生成动态代码并执行它以获得答案,而无需编写太多代码。只需在.NET中搜索运行时生成的代码,就会有很多例子。
或者,您可以创建一个实际的解析器并生成一个小的解析树,然后用于评估表达式。对于基本表达式,这也很简单。检查codeplex,因为我相信他们有一个数学解析器。或者只是查看BNF,其中包括示例。任何介绍编译器概念的网站都会将此作为基本示例。
答案 3 :(得分:4)
我知道这已经过时了,但我遇到了这个尝试开发计算器作为更大的应用程序的一部分并使用接受的答案遇到一些问题。链接非常有助于理解和解决这个问题,不应该打折扣。我正在用Java编写一个Android应用程序,对于表达式“string”中的每个项目,我实际上在用户在键盘上键入时将String存储在ArrayList中。对于中缀到后缀的转换,我迭代遍历ArrayList中的每个String,然后评估新排列的字符串后缀ArrayList。对于少数操作数/运算符来说这是非常好的,但是更长的计算始终是关闭的,特别是当表达式开始评估为非整数时。在Infix to Postfix conversion提供的链接中,如果扫描的项目是操作员且topStack项目具有更高的优先级,则建议弹出堆栈。我发现这几乎是正确的。如果topStack项的优先级高于或等于扫描的操作符,则弹出topStack项最终使我的计算结果正确。希望这将有助于解决这个问题的任何人,并感谢Justin Poliey(和fas?)提供一些宝贵的链接。
答案 4 :(得分:3)
如果你有一个“永远在线”的应用程序,只需将数学字符串发布到谷歌并解析结果。简单的方法,但不确定这是否是你需要的 - 但我猜想是聪明的。
答案 5 :(得分:3)
相关问题Equation (expression) parser with precedence?也提供了一些有关如何开始使用此信息的良好信息。
- 亚当
答案 6 :(得分:1)
假设您的输入是字符串格式的中缀表达式,您可以将其转换为postfix,并使用一对堆栈:运算符堆栈和操作数堆栈,从那里开始处理解决方案。您可以在Wikipedia链接中找到一般算法信息。
答案 7 :(得分:1)
ANTLR是一个非常好的LL(*)解析器生成器。我高度推荐它。
答案 8 :(得分:1)
开发人员总是希望采用干净的方法,并尝试从头开始实现解析逻辑,通常以Dijkstra Shunting-Yard Algorithm结束。结果是整洁的代码,但可能与bug有关。我开发了这样一个API JMEP,它可以完成所有这些工作,但是我需要花费数年时间才能获得稳定的代码。
即使完成了所有这些工作,即使已经完成所有工作,即使从那个项目页面我也可以看到我正在认真考虑切换到使用JavaCC或ANTLR。
答案 9 :(得分:1)
我几年前写的一篇文章支持算术运算,方程求解,微积分,积分微积分,基本统计量,函数/公式定义,图形等。
它叫ParserNG,它是免费的。
计算表达式非常简单:
MathExpression expr = new MathExpression("(34+32)-44/(8+9(3+2))-22");
System.out.println("result: " + expr.solve());
result: 43.16981132075472
或者使用变量并计算简单表达式:
MathExpression expr = new MathExpression("r=3;P=2*pi*r;");
System.out.println("result: " + expr.getValue("P"));
或使用功能:
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
或者在给定点上评估导数(请注意,它在幕后进行了符号微分(不是数字),因此精度不受数值近似误差的限制):
MathExpression expr = new MathExpression("f(x)=x^3*ln(x); diff(f,3,1)");
System.out.println("result: " + expr.solve());
result: 38.66253179403897
在x = 3时一次区分 x^3 * ln(x)
。
您现在可以区分的次数是1。
或用于数值积分:
MathExpression expr = new MathExpression("f(x)=2*x; intg(f,1,3)");
System.out.println("result: " + expr.solve());
result: 7.999999999998261... approx: 8
此解析器非常快,并且具有许多其他功能。
通过与Objective C的绑定将其移植到Swift的工作已经结束,我们已经在其他迭代用例中将其用于图形化应用程序中。
免责声明:ParserNG是我创作的。