2012-10-26 9 views
5

Lernen, die Parsec-Bibliothek zu verwenden, Teil der Hausaufgaben.Haskell Parsec überspringen Sie alle Wörter, die nicht vordefiniert

EDIT: Vorschläge zur Verwendung anderer Bibliotheken sind willkommen, der Punkt ist das Parsen.

Was ich will, ist, alle Wörter mit einem Großbuchstaben und vier Himmelsrichtungen aus jedem Satz zu extrahieren. Beispiel: "Belgien liegt total südlich von Holland." sollte "Belgien Südholland" finden und zurückgeben.

Was ich nicht herausfinden kann ist, wie man jeden Input ignoriert, der nicht Kompassrichtung ist. Ich suchte etwas entlang der Linien von

'many (not compassDirection >> space)' 

zu finden, aber g (h) oogle hilft mir nicht.

Der folgende Code ist offensichtlich auf der 'viele' Funktion fest.

readExpr :: String -> String 
readExpr input = case parse (parseLine) "" input of 
    Left err -> "No match: " ++ show err 
    Right val -> "Found: " ++ showVal val 

parseLine :: Parser GraphValue 
parseLine = do 
      x <- parseCountry 
      space 
      many (some (noneOf " ") >> space) 
      y <- parseCompass 
      space 
      many (some (noneOf " ") >> space) 
      z <- parseCountry 
      return $ Direction [x,y,z] 

compassDirection :: Parser String 
compassDirection = string "north" <|> 
        string "south" <|> 
        string "east" <|> 
        string "west" 

parseCountry :: Parser GraphValue 
parseCountry = do 
       c <- upper 
       x <- many (lower) 
       return $ Country (c:x) 

parseCompass :: Parser GraphValue 
parseCompass = do 
       x <- compassDirection 
       return $ Compass x 
+1

(Nur stilistisch könnte man 'compassDirection = Wahl $ map string [" Norden "," Süden "," Osten "," Westen "]'.) – huon

+0

Gut gemacht, um ehrlich zu sein, klar, zeigt gute Anstrengung zu Lösen Sie das Problem bis jetzt und stellen Sie Ihren vorhandenen Code zur Verfügung. Eine gute Frage. +1 – AndrewC

Antwort

4

Ich werde nicht ins Detail gehen, da dies Hausaufgaben ist und das OP sagte das "wichtige Ding ist das Parsing".


Die Art, wie ich dieses Problem lösen würde:

  • tokenize den Eingang. Brechen Sie es in Worte; dies wird den echten Parsing-Schritt davon befreien, sich um Token-Definitionen (d. h. "is% # @ [Teil eines Wortes?") oder Leerzeichen kümmern zu müssen. Dies könnte so einfach wie words oder Sie könnten Parsec für die Tokenisierung verwenden. Dann haben Sie [Token] (oder [String], wenn Sie bevorzugen).

  • ein Parser für Kompassrichtungen. Sie haben dies bereits (gute Arbeit), aber es muss ein bisschen geändert werden, wenn die Eingabe [String] statt String ist.

  • ein Parser für Wörter, die mit einem Großbuchstaben beginnen.

  • ein Parser für alles andere, der immer dann erfolgreich ist, wenn ein Token angezeigt wird, das keine Kompassrichtung oder ein Wort ist, das mit einem Großbuchstaben beginnt.

  • ein Parser, der auf jedem Token funktioniert, aber zwischen guten und schlechten Dingen unterscheidet, vielleicht mit einem algebraischen Datentyp.

  • ein Parser, ohne dass zu klar auf viele Token

Hoffentlich ist klar, funktioniert; Sie müssen sich immer noch Gedanken darüber machen, wann Sie den Müll entsorgen sollen. Die Grundidee besteht darin, das Problem in viele kleine Teilprobleme zu zerlegen, die Teilprobleme zu lösen und diese Lösungen zusammen zu kleben.

0

Können Sie nicht einfach die Zeichenfolge in words, filter diejenigen teilen, die mit einem Großbuchstaben beginnen oder sind eine Kompassrichtung, und dann unwords sie wieder zusammen? Keine Notwendigkeit, die Pistole Parsec herauszuziehen.

+1

Dies ist nur ich den Dreh raus die Grundlagen.Schließlich sollten wir die natürliche Sprache so genau wie möglich analysieren. "Deutschland und Italien teilen keine Grenze, aber Belgien und Schweden." Ich dachte, meine beste Wette wäre, herauszufinden, wie man wirklich grundlegende vordefinierte Sätze parsen kann. – Taelia

3

Ich werde Ihnen sagen, wie ich anfangen würde und dann beraten, wie ich weitermachen würde.

Ich würde dies auf eine abstrakte Datenstruktur stützen - wie Sie zusätzliche Wörter hinzufügen können Sie sie klassifizieren enger:

data Word = Country String | Direction NSEW | Unclassified String 
data NESW = North | East | South | West 

so meine Antwort darauf, wie Sie Worte überspringen Sie nicht wissen, ist, dass Sie müssen nicht - lassen Sie sie als nicht klassifiziert.

Der Anwendungsstil ist schöner als der monadische Stil.

Ich denke compassDirection Kapitelle erlauben sollte:

compassDirection :: Parser NESW 
compassDirection = north <|> south <|> east <|> west where 
    north = North <$ (string "north" <|> string "North") 
    east = ... 

Sie countryCountry <$> ((:) <$> upper <*> many lower)

Dann können Sie ein allumfassendes Unclassified <$> many letter mit definieren können.

Ihr Wortanalysierer kann derzeit

word = compassDirection <|> country <|> unclassified 

, aber feststellen, dass compassDirection vor country kommen muss, weil sonst countryNorth entsprechen würde.

Sie können im Moment

words = word `sepBy1` space 

tun, die in Ordnung ist, aber man muss muss muss word oder words nicht verwenden, wenn Sie analysieren Sätze mehr richtig, weil Sie die Kontrolle über das verlieren, was das Wort ist. An diesem Punkt würden Sie noun, adjective, nounPhrase, verb, adjective, adjectivalPhrase usw. benötigen, um eine Satzstruktur zu verwenden. Sätze, die Sie nicht analysieren können, bedeuten, dass Sie Ihrer Grammatik neue Konstrukte hinzufügen müssen.

Es lohnt sich, dass Wortparser den Whitespace nach ihnen (oder davor) schlucken oder Refactoring mit einem Präprozessor durchführen, der Wörter von Leerzeichen und Interpunktion trennt. Erwägen Sie, einen fullStop Parser zu haben, wenn Sie Brite sind, oder einen period Parser, wenn Sie Amerikaner sind. Verwenden Sie es, wenn Sie einen Satzparser erstellen.

Die Verwendung von anwendungsspezifischen Funktionen und Funktionen höherer Ordnung macht es viel einfacher, Ihre Grammatik zu schreiben, weil Sie sie nicht mit monadischer Notation überladen haben, und sie wird wie Sätze aussehen. Beispiel: Sie könnten nvn = NVN <$> noun <*> verb <*> noun tun, wenn Sie einen AST-Ansatz (Abstract Data Structure) mit einem Konstruktor pro Grammatikobjekt verwenden möchten. Wenn Sie es vorziehen, ein paar Wörter zu haben, die alle denselben Typ haben, können Sie nvn = sequence [noun,verb,noun] tun.

Die meisten Computersprachen werden mit einem AST-Ansatz geparst, aber ich habe keine direkte Erfahrung mit Parsing natürlicher Sprache, die ich aus dem Linguistik-Abschluss meiner Frau übernommen habe.

Wenn Sie sich hinsetzen und schreiben, wie Sie Kategorien von Wörtern, Phrasen, Klauseln und Sätzen miteinander kombinieren können, können Sie den Parser ziemlich schnell schreiben.

+0

Erstaunliche Antwort, danke für die Mühe. Ich lasse die Informationen auf mich fallen und dann werde ich versuchen, all das umzusetzen. – Taelia