【问题标题】:Parse recursive data with parsec使用 parsec 解析递归数据
【发布时间】:2013-01-18 09:40:48
【问题描述】:
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解析器从list解析Cons 0 (Cons 1 (Cons 2 (Cons 5 Nil)))

ps:最好不解析[Int]并将其转换为List Int的纯解析器。

【问题讨论】:

    标签: haskell parsec recursive-datastructures attoparsec


    【解决方案1】:

    像这样:

    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)))
    

    【讨论】:

      【解决方案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
      

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

      【讨论】:

      • 这是可编译的,甚至是可类型检查的吗?
      • 糟糕,抱歉。我将解析器重命名为与 kosmikus 答案中的名称相同,但在where-clause 中忘记了这样做。编辑为可编译。
      • 这实际上是一个递归解析器,按照要求。
      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        我想说我们可以通过检查 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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多