Ich nehme an der Coursera-Klasse für Compiler teil und in der Lektion über Lexer wird angedeutet, dass es einen Zeit-Raum-Kompromiss zwischen der Verwendung eines nicht deterministischen endlichen Automaten (NFA) und eines deterministischen endlichen Automaten (DFA) zum Parsen regulärer Ausdrücke gibt. Wenn ich das richtig verstehe, besteht der Kompromiss darin, dass ein NFA kleiner ist, das Durchlaufen jedoch zeitaufwändiger ist, da alle möglichen Zustände gleichzeitig betrachtet werden müssen und daher meistens in einen DFA umgewandelt werden. Gibt es Lexer, die im "echten" Leben NFAs anstelle von DFAs verwenden, dh einen Compiler, der in der Produktion verwendet wird und nicht nur ein Proof of Concept ist?
7
Antworten:
Ich sehe nur zwei Anwendungen der Verwendung eines NFA (oder vielmehr seines Leistungsautomaten ohne Aufschreiben) anstelle eines minimierten DFA:
Seltsame Syntax, die Ihren DFA in die Luft jagen kann
Wenn Sie die letzte Regel als Vorrang nehmen, muss Ihr Lexer prüfen, ob ein Bezeichner "_" in den letzten 256 Symbolen enthält, und in diesem Fall verkürzen.
quelle
/*
Starten eines Kommentars und springen zum Abgleich*/
im C-Code. Außerdem wäre eine Sprache, die das enthält, für Menschen so gut wie unmöglich zu lesen.Kompilierte lexikalische Analysatoren kompilieren die NFA zu einem DFA.
Gut interpretierte Matcher für reguläre Ausdrücke verwenden dagegen den Thompson-Algorithmus, der die NFA mit Memoisierung simuliert. Dies entspricht dem Kompilieren des NFA zu einem DFA, aber Sie erstellen DFA-Zustände nur bei Bedarf, wenn sie benötigt werden. Bei jedem Schritt besteht Ihr deterministischer Zustand aus einer Reihe von NFA-Zuständen. Wenn Sie dann das nächste Eingabezeichen erhalten, wechseln Sie zu einer neuen Reihe von NFA-Zuständen. Sie speichern zuvor gesehene Zustände und ihre Ausgabeübergänge in einer Hash-Tabelle. Die Hash-Tabelle wird geleert, wenn sie voll ist, sie wächst nicht ohne Bindung.
Der Grund, warum Sie dies auf diese Weise tun, ist, dass die Konvertierung des NFA in DFA in der Größe des regulären Ausdrucks exponentiell dauern kann. Dies möchten Sie sicherlich nicht tun, wenn Sie den regulären Ausdruck nur einmal auswerten.
RE2 ist ein Beispiel für eine Regex-Engine, die (im Wesentlichen) den Thompson-Algorithmus verwendet. Ich kann die brillanten Blog-Beiträge des RE2-Autors Russ Cox nur empfehlen, wenn Sie mehr erfahren möchten (einschließlich vieler historischer Informationen und experimenteller Vergleiche vieler verschiedener Ansätze zur Regex-Suche).
Ich kann auch die E-Mail-Kette " Warum GNU grep schnell ist " nur empfehlen . Lektion 1 lautet: Der häufigste Fall für die Regex-Suche ist die einfache Zeichenfolgensuche.
quelle
Ich wäre überrascht, wenn sie es tun würden. Die Erstellung des Lexers erfolgt einmal (hoffentlich), das Ergebnis wird millionenfach verwendet (denken Sie nur daran, wie viele Token sich in Ihrer mittelgroßen Quelldatei befinden). Wenn es also keine sehr ungewöhnlichen Umstände gibt, lohnt es sich, den Lexer so schnell (und andere ressourcenschonend) wie möglich zu machen, dh einen minimalen DFA anzustreben.
quelle
Im streng formalen Sinne nein. Nichtdeterminismus im theoretischen / mathematischen Sinne ermöglicht es einer Maschine, einen Berechnungspfad basierend darauf zu wählen, ob er schließlich zu einem akzeptierenden Zustand führt oder nicht, ohne in der Eingabe weiter nach vorne zu schauen . In diesem strengen Sinne handelt es sich also um eine Eigenschaft, die nur für theoretische Untersuchungen geeignet ist, und es gibt keine echte nicht deterministische Maschine. Insbesondere in diesem Fall können Sie keine NFA erstellen, es sei denn, Sie können in die Zukunft sehen In diesem Fall ist das Erstellen eines Compilers mit diesem Talent eine Verschwendung! ;).
Nichtdeterministisch und Nichtdeterminismus werden jedoch häufig in einem schwächeren, verschwommen definierten Sinne verwendet. Manchmal kann es randomisiert / probabilistisch bedeuten - der Algorithmus wirft eine Münze, in einer formalen Umgebung wird dies als probabilistischer / randomisierter Algorithmus untersucht und nicht als Nichtdeterminismus bezeichnet. Eine andere Verwendung ist ein Algorithmus, der bei zwei Läufen mit derselben Eingabe nicht unbedingt dieselbe Ausgabe erzeugt - er ist möglicherweise nicht zufällig, aber ein Teil seines Verhaltens ist nicht spezifiziert, sodass möglicherweise mehrere gültige Ausgaben vorliegen (ich persönlich denke dies Definition stammt aus verwirrend kommt un -determined und nicht -deterministic.
Trotzdem könnten Sie im Prinzip einen Lexer bauen, der in einem dieser schwächeren, informellen Sinne nicht deterministisch ist, aber es wäre kein NFA (das ist ein striktes formales Maschinenmodell), und ich kann mir nicht vorstellen, dass es ein Absturz sein würde heiße Idee auch - ein Lexer muss ziemlich vorhersehbar sein.
Die letzte Option besteht darin, dass Sie Nichtdeterminismus durch Backtracking oder Parallelität simulieren können. In diesem Fall verlieren Sie jedoch die offensichtliche Effizienz des Nichtdeterminismus, da Sie ihn effektiv in eine deterministische Berechnung umwandeln, sodass Sie nicht besser sind aus als mit einem DFA.
quelle