[QMathExpr]数学表达式解析和执行工具

QMathExpr 是 QDAC 家族的一个新成员,为您提供数学表达式的解析和计算功能。它支持以下特性:

  • 加(+)、减(-)、乘(*)、除(/)、求余(%)运算,除法时支持整除和常规除法运算。由于 % 被用做求余运算符,所以如果要当做百分号处理,请先直接将 % 替换为 /100 即可,如 10*2% 替换成 10*2/100,然后再解析执行。
  • 比较操作符支持(>、>=、=、<、<=)
  • 逻辑非支持(!)
  • 支持字符串相加
  • 内置 26 个常规函数,包括:
    • 统计函数:Max(最大值)、Min(最小值)、Avg(平均值)、Sum(求和)、StdDev(标准方差)
    • 三角函数:Sin(正弦)、Cos(余弦)、Tan(正切)、ASin(arcsin,反正弦)、ACos(arccos,反余弦)、ATan(arctan,反正切),ATan2(arctan2,方位 )
    • 冥和指数运算函数:Pow(幂)、Ln(自然对数)、Log(对数)、Sqrt(平方根,等价于Pow(X,0.5))、Exp(等价于Pow(E,x))
    • 其它函数:Rand(随机数)、IIF(条件取值,条件为真时取第二个参数的值,否则取第三个参数值)、ABS(求绝对值)、Trunc(截取整数部分)、Frac(截取小数部分)、Round(四舍五入)、Ceil(上取整)、Floor(下取整)、Fact(阶乘)
    • 两个常量:PI(π,3.14159265358979)和自然对数底数e(2.71828182845905)
  • 支持自定义常量、变量和自定义函数支持,函数可支持不定参数
  • 支持将类型于 2sin(x) 的表达式自动解析为 2*sin(x) (注:需要开启NumIdentAsMultiply 选项)
  • 支持计算机整数相除规则,即 1/2 的结果为 0 而不是 0.5(注:需关闭 UseStdDiv 选项,默认为 true)

【使用方法】

1、创建 TMathExpression 的实例,您可以将它赋给 IMathExpression 接口,以减少需要自己释放对象的麻烦。

var
AExpr:IMathExpression;
AResult:Variant;
begin
AExpr:=TMathExpression.Create;

2、如果表达式只计算一次,那么直接调用 Eval 函数,将表达式传入即可。但如果表达式要多次重用,建议先调用 Parse,然后再多次调用 Eval 来计算表达式的值,以减少解析表达式的开销。

  • 直接调用示例:

AResult:=AExpr.Eval(‘1+Max(2,3)’);
  • 间接调用示例

AExpr.Parse(‘1+Max(2,3)’);
AResult:=AExpr.Eval;

3、在上面的例子中,Max 是函数。要增加自定义函数,可以调用 Add 成员函数来添加。Add 函数有两个重载:

function Add(const AVarName: String; AVolatile: TMathVolatile = mvVolatile)
: TMathVar; overload;
function Add(const AFuncName: String; AMinParams, AMaxParams: Integer;
ACallback: TMathValueEvent; AVolatile: TMathVolatile = mvVolatile)
: TMathVar; overload;

第一个重载可以认为是第二个重载的简化版本,相当于不需要参数的常量、变量或函数定义。我们当然也可以在第一个重载返回的对象里设置额外的参数控制。我们这里演示下它们的用法:

  • 添加一个常量,比如我们定义 X 为一个常量,值为100

AExpr.Add(‘X’,TMathVolatile.mvImmutable).Value:=100
  • 添加一个变量或函数,实际上在 QMathExpr 中,变量被认为是一个无参数的函数,我们通过 TMathVar.OnGetValue 事件来获取当前的值。函数的稳定性在 QMathExpr 中包含三种:mvImmutable(常量,无论什么时候调用,返回值都是固定的)、mvStable(稳定,在一次计算中,其值只获取一次,后面值不再变化,直到下一次计算)、mvVolatile(易变,每一个引用的位置都需要单独计算,不同位置返回的值是不一样的)。假设为我定义一个函数 List,计算函数的每个参数,并返回最后一个参数的值。

procedure DoList(Sender: TObject; AExpr: TMathVar);
var
I:Integer;
AResult:Variant;
begin
for I:=0 to High(AExpr.Params) do
AResult:=AExpr.Params[I].Value;
AExpr.Value:=AResult;//设置返回值
end;
….
AExpr.Add(‘LIST’,1,MaxInt,DoList);//注册函数

AResult:=AExpr.Eval(‘List(1,2,3)’);//AResult=3

对于函数来说,如果参数的数量是固定的,则应将最大参数个数( AMaxParams ) 和 ( AMinParams )值设置为同一个值。如果是 mvImmutable 类型的值,要注册成常量,因为参数不影响返回值的值,所以它的参数个数没有意义,可以采用第一种注册方式。AMinParams 的值限制了最少的参数个数,AMaxParams 限制了最大的参数个数,如果不限制,那么请将其设置为足够大的整数,如 MaxInt 。这个值的设置会在解析表达式时检查,所以在进入执行阶段,我们可以认为它的个数是符合要求的。

【注意】请不要在函数中修改相关参数数量和值,它的行为并没有进行定义,所以代码也不保证这方面的逻辑正确。

分享到: