支持函数,变量的算术表达式计算(一、计算后缀表达式)

12 + sin(sqr(9) + 9) + abs(-90)
给这么一段字符串给你, 要如何才能正确计算出它的值? (注: 值应为 103 )

算术表达式的计算有几种方法,本文只讨论“后缀表达式(也叫'逆波兰表达式')方法”
后缀表达式是啥意思呢? 顾名思义,就是操作符在操作数的后面,比如 12+36 转换为后缀表达式后就变成
12 36 +

注:本文中的后缀表达式是用 List 存储,当然用 Stack 也可以(可能用栈会更好)

后缀表达式可以将复杂的算术表达式变得很简单,它的计算逻辑为
1.遍历整个后缀表达式
2.如果后缀表达式当前节点是数字,则跳过,继续往下遍历
3.如果后缀表达式当前节点是操作符,则将前两个节点取出,用当前操作符作运算
  计算完后,将结果存入当前结点, 并删除前两个操作数节点
重复上面三个过程,直到节点数为 1 (此节点的数据即为最终数据)

我们先来模拟一下 12+36*9 这个表达式的计算
先转换为后缀表达式  12 36 9 * +  (转换方法见后续文章)

1. 遍历表达式直到遇见操作符 *  (第4个节点)
2. 取出前两个操作数(36 和 9) 和 * 作运算,结果存入当前节点
   执行完此步骤后,后缀表达式为
   12 324 +
3. 再重复1,2两步骤,即可得到最终结果  336 , 是不是很简单呢?

下面我们来构造一个可计算后缀表达式的类
先定义节点类型枚举

    /// <summary>
    
/// 节点类型
    
/// </summary>

    public enum TokenType
    
{
        
/// <summary>
        
/// 操作数
        
/// </summary>

        Numeric,
        
/// <summary>
        
/// 操作符
        
/// </summary>

        Operator
    }

操作符枚举

    public enum OperatorType
    
{
        Plus, 
//"+",
        Subtract,//  "-",
        MultiPly,//  "*",
        Divide, //"/",
        
//后面可继续添加函数等
    }


节点类

    /// <summary>
    
/// 节点类
    
/// </summary>

    public class ExpressionToken
    
{
        
private TokenType type;

        
/// <summary>
        
/// 节点类型
        
/// </summary>

        public TokenType Type
        
{
            
get return type; }
            
set { type = value; }
        }

        
private object data;

        
/// <summary>
        
/// 节点数据
        
/// </summary>

        public object Data
        
{
            
get return data; }
            
set { data = value; }
        }


        
/// <summary>
        
/// 构造函数
        
/// </summary>
        
/// <param name="type">节点类型</param>
        
/// <param name="data">节点数据</param>

        public ExpressionToken(TokenType type, object data)
        
{
            
this.type = type;
            
this.data = data;
        }

    }

表达式计算类

    public class Expression
    
{
        
/// <summary>
        
/// 表达式节点列表
        
/// </summary>

        List<ExpressionToken> lstExp = new List<ExpressionToken>();

        
string strExpression;
        
public Expression(string exp)
        
{
            
if (exp == null)
                
throw new ArgumentNullException();
            strExpression 
= exp;
        }


        
/// <summary>
        
/// 开始计算
        
/// </summary>
        
/// <returns></returns>

        private object CalcInner(List<ExpressionToken> exp)
        
{
            
int index = 0;
            
//储存数据
            List<decimal?> digit = new List<decimal?>();
            
while (index < exp.Count)
            
{
                
if (exp.Count == 1 && exp[0].Type == TokenType.Numeric)
                    
break;
                ExpressionToken token 
= exp[index];
                
switch (token.Type)
                
{
                    
//如果是数字,则将值存入 digit 中
                    case TokenType.Numeric:
                        digit.Add(Convert.ToDecimal(exp[index].Data));
                        index
++;
                        
break;
                    
case TokenType.Operator:
                        
//二元表达式,需要二个参数, 如果是函数的话,可能需要更多的参数
                        int paramCount = 2
                        
//计算操作数的值
                        if (digit.Count < paramCount)
                        
{
                            
throw new ExpressionException("缺少操作数");
                        }

                        
//传入参数
                        decimal?[] data = new decimal?[paramCount];
                        
for (int i = 0; i < paramCount; i++)
                        
{
                            data[i] 
= digit[index - paramCount + i];
                        }

                        
//将计算结果再存入当前节点
                        exp[index].Data = CalcOperator((OperatorType)token.Data, data);
                        exp[index].Type 
= TokenType.Numeric;
                        
//将操作数节点删除
                        for (int i = 0; i < paramCount; i++)
                        
{
                            exp.RemoveAt(index 
- i - 1);
                            digit.RemoveAt(index 
- i - 1);
                        }

                        index 
-= paramCount;
                        
break;
                    
default:
                        
break;
                }

            }

            
if (exp.Count == 1)
            
{
                
switch (exp[0].Type)
                
{
                    
case TokenType.Numeric:
                        
return exp[0].Data;
                    
default:
                        
throw new ExpressionException("缺少操作数"1002);
                }

            }

            
else
            
{
                
throw new ExpressionException("缺少操作符或操作数"1002);
            }

        }

        
public object Calc()
        
{
            
return CalcInner(lstExp);
        }


        
/// <summary>
        
/// 计算表达式的值
        
/// (因为不确定参数有几个,所以用数组传进来)
        
/// 比如,函数可能只需要1个参数,也可能需要3个参数
        
/// </summary>

        public object CalcOperator(OperatorType op, decimal?[] data)
        
{
            
//暂只编号写基本四则运算的代码,无函数计算
            decimal? d1 = data[0];
            
decimal? d2 = data[1];
            
if (d1 == null || d2 == null)
                
return DBNull.Value;
            
switch (op)
            
{
                
case OperatorType.Plus:
                    
return d1 + d2;
                
case OperatorType.Subtract:
                    
return d1 - d2;
                
case OperatorType.MultiPly:
                    
return d1 * d2;
                
case OperatorType.Divide:
                    
if (d2 == 0)
                        
throw new DivideByZeroException();
                    
return d1 / d2;
            }

            
return 0;
        }

}

里面的一些基本函数不作赘述。

该函数使用很简单,只有个Calc方法
Expression exp = new Expression("12+36*9");
object result = exp.Calc()); //当然,现在还没转换后缀表达式功能,所以无法计算 :)

   ...........待续

posted @ 2007-12-29 11:29  在天空飞翔  阅读(5830)  评论(17编辑  收藏  举报