基於DynamicExpresso的自定義表示式計算
阿新 • • 發佈:2022-03-15
基於DynamicExpresso的自定義表示式計算
專案來由
公司某部門要根據原始資料,進行復雜運算。得到一些指標。因為用excel計算需要使用人的excel技能非常高,而且很繁瑣。所以需要It幫忙做一個這樣的演算法庫,可以直接呼叫。但後來因為公司採購了MES,Mes中包含該功能,所以專案終止了。但花了1周研究的東西,還是不希望就這樣流產了。特放到部落格園上,希望各位大牛能有閒的時候,能幫忙一起優化。如果能把 DynamicExpresso 這玩意兒也自己寫了,還是相當有意義的。程式碼我會放到 gitee上 。大家有需要的儘管拿去用。目前已經實現的內容是,單資料來源已經實現了自定義計算的功能。下面會做詳細介紹。
先看看 DynamicExpresso 能幹什麼
DynamicExpresso GitHub專案地址
文件裡面寫的很清楚了。我只闡述我用過的內容,他的執行規則是這樣的:
以下是具體的應用例項
- 表示式運算:
var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");
- 自定義方法:
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)"));
- 搜尋資料,並運算
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最接近的數。
# 實則上述表示式 只是一個過濾條件,實際上需要根據過濾條件得到指定的變數值。
b 和 a 類似,這裡就不多做介紹了。
表示式計算的結構設計和實現思路
-
使用者輸入引數。
-
根據表示式,獲取需要的資料,再賦值給相應的變數。
-
重複步驟2,直到表示式需要的所有引數都被賦值。
-
計算表示式。
定義瞭如下類結構
介面名稱 | 作用 |
---|---|
IArg | 所有引數的基類介面 |
IArgConverter | 引數的型別轉換行為 |
IConditional | 查詢條件介面 |
IAlgorithm | 演算法介面 |
IAlgorithmBehaviours | 演算法行為介面 |
IArg | 所有引數的基類介面 |
實則 IAlgorithmBehaviours 抽象的並不規範。有興趣的朋友希望能一起改進這個專案。可以聯絡我郵箱 [email protected].
整個演算法結構我用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": "批次",
"value": 1
},
{
"name": "u_arg_13",
"display": "孔位",
"value": 1
},
{
"name": "u_arg_0",
"display": "溫度1",
"value": 25
},
{
"name": "u_arg_1",
"display": "溫度2",
"value": 85
},
{
"name": "u_arg_2",
"display": "功率",
"value": 80
},
{
"name": "u_arg_9",
"display": "第一個點",
"value": "(1.3454533,1.0822343)"
},
{
"name": "u_arg_10",
"display": "第二個點",
"value": "(1.3454533,1.0822343)"
},
{
"name": "u_arg_11",
"display": "第三個點",
"value": "(1.3454533,1.0822343)"
},
{
"name": "u_arg_12",
"display": "第四個點",
"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資料。
專案地址:自定義表示式計算
如果本篇部落格對你有幫助,請幫忙推薦一下,謝謝!