@pat.wasiewiczPiszemy trochę bardziej złożony kalkulator w C#.NET - #3
Piszemy trochę bardziej złożony kalkulator w C#.NET - #3
17.04.2013 23:57
Przypomnienie
Witam ponownie po dłuższej przerwie. Przypomnę pierwsze dwa wpisy dot. kalkualtora: Część1 : stworzyliśmy klasę Expression Część 2 : pokazaliśmy jak przekształcić wyrażenie zapisane postfiksowo w string'u do klasy ExpressionExpression z notacji infiksowej
Schemat będzie wyglądał tak: zmieniamy wyrażenie napisane infiksowo na postfiksowo a potem korzystając z metody napisanej w części #2 dostaniemy instancję klasy Expression.
Algorytm zmiany wyrażenia infiksowego do ONP znajdziemy na wikipedii.
Zacznijmy od metody pomocniczej:
private static byte GetPriority(char sign) { switch (sign) { case '(': return 0; case ')': case '+': return 1; case '*': case '/': return 2; default: throw new ArgumentException("Błędny operator!"); } }
Teraz już właściwy kod:
public static Expression FromInfix(string input) { var output = new StringBuilder(); var operators = new Stack<char>(); var arguments = Regex.Split(input, @"\s+"); foreach (var sym in arguments) { // jeżeli argument if (!Regex.IsMatch(sym, @"^[\+|\*|/|(|)]$")) { output.Append(" "); output.Append(sym); } else { // dostaliśmy operator var symChar = Convert.ToChar(sym); switch (symChar) { case '(': operators.Push(symChar); break; case ')': var leftBracket = operators.Pop(); while (!leftBracket.Equals('(')) { output.Append(" "); output.Append(leftBracket); leftBracket = operators.Pop(); } break; case '*': case '+': case '/': // stos pusty albo operator na wierzchołku ma mniejszy priorytet if (operators.Count == 0 || GetPriority(operators.Peek()) < GetPriority(symChar)) { operators.Push(symChar); break; } var oper = operators.Peek(); // zdejmij wszystkie operatory o wiekszym bądź równym priorytecie i zapisz na wyjściu while (GetPriority(oper) >= GetPriority(symChar) && operators.Count > 0) { output.Append(" "); output.Append(oper); operators.Pop(); oper = operators.Peek(); } // wrzuć na stos aktualny operator operators.Push(symChar); break; } } } // jeśli jakiś operator został na stosie, też go zapisz na wyjściu if (operators.Count == 1) { output.Append(" "); output.Append(operators.Peek()); } // usuń pierwszą spację output = output.Remove(0, 1); return FromOnp(output.ToString()); }
Teraz możemy przetesować nasz kod:
var expr5 = Expression.FromInfix("( 12 + 3 ) * x"); Console.Write( "Wyrażenie wygląda tak: {0}\nJego wartość to {1}\n" + "Pochodna to {2}\nCałka od 1 do 2 to {3}\n\n", expr5, expr5.Calculate(4), expr5.Derivative(), expr5.Integral(1, 2));
Co da nam wynik: