我一直在努力学习Haskell中的函数解析,作为练习,我想使用between
中的函数sepBy1
和Text.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
正在做某事,即使我只想要第三次解析。
我认为我正在错误地编写两个解析器between
和sepBy1
,或者sepBy1
没有按我认为的那样做。我如何实际实现这个元组解析器?我也很感激任何风格的建议(例如,tuplify2
让我有点烦恼。)
答案 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
。