数学解析器的智能设计?
Posted
技术标签:
【中文标题】数学解析器的智能设计?【英文标题】:Smart design of a math parser? 【发布时间】:2010-09-11 23:46:36 【问题描述】:设计数学解析器最聪明的方法是什么?我的意思是一个接受数学字符串(如:“2 + 3 / 2 + (2 * 5)”)并返回计算值的函数?很久以前我确实在VB6中写过一个,但它最终变得臃肿而且不是很便携(或者就此而言很聪明......)。一般的想法,伪代码或真实代码表示赞赏。
【问题讨论】:
您可以在这里查看调车场算法的 java 实现:projects.congrace.de/exp4j 查看我在***.com/questions/28256/…发布的代码 【参考方案1】:从被问到这个问题的 11 年后:如果你不想重新发明***,那里有许多奇特的数学解析器。
我几年前写过一个,它支持算术运算、方程求解、微积分、积分、基本统计、函数/公式定义、绘图等。
它叫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 由我创作。
【讨论】:
这比 NCalc 有什么优势? github.com/ncalc/ncalc 您需要查看两者的文档以了解哪个功能更多,尤其是哪个具有您需要的功能。不过我说的是 ParserNG,它有很多功能,可能还会有更多功能。除了表达式评估之外,它还适用于变量创建、函数创建、方程求解、矩阵和统计函数、微分和积分等。【参考方案2】:开发人员总是希望有一个干净的方法,并尝试从头开始实现解析逻辑,通常以Dijkstra Shunting-Yard Algorithm 结束。结果是看起来很整洁的代码,但可能充满了错误。我开发了这样一个 API,JMEP,它可以完成所有这些,但我花了好几年才拥有稳定的代码。
即使完成了所有这些工作,您甚至可以从那个项目页面看到我正在认真考虑切换到使用 JavaCC 或 ANTLR,即使在所有工作已经完成之后。
【讨论】:
【参考方案3】:我知道这是旧的,但我在尝试开发计算器作为更大应用程序的一部分时遇到了这个问题,并使用公认的答案遇到了一些问题。这些链接对理解和解决这个问题非常有帮助,不应小看。我正在用 Java 编写一个 android 应用程序,对于表达式“字符串”中的每个项目,当用户在键盘上键入时,我实际上将一个字符串存储在 ArrayList 中。对于中缀到后缀的转换,我遍历了 ArrayList 中的每个字符串,然后评估了新排列的字符串的后缀 ArrayList。这对于少量的操作数/运算符来说非常棒,但是更长的计算始终是关闭的,尤其是当表达式开始计算为非整数时。在为Infix to Postfix conversion 提供的链接中,如果扫描的项目是操作员并且topStack 项目具有更高的优先级,它建议弹出堆栈。我发现这几乎是正确的。如果 topStack 项目的优先级高于或等于扫描的运算符,则弹出 topStack 项目最终使我的计算结果正确。希望这将帮助任何解决此问题的人,并感谢 Justin Poliey(和 fas?)提供了一些宝贵的链接。
【讨论】:
【参考方案4】:一个非常好的方法将涉及两个步骤。第一步涉及converting the expression from infix to postfix(例如通过Dijkstra's shunting yard)符号。一旦完成,写一个postfix evaluator就很简单了。
【讨论】:
我不认为调车场算法可以处理正确的一元负绑定到指数基数。即它不能将 -2^3 解析为 -(2^3),只能解析为 (-2)^3。不过我可能是错的,我不确定。 @mebob 这对我来说似乎是用户输入错误。我不会费心去纠正它。如果我感觉不错,我可能会添加一些东西来检查它并提醒用户他们可能错了。【参考方案5】:ANTLR 是一个非常好的 LL(*) 解析器生成器。我强烈推荐它。
【讨论】:
【参考方案6】:我写了几篇关于设计数学解析器的博文。有一个通用的introduction,关于grammars的基础知识,sample implementation written in Ruby和一个test suite。也许您会发现这些材料很有用。
【讨论】:
【参考方案7】:相关问题Equation (expression) parser with precedence? 也提供了一些关于如何开始使用的很好的信息。
-亚当
【讨论】:
【参考方案8】:如果您有一个“永远在线”的应用程序,只需将数学字符串发布到 google 并解析结果。简单的方法,但不确定这是否是您需要的 - 但我猜在某种程度上很聪明。
【讨论】:
这就是我的意思,你很聪明,知道如何完成工作;-)【参考方案9】:假设您的输入是字符串格式的中缀表达式,您可以将其转换为postfix,并使用一对堆栈:一个运算符堆栈和一个操作数堆栈,从那里开始求解。您可以在 Wikipedia 链接中找到一般算法信息。
【讨论】:
【参考方案10】:您有几种方法。您可以生成动态代码并执行它以获得答案,而无需编写太多代码。只需在 .NET 中搜索运行时生成的代码,周围就有大量示例。
或者,您可以创建一个实际的解析器并生成一个小解析树,然后用于评估表达式。同样,这对于基本表达式来说非常简单。查看 codeplex,因为我相信他们那里有一个数学解析器。或者只是查找将包含示例的 BNF。任何介绍编译器概念的网站都会将此作为基本示例。
Codeplex Expression Evaluator
【讨论】:
以上是关于数学解析器的智能设计?的主要内容,如果未能解决你的问题,请参考以下文章
android中XML SAX解析器、Pull解析器和DOM解析器的区别