思想

拨开迷雾见青天

导航

字符串公式解析器——使用“逆波兰式算法”及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、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。

 

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

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

定义操作数类型枚举
 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     }
操作数类 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 { getset; }
22 
23         /// <summary>
24         /// 关键字
25         /// </summary>
26         public string Key { getset; }
27 
28         /// <summary>
29         /// 操作数值
30         /// </summary>
31         public object Value { getset; }
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     }

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

运算符类型枚举
  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     }
运算符类
    public class Operator
    {
        
public Operator(OperatorType type, string value)
        {
            
this.Type = type;
            
this.Value = value;
        }

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

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


        
/// <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;
                
defaultreturn 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;
                
defaultreturn 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类中的变量和属性定义

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、检查特殊符号是否匹配的方法

检查表达式中特殊符号(双引号、单引号、井号、左右括号)是否匹配
 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、查找运算符位置

从表达式中查找运算符位置
 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、解析字符串表达式,算法的关键实现

语法解析,将中缀表达式转换成后缀表达式(即逆波兰表达式)
  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、计算

对逆波兰表达式求值
  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 == 0return 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 }

posted on 2011-08-02 13:39  Will Lu  阅读(10698)  评论(9编辑  收藏  举报