首页 > 解决方案 > 使用带有 Parsec 的累加器进行 Haskell 解析

问题描述

Haskell初学者在这里。

假设我有一个解析器,我提供了一些信息,它返回解析部分的结果和下一部分所需的信息。

readSection :: Info -> Parser (Section, Info)

我希望能够使用many组合器解析多个部分,但我无法让这些类型工作。我需要某种方式让解析器接受Info之前的计算。这种结构让我想起了折叠(因为之前的结果可以只存储在累加器中),但我不确定如何在这里应用它。

readSections :: Info -> Parser [Section]
readSections startInfo = do
  let parser = readSection startInfo
  -- ???
  return $ many readSection

也许有更好的方法来做这样的事情,所以任何指导都将不胜感激。谢谢!

标签: haskellparsec

解决方案


如果您正在寻找现有的组合器,这看起来像是unfoldrMfrom monad-loops(以及其他地方)的应用程序。为了避免必须导入它,它可以定义为:

unfoldrM :: Monad m => (a -> m (Maybe (b, a))) -> a -> m [b]
unfoldrM f a = do
  r <- f a
  case r of
    Nothing -> return []
    Just (b, a') -> (b:) <$> unfoldrM f a'

基本上,它需要一个类型的初始种子a并使用它来单子生成值和新种子。你的种子就是你的信息:你使用信息单子生成一个解析的部分加上一个新版本的信息。

您只需要坚持optionMaybe提供Just/Nothing开关,以便unfoldrM知道它何时到达所有部分的末尾:

readSections = unfoldrM (optionMaybe . readSection)

然而,作为一个 Haskell 初学者,看看你是如何从头开始做这类事情可能会有所帮助。组合器many并不神奇。它基本上相当于相对简单的一元计算:

many p = (do x <- p
             xs <- many p
             return (x:xs))
         <|> return []

因此,您可以readSections以类似的方式从头开始编写一元计算:

readSections' :: Info -> Parser [Section]
readSections' i = (do -- try to read a section, getting updated info
                      (s, i') <- readSection i
                      -- read zero or more remaining sections with updated info
                      ss <- readSections' i'
                      -- return the sections
                      return (s:ss))
                  -- if no more sections to read, return an empty list
                  <|> return []

推荐阅读