Better approach for this function that calculates a mathematical expression from a string?

45 Views Asked by At

The problematic to resolve:

Given a string that contains a mathematical expression, I need a function on C# language that receives that string and returns its decimal result that respects the operators hierarchy. So for example:

CalculateExpression("10×2-5÷4"); should return 18.75.

Some considerations:

  • My initial approach is based on a stack method by using a Reverse Polish Notation (RPN).
  • I'm not taking into consideration parenthesis ( ) nor this caret: ^ because for my project case, the user will never be able to type those characters. So for operators, the only ones that would be available are : + - × ÷.
  • The function should recognize decimals dots, so for example CalculateExpression("15.4+5.5"); should return 20.9.

What I did:

While it works as I wanted, I'd like to receive feedback on the overall algorithm process, so I can enhance it or maybe there is something I didn't consider that someone would if it is seen or maybe there is already a library that does something done here.

Any tip works.

I tried to put comments on the most important things done and overall, what I'm doing in each section:

private static decimal CalculateInput(string input)
    {
        //the same input but splitted, so: "10+5" becomes ["10", "+", "5"]
        List<string> splittedInput = new List<string>();
        //stack for storing operators based on the RPN algorithm
        Stack<string> operatorStack = new Stack<string>();
        //the same splittedInput but formatted as PostFix, so: [10, +, 5] becomes ["10", "5", "+"]
        List<string> splittedPostFixExp = new List<string>();

        string strNumber = "";
        foreach (char theChar in input)
        {//looping through the string to append each operand and operator to the splittedInput List
            if (theChar != '+' && theChar != '-' && theChar != '×' && theChar != '÷')
            {
                strNumber += theChar;
            }
            else
            {
                splittedInput.Add(strNumber);
                splittedInput.Add(theChar.ToString());
                strNumber = string.Empty;
            }
        }
        splittedInput.Add(strNumber);

        //formatting the splittedInput to the PostFixed format
        foreach (string token in splittedInput)
        {
            if (token != "+" && token != "-" && token != "×" && token != "÷") //isOperand
            {
                splittedPostFixExp.Add(token);
            }
            else
            {
                if (operatorStack.Count != 0)
                {
                    if ((token == "×" || token == "÷") && (operatorStack.Peek() == "+" || operatorStack.Peek() == "-"))
                    {//upcoming token has greater precedence than top element on stack
                        operatorStack.Push(token);
                    }
                    else if ((token == "+" || token == "-") && (operatorStack.Peek() == "×" || operatorStack.Peek() == "÷"))
                    {//upcoming token has lower precedence than top element on stack
                        PopUntilLowerPrecedence(token);
                    }
                    else
                    {//upcoming token has the same precedence than top element on stack (basically the same as the previous condition)
                        PopUntilLowerPrecedence(token);
                    }
                }
                else
                {
                    operatorStack.Push(token);
                }
            }
        }

        while (operatorStack.Count != 0)
        {//Poping each operator in the stack to the end of the PostFixExpression
            splittedPostFixExp.Add(operatorStack.Pop());
        }

        void PopUntilLowerPrecedence(string token)
        {//function for poping the stack until the token has higher precedence than operatorStack.Peek()
            while (operatorStack.Count != 0)
            {
                if (((token == "×" || token == "÷") && (operatorStack.Peek() == "+" || operatorStack.Peek() == "-")))
                {//upcoming token has NOW greater precendence
                    break;
                }
                splittedPostFixExp.Add(operatorStack.Pop());
            }
            operatorStack.Push(token);
        }

        //calculating result by using the PostFixExpression
        for (int i = 0; i < splittedPostFixExp.Count; i++)
        {
            if (splittedPostFixExp[i] == "+" || splittedPostFixExp[i] == "-" || splittedPostFixExp[i] == "×" || splittedPostFixExp[i] == "÷")
            {
                decimal left = decimal.Parse(splittedPostFixExp[i - 2]);
                decimal right = decimal.Parse(splittedPostFixExp[i - 1]);
                decimal result = 0M;

                switch (splittedPostFixExp[i])
                {
                    case "+":
                        result = left + right;
                        break;
                    case "-":
                        result = left - right;
                        break;
                    case "×":
                        result = left * right;
                        break;
                    case "÷":
                        result = left / right;
                        break;
                }

                for (int j = i; j > i - 3; j--)
                {
                    splittedPostFixExp.RemoveAt(j);
                }

                splittedPostFixExp.Insert(i - 2, result.ToString());

                i = 0;
            }
        }

        return decimal.Parse(splittedPostFixExp[0]);
    }

Thanks in advance! Wishing you a nice day:)

0

There are 0 best solutions below