【问题标题】:How to parse arbitrary lists with Haskell parsers?如何使用 Haskell 解析器解析任意列表?
【发布时间】:2014-11-25 06:52:18
【问题描述】:

是否可以使用其中一个解析库(例如Parsec)来解析不同于字符串的内容?我该怎么做?

为了简单起见,我们假设输入是一个整数列表[Int]。任务可能是

  • 去掉前导零
  • 将其余部分解析为模式(S+L+)*,其中S 是小于10 的数字,L 是大于或等于10 的数字。
  • 返回元组列表(Int,Int),其中fstS 的乘积,sndL 整数的乘积

如果有人能展示如何编写这样的解析器(或类似的东西),那就太好了。

【问题讨论】:

    标签: parsing haskell parsec


    【解决方案1】:

    是的,正如 user5402 指出的那样,Parsec 可以解析Stream 的任何实例,包括任意列表。由于没有预定义的令牌解析器(如文本一样),您必须自己滚动,(myToken 下面)使用例如tokenPrim

    我觉得唯一有点尴尬的是“源位置”的处理。 SourcePos 是一种抽象类型(而不是类型类),并迫使我使用它的“文件名/行/列”格式,这在这里感觉有点不自然。

    无论如何,这是代码(为简洁起见,没有跳过前导零)

    import Text.Parsec
    
    myToken ::  (Show a) => (a -> Bool) -> Parsec [a] () a
    myToken test = tokenPrim show incPos $ justIf test where
      incPos pos _ _ = incSourceColumn pos 1
      justIf test x = if (test x) then Just x else Nothing
    
    small = myToken  (< 10)
    large = myToken  (>= 10)
    
    smallLargePattern = do
      smallints <- many1 small
      largeints <- many1 large
      let prod = foldl1 (*)
      return (prod smallints, prod largeints)
    
    myIntListParser :: Parsec [Int] () [(Int,Int)]
    myIntListParser = many smallLargePattern
    
    testMe :: [Int] -> [(Int, Int)]
    testMe xs = case parse myIntListParser "your list" xs of
      Left err -> error $ show err
      Right result -> result
    

    试一试:

    *Main> testMe [1,2,55,33,3,5,99]
    [(2,1815),(15,99)]
    *Main> testMe [1,2,55,33,3,5,99,1]
    *** Exception: "your list" (line 1, column 9):
    unexpected end of input
    

    注意错误消息中尴尬的行/列格式

    当然可以写一个函数sanitiseSourcePos :: SourcePos -&gt; MyListPosition

    【讨论】:

      【解决方案2】:

      很可能有一种方法可以让 Parsec 使用[a] 作为流类型,但是解析器组合器背后的想法实际上非常简单,并且滚动您自己的库并不是很困难。

      我推荐的一个非常容易访问的资源是 Graham Hutton 和 Erik Meijer 的 Monadic Parsing in Haskell

      确实,现在 Erik Meijer 正在 edx.org (link) 上教授 Haskell/函数式编程入门课程,第 7 课是关于函数式解析器的。正如他在讲座的介绍中所说:

      "... 如果不编写自己的解析器组合器库,任何人都无法走上掌握函数式编程的道路。我们首先解释什么是解析器以及它们如何自然地被视为副作用函数。接下来我们定义一个数字基本解析器和用于组合解析器的高阶函数。..."

      【讨论】:

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