Implementierung von 'read' für einen linksassoziativen Baum in Haskell

8

Es fällt mir schwer, Read für eine Baumstruktur. Ich möchte eine links-assoziative Zeichenfolge (mit Parens) wie ABC(DE)F verwenden und sie in einen Baum umwandeln. Dieses spezielle Beispiel entspricht dem Baum

.

Hier ist der Datentyp, den ich verwende (obwohl ich für Vorschläge offen bin):

%Vor%

Dieser bestimmte Baum wäre in Haskell:

%Vor%

Meine show Funktion sieht folgendermaßen aus:

%Vor%

Ich möchte eine read -Funktion machen, so dass

%Vor%     
Snowball 02.05.2012, 05:12
quelle

3 Antworten

13

Dies ist eine Situation, in der die Verwendung einer Parsing-Bibliothek den Code erstaunlich kurz und extrem ausdrucksstark macht. (Ich war erstaunt, dass es so war, als ich experimentierte, um das zu beantworten!)

Ich werde Parsec verwenden (dieser Artikel enthält einige Links für weitere Informationen) und es in " Anwendungsmodus "(statt monadisch), da wir die zusätzliche Kraft / Fußschießfähigkeit von Monaden nicht benötigen.

Code

Zuerst die verschiedenen Importe und Definitionen:

%Vor%

Nun ist die Grundeinheit des Baumes entweder ein einzelnes Zeichen (das ist keine Klammer) oder ein eingeklammerter Baum. Der eingeklammerte Baum ist nur ein normaler Baum zwischen ( und ) . Und ein normaler Baum ist nur Einheiten, die links-assoziativ in Zweige gesetzt werden (es ist extrem selbstrekursiv). In Haskell mit Parsec:

%Vor%

(Ja, das ist der ganze Parser!)

Wenn wir wollten, könnten wir ohne paren und unit auskommen, aber der obige Code ist sehr aussagekräftig, also können wir ihn so lassen wie er ist.

Als kurze Erklärung (Ich habe Links zur Dokumentation bereitgestellt):

  • (<|>) bedeutet im Grunde "linker Parser oder rechter Parser";
  • (<?>) ermöglicht es Ihnen, schönere Fehlermeldungen zu erstellen;
  • noneOf wird parse alles, was nicht in der angegebenen Liste von Zeichen ist;
  • between dauert drei Parser, und gibt den Wert des dritten Parsers zurück, solange er durch die erste und die zweite begrenzt ist;
  • char analysiert sein Argument wörtlich.
  • many1 analysiert ein oder mehrere seiner Argumente in eine Liste (es scheint, dass die leere Zeichenfolge ungültig ist, daher many1 , anstatt many , die null oder mehr analysiert);
  • eof stimmt überein das Ende der Eingabe.

Wir können % verwenden. co_de% Funktion zum Ausführen des Parsers (es gibt parse zurück, Either ParseError Tree ist ein Fehler und Left ist ein korrekter Parse).

Als Right

Wenn Sie es als read like-Funktion verwenden, könnte das etwa so aussehen:

%Vor%

(Ich habe read verwendet, um Konflikte mit read' zu vermeiden; wenn Sie eine Prelude.read -Instanz haben wollen, müssen Sie etwas mehr arbeiten, um Read (oder was auch immer erforderlich) zu implementieren, aber es sollte nicht zu schwer mit dem eigentlichen Parsing bereits abgeschlossen sein.)

Beispiele

Einige grundlegende Beispiele:

%Vor%

Fehler demonstrieren:

%Vor%

Und schließlich der Unterschied zwischen readPrec und tree :

%Vor%

Fazit

Parsec ist unglaublich! Diese Antwort kann lang sein, aber der Kern davon ist nur 5 oder 6 Zeilen Code, die die ganze Arbeit erledigen.

    
huon 02.05.2012, 08:28
quelle
6

Das sieht sehr ähnlich aus wie eine Stapelstruktur. Wenn Sie auf Ihre Eingabe-Zeichenfolge "ABC(DE)F" stoßen, finden Sie Leaf ein beliebiges Atom, das Sie finden (nicht-Klammern), und fügen es in eine Akkumulatorliste ein. Wenn Sie 2 Einträge in der Liste haben, geben Sie Branch zusammen. Dies könnte mit etwas wie (Hinweis, ungetestet, nur um eine Idee zu geben) gemacht werden:

%Vor%

Das mag einige Modifikationen erfordern, aber ich denke, es ist genug, um Sie auf die richtige Spur zu bringen.

    
ScottWest 02.05.2012 05:48
quelle
3

Die Parsec-Antwort von dbaupp ist sehr einfach zu verstehen. Als Beispiel für einen "Low-Level" -Ansatz ist hier ein handgeschriebener Parser, der eine Erfolgsfortsetzung verwendet, um das links-assoziative Baumgebäude zu behandeln:

%Vor%     
Chris Kuklewicz 02.05.2012 13:34
quelle

Tags und Links