字符串公式解析器——使用“逆波兰式算法”及C#实现

      从5月中旬到7月中旬,我一直在做焊接工程中接头图的参数化和自动化生成软件。主要是将各种标准接头图分解为一个个的图元,并自定义图元参数和图参数,用户在使用时,只需修改相关参数值,即能生成其所需要的接头图,无须再人工用CAD软件手动绘画。如下图所示。

 

 

 

      其中采用了自定义公式来描述参数的变化将导致的图形变化。例如对于厚度的变化,定义的公式为:“dotAll.x=dotAll.x*t1/dot1.y;dotAll.y=dotAll.y*t1/dot1.y;”。这个公式表示对于图形中的所有点,当厚度变化时,点的x坐标变为 原x坐标值乘以新的厚度,再除以原来的序号为1的点的y坐标(即原来的厚度),同理对于y坐标的变化也一样。

      最终公式将解析成 x=2.0 * 3 / 1.5, 最终结果为 4.0。问题的焦点在于如何将“2.0 * 3 / 1.5”这种字符串的表达式,让计算机能理解,并计算出结果。这里我采用了“逆波兰式算法”来解决这个问题。

     关于“逆波兰式算法”的具体内容,请看这里 http://baike.baidu.com/view/2582.htm ,里面解释得很好。

     最终程序里面的算法描述如下 :

 

逆波兰式算法
       1、从左至右扫描一中缀表达式。
                2、若读取的是操作数,则判断该操作数的类型,并将该操作数存入操作数堆栈
                3、若读取的是运算符
                   (1) 该运算符为左括号"(",则直接存入运算符堆栈。
                   (2) 该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止,此时抛弃该左括号。
                   (3) 该运算符为非括号运算符:
                       (a) 若运算符堆栈栈顶的运算符为左括号,则直接存入运算符堆栈。
                       (b) 若比运算符堆栈栈顶的运算符优先级高,则直接存入运算符堆栈。
                       (c) 若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到操作数堆栈,直至运算符栈栈顶运算符低于(不包括等于)该运算符优先级,或为左括号,
                           并将当前运算符压入运算符堆栈。
                4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。

 

  下面我们来具体实现下这个算法,代码中注释较全,就不详细解说了。

  首先,我们来定义下操作数类。

ExpandedBlockStart.gif 定义操作数类型枚举
 1       ///   <summary>
 2       ///  操作数类型
 3       ///   </summary>
 4       public   enum  OperandType
 5      {
 6           ///   <summary>
 7           ///  函数
 8           ///   </summary>
 9          FUNC  =   1 ,
10 
11           ///   <summary>
12           ///  日期
13           ///   </summary>
14          DATE  =   2 ,
15 
16           ///   <summary>
17           ///  数字
18           ///   </summary>
19          NUMBER  =   3 ,
20 
21           ///   <summary>
22           ///  布尔
23           ///   </summary>
24          BOOLEAN  =   4 ,
25 
26           ///   <summary>
27           ///  字符串
28           ///   </summary>
29          STRING  =   5
30 
31      }
ExpandedBlockStart.gif 操作数类 Operand
 1    public   class  Operand
 2      {
 3           #region  Constructed Function
 4           public  Operand(OperandType type,  object  value)
 5          {
 6               this .Type  =  type;
 7               this .Value  =  value;
 8          }
 9 
10           public  Operand( string  opd,  object  value)
11          {
12               this .Type  =  ConvertOperand(opd);
13               this .Value  =  value;
14          }
15           #endregion
16 
17           #region  Variable & Property
18           ///   <summary>
19           ///  操作数类型
20           ///   </summary>
21           public  OperandType Type {  get set ; }
22 
23           ///   <summary>
24           ///  关键字
25           ///   </summary>
26           public   string  Key {  get set ; }
27 
28           ///   <summary>
29           ///  操作数值
30           ///   </summary>
31           public   object  Value {  get set ; }
32 
33           #endregion
34 
35           #region  Public Method
36           ///   <summary>
37           ///  转换操作数到指定的类型
38           ///   </summary>
39           ///   <param name="opd"> 操作数 </param>
40           ///   <returns> 返回对应的操作数类型 </returns>
41           public   static  OperandType ConvertOperand( string  opd)
42          {
43               if  (opd.IndexOf( " ( " >   - 1 )
44              {
45                   return  OperandType.FUNC;
46              }
47               else   if  (IsNumber(opd))
48              {
49                   return  OperandType.NUMBER;
50              }
51               else   if  (IsDate(opd))
52              {
53                   return  OperandType.DATE;
54              }
55               else
56              {
57                   return  OperandType.STRING;
58              }
59          }
60 
61           ///   <summary>
62           ///  判断对象是否为数字
63           ///   </summary>
64           ///   <param name="value"> 对象值 </param>
65           ///   <returns> 是返回真,否返回假 </returns>
66           public   static   bool  IsNumber( object  value)
67          {
68               double  val;
69               return   double .TryParse(value.ToString(),  out  val);
70          }
71 
72           ///   <summary>
73           ///  判断对象是否为日期
74           ///   </summary>
75           ///   <param name="value"> 对象值 </param>
76           ///   <returns> 是返回真,否返回假 </returns>
77           public   static   bool  IsDate( object  value)
78          {
79              DateTime dt;
80               return  DateTime.TryParse(value.ToString(),  out  dt);
81          }
82           #endregion
83      }

     然后,我们来定义下运算符类。

ExpandedBlockStart.gif 运算符类型枚举
  1  ///   <summary>
  2       ///  运算符类型(从上到下优先级依次递减),数值越大,优先级越低
  3       ///   </summary>
  4       public   enum  OperatorType
  5      {
  6           ///   <summary>
  7           ///  左括号:(,left bracket
  8           ///   </summary>
  9          LB  =   10 ,
 10 
 11           ///   <summary>
 12           ///  右括号),right bracket
 13           ///   </summary>
 14          RB  =   11 ,
 15 
 16           ///   <summary>
 17           ///  逻辑非,!,NOT
 18           ///   </summary>
 19          NOT  =   20 ,
 20 
 21           ///   <summary>
 22           ///  正号,+,positive sign
 23           ///   </summary>
 24          PS  =   21 ,
 25 
 26           ///   <summary>
 27           ///  负号,-,negative sign
 28           ///   </summary>
 29          NS  =   22 ,
 30 
 31           ///   <summary>
 32           ///  正切,tan
 33           ///   </summary>
 34          TAN  =   23 ,
 35           ///   <summary>
 36           ///  反正切,atan
 37           ///   </summary>
 38          ATAN  =   24 ,
 39 
 40 
 41           ///   <summary>
 42           ///  乘,*,multiplication
 43           ///   </summary>
 44          MUL  =   30 ,
 45 
 46           ///   <summary>
 47           ///  除,/,division
 48           ///   </summary>
 49          DIV  =   31 ,
 50 
 51           ///   <summary>
 52           ///  余,%,modulus
 53           ///   </summary>
 54          MOD  =   32 ,
 55 
 56           ///   <summary>
 57           ///  加,+,Addition
 58           ///   </summary>
 59          ADD  =   40 ,
 60 
 61           ///   <summary>
 62           ///  减,-,subtraction
 63           ///   </summary>
 64          SUB  =   41 ,
 65 
 66           ///   <summary>
 67           ///  小于,less than
 68           ///   </summary>
 69          LT  =   50 ,
 70 
 71           ///   <summary>
 72           ///  小于或等于,less than or equal to
 73           ///   </summary>
 74          LE  =   51 ,
 75 
 76           ///   <summary>
 77           ///  大于,>,greater than
 78           ///   </summary>
 79          GT  =   52 ,
 80 
 81           ///   <summary>
 82           ///  大于或等于,>=,greater than or equal to
 83           ///   </summary>
 84          GE  =   53 ,
 85 
 86           ///   <summary>
 87           ///  等于,=,equal to
 88           ///   </summary>
 89          ET  =   60 ,
 90 
 91           ///   <summary>
 92           ///  不等于,unequal to
 93           ///   </summary>
 94          UT  =   61 ,
 95 
 96           ///   <summary>
 97           ///  逻辑与,&,AND
 98           ///   </summary>
 99          AND  =   70 ,
100 
101           ///   <summary>
102           ///  逻辑或,|,OR
103           ///   </summary>
104          OR  =   71 ,
105 
106           ///   <summary>
107           ///  逗号,comma
108           ///   </summary>
109          CA  =   80 ,
110 
111           ///   <summary>
112           ///  结束符号 @
113           ///   </summary>
114          END  =   255 ,
115 
116           ///   <summary>
117           ///  错误符号
118           ///   </summary>
119          ERR  =   256
120 
121      }
ExpandedBlockStart.gif 运算符类
     public   class  Operator
    {
        
public  Operator(OperatorType type,  string  value)
        {
            
this .Type  =  type;
            
this .Value  =  value;
        }

        
///   <summary>
        
///  运算符类型
        
///   </summary>
         public  OperatorType Type {  get set ; }

        
///   <summary>
        
///  运算符值
        
///   </summary>
         public   string  Value {  get set ; }


        
///   <summary>
        
///  对于>或者&lt;运算符,判断实际是否为>=,&lt;&gt;、&lt;=,并调整当前运算符位置
        
///   </summary>
        
///   <param name="currentOpt"> 当前运算符 </param>
        
///   <param name="currentExp"> 当前表达式 </param>
        
///   <param name="currentOptPos"> 当前运算符位置 </param>
        
///   <param name="adjustOptPos"> 调整后运算符位置 </param>
        
///   <returns> 返回调整后的运算符 </returns>
         public   static   string  AdjustOperator( string  currentOpt,  string  currentExp,  ref   int  currentOptPos)
        {
            
switch  (currentOpt)
            {
                
case   " < " :
                    
if  (currentExp.Substring(currentOptPos,  2 ==   " <= " )
                    {
                        currentOptPos
++ ;
                        
return   " <= " ;
                    }
                    
if  (currentExp.Substring(currentOptPos,  2 ==   " <> " )
                    {
                        currentOptPos
++ ;
                        
return   " <> " ;
                    }
                    
return   " < " ;

                
case   " > " :
                    
if  (currentExp.Substring(currentOptPos,  2 ==   " >= " )
                    {
                        currentOptPos
++ ;
                        
return   " >= " ;
                    }
                    
return   " > " ;
                
case   " t " :
                    
if  (currentExp.Substring(currentOptPos,  3 ==   " tan " )
                    {
                        currentOptPos 
+=   2 ;
                        
return   " tan " ;
                    }
                    
return   " error " ;
                
case   " a " :
                    
if  (currentExp.Substring(currentOptPos,  4 ==   " atan " )
                    {
                        currentOptPos 
+=   3 ;
                        
return   " atan " ;
                    }
                    
return   " error " ;
                
default :
                    
return  currentOpt;
            }
        }

        
///   <summary>
        
///  转换运算符到指定的类型
        
///   </summary>
        
///   <param name="opt"> 运算符 </param>
        
///   <param name="isBinaryOperator"> 是否为二元运算符 </param>
        
///   <returns> 返回指定的运算符类型 </returns>
         public   static  OperatorType ConvertOperator( string  opt,  bool  isBinaryOperator)
        {
            
switch  (opt)
            {
                
case   " ! " return  OperatorType.NOT;
                
case   " + " return  isBinaryOperator  ?  OperatorType.ADD : OperatorType.PS;
                
case   " - " return  isBinaryOperator  ?  OperatorType.SUB : OperatorType.NS;
                
case   " * " return  isBinaryOperator  ?  OperatorType.MUL : OperatorType.ERR;
                
case   " / " return  isBinaryOperator  ?  OperatorType.DIV : OperatorType.ERR;
                
case   " % " return  isBinaryOperator  ?  OperatorType.MOD : OperatorType.ERR;
                
case   " < " return  isBinaryOperator  ?  OperatorType.LT : OperatorType.ERR;
                
case   " > " return  isBinaryOperator  ?  OperatorType.GT : OperatorType.ERR;
                
case   " <= " return  isBinaryOperator  ?  OperatorType.LE : OperatorType.ERR;
                
case   " >= " return  isBinaryOperator  ?  OperatorType.GE : OperatorType.ERR;
                
case   " <> " return  isBinaryOperator  ?  OperatorType.UT : OperatorType.ERR;
                
case   " = " return  isBinaryOperator  ?  OperatorType.ET : OperatorType.ERR;
                
case   " & " return  isBinaryOperator  ?  OperatorType.AND : OperatorType.ERR;
                
case   " | " return  isBinaryOperator  ?  OperatorType.OR : OperatorType.ERR;
                
case   " , " return  isBinaryOperator  ?  OperatorType.CA : OperatorType.ERR;
                
case   " @ " return  isBinaryOperator  ?  OperatorType.END : OperatorType.ERR;
                
default return  OperatorType.ERR;
            }
        }

        
///   <summary>
        
///  转换运算符到指定的类型
        
///   </summary>
        
///   <param name="opt"> 运算符 </param>
        
///   <returns> 返回指定的运算符类型 </returns>
         public   static  OperatorType ConvertOperator( string  opt)
        {
            
switch  (opt)
            {
                
case   " ! " return  OperatorType.NOT;
                
case   " + " return  OperatorType.ADD;
                
case   " - " return  OperatorType.SUB;
                
case   " * " return  OperatorType.MUL;
                
case   " / " return  OperatorType.DIV;
                
case   " % " return  OperatorType.MOD;
                
case   " < " return  OperatorType.LT;
                
case   " > " return  OperatorType.GT;
                
case   " <= " return  OperatorType.LE;
                
case   " >= " return  OperatorType.GE;
                
case   " <> " return  OperatorType.UT;
                
case   " = " return  OperatorType.ET;
                
case   " & " return  OperatorType.AND;
                
case   " | " return  OperatorType.OR;
                
case   " , " return  OperatorType.CA;
                
case   " @ " return  OperatorType.END;
                
case   " tan " return  OperatorType.TAN;
                
case   " atan " return  OperatorType.ATAN;
                
default return  OperatorType.ERR;
            }
        }

        
///   <summary>
        
///  运算符是否为二元运算符,该方法有问题,暂不使用
        
///   </summary>
        
///   <param name="tokens"> 语法单元堆栈 </param>
        
///   <param name="operators"> 运算符堆栈 </param>
        
///   <param name="currentOpd"> 当前操作数 </param>
        
///   <returns> 是返回真,否返回假 </returns>
         public   static   bool  IsBinaryOperator( ref  Stack < object >  tokens,  ref  Stack < Operator >  operators,  string  currentOpd)
        {
            
if  (currentOpd  !=   "" )
            {
                
return   true ;
            }
            
else
            {
                
object  token  =  tokens.Peek();
                
if  (token  is  Operand)
                {
                    
if  (operators.Peek().Type  !=  OperatorType.LB)
                    {
                        
return   true ;
                    }
                    
else
                    {
                        
return   false ;
                    }
                }
                
else
                {
                    
if  (((Operator)token).Type  ==  OperatorType.RB)
                    {
                        
return   true ;
                    }
                    
else
                    {
                        
return   false ;
                    }
                }
            }
        }

        
///   <summary>
        
///  运算符优先级比较
        
///   </summary>
        
///   <param name="optA"> 运算符类型A </param>
        
///   <param name="optB"> 运算符类型B </param>
        
///   <returns> A与B相比,-1,低;0,相等;1,高 </returns>
         public   static   int  ComparePriority(OperatorType optA, OperatorType optB)
        {
            
if  (optA  ==  optB)
            {
                
// A、B优先级相等
                 return   0 ;
            }

            
// 乘,除,余(*,/,%)
             if  ((optA  >=  OperatorType.MUL  &&  optA  <=  OperatorType.MOD)  &&
                (optB 
>=  OperatorType.MUL  &&  optB  <=  OperatorType.MOD))
            {
                
return   0 ;
            }
            
// 加,减(+,-)
             if  ((optA  >=  OperatorType.ADD  &&  optA  <=  OperatorType.SUB)  &&
                (optB 
>=  OperatorType.ADD  &&  optB  <=  OperatorType.SUB))
            {
                
return   0 ;
            }
            
// 小于,小于或等于,大于,大于或等于(<,<=,>,>=)
             if  ((optA  >=  OperatorType.LT  &&  optA  <=  OperatorType.GE)  &&
                (optB 
>=  OperatorType.LT  &&  optB  <=  OperatorType.GE))
            {
                
return   0 ;
            }
            
// 等于,不等于(=,<>)
             if  ((optA  >=  OperatorType.ET  &&  optA  <=  OperatorType.UT)  &&
                (optB 
>=  OperatorType.ET  &&  optB  <=  OperatorType.UT))
            {
                
return   0 ;
            }
            
// 三角函数
             if  ((optA >= OperatorType.TAN  &&  optA <= OperatorType.ATAN) &&
                    (optB 
>=  OperatorType.TAN  &&  optB  <=  OperatorType.ATAN))
            {
                
return   0 ;
            }

            
if  (optA  <  optB)
            {
                
// A优先级高于B
                 return   1 ;
            }

            
// A优先级低于B
             return   - 1 ;

        }
    }

    最后,我们来实现算法类RPN,此部分代码较多,因此分成几部分。

    1、RPN类中的变量和属性定义

ExpandedBlockStart.gif RPN类 变量和属性
 1  ///   <summary>
 2       ///  Reverse Polish Notation
 3       ///  逆波兰式
 4       ///   </summary>
 5       public   class  RPN
 6      {
 7          Stack < object >  m_tokens  =   new  Stack < object > ();             // 最终逆波兰式堆栈
 8           ///   <summary>
 9           ///  最终逆波兰式堆栈
10           ///   </summary>
11           public  Stack < object >  Tokens
12          {
13               get  {  return  m_tokens; }
14          }
15 
16           private   string  _RPNExpression;
17           ///   <summary>
18           ///  生成的逆波兰式字符串
19           ///   </summary>
20           public   string  RPNExpression
21          {
22               get
23              {
24                   if  (_RPNExpression  ==   null )
25                  {
26                       foreach  (var item  in  Tokens)
27                      {
28                           if  (item  is  Operand)
29                          {
30                              _RPNExpression  +=  ((Operand)item).Value  +   " , " ;
31                          }
32                           if  (item  is  Operator)
33                          {
34                              _RPNExpression  +=  ((Operator)item).Value  +   " , " ;
35                          }
36                      }
37                  }
38                   return  _RPNExpression;
39              }
40          }
41 
42          List < string >  m_Operators  =   new  List < string > ( new   string []{
43               " ( " , " tan " , " ) " , " atan " , " ! " , " * " , " / " , " % " , " + " , " - " , " < " , " > " , " = " , " & " , " | " , " , " , " @ " });     // 允许使用的运算符
44  }

    2、检查特殊符号是否匹配的方法

ExpandedBlockStart.gif 检查表达式中特殊符号(双引号、单引号、井号、左右括号)是否匹配
 1           private   bool  IsMatching( string  exp)
 2          {
 3               string  opt  =   "" ;     // 临时存储 " ' # (
 4 
 5               for  ( int  i  =   0 ; i  <  exp.Length; i ++ )
 6              {
 7                   string  chr  =  exp.Substring(i,  1 );    // 读取每个字符
 8                   if  ( " \"'# " .Contains(chr))    // 当前字符是双引号、单引号、井号的一种
 9                  {
10                       if  (opt.Contains(chr))   // 之前已经读到过该字符
11                      {
12                          opt  =  opt.Remove(opt.IndexOf(chr),  1 );   // 移除之前读到的该字符,即匹配的字符
13                      }
14                       else
15                      {
16                          opt  +=  chr;      // 第一次读到该字符时,存储
17                      }
18                  }
19                   else   if  ( " () " .Contains(chr))     // 左右括号
20                  {
21                       if  (chr  ==   " ( " )
22                      {
23                          opt  +=  chr;
24                      }
25                       else   if  (chr  ==   " ) " )
26                      {
27                           if  (opt.Contains( " ( " ))
28                          {
29                              opt  =  opt.Remove(opt.IndexOf( " ( " ),  1 );
30                          }
31                           else
32                          {
33                               return   false ;
34                          }
35                      }
36                  }
37              }
38               return  (opt  ==   "" );
39          }

    3、查找运算符位置

ExpandedBlockStart.gif 从表达式中查找运算符位置
 1    ///   <summary>
 2           ///  从表达式中查找运算符位置
 3           ///   </summary>
 4           ///   <param name="exp"> 表达式 </param>
 5           ///   <param name="findOpt"> 要查找的运算符 </param>
 6           ///   <returns> 返回运算符位置 </returns>
 7           private   int  FindOperator( string  exp,  string  findOpt)
 8          {
 9               string  opt  =   "" ;
10               for  ( int  i  =   0 ; i  <  exp.Length; i ++ )
11              {
12                   string  chr  =  exp.Substring(i,  1 );
13                   if  ( " \"'# " .Contains(chr)) // 忽略双引号、单引号、井号中的运算符
14                  {
15                       if  (opt.Contains(chr))
16                      {
17                          opt  =  opt.Remove(opt.IndexOf(chr),  1 );
18                      }
19                       else
20                      {
21                          opt  +=  chr;
22                      }
23                  }
24                   if  (opt  ==   "" )
25                  {
26                       if  (findOpt  !=   "" )
27                      {
28                           if  (findOpt  ==  chr)
29                          {
30                               return  i;
31                          }
32                      }
33                       else
34                      {
35                           if  (m_Operators.Exists(x  =>  x.Contains(chr)))
36                          {
37                               return  i;
38                          }
39                      }
40                  }
41              }
42               return   - 1 ;
43          }

  4、解析字符串表达式,算法的关键实现

ExpandedBlockStart.gif 语法解析,将中缀表达式转换成后缀表达式(即逆波兰表达式)
  1           public   bool  Parse( string  exp)
  2          {
  3              m_tokens.Clear(); // 清空语法单元堆栈
  4               if  (exp.Trim()  ==   "" ) // 表达式不能为空
  5              {
  6                   return   false ;
  7              }
  8               else   if  ( ! this .IsMatching(exp)) // 括号、引号、单引号等必须配对
  9              {
 10                   return   false ;
 11              }
 12 
 13              Stack < object >  operands  =   new  Stack < object > ();              // 操作数堆栈
 14              Stack < Operator >  operators  =   new  Stack < Operator > ();       // 运算符堆栈
 15              OperatorType optType  =  OperatorType.ERR;                 // 运算符类型
 16               string  curOpd  =   "" ;                                  // 当前操作数
 17               string  curOpt  =   "" ;                                  // 当前运算符
 18               int  curPos  =   0 ;                                      // 当前位置
 19               // int funcCount = 0;                                         // 函数数量
 20 
 21              curPos  =  FindOperator(exp,  "" );
 22 
 23              exp  +=   " @ " // 结束操作符
 24               while  ( true )
 25              {
 26                  curPos  =  FindOperator(exp,  "" );
 27 
 28                  curOpd  =  exp.Substring( 0 , curPos).Trim();
 29                  curOpt  =  exp.Substring(curPos,  1 );
 30 
 31                   //测试代码 / //
 32                   // System.Diagnostics.Debug.WriteLine("***************");
 33                   // System.Diagnostics.Debug.WriteLine("当前读取的操作数:" + curOpd);
 34 
 35                   // foreach (var item in operands.ToArray())
 36                   // {
 37                   //     if (item is Operand)
 38                   //     {
 39                   //         System.Diagnostics.Debug.WriteLine("操作数栈:" + ((Operand)item).Value);
 40                   //     }
 41                   //     if (item is Operator)
 42                   //     {
 43                   //         System.Diagnostics.Debug.WriteLine("操作数栈:" + ((Operator)item).Value);
 44                   //     }
 45                   // }
 46 
 47                   // System.Diagnostics.Debug.WriteLine("当前读取的运算符:" + curOpt);
 48                   // foreach (var item in operators.ToArray())
 49                   // {
 50                   //     System.Diagnostics.Debug.WriteLine("运算符栈:" + item.Value);
 51                   // }
 52                   // //
 53 
 54                   // 存储当前操作数到操作数堆栈
 55                   if  (curOpd  !=   "" )
 56                  {
 57                      operands.Push( new  Operand(curOpd, curOpd));
 58                  }
 59 
 60                   // 若当前运算符为结束运算符,则停止循环
 61                   if  (curOpt  ==   " @ " )
 62                  {
 63                       break ;
 64                  }
 65                   // 若当前运算符为左括号,则直接存入堆栈。
 66                   if  (curOpt  ==   " ( " )
 67                  {
 68                      operators.Push( new  Operator(OperatorType.LB,  " ( " ));
 69                      exp  =  exp.Substring(curPos  +   1 ).Trim();
 70                       continue ;
 71                  }
 72 
 73                   // 若当前运算符为右括号,则依次弹出运算符堆栈中的运算符并存入到操作数堆栈,直到遇到左括号为止,此时抛弃该左括号.
 74                   if  (curOpt  ==   " ) " )
 75                  {
 76                       while  (operators.Count  >   0 )
 77                      {
 78                           if  (operators.Peek().Type  !=  OperatorType.LB)
 79                          {
 80                              operands.Push(operators.Pop());
 81                          }
 82                           else
 83                          {
 84                              operators.Pop();
 85                               break ;
 86                          }
 87                      }
 88                      exp  =  exp.Substring(curPos  +   1 ).Trim();
 89                       continue ;
 90                  }
 91 
 92 
 93                   // 调整运算符
 94                  curOpt  =  Operator.AdjustOperator(curOpt, exp,  ref  curPos);
 95 
 96                  optType  =  Operator.ConvertOperator(curOpt);
 97 
 98                   // 若运算符堆栈为空,或者若运算符堆栈栈顶为左括号,则将当前运算符直接存入运算符堆栈.
 99                   if  (operators.Count  ==   0   ||  operators.Peek().Type  ==  OperatorType.LB)
100                  {
101                      operators.Push( new  Operator(optType, curOpt));
102                      exp  =  exp.Substring(curPos  +   1 ).Trim();
103                       continue ;
104                  }
105 
106                   // 若当前运算符优先级大于运算符栈顶的运算符,则将当前运算符直接存入运算符堆栈.
107                   if  (Operator.ComparePriority(optType, operators.Peek().Type)  >   0 )
108                  {
109                      operators.Push( new  Operator(optType, curOpt));
110                  }
111                   else
112                  {
113                       // 若当前运算符若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到操作数堆栈,直至运算符栈栈顶运算符低于(不包括等于)该运算符优先级,
114                       // 或运算符栈栈顶运算符为左括号
115                       // 并将当前运算符压入运算符堆栈。
116                       while  (operators.Count  >   0 )
117                      {
118                           if  (Operator.ComparePriority(optType, operators.Peek().Type)  <=   0   &&  operators.Peek().Type  !=  OperatorType.LB)
119                          {
120                              operands.Push(operators.Pop());
121 
122                               if  (operators.Count  ==   0 )
123                              {
124                                  operators.Push( new  Operator(optType, curOpt));
125                                   break ;
126                              }
127                          }
128                           else
129                          {
130                              operators.Push( new  Operator(optType, curOpt));
131                               break ;
132                          }
133                      }
134 
135                  }
136                  exp  =  exp.Substring(curPos  +   1 ).Trim();
137              }
138               // 转换完成,若运算符堆栈中尚有运算符时,
139               // 则依序取出运算符到操作数堆栈,直到运算符堆栈为空
140               while  (operators.Count  >   0 )
141              {
142                  operands.Push(operators.Pop());
143              }
144               // 调整操作数栈中对象的顺序并输出到最终栈
145               while  (operands.Count  >   0 )
146              {
147                  m_tokens.Push(operands.Pop());
148              }
149 
150               return   true ;
151          }

    5、计算

ExpandedBlockStart.gif 对逆波兰表达式求值
  1           public   object  Evaluate()
  2          {
  3               /*
  4                逆波兰表达式求值算法:
  5                1、循环扫描语法单元的项目。
  6                2、如果扫描的项目是操作数,则将其压入操作数堆栈,并扫描下一个项目。
  7                3、如果扫描的项目是一个二元运算符,则对栈的顶上两个操作数执行该运算。
  8                4、如果扫描的项目是一个一元运算符,则对栈的最顶上操作数执行该运算。
  9                5、将运算结果重新压入堆栈。
 10                6、重复步骤2-5,堆栈中即为结果值。
 11               */
 12 
 13               if  (m_tokens.Count  ==   0 return   null ;
 14 
 15               object  value  =   null ;
 16              Stack < Operand >  opds  =   new  Stack < Operand > ();
 17              Stack < object >  pars  =   new  Stack < object > ();
 18              Operand opdA, opdB;
 19 
 20               foreach  ( object  item  in  m_tokens)
 21              {
 22                   if  (item  is  Operand)
 23                  {
 24                       // TODO 解析公式,替换参数
 25 
 26                       // 如果为操作数则压入操作数堆栈
 27                      opds.Push((Operand)item);
 28                  }
 29                   else
 30                  {
 31                       switch  (((Operator)item).Type)
 32                      {
 33                           #region  乘,*,multiplication
 34                           case  OperatorType.MUL:
 35                              opdA  =  opds.Pop();
 36                              opdB  =  opds.Pop();
 37                               if  (Operand.IsNumber(opdA.Value)  &&  Operand.IsNumber(opdB.Value))
 38                              {
 39                                  opds.Push( new  Operand(OperandType.NUMBER,  double .Parse(opdB.Value.ToString())  *   double .Parse(opdA.Value.ToString())));
 40                              }
 41                               else
 42                              {
 43                                   throw   new  Exception( " 乘运算的两个操作数必须均为数字 " );
 44                              }
 45                               break ;
 46                           #endregion
 47 
 48                           #region  除,/,division
 49                           case  OperatorType.DIV:
 50                              opdA  =  opds.Pop();
 51                              opdB  =  opds.Pop();
 52                               if  (Operand.IsNumber(opdA.Value)  &&  Operand.IsNumber(opdB.Value))
 53                              {
 54                                  opds.Push( new  Operand(OperandType.NUMBER,  double .Parse(opdB.Value.ToString())  /   double .Parse(opdA.Value.ToString())));
 55                              }
 56                               else
 57                              {
 58                                   throw   new  Exception( " 除运算的两个操作数必须均为数字 " );
 59                              }
 60                               break ;
 61                           #endregion
 62 
 63                           #region  余,%,modulus
 64                           case  OperatorType.MOD:
 65                              opdA  =  opds.Pop();
 66                              opdB  =  opds.Pop();
 67                               if  (Operand.IsNumber(opdA.Value)  &&  Operand.IsNumber(opdB.Value))
 68                              {
 69                                  opds.Push( new  Operand(OperandType.NUMBER,  double .Parse(opdB.Value.ToString())  %   double .Parse(opdA.Value.ToString())));
 70                              }
 71                               else
 72                              {
 73                                   throw   new  Exception( " 余运算的两个操作数必须均为数字 " );
 74                              }
 75                               break ;
 76                           #endregion
 77 
 78                           #region  加,+,Addition
 79                           case  OperatorType.ADD:
 80                              opdA  =  opds.Pop();
 81                              opdB  =  opds.Pop();
 82                               if  (Operand.IsNumber(opdA.Value)  &&  Operand.IsNumber(opdB.Value))
 83                              {
 84                                  opds.Push( new  Operand(OperandType.NUMBER,  double .Parse(opdB.Value.ToString())  +   double .Parse(opdA.Value.ToString())));
 85                              }
 86                               else
 87                              {
 88                                   throw   new  Exception( " 加运算的两个操作数必须均为数字 " );
 89                              }
 90                               break ;
 91                           #endregion
 92 
 93                           #region  减,-,subtraction
 94                           case  OperatorType.SUB:
 95                              opdA  =  opds.Pop();
 96                              opdB  =  opds.Pop();
 97                               if  (Operand.IsNumber(opdA.Value)  &&  Operand.IsNumber(opdB.Value))
 98                              {
 99                                  opds.Push( new  Operand(OperandType.NUMBER,  double .Parse(opdB.Value.ToString())  -   double .Parse(opdA.Value.ToString())));
100                              }
101                               else
102                              {
103                                   throw   new  Exception( " 减运算的两个操作数必须均为数字 " );
104                              }
105                               break ;
106                           #endregion
107 
108                           #region  正切,tan,subtraction
109                           case  OperatorType.TAN:
110                              opdA  =  opds.Pop();
111                               if  (Operand.IsNumber(opdA.Value))
112                              {
113                                  opds.Push( new  Operand(OperandType.NUMBER, Math.Tan( double .Parse(opdA.Value.ToString()) * Math.PI / 180 )));
114                              }
115                               else
116                              {
117                                   throw   new  Exception( " 正切运算的1个操作数必须均为角度数字 " );
118                              }
119                               break ;
120                           #endregion
121 
122                           #region  反正切,atan,subtraction
123                           case  OperatorType.ATAN:
124                              opdA  =  opds.Pop();
125                               if  (Operand.IsNumber(opdA.Value))
126                              {
127                                  opds.Push( new  Operand(OperandType.NUMBER, Math.Atan( double .Parse(opdA.Value.ToString()))));
128                              }
129                               else
130                              {
131                                   throw   new  Exception( " 反正切运算的1个操作数必须均为数字 " );
132                              }
133                               break ;
134                           #endregion
135 
136                      }
137                  }
138              }
139 
140               if  (opds.Count  ==   1 )
141              {
142                  value  =  opds.Pop().Value;
143              }
144 
145               return  value;
146          }

   

  至此,算法实现已经完毕,下面我们来看下,如何使用RPN类来计算表达式。

1  private   void  Test()
2  {
3       string  tmpExp = " 1.0+3/2-tan(45)/(1+1) " ;
4      RPN rpn  =   new  RPN();
5       if  (rpn.Parse(tmpExp))
6      {
7          Console.WriteLine(rpn.Evaluate());
8      }
9  }

转载于:https://www.cnblogs.com/lxfqlcz/archive/2011/08/02/2124854.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值