golang实现的公式引擎formula

yidane · 2019-10-19 16:32:50 · 4777 次点击 · 预计阅读时间 5 分钟 · 大约8小时之前 开始浏览    
这是一个创建于 2019-10-19 16:32:50 的文章,其中的信息可能已经有所发展或是发生改变。

引言 : 由于之前开发一个报表系统,需要对数据进行自定义计算,故开发设计了一个数学公式引擎

1、内置函数

支持对数学运算的编译执行,主要支持以下数学公式:

  • 支持四则运算
  • 支持三角函数
  • 支持位运算
  • 支持三元操作
  • 支持自定义参数
  • 支持自定义函数
  • 支持公式缓存

内置函数如下表

函数名称 参数个数 调用方式 结果
abs 1 abs(-1) 1
acos 1 acos(sqrt(3)/2) 0.5235987755982991 (π/6)
asin 1 asin(1/2) 0.5235987755982991 (π/6)
asin 1 asin(1) π/2
atan 1 atan(1) 0.7853981633974483 (π/4)
ceil 1 ceil(3.4) 4
concat n concat(1,23,hello) 123hello
cos 1 cos(π/3) 0.5000000000000001
/ 2 3/4 0.75
exp 1 exp(3.3) 27.112638920657883
floor 1 floor(2.2) 2
> 2 3 > 2 true
iif 3 iif(3 > 2,π,10) 3.141592653589793
\< 2 3 < 2 false
in n in(3,3,4,5) true
ln 1 ln(2.718281828) 0.9999999998311266
log2 1 log2(16) 4
log10 1 log10(100000) 5
log 2 log(100,10) 2
max n max(-1,2,3.1) = 3.1 3.1
min n min(-1,2,3.1) = -1 -1
mod 2 mod(5,2) 1
* 2 3*3.4 10.2
+ 2 5+10 15
pow 2 pow(10,2) 100
round 2 round(100.11) 100
sign 1 sign(100) false
sin 1 sin(π/6) 0.49999999999999994
- 2 3-6 -3
tan 1 tan(π/4) 1
truncate 1 truncate(12.3) 12
>> 2 2>>1 1
<< 2 1<<1 2

1.1 调用方法

  • 1.1.1 普通数学公式调用
expression:=formula.NewExpression("1+2")
result,err:=expression.Evaluate()
if err!=nil{
    //handle err
}

v,err:= result.Int64()
if err!=nil{
    //handle err
}

//v should equal 3
  • 1.1.2 自定义参数调用
expression := NewExpression("[i]+[j]")
err := expression.AddParameter("i", 1)
if err != nil {
    t.Fatal(err)
}
err = expression.AddParameter("j", 2)
if err != nil {
    t.Fatal(err)
}
result, err := expression.Evaluate()
//handle result

2 自定义公式开发

2.1 实现接口 opt.Function

type CustomFunction struct {
}

func (*CustomFunction) Name() string {
    return "CustomFunction"
}

func (f *CustomFunction) Evaluate(context *opt.FormulaContext, args ...*opt.LogicalExpression) (*opt.Argument, error) {
    err := opt.MatchTwoArgument(f.Name(), args...)
    if err != nil {
        return nil, err
    }

    left, err := (*args[0]).Evaluate(context)
    if err != nil {
        return nil, err
    }

    leftValue, err := left.Int64()
    if err != nil {
        return nil, err
    }

    right, err := (*args[1]).Evaluate(context)
    if err != nil {
        return nil, err
    }

    rightValue, err := right.Int64()
    if err != nil {
        return nil, err
    }

    return opt.NewArgumentWithType(leftValue+rightValue+1, reflect.Int64), nil
}

2.2 注册自定义函数

func init() {
    var f opt.Function = new(CustomFunction)
    err := formula.Register(&f)
    if err != nil {
        log.Fatal(err)
    }
}

2.3 调用自定义函数

expression := formula.NewExpression("CustomFunction(1,2)")
result, err := expression.Evaluate()
if err != nil {
    log.Fatal(err)
}

v, err := result.Int64()
if err != nil {
    log.Fatal(err)
}

if v != 4 { //CustomFunction: i+j+1
    log.Fatal("error")
}

log.Println("custom function succeed")

3 性能测试

BenchmarkOnePlusOne-8          50000         26676 ns/op
BenchmarkOne-8                  100000         19401 ns/op
BenchmarkComplexOne-8          10000        180650 ns/op
BenchmarkSin-8                  20000         78591 ns/op

4 源码地址

formula


有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4777 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传