围棋中的参数化方程求解器

2020-09-25 22:45:07

有时,您无法提前知道表达式将是什么样子,或者您希望这些表达式是可配置的。可能您有一组数据在应用程序中运行,并且您希望允许用户在将其提交到数据库之前指定在其上运行的一些验证。或者,您可能已经编写了一个监视框架,该框架能够收集一系列指标,然后评估几个表达式以查看是否应该对任何指标发出警报,但是每个监视器的警报条件是不同的。

很多人最终写出了适合他们需要的半熟风格的评估语言,但并不完整。或者他们最终将表达式烘焙到实际的可执行文件中,即使他们知道它可能会发生变化。这些策略可能会奏效,但它们需要时间来实现,需要用户学习的时间,并且会随着需求的变化而引发技术债务。这个库旨在涵盖所有普通的类似C语言的表达式,这样您就不必重新发明计算机上最古老的轮子之一。

表达式,错误:=govaluate。NewEvaluableExpression(";10>;0";);Result,Err:=表达式。Evaluate(Nil);//结果现在设置为布尔值";true";。

表达式,错误:=govaluate。NewEvaluableExpression(";foo>;0";);参数:=make(map[String]interface{},8)参数[";foo";]=-1;result,err:=表达式。Evaluate(Parameters);//结果现在设置为布尔值";false";。

这很酷,但几乎可以肯定我们可以用代码完成所有这些工作。那么涉及到一些数学的复杂用例呢?

表达式,错误:=govaluate。NewEvaluableExpression(";(Requests_Made*Requests_Successed/100)>;=90";);参数:=make(map[String]interface{},8)Parameters[";Requests_Made";]=100;Parameters[";Requests_Successed";]=80;Result,Err:=Expression。Evaluate(Parameters);//结果现在设置为布尔值";false";。

或者,您可能想检查活动检查(";Smoketest&34;)页面的状态,它将是一个字符串?

表达式,错误:=govaluate。NewEvaluableExpression(";http_response_body==';service is ok';";);参数:=make(map[string]interface{},8)参数[";http_response_body";]=";service is ok";result,err:=expression。Evaluate(Parameters);//结果现在设置为布尔值";true";。

这些示例都返回了布尔值,但是返回数值的可能性也是一样的。

表达式,错误:=govaluate。NewEvaluableExpression(";(mem_use/total_mem)*100";);参数:=make(map[string]interface{},8)参数[";total_mem";]=1024;参数[";mem_use";]=512;result,err:=表达式。Evaluate(Parameters);//结果现在设置为";50.0";,即float64值。

您还可以进行日期解析,尽管格式有一定的限制。坚持使用RF3339、ISO8061、Unix日期或红宝石日期格式。如果您在获取要解析的日期字符串时遇到问题,请检查实际使用的格式列表:parsing.go:248。

表达式,错误:=govaluate。NewEvaluableExpression(";';2014-01-02';>;';2014-01-01 23:59:59';";);result,err:=表达式。EVALUATE(Nil);//结果现在设置为TRUE。

表达式只解析一次,并且可以多次重复使用。解析是该过程的计算密集型阶段,因此,如果您打算将相同的表达式与不同的参数一起使用,只需解析一次。就像这样;

表达式,错误:=govaluate。NewEvaluableExpression(";Response_Time<;=100";);参数:=make(map[String]interface{},8)for{Parameters[";Response_Time";]=pingSomething();Result,Err:=Expression。评估(参数)}。

运算符的正常C标准顺序是受尊重的。编写表达式时,请确保正确排序运算符,或者使用括号来阐明应该首先运行表达式的哪些部分。

有时,您的参数包含空格、斜杠、加号、与号或一些其他字符,该库将其解释为特殊字符。例如,以下表达式的作用可能与预期不同:

如上所述,库将其解析为";[响应]减去[时间]小于100";。实际上,响应时间应该是一个恰好带有破折号的变量。

有两种方法可以解决此问题。首先,您可以转义整个参数名:

可以在表达式中的任何位置使用反斜杠对下一个字符进行转义。方括号参数名称可以随时使用,而不是纯参数名称。

您可能会遇到希望在表达式执行期间对参数调用函数的情况。也许您想聚合某些数据集,但在重新编写表达式本身之前,您不知道要使用的确切聚合。或者,您可能要执行一个没有运算符的数学运算,比如log、tan或sqrt。对于这样的情况,您可以将函数映射到NewEvaluableExpressionWithFunctions,这样就可以在执行期间使用它们。例如,

函数:=map[String]govaluate。ExpressionFunction{";strlen";:func(args...。Interface{})(interface{},error){Length:=Len(args[0].(String))Return(Float64)(Length),nil},}expString:=";strlen(';someReallyLongInputString';)<;=16";表达式,_:=govaluate。NewEvaluableExpressionWithFunctions(expString,Functions)result,_:=表达式。Evaluate(Nil)//结果现在是布尔值";FALSE";

函数可以接受任意数量的参数,正确地处理嵌套函数,并且参数可以是任何类型(即使这个库运算符都不支持该类型的求值)。例如,表达式中这些函数的用法都是有效的(假设给定了适当的函数和参数):

函数不能作为参数传递,它们必须在解析表达式时已知,并且在解析后不可更改。

如果参数中有结构,则可以按常规方式访问它们的字段和方法。例如,给定一个结构,其方法";Echo";在参数中显示为foo,则以下内容是有效的:

但是,目前不支持访问映射中的值。因此,以下操作将不起作用。

这可能很方便,但请注意,使用访问器涉及到很多反射。这使得表达式的速度比仅使用参数慢大约4倍(要在系统上进行更精确的测量,请参考基准测试)。如果确实合理,作者建议事先将您关心的值提取到参数映射中,或者定义一个实现Parameters接口的结构,该结构可以根据需要获取字段。如果有您想要使用的函数,最好将它们作为表达式函数传递(参见上一节)。这些方法不使用反射,并且设计得既快又干净。

日期常量(单引号,使用RFC3339、ISO8601、Ruby Date或Unix Date的任何排列;使用任何字符串常量自动尝试日期解析)。

某些运算符在与某些类型一起使用时没有意义。例如,得到一个字符串的模数意味着什么?如果您检查两个数字是否在逻辑上AND运算在一起,会发生什么情况?

对于这些问题的答案,每个人都有不同的直觉。为防止混淆,此库将拒绝对操作没有明确含义的类型进行操作。有关哪些运算符对哪些类型有效的详细信息,请参见MANUAL.md。

如果您担心这个库的开销,那么这个repo中内置了一系列基准测试。您可以使用go test-bench=..来运行它们。该库的构建着眼于快速,但尚未进行积极的概要分析和优化。不过,对于大多数应用程序来说,这是完全没有问题的。

为了对性能有一个非常粗略的了解,下面是在第三代Macbook Pro(Linux Mint17.1)上运行基准测试的结果输出。

BenchmarkSingleParse-12 1000000 1382 ns/opBenchmarkSimpleParse-12 200000 10771 ns/opBenchmarkFullParse-12 30000 49383 ns/opBenchmarkEvaluationSingle-12 50000000 30.1 ns/opBenchmarkEvaluationNumericWrital-12 10000000 10000000 119 ns/opBenchmarkEvaluationDocalModifier-12 10000000 236 ns/opBenchmarkEvaluationParameters-12 5000000 260 ns/opBenchmarkEvaluationParametersModifiers-12 3000000 547 ns/opBenchmarkRegexpression-12 50000000 49383 ns/opBenchmarkRegexpression-12 10000000 1392 ns/opBenchmarkRegexpression-12 1000000 1392 ns/opBenchmarkRegexpression-12 1000000 20357 ns/opBenchmarkRegexpression-12 100000 20357 ns/opBenchmarkRegexpression-12 1000000 1392 ns/opBenchmarkRegexpression-12 1000000 963 ns/opBenchmarkRegexpression-12 100000 20357 ns/opBenchmarkRegexpression-12 100000 1392 ns/opBenchmarkRegexpression

虽然这个库很少会导致API中断,但它可能(并且已经)发生了。如果您在生产中使用此功能,请提交您测试过的供应商,或使用gopkg.in重定向您的导入(例如,import";gopkg.in/knetic/govaluate.v2&34;)。主分支(虽然不常见)在某些时候可能包含API破坏性更改,除了创建新的主要版本之外,作者将无法将这些更改传达给下游。

版本会显式声明API中断发生的时间,如果没有指定API中断,升级应该是安全的。

这个项目是按照麻省理工学院的通用许可证授权的。只要您在您的作品中为作者提供适当的信用,您就可以自由地集成、派生和使用此代码,只要您觉得合适,而无需咨询作者。