ToB企服应用市场:ToB评测及商务社交产业平台

标题: 字符串表达式计算(a+b/(a-b))的思路与实践 [打印本页]

作者: 南七星之家    时间: 2023-11-27 00:05
标题: 字符串表达式计算(a+b/(a-b))的思路与实践
前言

为满足业务需要,需要为项目中自定义模板添加一个计算字段的组件,通过设置字符串表达式,使用时在改变表达式其中一个字段的数据时,自动计算另外一个字段的值。
本篇为上篇,介绍原理,简单实现一个工具,输入字符串表达式,解析其中的参数,输入参数计算结果。
下篇将基于此封装实现对Mongo查询语法的封装,通过addFields的方式转换表达式,后续等封装成NuGet包再分享
实现如下所示
  1. 输入 1+1  输出 2
  2. 输入 a+1 参数a:1 输出 2
  3. 输入 (a+1)*b 输入a:1,b:1 输出 2
  4. 输入 (a+1-(2+a)*3/3)/a+3 输入a:1 输出 2
复制代码

实现思路

想要实现上面这个功能,需要先了解诸如 (a+1-(2+a)*3/3)/a+3 这个是什么?
维基百科:中缀表示法(或中缀记法)是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4)。与前缀表达式(例:+ 3 4 )或后缀表达式(例:3 4 + )相比,中缀表达式不容易被电脑解析逻辑优先顺序,但仍被许多程序语言使用,因为它符合大多数自然语言的写法。
前缀表示法 (+ 3 4 )也叫 波兰表示法
后缀表示法 (3 4 + )也叫 逆波兰表示法
在维基百科的说明中,也给出了和其相关的另外两种表示法,以及用于把中缀表达式转换到后缀表达式或树的算法:调度场算法 ,如下图所示

实现代码

找了很多的开源项目,最终基于 qinfengzhu/Evaluator ,实现了上述功能。
调用代码
  1. using Evaluator;
  2. using System.Text.RegularExpressions;
  3. Console.WriteLine("字符串表达式计算工具");
  4. EvalTest();
  5. void EvalTest()
  6. {
  7.     Console.WriteLine("----------------------------------------------------");
  8.     var parse = new EvalParser();
  9.     Console.Write("请输入表达式:");//a+b*3/5+a
  10.     var evalStr = Console.ReadLine();
  11.     if (string.IsNullOrEmpty(evalStr))
  12.     {
  13.         Console.WriteLine("Game Over");
  14.         return;
  15.     }
  16.     //解析其中的变量并让用户输入
  17.     var matchs = Regex.Matches(evalStr, @"\b[\w$]+\b");
  18.     var paramsDic = new Dictionary<string, object>();
  19.     //预定义参数
  20.     paramsDic.Add("now_year", DateTime.Now.Year);
  21.     paramsDic.Add("now_month", DateTime.Now.Month);
  22.     paramsDic.Add("now_day", DateTime.Now.Day);
  23.     foreach (Match match in matchs)
  24.     {
  25.         if (decimal.TryParse(match.Value, out decimal kp))
  26.             continue;
  27.         if (!paramsDic.ContainsKey(match.Value))
  28.         {
  29.             Console.Write($"请输入数字变量【{match.Value}】:");
  30.             var paramValue = Console.ReadLine();
  31.             decimal dvalue;
  32.             while (!decimal.TryParse(paramValue, out dvalue))
  33.             {
  34.                 Console.WriteLine($"输入有误,请输入数字变量【{match.Value}】:");
  35.                 paramValue = Console.ReadLine();
  36.             }
  37.             paramsDic.Add(match.Value, dvalue);
  38.         }
  39.     }
  40.     var result = parse.EvalNumber(evalStr, paramsDic);
  41.     Console.WriteLine($"结果:{result}");
  42.     EvalTest();
  43. }
复制代码
EvalParser 类的实现

通过上面调用代码可以看到,核心的计算类是 EvalParser ,调用其 EvalNumber 进行计算
EvalNumber 实现

ParserInfixExpression 表达式转换核心代码
</ul>EvalDate 实现指定日期类型输出

因项目需要,需要将当前日期,当前时间加入默认变量,并支持加入计算公式中,计算的结果也可以选择是日期或者数值。
需要实现这个功能,需要先定义好,时间如何计算,我们将日期时间转换成时间戳来进行转换后参与计算,计算完成后再转换成日期即可。
所以只需要在上面的数值计算包裹一层就可以得到日期的计算结果
代码中的数据定义

其他数据定义 OperatorChar EvalItem EItemType CharExtension 可以查看完整demo
相关说明

后语

期间找了很多开源项目参考,需求的独特性,最终是实现了功能
整个计算字段的实现花了3周时间,终于是顺利上线。
沉迷学习,无法自拔。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4