2012-10-26 9 views
5

Imparare ad usare la libreria Parsec, parte dei compiti.Haskell Parsec salta tutte le parole che non sono predefinite

MODIFICA: I suggerimenti per utilizzare altre librerie sono benvenuti, il punto è l'analisi.

Quello che voglio è estrarre tutte le parole con una lettera maiuscola e quattro direzioni della bussola da qualsiasi frase. Esempio: "Il Belgio si trova totalmente a sud dell'Olanda". dovrebbe trovare e restituire "Belgio Olanda meridionale".

Quello che non riesco a capire è come ignorare (mangiare) qualsiasi input che non sia una direzione della bussola. Stavo cercando di trovare qualcosa sulla falsariga di

'many (not compassDirection >> space)' 

ma g (h) oogle non mi sta aiutando.

Il seguente codice è ovviamente bloccato sulla funzione "molti".

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

(Proprio stilisticamente, potresti scrivere 'compassDirection = choice $ map stringa [" nord "," sud "," est "," ovest "]'.) – huon

+0

Complimenti per essere onesti, chiari, mostrando un buon sforzo per risolvere il problema fino ad ora e fornire il codice esistente. Una buona domanda +1 – AndrewC

risposta

4

Non entrerò nello specifico poiché questo è compito a casa e l'OP ha detto che "l'importante è l'analisi".


Il modo in cui mi piacerebbe risolvere questo problema:

  • tokenize l'ingresso. Spezzalo in parole; questo libererà il vero passo di parsing da doversi preoccupare delle definizioni di token (cioè "è% # @ [parte di una parola?") o spazio bianco. Questo potrebbe essere semplice come words oppure è possibile utilizzare Parsec per la tokenizzazione. Quindi avrai [Token] (o [String] se preferisci).

  • un parser per le direzioni della bussola. Hai già questo (buon lavoro), ma dovrà essere modificato un po 'se l'input è anziché String.

  • un parser per le parole che iniziano con una lettera maiuscola.

  • un parser per tutto il resto, che riesce ogni volta che vede un token che non è una direzione della bussola o una parola che inizia con un tappo.

  • un parser che funziona su qualsiasi token, ma distingue tra roba buona e roba cattiva, forse utilizzando un tipo di dati algebrico.

  • un parser che funziona su un sacco di gettoni

Speriamo che questo è chiaro, senza essere troppo chiaro; dovresti ancora preoccuparti di quando scartare la spazzatura, per esempio. L'idea di base è quella di suddividere il problema in molti piccoli sotto-problemi, risolvere i problemi secondari, quindi incollare le soluzioni insieme.

0

Non puoi semplicemente dividere la stringa in words, filter quelli che iniziano con una lettera maiuscola o sono una direzione della bussola, e quindi unwords di nuovo insieme? Non è necessario estrarre la pistola Parsec.

+1

Questo è solo io che sto imparando le basi.Alla fine, dovremmo analizzare il linguaggio naturale il più vicino possibile. "La Germania e l'Italia non condividono una frontiera, ma il Belgio e la Svezia lo fanno". Ho immaginato che la mia migliore scommessa sarebbe iniziare a capire come analizzare le frasi predefinite di base reali. – Taelia

3

Ho intenzione di dirti come vorrei iniziare e poi consigli su come porterei avanti.

avrei baso questo su una struttura dati astratta - quando si aggiungono le parole in più si possono classificare più da vicino:

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

quindi la mia risposta a come si salta parole che non conoscono è che non è necessario - lasciarli come non classificati.

Lo stile applicativo è più bello dello stile monadico.

Penso compassDirection dovrebbe permettere capitali:

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

È possibile definire country utilizzando Country <$> ((:) <$> upper <*> many lower)

Poi si può avere un catch-all Unclassified <$> many letter.

tua parola parser può attualmente essere

word = compassDirection <|> country <|> unclassified 

a meno di notare che compassDirection deve venire prima country perché altrimenti sarebbe partita countryNorth.

Si può fare

words = word `sepBy1` space 

in questo momento va bene così, ma è necessario non deve necessario utilizzare word o words frasi quando si sta analizzando più propriamente, perché si perde il controllo su ciò che la parola è. A quel punto avresti bisogno di noun, adjective, nounPhrase, verb, adjective, adjectivalPhrase ecc per passare una struttura di frasi. Le frasi che non puoi analizzare significano che devi aggiungere nuovi costrutti alla tua grammatica.

Vale la pena fare in modo che i parser di parole inghiottiscano lo spazio bianco dopo di loro (o prima), o refactoring utilizzando un preprocessore che divide le parole da spazi e segni di punteggiatura. Considera di avere un parser fullStop se sei inglese o un parser period se sei americano. Usalo quando crei un parser di frase.

L'uso delle funzioni di ordine superiore e applicativo renderà molto più chiaro scrivere la grammatica, perché non lo farai ingombrare con la notazione monadica e sembrerà una frase. Esempio: è possibile eseguire nvn = NVN <$> noun <*> verb <*> noun se si desidera utilizzare principalmente un approccio AST (Abstract Data Structure) con un costruttore per oggetto grammaticale. Se si preferisce avere un gruppo di parole che si trovano nello stesso tipo, è possibile eseguire nvn = sequence [noun,verb,noun].

La maggior parte dei linguaggi per computer viene analizzata utilizzando un approccio AST, ma non ho alcuna esperienza diretta di analisi del linguaggio naturale al di là di ciò che ho imparato di seconda mano dal diploma di linguistica di mia moglie.

Se ti siedi e scrivi come combinare categorie di parole, frasi, frasi e frasi insieme, sarai in grado di scrivere il parser abbastanza velocemente.

+0

Sorprendente risposta, grazie per lo sforzo. Farò affondare le informazioni su di me e poi cercherò di implementare tutto questo. – Taelia

Problemi correlati