# 自定义表达式计算 **Repository Path**: xskj001/custom-expression-evaluation ## Basic Information - **Project Name**: 自定义表达式计算 - **Description**: 自定义表达式计算,支持在计算中使用自定义函数如:Pow Like Min Max 等。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: https://www.cnblogs.com/LearningC - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-03-15 - **Last Updated**: 2023-10-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 基于DynamicExpresso的自定义表达式计算 ## 项目来由 公司某部门要根据原始数据,进行复杂运算。得到一些指标。因为用excel计算需要使用人的excel技能非常高,而且很繁琐。所以需要It帮忙做一个这样的算法库,可以直接调用。但后来因为公司采购了MES,Mes中包含该功能,所以项目终止了。但花了1周研究的东西,还是不希望就这样流产了。特放到博客园上,希望各位大牛能有闲的时候,能帮忙一起优化。如果能把 DynamicExpresso 这玩意儿也自己写了,还是相当有意义的。代码我会放到 gitee上 。大家有需要的尽管拿去用。目前已经实现的内容是,单数据源已经实现了自定义计算的功能。下面会做详细介绍。 ## 先看看 DynamicExpresso 能干什么 [DynamicExpresso GitHub项目地址](https://github.com/dynamicexpresso/DynamicExpresso) 文档里面写的很清楚了。我只阐述我用过的内容,他的运行规则是这样的: ![输入图片说明](1.png) ### 以下是具体的应用实例 1. 表达式运算: ``` var interpreter = new Interpreter(); var result = interpreter.Eval("8 / 2 + 2"); ``` 2. 自定义方法: ``` Func pow = (x, y) => Math.Pow(x, y); var target = new Interpreter().SetFunction("pow", pow); Assert.AreEqual(9.0, target.Eval("pow(3, 2)")); ``` 3. 搜索数据,并运算 ``` class Customer { public string Name { get; set; } public int Age { get; set; } public char Gender { get; set; } } [Test] public void Linq_Where() { var customers = new List { new Customer() { Name = "David", Age = 31, Gender = 'M' }, new Customer() { Name = "Mary", Age = 29, Gender = 'F' }, new Customer() { Name = "Jack", Age = 2, Gender = 'M' }, new Customer() { Name = "Marta", Age = 1, Gender = 'F' }, new Customer() { Name = "Moses", Age = 120, Gender = 'M' }, }; string whereExpression = "customer.Age > 18 && customer.Gender == 'F'"; var interpreter = new Interpreter(); Func dynamicWhere = interpreter.ParseAsDelegate>(whereExpression, "customer"); Assert.AreEqual(1, customers.Where(dynamicWhere).Count()); } ``` 以上实例很具备代表性了。 ## 表达式计算的结构设计和实现思路 1. 用户输入参数。 2. 根据表达式,获取需要的数据,再赋值给相应的变量。 3. 重复步骤2,直到表达式需要的所有参数都被赋值。 4. 计算表达式。 ### 定义了如下类结构 ![输入图片说明](2.png) | 接口名称 | 作用 | |:-----|:-----| |IArg|所有参数的基类接口| |IArgConverter|参数的类型转换行为| |IConditional|查询条件接口| |IAlgorithm|算法接口| |IAlgorithmBehaviours|算法行为接口| |IArg|所有参数的基类接口| 实则 IAlgorithmBehaviours 抽象的并不规范。有兴趣的朋友希望能一起改进这个项目。可以联系我邮箱 1102043058@qq.com. 整个算法结构我用json格式定义。前端提交到接口,接口解析数据,并反序列化给 算法处理程序。计算结果即可。 算法结构: ``` { "TableName": "tablename", "DataFilter": "HoleID == u_arg_13 && ( Temperature == u_arg_0 || Temperature == u_arg_1 ) && TestType = u_arg_14", "UserParameters": [ { "name": "u_arg_14", "display": "批次", "datatype":"double", "value": 1 }, { "name": "u_arg_13", "display": "孔位", "datatype":"double", "value": 1 }, { "name": "u_arg_0", "display": "温度1", "datatype":"double", "value": 25 }, { "name": "u_arg_1", "display": "温度2", "datatype":"double", "value": 85 }, { "name": "u_arg_2", "display": "功率", "datatype":"double", "value": 80 }, { "name": "u_arg_9", "display": "第一个点", "datatype":"point", "value": "(1.3454533,1.0822343)" }, { "name": "u_arg_10", "display": "第二个点", "datatype":"point", "value": "(1.3454533,1.0822343)" }, { "name": "u_arg_11", "display": "第三个点", "datatype":"point", "value": "(1.3454533,1.0822343)" }, { "name": "u_arg_12", "display": "第四个点", "datatype":"point", "value": "(1.3454533,1.0822343)" } ], "Conditions": [ { "assigns": [ "u_arg_3 = current", "u_arg_4 = MDPCurrent" ], "condition": "Power == like(u_arg_2,\"Power\",\" Temperature == u_arg_0\") && Temperature == u_arg_0" }, { "assigns": [ "u_arg_5 = current" ], "condition": "MPDCurrent == u_arg_4 && Temperature == u_arg_2" } ], "Expression": "1 - (u_arg_3 - ith1(u_arg_9,u_arg_10)) / (u_arg_5 - ith1(u_arg_11,u_arg_12))" } ``` *ith1,like 为自定义方法。* 实际调用情况是这样的。 调用代码 ``` Stopwatch watch = new Stopwatch(); watch.Start(); List inputs = new List(); inputs.Add(new ArgUser() { Name = "arg0", Display = "温度1", Value = "25", DataType = "double" }); inputs.Add(new ArgUser() { Name = "arg1", Display = "温度2", Value = "85", DataType = "double" }); inputs.Add(new ArgUser() { Name = "arg2", Display = "功率", Value = "17.3", DataType = "double" }); List querys = new List(); Conditional conditional = new Conditional() { Assign = new List() { "arg3 = Current", "arg4 = MPDCurrent" }, Condition = "d.Power == like(arg2,\"d.Power\",\"d.Temperature = arg0\") && d.Temperature == arg0" }; querys.Add(conditional); Algorithm algorithm = new Algorithm() { UserParameters = inputs, Conditions = null, Expression = "arg1 + arg2 + arg0", Name = "测试算法" }; AlgorithmProcess process = new AlgorithmProcess(datas, algorithm); Console.WriteLine($"算法结果:{process.Execute()}"); watch.Stop(); Console.WriteLine($"整体耗时:{watch.Elapsed.TotalSeconds}"); ``` **datas是List数据。** ![输入图片说明](3.png)