一文教你如何使用go语言中的动态表达式工具goevaluate

148 阅读3分钟

前言:

在实际的业务开发而言,总会存在大大小小的规则判断,如判断某个用户年龄>12等,这时候,一般通用的写法都是采取动态表达式的方式,来方便规则本身的判断编写。

govaluate与 JavaScript 中的eval功能类似,用于计算任意表达式的值。此类功能函数在 JavaScript/Python 等动态语言中比较常见。govaluate让 Go 这个编译型语言也有了这个能力!

下载方式:

go get github.com/Knetic/govaluate

官方说明:

提供支持评估任意C语言风格的算术/字符串表达式。 有时候,你无法提前知道表达式的具体形式,或者你希望这些表达式是可配置的。也许你的应用程序中有一组数据,你希望允许用户在将数据提交到数据库之前指定一些验证。或者你编写了一个监控框架,该框架能够收集大量指标,然后评估一些表达式以确定是否应对某些指标发出警报,但每个监控的警报条件都不同。 很多人最终会编写自己的一种不完整的评估语言,或者将表达式直接嵌入到可执行文件中,即使他们知道这些表达式可能会发生变化。这些策略可能有效,但它们需要时间来实现,用户需要时间来学习,并且随着需求的变化会引入技术债务。这个库旨在涵盖所有常见的 C 风格表达式,这样你就不必重新发明计算机上最古老的轮子之一。

使用方法

(1)利用表达式直接执行:

package main

import (
	"fmt"
	"github.com/Knetic/govaluate"
)

func main() {
	expression, err := govaluate.NewEvaluableExpression("10 > 0")
	if err!=nil{
		fmt.Println(err.Error())
		return
	}
	result, err := expression.Evaluate(nil)
	// result is now set to "true", the bool value.
	if err!=nil{
		fmt.Println(err.Error())
		return
	}
	fmt.Println(result)
	return
}

这里,是实则判断了"10 > 0"这一个表达式的执行值

(2)采取动态入参的方式执行:


package main

import (
	"fmt"
	"github.com/Knetic/govaluate"
)

func main() {
	expression, err := govaluate.NewEvaluableExpression("foo > 0");
	if err!=nil{
		fmt.Println(err.Error())
		return
	}
	parameters := make(map[string]interface{}, 8)
	parameters["foo"] = -1;

	result, err := expression.Evaluate(parameters);
	if err!=nil{
		fmt.Println(err.Error())
		return
	}
	// result is now set to "false", the bool value.
	fmt.Println(result)
	return
}

这里,实则是将foo替换成了-1,去执行了“-1>0”这个表达式

(3)判断时间:

package main

import (
	"fmt"
	"github.com/Knetic/govaluate"
)

func main() {
	expression, err := govaluate.NewEvaluableExpression("'2014-01-02' > '2014-01-01 23:59:59'")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	result, err := expression.Evaluate(nil);
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	// result is now set to "false", the bool value.
	fmt.Println(result)
	return
}


支持的比对方式:

  • 修饰符:+ - / * & | ^ ** % >> <<
  • 比较运算符:> >= < <= == != =~ !~
  • 逻辑运算符:|| &&
  • 数值常量:64位浮点数(例如:12345.678)
  • 字符串常量:单引号(例如:'foobar')
  • 日期常量:单引号,使用任何 RFC3339、ISO8601、ruby 日期或 unix 日期的组合;日期解析会自动尝试任何字符串常量
  • 布尔常量:true false
  • 括号:用于控制运算顺序 ( )
  • 数组:用逗号分隔的任何内容,括在括号内 (例如:(1, 2, 'foo'))
  • 前缀:! - ~
  • 三元条件运算符:? :
  • 空合并运算符:??

一些特殊的使用方式:

(1)自定义函数:主要是用来一些自定义的计算


package main
 
import (
    "fmt"
    "github.com/Knetic/govaluate"
)
 
func main() {  functions := map[string]govaluate.ExpressionFunction{
        "strlen": func(args ...interface{}) (interface{}, error) {
            length := len(args[0].(string))
            return length, nil
        },
    }
 
    exprString := "strlen('teststring')"
    expr, _ := govaluate.NewEvaluableExpressionWithFunctions(exprString, functions)
    result, _ := expr.Evaluate(nil)
    // 这里会输出8
    fmt.Println(result)
 
}

(2)访问器:主要是更自由的对结构体进行操作等等

package main
 
import (
    "fmt"
    "github.com/Knetic/govaluate"
)
 
type User struct {
    FirstName string
    LastName  string
    Age       int
}
 
func (u User) Fullname() string {
    return u.FirstName + " " + u.LastName
}
 
func main() {
    u := User{FirstName: "huang", LastName: "zijian", Age: 18}
    parameters := make(map[string]interface{})
    parameters["u"] = u
 
    expr, _ := govaluate.NewEvaluableExpression("u.Fullname()")
    result, _ := expr.Evaluate(parameters)
    fmt.Println("user", result)
 
    expr, _ = govaluate.NewEvaluableExpression("u.Age > 18")
    result, _ = expr.Evaluate(parameters)
    fmt.Println("age > 18?", result)
}


总结:

govaluate 是一个很好的规则判断工具,你要不也来试试看?