我对Text.Parsec
的使用有点生疏。如果我只想返回匹配的字符串,这是惯用的吗?
category :: Stream s m Char => ParsecT s u m [Char]
category = concat <$> (many1 $ (:) <$> char '/' <*> (many1 $ noneOf "/\n"))
我觉得我可能忽略了liftM concat . many1
或(:) <$> p1 <*> p2
的现有运营商,但我不确定。
答案 0 :(得分:4)
我认为没关系。一点点明智的命名会让它变得更漂亮:
category = concat <$> many1 segment
where
segment = (:) <$> char '/' <*> many1 (noneOf "/\n")
答案 1 :(得分:3)
我认为稍微更多惯用Parsec来返回更结构化的东西,例如字符串列表:
catList :: Parser [String]
catList = char '/' *> many1 alphaNum `sepBy1` char '/'
我认为没有像你想知道的那样的组合器,但这是Haskell,并且你自己的控制结构或组合器总是可用的:
concatMany1 :: Parser [a] -> Parser [a]
concatMany1 p = concat <$> many1 p
catConcat = concatMany1 $ (:) <$> char '/' <*> many1 alphaNum
但是这个下一个组合器甚至更好,至少是惯用的Haskell:
infixr 5 <:>
(<:>) :: Applicative f => f a -> f [a] -> f [a]
hd <:> tl = (:) <$> hd <*> tl
现在我们可以写
了catCons :: Parser String
catCons = concatMany1 (char '/' <:> many1 alphaNum)
但顺便说一句
contrivedExample :: IO String
contrivedExample = getChar <:> getLine
moreContrived :: String -> Maybe String
moreContrived name = find isLetter name <:> lookup name symbolTable
您会注意到我使用了alphaNum
noneOf "/\n"
。我认为noneOf
不是好的做法;解析器应该非常小心地接受正确的事情。您是否确定要让解析器接受/qwerty/12345/!"£$%^&*()@:?><.,#{}[] \/ "/" /-=_+~
?它真的应该对/usr\local\bin
感到满意吗?
就目前而言,只要解析器以/
开头,并且在\n
之前以/
为结尾,您的解析器就会接受任何字符串。我认为您应该使用alphaNum <|> oneOf "_-.',~+"
或类似内容重写它,而不是使用noneOf
。使用noneOf
可以避免考虑应该允许什么,并专注于获取解析的正面示例,而不是仅解析 正面示例。
我也总是去Parser a
而不是Stream s m t => ParsecT s u m a
。这只是懒惰的打字,但让我假装我这样做是为了让我的代码更清楚,我们呢? :)当然,使用什么类型的签名适合你。