C# 逆波兰表达式解析公式

本文介绍了如何使用C#处理逆波兰表达式,包括将中缀表达式转换为后缀表达式,并计算后缀表达式。通过堆栈数据结构实现,详细解释了转换和计算过程,涉及括号匹配、运算符优先级、错误处理和特殊字符处理。
摘要由CSDN通过智能技术生成

你是否遇到这样的需求:如何计算“1.0+3/2-tan(45)/(1+1)+Abs(-10)-floor(2.55)” 这一串字符串的值?

 

思路:

定义:

逆波兰表达式又叫做后缀表达式。在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法,按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。

人类最熟悉的一种表达式1+2,(1+2)*3,3+42+4等都是中缀表示法。对于人们来说,也是最直观的一种求值方式,先算括号里的,然后算乘除,最后算加减,但是,计算机处理中缀表达式却并不方便。

然后我们还需明确一些概念,下面通过我们最熟悉的中缀表达式画出一棵语法树来直观认识一下前后缀表达式的生成。以A+B*(C-D)-E*F为例:

从上面可以发现,其实问题就2个:

1. 将人类常用的中缀表达式转换为后缀表达式

2. 计算后缀表达式。

转换为后缀表达式

这里我们要用到堆栈Stack, 详细概念可以点这里。

1.数字直接入栈
2.运算符要与栈顶元素比较
 ①栈为空直接入栈
 ②运算符优先级大于栈顶元素优先级则直接入栈
 ③小于或等于则出栈入列,再与栈顶元素进行比较,直到运算符优先级小于栈顶元
    素优先级后,操作符再入栈
3.操作符是 ( 则无条件入栈
4.操作符为 ),则依次出栈入列,直到匹配到第一个(为止,此操作符直接舍弃,(直接出栈舍弃

代码实现如下,首先定义操作数类型,因为我们不仅想计算简单的加减乘除,还想实现一些简单的函数和逻辑运算。

    /// <summary>
    /// 操作数类型
    /// </summary>
    public enum Type
    {
        /// <summary>
        /// 函数
        /// </summary>
        FUNC = 1,

        /// <summary>
        /// 日期
        /// </summary>
        DATE = 2,

        /// <summary>
        /// 数字
        /// </summary>
        NUMBER = 3,

        /// <summary>
        /// 布尔
        /// TODO 
        /// </summary>
        BOOLEAN = 4,

        /// <summary>
        /// 字符串
        /// </summary>
        STRING = 5
    }

然后我们定义一个操作数类:

    /// <summary>
    /// 操作数类
    /// </summary>
    public class Operand
    {
        public Operand(Type type, object value)
        {
            this.Type = type;
            this.Value = value;
        }

        public Operand(string opd, object value)
        {
            this.Type = ConvertOperand(opd);
            this.Value = value;
        }
       
        /// <summary>
        /// 操作数类型
        /// </summary>
        public Type Type { get; set; }

        /// <summary>
        /// 关键字
        /// </summary>
        public string Key { get; set; }

        /// <summary>
        /// 操作数值
        /// </summary>
        public object Value { get; set; }

        /// <summary>
        /// 转换操作数到指定的类型
        /// </summary>
        /// <param name="opd">操作数</param>
        /// <returns>返回对应的操作数类型</returns>
        public static Type ConvertOperand(string opd)
        {
            if (opd.IndexOf("(") > -1)
            {
                return Type.FUNC;
            }
            else if (IsNumber(opd))
            {
                return Type.NUMBER;
            }
            else if (IsDate(opd))
            {
                return Type.DATE;
            }
            else
            {
                return Type.STRING;
            }
        }

        /// <summary>
        /// 判断对象是否为数字
        /// </summary>
        /// <param name="value">对象值</param>
        /// <returns>是返回真,否返回假</returns>
        public static bool IsNumber(object value)
        {
            return Regex.IsMatch(value.ToString(), @"^[+-]?\d*[.]?\d*$");
            // return double.TryParse(value.ToString(), out _); 用正则表达式快一点
        }

        /// <summary>
        /// 判断对象是否为日期
        /// </summary>
        /// <param name="value">对象值</param>
        /// <returns>是返回真,否返回假</returns>
        public static bool IsDate(object value)
        {
            return DateTime.TryParse(value.ToString(), out _);
        }
    }

然后定义运算符的枚举,从小到大优先级递减:

    /// <summary>
    /// 运算符类型(从上到下优先级依次递减),数值越大,优先级越低
    /// </summary>
    public enum OptType
    {
        #region 最高优先级操作符

        /// <summary>
        /// 左括号:(,left bracket
        /// </summary>
        LB = 1,

        /// <summary>
        /// 右括号),right bracket
        /// </summary>
        RB = 2,

        /// <summary>
        /// 逻辑非,!,NOT
        /// </summary>
        NOT = 3,

        /// <summary>
        /// 正号,+,positive sign
        /// </summary>
        PS = 5,

        /// <summary>
        /// 负号,-,negative sign
        /// </summary>
        NS = 7,

        #endregion

        #region 函数

        /// <summary>
        /// 正切,tan
        /// </summary>
        TAN = 10,

        /// <summary>
        /// 正切,tan
        /// </summary>
        COT = 11,

        /// <summary>
        /// 反正切,atan
        /// </summary>
        ATAN = 12,

        /// <summary>
        /// 正弦
        /// </summary>
        SIN = 13,

        /// <summary>
        /// 余弦
        /// </summary>
        COS = 14,

        /// <summary>
        /// 绝对值
        /// </summary>
        Abs = 15,
        
        /// <summary>
        /// 开平方
        /// </summary>
        Sqrt = 16,

        /// <summary>
        /// 向下取整
        /// </summary>
        Floor = 17,

        /// <summary>
        /// 向上取整
        /// </summary>
        Ceiling = 18,

        /// <summary>
        /// 四舍五入2位小数
        /// </summary>
        Round = 19,

        /// <summary>
        /// 平方
        /// </summary>
        Pow = 20,

        /// <summary>
        /// 立方
        /// </summary>
        Cube = 21,

        /// <summary>
        /// 自然对数
        /// </summary>
        Ln = 22,

        /// <summary>
        /// 10为底的对数
        /// </summary>
        Log = 23,

        #endregion

        #region 其他操作符

        /// <summary>
        /// 乘,*,multiplication
        /// </summary>
        MUL = 130,

        /// <summary>
        /// 除,/,division
        /// </summary>
        DIV = 131,

        /// <summary>
        /// 余,%,modulus
        /// </summary>
        MOD = 132,

        /// <summary>
        /// 加,+,Addition
        /// </summary>
        ADD = 140,

        /// <summary>
        /// 减,-,subtraction
        /// </summary>
        SUB = 141,

        /// <summary>
        /// 小于,less than
        /// </summary>
        LT = 150,

        /// <summary>
        /// 小于或等于,less than or equal to
        /// </summary>
        LE = 151,

        /// <summary>
        /// 大于,>,greater than
        /// </summary>
        GT = 152,

        /// <summary>
        /// 大于或等于,>=,greater than or equal to
        /// </summary>
        GE = 153,

        /// <summary>
        /// 等于,=,equal to
        /// </summary>
        ET = 160,

        /// <summary>
        /// 不等于,unequal to
        /// </summary>
        UT = 161,

        /// <summary>
        /// 逻辑与,&,AND
        /// </summary>
        AND = 170,

        /// <summary>
        /// 逻辑或,|,OR
        /// </summary>
        OR = 171,

        /// <summary>
        /// 逗号,comma
        /// </summary>
        CA = 180,

        /// <summary>
        /// 结束符号 @
        /// </summary>
        END = 255,

        /// <summary>
        /// 错误符号
        /// </summary>
        ERR = 256

        #endregion
        
    }

再定义一个运算符类,用来把输入字符串中的运算符提取出来:

    /// <summary>
    /// 运算符类
    /// </summary>
    public class Operator
    {
        public Operator(OptType type, string value)
        {
            this.Type = type;
            this.Value = value;
        }

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

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

        /// <summary>
        /// 对于>或者&lt;运算符,判断实际是否为>=,&lt;&gt;、&lt;=,并调整当前运算符位置
        /// </summary>
        /// <param name="curr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值