代码改变世界

[原创]从中文智能提示到表达式识别与计算

2009-07-29 18:23  水随风  阅读(539)  评论(0编辑  收藏  举报

前言

在此处我以表达式“3 + ( 4 * ( 1 + ( 1 + 2 ) ) ) * ( 1 + 2 ) ( 3 + 2 )”,虽然谈到是中文提示到这篇的,但是为了画图和解释方便,暂且用数字直接代替。

事实上,中文表达式到具体的值只有一步而已,那就是识别=>反射=>读属性。

通过上述表达式可以看出,一个表达式中有两个关键元素,即:操作符(operator)和操作数(operand).

对于操作符而言,常见是分为一元和二元的,对于二元操作符自然是有2个操作数的,不同操作数之间类型的不同也导致了结果的输出的不同。

还要考虑操作符的优先级顺序,因为在表达式中,优先级决定了谁优先计算,这步的实现直接导致了表达式表达意义的对与错。

所以在对一段表达式做计算前,必须要完成三个步骤

1.合法性检测=>a.是否包含未完全匹配的小括号.异常反馈及处理。

                      b.是否出现无意义的参量或者表达式

                      c.非法操作符或非法操作数

2.表达式分析=> a.是否包含有中文表达式(如果有则要进行反射取值)

             b.操作符的可操作类型,操作元个数

3.操作数的类型,类型的溢出=>字符串,数值等类型的比较。

STEP1:计算单元

既然本文是写表达式识别,那么就要先从表达式存储结构开始,由下图可以看出事例表达式即将被分离成若干计算单元:

 Presentation1

 

由上图可以看出,最终此表达式被简化了,那么这些操作数和操作符,都以树的结构被构建出来,因此创建了一个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,而如果在这个地方把所有的情况全部例举全,那是何等的壮观啊!!

此时,我想这些操作符的特性都限定在一个类里面,然后用一个操作符的集合去管理这些类。

Presentation2