2014-06-10 10 views
5

Sto provando a separare una stringa tramite ",", ", and" e "and", quindi restituire qualsiasi cosa ci fosse nel mezzo. Un esempio di ciò che finora è come segue:Utilizzo della stringa sepBy in Attoparsec

import Data.Attoparsec.Text 

sepTestParser = nameSep ((takeWhile1 $ inClass "-'a-zA-Z") <* space) 
nameSep p = p `sepBy` (string " and " <|> string ", and" <|> ", ") 

main = do 
    print $ parseOnly sepTestParser "This test and that test, this test particularly." 

Vorrei che l'uscita sia ["This test", "that test", "this test particularly."]. Ho un vago senso che quello che sto facendo è sbagliato, ma non riesco a capire il perché.

+0

Perché non 'nameSep. takeWhile1 $ inClass "\ t-'a-zA-Z" '? - Il tuo output chiaramente non tratta gli spazi in modo diverso, perché non includerli nella classe di caratteri? Se ti piace 'space' invece di caratteri espliciti come' "\ t" 'ecc, puoi usare' nameSep. takeWhile1 $ inClass "-'a-zA-Z" <|> spazio' – AndrewC

risposta

4

Nota: questa risposta è scritta in literate Haskell. Salvalo come Example.lhs e caricalo in GHCi o simile.

Il fatto è che, sepBy è implementato come:

sepBy p s = liftA2 (:) p ((s *> sepBy1 p s) <|> pure []) <|> pure [] 

Ciò significa che la seconda parser s sarà chiamato dopo la prima parser è riuscita. Questo significa anche, che se si dovesse aggiungere spazio bianco alla classe di personaggi, che, ne risulterebbe con

["This test and that test","this test particularly"] 

dal and è ora leggibile dalla p. Questo non è facile da risolvere: è necessario guardare avanti non appena si colpisce uno spazio, e controllare se dopo un numero arbitrario di spazi un "e" segue, e se lo fa, smettere di analizzare. Solo quindi funzionerà un parser scritto con sepBy.

così lascia scrivere un parser che prende le parole invece (il resto di questa risposta è alfabetizzata Haskell):

> {-# LANGUAGE OverloadedStrings #-} 
> import Control.Applicative 
> import Data.Attoparsec.Text 
> import qualified Data.Text as T 
> import Control.Monad (mzero) 

> word = takeWhile1 . inClass $ "-'a-zA-Z" 
> 
> wordsP = fmap (T.intercalate " ") $ k `sepBy` many space 
> where k = do 
>   a <- word 
>   if (a == "and") then mzero 
>       else return a 

wordsP ora prende più parole fino a quando non sia colpisce qualcosa, che non è una parola, o una parola quello è uguale a "e". La tornato mzero indica un parsing failure, in cui un altro parser può assumere:

> andP = many space *> "and" *> many1 space *> pure() 
> 
> limiter = choice [ 
>  "," *> andP, 
>  "," *> many1 space *> pure(), 
>  andP 
> ] 

limiter è in gran parte lo stesso parser che hai già scritto, è lo stesso del regex /,\s+and|,\s+|\s*and\s+/.

Ora possiamo effettivamente utilizzare sepBy, dal momento che il nostro primo parser non si sovrappone con la seconda più:

> test = "This test and that test, this test particular, and even that test" 
> 
> main = print $ parseOnly (wordsP `sepBy` limiter) test 

Il risultato è ["This test","that test","this test particular","even that test"], proprio come volevamo. Nota che questo particolare parser non conserva gli spazi bianchi.

Quindi, ogni volta che si desidera creare un parser con sepBy, assicurarsi che entrambi i parser non si sovrappongano.

+0

@atc: questo è letteralmente Haskell. Pertanto, '' 'all'inizio della riga sono significativi. – Zeta

+0

Oh, mi sono perso qualcosa? Quanto è significativo? Sembrava un codice sorgente dritto per me, e quindi copia/incollabile in un file repl o di origine. > Reso così ingombrante. Cosa fa '>' fare qui? –

+1

Non è copia/incollabile in un REPL in alcun modo, poiché la maggior parte delle funzioni sono definite su più righe. [Ho collegato una spiegazione] (https://wiki.haskell.org/Literate_programming), ma ecco un breve riassunto: puoi semplicemente copiare e incollare __all__ della risposta (inclusa la spiegazione/testo) in un file '.lhs 'file e caricarlo in GHCi (o compilarlo con GHC). Nota che alcune linee non sono pensate per essere compilate, ad es. la prima spiegazione 'sepBy p ...' o l'elenco di primo livello; non avrebbero funzionato in entrambi i modi. – Zeta