Evaluate Math Expression
摘要Abstract:本文简要介绍了数学表达式解析求值的几款开源软件,并结合程序代码说明了OpenCascade中表达式包的用法。也简要介绍了表达式解析求值在AVEVA Paragon模块中的应用。
关键字Key Words:Expression, Paragon, OpenCascade Expr package, muParser, MTParser
一、引言 Introduction
算术表达式中最常见的表示法形式有中缀、前缀和后缀表示法。中缀表示法(Infix notation)是书写表达式的常见方式,而前缀(prefix notation)、后缀表示法(postfix notation)主要用于计算机科学领域。算术表达式只包含操作数(operands)、二元操作符(binary operators)和一种括号(one kind of parentheses)。
Knuth将编写程序计算表达式的方法概括为三个步骤:
l 对中缀表达式进行语法分析;
l 中缀表达式到后缀表达式的转换;
l 对后缀表达式求值;
在《数据结构与算法》等相关的书籍中,还有如何将中缀表达式转换为后缀表达式的具体算法。
在AVEAV Plant(PDMS)的Paragon模块定义部件时,使用了参数化的表达式,从而实现了参数化的部件定义。其中参数化表达式的解析计算是其一个关键技术点。最近在写模型导出程序(Model Data Exchange Addin)时就遇到了这个问题,即参数化表达式的计算。好在AVEVA .Net给出了表达式计算类DbExpression,可以对PML的表达式进行解析计算,顺利解决了问题。
若想在自己的程序中实现数学表达式的解析求值,也有一些开源的工具可供选用。有些C++的数学表达式计算程序速度、效率还是很高的。在查看OpenCascade的文档时,发现其也有表达式求值的包,可用来对表达式进行计算。
发现OpenCascade已经有很多功能与AVEVA产品PDMS中的功能很类似,也难怪OpenCascade的前身Matra-Data Vision公司早期也有个集机械设计与工厂设计于一身的企业级并行工程解决方案Euclid集成系统。在1998年时Dassault Systèmes收购了EUCLID QUANTUM。
二、几款开源软件介绍 Introduce some tools
网上有很多开源的数学表达式解析求值的库,如:
l fparser
l muParser
l MTParse
在开源的二维CAD软件LibreCAD中就使用了muParser来对输入命令中的表达式进行解析。早期版本中使用了fparser。muParser小巧精干,提供LIB、DLL及源代码入方式。muParser最大的优点就是速度较快,更适合于实时性要求较高的环境,如Unix系统和单片机环境。源程序下载及文档说明可参考:
http://www.codeproject.com/cpp/FastMathParser.asp
MTParser优雅简洁,提供LIB、COM、源代码三种引入方式,是用典型C++风格编写的解析器,和muParser不同,它引入了多种设计模式,因而可扩展性的可维护性都很强。如作者所说,速度并不是TMParser最优先考虑的,也许文中所说的可扩展、可维护、易于使用、健壮性才是它的最大优势。源程序下载及设计文档可参考:
http://www.codeproject.com/Articles/7335/An-extensible-math-expression-parser-with-plug-ins
下面以一个具体实例来说明muParser的用法:(将muParser以源代码引入的方式加入到项目中),再编译如下代码:
2 * Copyright (c) 2013 eryar All Rights Reserved.
3 *
4 * File : Main.cpp
5 * Author : eryar@163.com
6 * Date : 2013-10-09
7 * Version : 1.0v
8 *
9 * Description : Evaluate expression in muParser.
10 *
11 */
12
13 #include <iostream>
14
15 #include "muParser.h"
16
17 int main()
18 {
19 mu::Parser parser;
20 parser.DefineNameChars("0123456789_"
21 "abcdefghijklmnopqrstuvwxyz"
22 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
23 "[]");
24
25 double dParam1 = 2.0;
26 double dParam2 = 3.1415926 / 2.0;
27
28 parser.DefineVar("PARA[1]", &dParam1);
29 parser.DefineVar("PARA[2]", &dParam2);
30
31 parser.SetExpr("10 * (PARA[1] + sin(PARA[2]))");
32
33 std::cout << parser.Eval() << std::endl;
34
35 }
程序运行结果如下所示:
2 Press any key to continue . . .
如上程序所示,使用muParser可实现参数化表达式的计算,功能与AVEVA Plant(PDMS)中的Paragon模块中参数化表达式的计算方式已经很相近了。使用方法很简单。
三、Evaluate Expression in OpenCascade
在OpenCascade的TKAdvTools中有Expr包和ExprIntrp包,这两个包可用来对表达式进行解析和求值。但是在其文档《Foundation Classes User’s Guide》中并没有对TKAdvTools的工具进行说明。下面只给出一个简单实例来说明其应用,有兴趣的读者可以结合源程序对其具体实现进行研究。示例代码如下所示:
2 * Copyright (c) 2013 eryar All Rights Reserved.
3 *
4 * File : Main.cpp
5 * Author : eryar@163.com
6 * Date : 2013-10-09
7 * Version : 1.0v
8 *
9 * Description : Evaluate expression in OpenCascade.
10 *
11 */
12
13 #define WNT
14 #include <TCollection_AsciiString.hxx>
15 #include <Expr_GeneralExpression.hxx>
16 #include <ExprIntrp_GenExp.hxx>
17
18 #pragma comment(lib, "TKernel.lib")
19 #pragma comment(lib, "TKAdvTools.lib")
20
21 int main(void)
22 {
23 TCollection_AsciiString strExpr("sin(3.1415926 / 6) * 2");
24 Handle_ExprIntrp_GenExp exprIntrp = ExprIntrp_GenExp::Create();
25 Handle_Expr_GeneralExpression genExpr = NULL;
26
27 exprIntrp->Process(strExpr);
28
29 genExpr = exprIntrp->Expression();
30
31 std::cout << genExpr->EvaluateNumeric() << std::endl;
32
33 return 0;
34 }
程序运行结果如下所示:
2 Press any key to continue . . .
OpenCascade的表达式求值功能也很强大,这里只以一个简单示例来抛砖引玉啦。感觉OpenCascade的表达式包也可以像muParser, MTParser一样作为一个独立的工具来使用。
四、结论 Conclusion
因为AVEVA Plant(PDMS)的Paragon模块中的参数化部件定义方式,注意到了参数表达式的解析与求值。由于ModelDataExchangeAddin程序的需要,才去找寻开源的表达式解析与求值的库,谁知最后还是使用了AVEVA .Net中现成的DbExpression类,简化了程序的开发。
OpenCascade中竟然也有表达式解析与求值的工具,作为早期机械设计与工厂设计软件系统,可能都考虑过参数化这种方式。深入挖掘其源代码,对类似PDMS的工厂设计系统实现原理的理解有很大帮助。