Advertisement
alexpeevk9

Shunting Yard Algorithm Calculator

Apr 12th, 2021
142
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 7.65 KB | None | 0 0
  1. // Първа и Втора задача са комбинирани в една задача като първа задача е описана в метода ReverseShuntingYard(), а втора в Calculator() – за да се проследят операциите една след друга.
  2.  
  3. namespace ConsoleApp2
  4. {
  5.     using System;
  6.     using System.Collections.Generic;
  7.     using System.Text;
  8.     class Program
  9.     {
  10.         static void Main()
  11.         {
  12.             // 6/(2*3-4)+(9/3-2)*2 TEST
  13.             // (9/(5-6/2)-1)*2+8/(7-3) HW
  14.             string str = "(9/(5-6/2)-1)*2+8/(7-3)";
  15.             Console.WriteLine(str);
  16.             string reverse = ReverseShuntingYard(str);
  17.             Console.WriteLine(reverse);
  18.             char finalSum = Calculator(reverse);
  19.             Console.WriteLine(finalSum);
  20.         }
  21.         private static string ReverseShuntingYard(string str)
  22.         {
  23.             Stack<char> stack = new Stack<char>();
  24.             StringBuilder finalResult = new StringBuilder(); // Създаваме си StringBuilder за да пестим RAM памет
  25.             for (int i = 0; i < str.Length; i++) // Обхождаме израза
  26.             {
  27.                 char x = str[i]; // стойността на позицията която сме в момента
  28.                 if (x == '(')
  29.                     stack.Push(x); // От условие: Ако е отваряща скоба, се добавя в стека;
  30.                 else if (x == ')')
  31.                 {
  32.                     //Ако е затваряща скоба, от стека в крайния израз се прехвърлят всички операции
  33.                     while (stack.Count > 0 && stack.Peek() != '(')
  34.                         finalResult.Append(stack.Pop());
  35.                     stack.Pop(); //докато се достигне отваряща скоба, която се премахва.
  36.                 }
  37.                 else if (char.IsDigit(x)) // От 0 до 9
  38.                 {
  39.                     finalResult.Append(x); // Ако поредният символ е число, се прехвърля в крайния израз;
  40.                 }
  41.                 else  // Тук могат да бъдат подадени '-','+','*','/' по условие няма други случаи
  42.                 {
  43.                     while (stack.Count > 0 && stack.Peek() != '(' && Prior(x) <= Prior(stack.Peek()))
  44.                         // Ако стекът е празен или стигнем да затваряща скоба спираме -> от условие
  45.                         //Ако е операция, от стека в крайния израз се прехвърлят всички операции
  46.                         //с равен или по - висок приоритет(Prior(x))
  47.                         finalResult.Append(stack.Pop());
  48.                     stack.Push(x); // Новата операция се добавя в стека
  49.                 }
  50.                 //За тестване
  51.                 //Console.WriteLine($"FinalResult: {finalResult.ToString()}");
  52.                 //Console.WriteLine($"Stack: {string.Join("", stack)}");
  53.             }
  54.             //Ако след прочитането на целия израз в стека са останали елементи,
  55.             //те се прехвърлят в крайния израз
  56.             while (stack.Count > 0)
  57.             {
  58.                 finalResult.Append(stack.Pop());
  59.             }
  60.  
  61.             return finalResult.ToString();
  62.         }
  63.         //По отношение на основните 4 операции се установяват следните приоритети:
  64.         //1: събиране и изваждане
  65.         //2: умножение и деление
  66.         private static int Prior(char c)
  67.         {
  68.             return c switch
  69.             {
  70.                 '+' => 1,
  71.                 '-' => 1,
  72.                 '*' => 2,
  73.                 _ => 2 // Default случеят е делене '/', защото по условие не се очаква да бъдат
  74.                 //подавани други стойности за да има exception
  75.             };
  76.         }
  77.         private static char Calculator(string str)
  78.         {
  79.             Stack<char> stack = new Stack<char>();
  80.             for (int i = 0; i < str.Length; i++) // Обхождаме ОПЗ
  81.             {
  82.  
  83.                 char x = str[i];  // стойността на позицията която сме в момента
  84.                 if (char.IsDigit(x)) // От 0 до 9
  85.                 {
  86.                     stack.Push(x);
  87.                 }
  88.                 else // По условие нямаме други случаи освен '*' '/' '+' '-' и горе споменатите 0 -9
  89.                 {
  90.                     // Взимаме Първият и Вторият елемент и извършваме дадената операция (в обратен ред понеже е стек)
  91.                     double secondNumber = double.Parse(FindFirstNumber(stack).ToString());
  92.                     double firstNumber = double.Parse(FindFirstNumber(stack).ToString());
  93.                     string charSum = Calculate(x, firstNumber, secondNumber).ToString();
  94.                     // Пресмятаме дадената операция и връщаме първият индекс на string-ът, защото стекът ни е от тип char
  95.                     stack.Push(charSum[0]);
  96.                 }
  97.             }
  98.             return stack.Peek();
  99.         }
  100.         private static char FindFirstNumber(Stack<char> stack)
  101.         {
  102.             char firstNumber = ' ';
  103.             bool foundNumber = false;
  104.             // Създаваме си опашка за да може в правилен ред да подредим същият ред на стека
  105.             Stack<char> queue = new Stack<char>();
  106.             while (stack.Count != 0)
  107.             {
  108.                 // Ако е число и още не сме намерили число
  109.                 if (char.IsDigit(stack.Peek()) && foundNumber == false)
  110.                 {
  111.                     firstNumber = stack.Pop();
  112.                     foundNumber = true; // След като сме намерили число не е нужно да презаписваме стойността на firstNumber
  113.                 }
  114.                 else
  115.                 {
  116.                     queue.Push(stack.Pop());
  117.                 }
  118.             }
  119.             if(firstNumber == ' ') // Ако не е намерило число сме сбъркали някъде и дава грешка
  120.             {
  121.                 throw new NotImplementedException("Error");
  122.             }
  123.             foreach (char symb in queue) // Връщаме стекът в началният му вид
  124.             {
  125.                 stack.Push(symb);
  126.             }
  127.  
  128.             return firstNumber;
  129.         }
  130.         private static double Calculate(char c, double firstNumber, double secondNumber)
  131.         {
  132.             return c switch
  133.             {
  134.                 '+' => firstNumber + secondNumber,
  135.                 '-' => firstNumber - secondNumber,
  136.                 '*' => firstNumber * secondNumber,
  137.                 _ => firstNumber / secondNumber // Default случеят е делене '/', защото по условие не се очаква да бъдат
  138.                 //подавани други стойности за да има exception
  139.             };
  140.         }
  141.     }
  142. }
  143.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement