[转]基于DynamicExpresso的自定义表达式计算

基于DynamicExpresso的自定义表达式计算

项目来由

公司某部门要根据原始数据,进行复杂运算。得到一些指标。因为用excel计算需要使用人的excel技能非常高,而且很繁琐。所以需要It帮忙做一个这样的算法库,可以直接调用。但后来因为公司采购了MES,Mes中包含该功能,所以项目终止了。但花了1周研究的东西,还是不希望就这样流产了。特放到博客园上,希望各位大牛能有闲的时候,能帮忙一起优化。如果能把 DynamicExpresso 这玩意儿也自己写了,还是相当有意义的。代码我会放到 gitee上  。大家有需要的尽管拿去用。目前已经实现的内容是,单数据源已经实现了自定义计算的功能。下面会做详细介绍。

先看看 DynamicExpresso 能干什么

DynamicExpresso GitHub项目地址
文档里面写的很清楚了。我只阐述我用过的内容,他的运行规则是这样的:
image

以下是具体的应用实例

  1. 表达式运算:
    var interpreter = new Interpreter();    var result = interpreter.Eval("8 / 2 + 2");
  1. 自定义方法:
    Func<double, double, double> pow = (x, y) => Math.Pow(x, y);    var target = new Interpreter().SetFunction("pow", pow);     Assert.AreEqual(9.0, target.Eval("pow(3, 2)"));
  1. 搜索数据,并运算
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<Customer> {		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<Customer, bool> dynamicWhere = interpreter.ParseAsDelegate<Func<Customer, bool>>(whereExpression, "customer");	Assert.AreEqual(1, customers.Where(dynamicWhere).Count());}

以上实例很具备代表性了。

再看看我们需要干什么

以实际需求为例
最终的表达式是这样的:

    result = 1 - (a - b) / (a1 - b1)    a,a1 需要通过条件查询得到。    b,b1 分别为两个点的斜率。

a的查询条件可能是这样的,下面定义为在项目中定义为Conditianal ,在方法中定义为 filter

        Score == like(u_arg_2,"Score") && Power == u_arg_0        # u_arg_2 为用户输入的参数。          # u_arg_0 为用户输入的参数。        # like 是一个内置函数,需要自己实现。比如,用户输入 12.4 那么需要找到在数据源中,Score字段跟 12.4最接近的数。        # 实则上述表达式 只是一个过滤条件,实际上需要根据过滤条件得到指定的变量值。

ba 类似,这里就不多做介绍了。

表达式计算的结构设计和实现思路

  1. 用户输入参数。

  2. 根据表达式,获取需要的数据,再赋值给相应的变量。

  3. 重复步骤2,直到表达式需要的所有参数都被赋值。

  4. 计算表达式。

定义了如下类结构

image

接口名称作用
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<IArg> inputs = new List<IArg>();    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<IConditional> querys = new List<IConditional>();    Conditional conditional = new Conditional()    {        Assign = new List<string>() { "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<Data> process = new AlgorithmProcess<Data>(datas, algorithm);    Console.WriteLine($"算法结果:{process.Execute()}");     watch.Stop();      Console.WriteLine($"整体耗时:{watch.Elapsed.TotalSeconds}");

datas是List数据。

image

项目地址:自定义表达式计算

如果本篇博客对你有帮助,请帮忙推荐一下,谢谢!


---------------------
作者:莫问哥哥
来源:CNBLOGS
原文:https://www.cnblogs.com/LearningC/p/16007884.html
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件
 
posted @ 2022-08-19 16:43  .Net凯  阅读(90)  评论(0编辑  收藏  举报