我认为使用 Parsec 返回更结构化的东西会稍微更惯用,例如字符串列表:
catList :: Parser [String]
catList = char '/' *> many1 alphaNum `sepBy1` char '/'
我认为没有像您想知道的那样的组合器,但这是 Haskell,并且 roll-your-own-control-structure-or-combinator 始终可用:
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
没有
You'll notice I've used alphaNum
where you used noneOf "/\n"
. I think noneOf
is not good practice; parsers should be really careful to accept onlt the right thing. Are you absolutely sure you want your parser to accept /qwerty/12345/!"£$%^&*()@:?><.,#{}[] \/ "/" /-=_+~
? Should it really be happy with /usr\local\bin
?
As it stands, your parser accepts any string as long as it starts with /
and ends before \n
with something that's not /
. I think you should rewrite it with alphaNum <|> oneOf "_-.',~+"
or similar instead of using noneOf
. Using noneOf
allows you to avoid thinking about what you should allow and focus on getting positive examples to parse instead of only positive examples to parse.
Parser
I've also always gone for Parser a
instead of Stream s m t => ParsecT s u m a
. That's just lazy typing, but let's pretend I did it to make it clearer what my code was doing, shall we? :) Use what type signature suits you, of course.