zoukankan      html  css  js  c++  java
  • 支持函数,变量的算术表达式计算(一、计算后缀表达式)

    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()); //当然,现在还没转换后缀表达式功能,所以无法计算 :)

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

  • 相关阅读:
    Ubuntu无法初始化软件包信息
    数组名做函数的参数
    Printf函数中%p代表什么数据类型
    assert()函数用法总结
    C语言printf 格式 zz
    零值指针指向何处? zz~
    命名空间“System.Net”中不存在类型或命名空间名称“Sockets”。是否缺少程序集引用?
    AD20配置为中文显示
    sharepoint:实现搜索功能
    sharepoint:爬网完成后上传新文档搜索不到(设定爬网Schedule)
  • 原文地址:https://www.cnblogs.com/michaelhuwei/p/1019658.html
Copyright © 2011-2022 走看看