質問

import Data.Attoparsec.Text.Lazy
import Data.Text.Lazy.Internal (Text)
import Data.Text.Lazy (pack)

data List a = Nil | Cons a (List a)

list :: Text
list = pack $ unlines
  [ "0"
  , "1"
  , "2"
  , "5"
  ]

How can List Int parser coud be implemented to parse Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil))) from list?

ps: pure parser without parsing a [Int] and converting it to List Int is preferable.

役に立ちましたか?

解決

Like this:

import Control.Applicative
-- rest of imports as in question

data List a = Nil | Cons a (List a)
  deriving Show -- for testing

-- definition of list as in question

parseList :: Parser (List Int)
parseList = foldr Cons Nil <$> many (decimal <* endOfLine)

Testing in GHCi:

*Main> parse parseList list
Done "" Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))

他のヒント

Without converting it from a list of ints:

import Data.Attoparsec.Text.Lazy
import Data.Text.Lazy (Text, pack)
import Control.Applicative

data List a = Nil | Cons a (List a)
  deriving Show

input :: Text
input = pack $ unlines [ "0", "1", "2", "5"]

list :: Parser (List Int)
list = cons <|> nil
  where
    cons = Cons <$> (decimal <* endOfLine) <*> list
    nil  = pure Nil 

main = print $ parse list input

As you can see, the list parser almost looks exactly like the datatype it's parsing.

As others have pointed out, you don't actually need to use recursion (although you can) to parse the list. But if you have a recursive grammar to parse, you can either use recursion in the parser (see bzn's answer and Petr's answer) or you can recurse on the result of the parser (for something like the nesting you see in Markdown). The latter I covered here: http://www.youtube.com/watch?v=nCwG9ijQMuQ&t=17m32s

I'd say we can do this by examining many':

many' :: (MonadPlus m) => m a -> m [a]
many' p = many_p
  where
    many_p = some_p `mplus` return []
    some_p = liftM2' (:) p many_p

We can make our own variant similarly:

many'' :: (MonadPlus m) => m a -> m (List a)
many'' p = many_p
  where
    many_p = some_p `mplus` return Nil
    some_p = liftM2 Cons p many_p

and apply it on any monadic parser.

(Note that many' uses its own liftM2' that is strict in the result of the first action. It's not exported by the module so I used an ordinary liftM2.)

Or we can make a more general variant that uses Alternative:

many'' :: (Alternative f) => f a -> f (List a)
many'' p = many_p
  where
    many_p = some_p <|> pure Nil
    some_p = Cons <$> p <*> many_p
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top