首页 文章

Haskell中的元组解析器

提问于
浏览
1

我一直在努力学习Haskell中的函数解析,作为练习,我想编写一个简单的元组解析器,使用 Text.ParserCombinators.ReadP 中的函数 betweensepBy1 . 基本上我想要 tupleAsIntPair :: ReadP (Int, Int) ,当使用 ReadP_to_S 进行解析时,会使用 "(3,4)" 等字符串并返回 ReadS 中的整数对 (3,4) . 目前我有:

import Text.ParserCombinators.ReadP

isNumericOrSep :: Char -> Bool
isNumericOrSep = flip elem $ "0123456789-, "

tuplify2 :: [Int] -> (Int, Int)
tuplify2 [x,y] = (x,y)

tupleAsIntPair :: ReadP (Int, Int)
tupleAsIntPair = fmap tuplify2 parsedList 
    where   parsedList = fmap (map read) $ sepBy1 noparens sep
            noparens = between open close $ many1 (satisfy isNumericOrSep)
            open = char '('
            close = char ')'
            sep = char ','

但是,当我尝试运行 (readP_to_S tupleAsIntPair) "(3,4)" 时,我得到一个无解析错误 . 另一方面,如果我全局定义 noparens 并运行 (readP_to_S noparens) "(3,4)" ,我得到 [("3,4","")] 并且如果我运行 (readP_to_S $ sepBy1 (many1 $ satisfy isNumericOrSep) sep) "3,4" ,我得到一个列表 [(["3"],",4"),(["3,"],"4"),(["3","4"],""),(["3,4"],"")] ,所以至少解析器 sepBy1 正在做某事,即使我只想要第三次解析 .

我想我正在错误地编写两个解析器 betweensepBy1 ,或者 sepBy1 没有做我认为应该做的事情 . 我如何实际实现这个元组解析器?我也很感激任何风格的建议(例如, tuplify2 让我有点烦恼) .

1 回答

  • 3

    第一个问题是你对 isNumericOrSep 的定义 . 您定义的列表包含 , 作为元素 . 这意味着 isNumericOrSep 将解析您用作分隔符的 , ,因此 parsedList 将失败,因为缺少 sep .

    isNumericOrSep :: Char -> Bool
    isNumericOrSep = flip elem $ "0123456789- "
    

    那么,你不想定义 isNumeric 吗?为什么在这个定义中需要分隔符?

    第二个问题是组合器的顺序 . 您的描述在两个 noparens 之间解析 sep ,其中 noparens 被定义为左括号,多个(但至少一个)数值和右括号的组合 . 所以,我猜你真正想要的是在开括号和右括号之间解析一个元组 .

    tupleAsIntPair :: ReadP (Int,Int)
    tupleAsIntPair = fmap tuplify2 parsedList
     where
      parsedList = fmap (map read) $ between open close $ sepBy1 noparens sep
      noparens = many1 (satisfy isNumericOrSep)
      open = char '('
      close = char ')'
      sep = char ','
    

    这产生以下结果:

    *Main> (readP_to_S tupleAsIntPair) "(3,4)"
     [((3,4),"")]
    

    有可能,我误解了你的目标 . 但在你的介绍中,我读到你要解析一个元组 - 但也许你想要解析许多元组?

    编辑:

    首先不解析括号 . 看看between的定义:

    between :: ReadP open -> ReadP close -> ReadP a -> ReadP a
    -- ^ @between open close p@ parses @open@, followed by @p@ and finally
    --   @close@. Only the value of @p@ is returned.
    between open close p = do _ <- open
                              x <- p
                              _ <- close
                             return x
    

    订单再次从左到右 . 首先是 open ,然后解析解析器 p 并在结尾 close .

相关问题