38

golang实现的公式引擎

 4 years ago
source link: https://www.tuicool.com/articles/iMziYzJ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK