4
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"
  ]

如何实现List Int解析器来解析Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))from list

ps:不解析 a[Int]并将其转换为的纯解析器List Int是可取的。

4

4 回答 4

7

像这样:

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)

在 GHCi 中进行测试:

*Main> parse parseList list
Done "" Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))
于 2013-02-03T15:48:44.353 回答
2

不从整数列表转换它:

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

如您所见,列表解析器看起来几乎与它正在解析的数据类型完全一样。

于 2013-02-03T16:06:55.877 回答
2

正如其他人指出的那样,您实际上不需要使用递归(尽管可以)来解析列表。但是,如果您要解析递归语法,则可以在解析器中使用递归(请参阅 bzn 的答案和 Petr 的答案),也可以对解析器的结果进行递归(类似于您在 Markdown 中看到的嵌套)。后者我在这里介绍:http ://www.youtube.com/watch?v=nCwG9ijQMuQ&t=17m32s

于 2013-02-05T18:03:38.903 回答
1

我想说我们可以通过检查来做到这一点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

我们可以类似地制作自己的变体:

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

并将其应用于任何一元解析器。

(注意,在第一个动作的结果中many'使用它自己liftM2'的严格。它不是由模块导出的,所以我使用了一个普通的liftM2.)

或者我们可以制作一个更通用的变体,使用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
于 2013-02-03T15:57:46.817 回答