Haskell中的元组解析器

时间:2013-09-03 05:41:38

标签: parsing haskell tuples

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

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 个答案:

答案 0 :(得分:3)

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

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

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

第二个问题是你的组合器的顺序。您的描述在两个sep之间解析noparens,其中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