逆波兰计算器 c#

using System;
using System.Collections.Generic;

namespace ANL
{
    /// <summary> 逆波兰计算器 </summary>
    class ReversePolishCalculator
    {
        public void Test()
        {
            Console.WriteLine("输入公式");
            string input = Console.ReadLine();

            // 解析输入的数据, 保存到链表上
            var operandList = AnalysisOperand(input);
            // 将链表数据转化成逆波兰表达式
            var reversePolishNotationList = Parse2ReversePolishNotation(operandList);
            // 计算逆波兰表达式
            double result = CountReversePolishNotation(reversePolishNotationList);
            // 打印结果
            Console.WriteLine("运算结果:" + result);
        }

        #region 中缀表达式拆分成数值和符号保存到链表上
        /// <summary> 算式分解存储在链表上 </summary>
        private static LinkedList<object> AnalysisOperand(string input)
        {
            var operand = new LinkedList<object>();
            if (string.IsNullOrEmpty(input))
            {
                return operand;
            }

            int startIdx = 0;
            int symbolIdx = 0;
            for(int i = 0, n = input.Length; i < n; ++i)
            {
                char curChar = input[i];
                if (curChar >= '0' && curChar <= '9' || curChar == ' ' || curChar == '.')
                {
                    // 数字 空格 ., 此时表示读取到数值了
                    // 将前面的运算符取出来
                    ExtractOperation(input, ref symbolIdx, i, ref operand);
                }
                else
                {
                    // 遇到非数字相关, 提取出前面的数字
                    if (startIdx != i)
                    {
                        // 添加数值
                        string valueStr = input.Substring(startIdx, i - startIdx);
                        double value = StringConvertDouble(valueStr);
                        operand.AddLast(value);
                    }
                    startIdx = i + 1;
                }
            }

            // 提取末尾的数据
            char fineChar = input[input.Length - 1];
            if (fineChar >= '0' && fineChar <= '9' || fineChar == ' ' || fineChar == '.')
            {
                // 数字 空格 ., 此时表示末尾数据是数字
                // 将前面的数值取出来
                string valueStr = input.Substring(startIdx, input.Length - startIdx);
                double value = StringConvertDouble(valueStr);
                operand.AddLast(value);
            }else
            {
                // 将前面的符号取出来
                ExtractOperation(input, ref symbolIdx, input.Length, ref operand);
            }

            PrintLinkList(operand, "中缀表达式:");

            return operand;
        }

        /// <summary> 提取运算符 </summary>
        private static void ExtractOperation(string input, ref int symbolSubIdx, int endIdx, ref LinkedList<object> operand)
        {
            if (symbolSubIdx == endIdx) {
                // 指定位置无法提取运算符, 将运算符的位置移动到下一个位置
                symbolSubIdx = endIdx + 1;
                return;
            };

            int startIdx = symbolSubIdx;
            for (int i = symbolSubIdx; i < endIdx; ++i)
            {
                // 几位数符号
                int symbolUnit = i - symbolSubIdx + 1;
                string symbolStr = input.Substring(symbolSubIdx, i - symbolSubIdx + 1);
                if (symbolUnit == 1)
                {
                    if (symbolStr.Equals(" ") || symbolStr.Equals("+") || symbolStr.Equals("-") || symbolStr.Equals("*") || symbolStr.Equals("/") ||
                        symbolStr.Equals("!") || symbolStr.Equals("^") || symbolStr.Equals("%") || symbolStr.Equals("(") || symbolStr.Equals(")"))
                    {
                        // 空格, 加, 减, 乘, 除, 阶乘, 幂, 求余, 括号
                        operand.AddLast(symbolStr);
                        symbolSubIdx = i + 1;
                    }
                    else if (symbolStr.Equals("h"))
                    {
                        if (operand.Count == 0 && operand.Last.Value is string) continue;
                        string preOperand = (string)operand.Last.Value;
                        // 前面是sin, cos, tan, cot, 则可以组合成4位的运算符
                        if (preOperand.Equals("sin") || preOperand.Equals("cos") || preOperand.Equals("tan"))
                        {
                            operand.RemoveLast();
                            operand.AddLast(preOperand + symbolStr);
                            symbolSubIdx = i + 1;
                        }
                    }
                }
                else if (symbolUnit == 2)
                {

                }
                else if (symbolUnit == 3)
                {
                    if (symbolStr.Equals("sin") || symbolStr.Equals("cos") || symbolStr.Equals("tan"))
                    {
                        // sin, cos, tan, cot
                        operand.AddLast(symbolStr);
                        symbolSubIdx = i + 1;
                    }
                }
            }
            if (symbolSubIdx < endIdx)
            {
                Console.WriteLine($"提取符号失败, 符号:{input.Substring(symbolSubIdx, endIdx - symbolSubIdx)}");
            }
            symbolSubIdx = endIdx + 1;
        }

        /// <summary> str转double </summary>
        private static double StringConvertDouble(string str)
        {
            double value = 0;
            if (!double.TryParse(str, out value))
            {
                Console.WriteLine($"字符串转化成数值失败, 字符串:{str}");
            }
            return value;
        }
        #endregion 中缀表达式拆分成数值和符号保存到链表上

        #region 将公式转换成逆波兰表达式
        /// <summary> 算式转化成逆波兰表达式 </summary>
        private static LinkedList<object> Parse2ReversePolishNotation(LinkedList<object> operandList)
        {
            // 运算符列表
            LinkedList<object> operationList = new LinkedList<object>();
            // 中间结果列表
            LinkedList<object> expressionList = new LinkedList<object>();

            foreach (var item in operandList)
            {
                if (item is double)
                {
                    // 如果是操作数,则直接追加到中间结果列表
                    expressionList.AddLast((double)item);
                }
                else
                {
                    if ((string)item == "(")
                    {
                        // 如果是 ( ,则直接放到运算符列表尾部,等待下一个最近的 右括号 与之配对。
                        operationList.AddLast(item);
                    }
                    else if ((string)item == ")")
                    {
                        // 如果是 ),则说明有一对括号已经配对(在表达式输入无误的情况下)。
                        // 丢弃它,然后从运算符列表中去除最后一个元素e,将e依次追加到中间结果列表里。一直循环,直到取出的最后一个元素e 是 左括号 ( ,同样丢弃他。
                        do
                        {
                            if (operationList.Count == 0)
                            {
                                Console.WriteLine($"不存在 (");
                                break;
                            }
                            if ((string)operationList.Last.Value == "(")
                            {
                                operationList.RemoveLast();
                                break;
                            }
                            expressionList.AddLast(operationList.Last.Value);
                            operationList.RemoveLast();
                        } while (true);
                    }
                    else
                    {
                        // 如果是运算符(用op1表示)
                        do
                        {
                            if (operationList.Count == 0 || (string)operationList.Last.Value == "(")
                            {
                                // 如果运算符列表最后一个元素(用op2表示) 不是运算符,则二者没有可比性,则直接将此运算符op1压栈。 例如栈顶是左括号 ( ,或者栈为空。
                                operationList.AddLast((string)item);
                                break;
                            }
                            else
                            {
                                // 如果运算符列表最后一个元素(用op2表示) 是运算符 ,则比较op1和 op2的优先级。如果op1 > op2 ,则直接将此运算符op1压栈。
                                int compareResult = CompareOperand((string)operationList.Last.Value, (string)item);
                                if (compareResult < 0)
                                {
                                    operationList.AddLast((string)item);
                                    break;
                                }
                                else
                                {
                                    // 如果不满足op1 > op2,则将op2出栈,并追加到中间结果列表,再试图将op1压栈,如果如果依然不满足 op1>新的栈顶op2,继续将新的op2弹出追加到储存中间结果列表 ,直到op1可以压入栈中为止。
                                    expressionList.AddLast(operationList.Last.Value);
                                    operationList.RemoveLast();
                                }
                            }
                        } while (true);
                    }
                }
            }

            // 如果运算符列表中还有元素,则依次取出追加到中间结果列表后,就得到了后缀表达式
            for (var iter = operationList.Last; iter != null; iter = iter.Previous)
            {
                expressionList.AddLast(iter.Value);
            }
            
            PrintLinkList(expressionList, "中间结果列表:");

            return expressionList;
        }

        /// <summary> 比较运算符的优先级 </summary>
        private static int CompareOperand(string operand1, string operand2)
        {
            if (operand1 == operand2) return 0;

            Dictionary<string, int> wightDic = new Dictionary<string, int>() {
                { "+", 0}, {"-", 0},
                { "*", 1}, { "/", 1}, { "%", 1},
                { "!", 2 }, {"^", 2},
                { "sin", 3 }, { "cos", 3 }, { "tan", 3 }, { "sinh", 3 }, { "cosh", 3 }, { "tanh", 3 }
            };

            int wight1 = 0;
            int wight2 = 0;
            if (!wightDic.TryGetValue(operand1, out wight1))
            {
                Console.WriteLine($"比较运算符时找不到运算符权重, operand:{operand1}");
            }
            if (!wightDic.TryGetValue(operand2, out wight2))
            {
                Console.WriteLine($"比较运算符时找不到运算符权重, operand:{operand2}");
            }
            return wight1 - wight2;
        }
        #endregion

        #region 逆波兰表达式计算
        /// <summary> 计算逆波兰表达式 </summary>
        private static double CountReversePolishNotation(LinkedList<object> reversePolishNotation)
        {
            if (reversePolishNotation.Count == 0)
            {
                return 0;
            }

            Stack<double> calculateStack = new Stack<double>();

            for (var iter = reversePolishNotation.First; iter != null; iter = iter.Next)
            {
                if (iter.Value is double)
                {
                    calculateStack.Push((double)iter.Value);
                }
                else if (iter.Value is string)
                {
                    PrintStack(calculateStack, $"{iter.Value}:");
                    double countNum = CountOperationResult(ref calculateStack, (string)iter.Value);
                    calculateStack.Push(countNum);
                }
            }

            return calculateStack.Pop();
        }

        /// <summary> 根据符号计算结果 </summary>
        private static double CountOperationResult(ref Stack<double> calculateStack, string symbol)
        {
            if (symbol == "+")
            {
                return calculateStack.Pop() + calculateStack.Pop();
            }
            else if (symbol == "-")
            {
                double num2 = calculateStack.Pop();
                return calculateStack.Pop() - num2;
            }
            else if (symbol == "*")
            {
                return calculateStack.Pop() * calculateStack.Pop();
            }
            else if (symbol == "/")
            {
                double num2 = calculateStack.Pop();
                return calculateStack.Pop() / num2;
            }
            else if (symbol == "!")
            {
                double num = calculateStack.Pop();
                if (num == 0) return 1;
                if (num < 0)
                {
                    Console.WriteLine("不能对符号进行阶乘");
                    return 0;
                }
                if (num - Math.Floor(num) > 1e-6 || num > 125)
                {
                    Console.WriteLine("小数或太大的数不能进行阶乘");
                    return 0;
                }
                int result = 1;
                for(int i = (int)Math.Floor(num); i > 0; --i)
                {
                    result *= i;
                }

                return result;
            }
            else if (symbol == "^")
            {
                double num2 = calculateStack.Pop();
                return Math.Pow(calculateStack.Pop(), num2);
            }
            else if (symbol == "%")
            {
                double num2 = calculateStack.Pop();
                return calculateStack.Pop() % num2;
            }
            else if (symbol == "sin")
            {
                return Math.Sin(calculateStack.Pop());
            }
            else if (symbol == "cos")
            {
                return Math.Cos(calculateStack.Pop());
            }
            else if (symbol == "tan")
            {
                return Math.Tan(calculateStack.Pop());
            }
            else if (symbol == "sinh")
            {
                return Math.Sinh(calculateStack.Pop());
            }
            else if (symbol == "cosh")
            {
                return Math.Cosh(calculateStack.Pop());
            }
            else if (symbol == "tanh")
            {
                return Math.Tanh(calculateStack.Pop());
            }
            else if (symbol == " ")
            {
                return calculateStack.Pop();
            }
            else
            {
                Console.WriteLine($"不存在运算符:{symbol}");
            }
            return calculateStack.Pop();
        }
        #endregion

        #region 打印
        /// <summary> 打印LinkedList链表数据 </summary>
        private static void PrintLinkList(LinkedList<object> linkedList, string extra)
        {
            // 打印
            var printStr = extra;
            foreach (var iter in linkedList)
            {
                printStr += (iter.ToString() + "|");
            }
            if (linkedList.Count > 0)
            {
                Console.WriteLine(printStr.Remove(printStr.Length - 1));
            }
            else
            {
                Console.WriteLine(printStr);
            }
        }

        /// <summary> 打印Stack数据 </summary>
        private static void PrintStack(Stack<double> stack, string extra)
        {
            // 打印
            var printStr = extra;
            foreach (var iter in stack)
            {
                printStr += (iter.ToString() + "|");
            }
            if (stack.Count > 0)
            {
                Console.WriteLine(printStr.Remove(printStr.Length - 1));
            }else
            {
                Console.WriteLine(printStr);
            }
        }
        #endregion 打印
    }
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值