[原创]从中文智能提示到表达式识别与计算
2009-07-29 18:23 水随风 阅读(542) 评论(0) 编辑 收藏 举报前言
在此处我以表达式“3 + ( 4 * ( 1 + ( 1 + 2 ) ) ) * ( 1 + 2 ) ( 3 + 2 )”,虽然谈到是中文提示到这篇的,但是为了画图和解释方便,暂且用数字直接代替。
事实上,中文表达式到具体的值只有一步而已,那就是识别=>反射=>读属性。
通过上述表达式可以看出,一个表达式中有两个关键元素,即:操作符(operator)和操作数(operand).
对于操作符而言,常见是分为一元和二元的,对于二元操作符自然是有2个操作数的,不同操作数之间类型的不同也导致了结果的输出的不同。
还要考虑操作符的优先级顺序,因为在表达式中,优先级决定了谁优先计算,这步的实现直接导致了表达式表达意义的对与错。
所以在对一段表达式做计算前,必须要完成三个步骤
1.合法性检测=>a.是否包含未完全匹配的小括号.异常反馈及处理。
b.是否出现无意义的参量或者表达式
c.非法操作符或非法操作数
2.表达式分析=> a.是否包含有中文表达式(如果有则要进行反射取值)
b.操作符的可操作类型,操作元个数
3.操作数的类型,类型的溢出=>字符串,数值等类型的比较。
STEP1:计算单元
既然本文是写表达式识别,那么就要先从表达式存储结构开始,由下图可以看出事例表达式即将被分离成若干计算单元:
由上图可以看出,最终此表达式被简化了,那么这些操作数和操作符,都以树的结构被构建出来,因此创建了一个FCNNodes的类来存储如此的计算小单元。
1: public class FCNNodes
2: {
3: #region 构造函数
4: public FCNNodes() { }
5:
6: public FCNNodes(FCNOprand oprand, FCNNodes oprn1, FCNNodes oprn2, int ndeep) : this(){……}
7:
8: public FCNNodes(FCNOprand oprand, FCNNodes oprn1, FCNNodes oprn2) : this(oprand, oprn1, oprn2, 0) {……}
9:
10: public FCNNodes(FCNOprand oprand, object value) : this() {…… }
11: #endregion
12:
13: #region 属性定义
14: /// <summary>
15: /// 运算符
16: /// </summary>
17: public FCNOprand Oprand{get; set;}
18: /// <summary>
19: /// 子计算节点1
20: /// </summary>
21: public FCNNodes Oprn1 {set;get;}
22: /// <summary>
23: /// 子计算节点2
24: /// </summary>
25: public FCNNodes Oprn2 {get;set;}
26: private object _value= null;
27: /// <summary>
28: /// 本计算结点内的计算结果
29: /// </summary>
30: public object Value
31: {
32: set { _value = value; }
33: get
34: {
35: if (_value != null)
36: {
37: return _value;
38: }
39: else
40: {
41:
42: /**
43: * 根据节点Oprand去找出对应的运算方法。
44: */
45: //计算式子
46: if ((this.Oprand & FCNOprand.CALCULATER) != 0)
47: {
48: FCNCalculater fcnc = new FCNCalculater();
49: _value = fcnc.Calculater(this);
50: }
51: //判断式子
52: if ((this.Oprand & FCNOprand.JUDAGER) != 0)
53: {
54: FCNJudager fcnj = new FCNJudager();
55: _value = fcnj.Judager(this);
56: }
57: return _value;
58: }
59: }
60: }
61: /// <summary>
62: /// 计算式深度
63: /// </summary>
64: public int nDeep{set;get;}
65: #endregion
66: }
STEP2:读取计算表达式
全匹配,从最内部开始,根据正则表达式,取一个表达式中最内部的表达式最后得到最简表达式。
最简表达式的计算符号识别,有很多的算法,此处只作为引子,会专门用一节去说明这个。
STEP3:如何计算
一个节点计算单元其目的就是为了算出Value值,所以外部只要访问这个Value自然就可以计算出来。在Value的内部,使用Oprand取找出对应的方法,Oprand是一个操作符枚举[Flags]。
到现在为止,分为两个计算类别:数值计算及逻辑计算。
数值计算的有:+-*/%
逻辑计算的有:==,>,<,!=,&&,||,>=,<=
这里只例举了一些常用的,当然你也可以自定义一些你认为比较有性格的计算符号-_-,它们的计算方式都被定义在FCNCalculater 和FCNJudager。(其实当我写出来后,我发现这种组织方法并不是很好的,之后会有一些说明。)
现在用FCNJudager为例子:
1: public class FCNJudager
2: {
3: public FCNJudager()
4: {
5:
6: }
7: /// <summary>
8: /// 判断访问器
9: /// </summary>
10: /// <param name="fcnn"></param>
11: /// <returns></returns>
12: public virtual bool Judager(FCNNodes fcnn)
13: {
14: /**
15: * 1.找出两者共有类型,如:oprn1是int型,oprn2是string型,那么返回string。
16: */
17: bool result = false;
18:
19: switch (fcnn.Oprand)
20: {
21: case FCNOprand.Equals:
22: {
23: #region Equals Logic
24: result = jageEqual(fcnn);
25: #endregion
26: }
27: break;
28: case FCNOprand.Bigthan:
29: {
30: #region Bigthan Logic
31: result = BigSmallThan(fcnn.Oprn1.Value, fcnn.Oprn2.Value);
32: #endregion
33: }
34: break;
35: case FCNOprand.Smallthan:
36: {
37: #region Smallthan Logic
38: result = !BigSmallThan(fcnn.Oprn2.Value, fcnn.Oprn1.Value);
39: #endregion
40: }
41: break;
42: case FCNOprand.NoEquals:
43: {
44: #region NoEquals Logic
45: result = !jageEqual(fcnn);
46: #endregion
47: }
48: break;
49: case FCNOprand.AND:
50: {
51: #region AND Logic
52: //True && False
53: if (FCNTypeChecker.Checker<bool>(fcnn.Oprn1.Value)
54: && FCNTypeChecker.Checker<bool>(fcnn.Oprn2.Value))
55: {
56: result = bool.Parse(fcnn.Oprn1.Value.ToString()) && bool.Parse(fcnn.Oprn2.Value.ToString());
57: }
58: #endregion
59: }
60: break;
61: case FCNOprand.OR:
62: {
63: #region OR Logic
64: //True || False
65: if (FCNTypeChecker.Checker<bool>(fcnn.Oprn1.Value)
66: && FCNTypeChecker.Checker<bool>(fcnn.Oprn2.Value))
67: {
68: result = bool.Parse(fcnn.Oprn1.Value.ToString()) || bool.Parse(fcnn.Oprn2.Value.ToString());
69: }
70: #endregion
71: }
72: break;
73: case FCNOprand.BigthanEquals:
74: {
75: #region BigthanEquals Logic
76: if (jageEqual(fcnn) || BigSmallThan(fcnn.Oprn1.Value, fcnn.Oprn2.Value))
77: {
78: result = true;
79: }
80: #endregion
81: }
82: break;
83: case FCNOprand.SmallthanEquals:
84: {
85: #region SmallthanEquals Logic
86: if (jageEqual(fcnn) || !BigSmallThan(fcnn.Oprn2.Value, fcnn.Oprn1.Value))
87: {
88: result = true;
89: }
90: #endregion
91: }
92: break;
93: }
94: return result;
95: }
96: /// <summary>
97: /// 大小比较
98: /// </summary>
99: /// <param name="value1"></param>
100: /// <param name="value2"></param>
101: /// <returns></returns>
102: private static bool BigSmallThan(object value1, object value2)
103: {
104: bool bresult = false;
105: //int -> datetime -> string
106: if (FCNTypeChecker.Checker<int>(value1.ToString())
107: && FCNTypeChecker.Checker<int>(value2.ToString()))
108: {
109: bresult = int.Parse(value1.ToString()) > int.Parse(value2.ToString());
110: }
111: if (FCNTypeChecker.Checker<long>(value1.ToString())
112: && FCNTypeChecker.Checker<long>(value2.ToString()))
113: {
114: bresult = long.Parse(value1.ToString()) > long.Parse(value2.ToString());
115: }
116: if (FCNTypeChecker.Checker<DateTime>(value1.ToString())
117: && FCNTypeChecker.Checker<DateTime>(value2.ToString()))
118: {
119: bresult = (DateTime.Parse(value1.ToString()).CompareTo(DateTime.Parse(value2.ToString())) == 1);
120: }
121:
122: if (value1.ToString().CompareTo(value2.ToString()) == 1)
123: {
124: bresult = true;
125: }
126: return bresult;
127: }
128: /// <summary>
129: /// 相等判断
130: /// </summary>
131: /// <param name="fcnn"></param>
132: /// <returns></returns>
133: private bool jageEqual(FCNNodes fcnn)
134: {
135: bool bresult = false;
136: if (FCNTypeChecker.Checker<int>(fcnn.Oprn1.Value.ToString())
137: && FCNTypeChecker.Checker<int>(fcnn.Oprn2.Value.ToString()))
138: {
139: bresult = int.Parse(fcnn.Oprn1.Value.ToString()) == int.Parse(fcnn.Oprn2.Value.ToString());
140: }
141: if (FCNTypeChecker.Checker<long>(fcnn.Oprn1.Value.ToString())
142: && FCNTypeChecker.Checker<long>(fcnn.Oprn2.Value.ToString()))
143: {
144: bresult = long.Parse(fcnn.Oprn1.Value.ToString()) == long.Parse(fcnn.Oprn2.Value.ToString());
145: }
146: if (fcnn.Oprn1.Value.ToString().Equals(fcnn.Oprn2.Value.ToString()))
147: {
148: bresult = true;
149: }
150: return bresult;
151: }
152: }
在判断式中是以这样的步骤进行的:
1.首先根据节点单元中的操作符找到对应的操作逻辑。
2.在操作逻辑中,获取节点单元的两个子节点的Value。
3.判断返回的Value是何种类型。
外话
对于第三点特别说明一下,此处给的事例是很简单的,主要是先判断是否为int,然后在判断是否为long,最后才是string.(当然在这里考虑的方面依然是很幼稚的!)
如果是整型会有溢出,如果是一个是int,一个是string,那么就以string返回,等等这些都需要做Check,而如果在这个地方把所有的情况全部例举全,那是何等的壮观啊!!
此时,我想这些操作符的特性都限定在一个类里面,然后用一个操作符的集合去管理这些类。