Hallo mal wieder.
Vielleicht habt ihr mitbekommen, dass ich vor kurzem einen Formelparser für meine MathUtils geschrieben habe. Der Funktioniert auch sehr schön, so wie er soll. Ich habe aber gerade bemerkt, dass die Ausführungsgeschwindigkeit meines Algorithmus' etwa 5 mal langsamer ist, als beim Quadsoft Expressionparser, und 10 mal langsamer, als bei Telcromes MathPro. Ich konnte die Ursache dafür bereits lokalisieren, und zwar ist es RegEx.
Ich parse den Eingabestring mit RegEx in die einzelnen Tokens, die dann per shunting-yard-Algorithmus in Postfixnotation umgestellt werden und rechne diese dann aus. Laut meinen Messungen benötigt RegEx über die Hälfte der Ausführungszeit. Die genannte Stelle sieht so aus:
Spoiler anzeigen
Wie ihr seht filtere ich mit RegEx alle Zahlen im Term raus und ersetze sie durch ein Zeichen, dass ich dann im Tokenizing-Schritt bequem auslesen kann.
Meine Frage ist jetzt: kann ich den RegEx-Aufruf durch irgendwas anderes ersetzen bzw. das Tokenizing komplett anders gestalten? Mir ist nichts anderes eingefallen.
Edit: mir geht es um die Zeilen 9, 10 und 11.
Vielleicht habt ihr mitbekommen, dass ich vor kurzem einen Formelparser für meine MathUtils geschrieben habe. Der Funktioniert auch sehr schön, so wie er soll. Ich habe aber gerade bemerkt, dass die Ausführungsgeschwindigkeit meines Algorithmus' etwa 5 mal langsamer ist, als beim Quadsoft Expressionparser, und 10 mal langsamer, als bei Telcromes MathPro. Ich konnte die Ursache dafür bereits lokalisieren, und zwar ist es RegEx.
Ich parse den Eingabestring mit RegEx in die einzelnen Tokens, die dann per shunting-yard-Algorithmus in Postfixnotation umgestellt werden und rechne diese dann aus. Laut meinen Messungen benötigt RegEx über die Hälfte der Ausführungszeit. Die genannte Stelle sieht so aus:
C#-Quellcode
- private static List<string> GetInfixTokens(string term)
- {
- //Leerzeichen entfernen und in Kleinbuchstaben konvertieren
- term = term.Replace(" ", string.Empty).ToLowerInvariant();
- var tokens = new List<string>();
- //mit RegEx alle Zahlen aussortieren
- var r = new Regex(@"(?<number>[0-9]+(\" + CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator + @"[0-9]+){0,1}(e[+\-]{0,1}[0-9]+){0,1})"); //(@"(((?<=(\(|^))(?<sign>[+\-]{0,1}))|(?<=.))(?<number>([0-9]+)(\.[0-9]+){0,1})");
- var numbers = r.Matches(term);
- term = r.Replace(term, "1");
- //Term in Tokens teilen
- var possibleTokens = new string[] { "+", "-", "*", "/", "^", "%", "sqrt", "root", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "ln", "log", "abs", "int", "(", ")", ";", "pi", "e" };
- var numberIndex = 0;
- while (term.Length > 0)
- {
- var validToken = false;
- //Zahlen prüfen
- if (term.StartsWith("1"))
- {
- tokens.Add(numbers[numberIndex].Groups["number"].Value);
- numberIndex++;
- if (term.Length > 1)
- term = term.Substring(1);
- else
- term = string.Empty;
- validToken = true;
- }
- //Operatoren, Klammern und Funktionen prüfen
- foreach (var token in possibleTokens)
- if (term.StartsWith(token))
- {
- if ((token == "+" || token == "-") && (tokens.Count == 0 || tokens.Last() == "(")) //Vorzeichen
- {
- if (token == "-")
- tokens.Add("!");
- }
- else if (token == "pi") //PI
- tokens.Add(System.Math.PI.ToString());
- else if (token == "e") //e
- tokens.Add(System.Math.E.ToString());
- else
- tokens.Add(token);
- if (term.Length > token.Length)
- term = term.Substring(token.Length);
- else
- term = string.Empty;
- validToken = true;
- break;
- }
- //Token nicht bekannt
- if (!validToken)
- throw new ArgumentException("Dieser Term enthält einen ungültigen Token.");
- }
- return tokens;
- }
Wie ihr seht filtere ich mit RegEx alle Zahlen im Term raus und ersetze sie durch ein Zeichen, dass ich dann im Tokenizing-Schritt bequem auslesen kann.
Meine Frage ist jetzt: kann ich den RegEx-Aufruf durch irgendwas anderes ersetzen bzw. das Tokenizing komplett anders gestalten? Mir ist nichts anderes eingefallen.
Edit: mir geht es um die Zeilen 9, 10 und 11.