[英]Haskell ReadP parse at least one of the list
我尝试使用 ReadP 标准库解析命令(创建)。 我的命令应该以字符串create
开头,然后至少包含一个词/标签/到期,可能还有一个选项。 这是我的实际表达:
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddWord <$> wordExpr)
<|> (AddTag <$> addTagExpr)
<|> (SetDue <$> dueExpr)
<|> (AddOpt <$> optExpr)
skipSpaces
return $ cmd : rest
问题是,如果我只用一个选项调用create
,它解析得很好。 但它不应该,因为我期望至少有一个词/标签/到期。 我该如何表达?
事实上,我使用了错误的运算符。 <++
,本地的、排他的、偏左的选择,更适合我的需求。 一旦匹配了一个表达式,它就不应该检查其他的:
notAnOpt arg = case arg of
AddOpt _ -> False
_ -> True
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddTag <$> addTagExpr)
<++ (SetDue <$> dueExpr)
<++ (AddOpt <$> optExpr)
<++ (AddWord <$> wordExpr)
skipSpaces
guard $ isJust $ find notAnOpt rest
return $ cmd : rest
一个简单的解决方案是使用Control.Monad
guard
。
假设像isOpt :: Arg -> Bool
这样的函数沿着
isOpt :: Arg -> Bool
isOpt (AddOpt _) = True
isOpt _ = False
那么您对createExpr
的定义更改为
createExpr :: ReadP [Arg]
createExpr = do
skipSpaces
cmd <- SetCmd <$> cmdAliasExpr ["create", "add"]
skipSpaces
rest <-
many1
$ (AddWord <$> wordExpr)
<|> (AddTag <$> addTagExpr)
<|> (SetDue <$> dueExpr)
<|> (AddOpt <$> optExpr)
guard $ at_least_one_non_optional rest
skipSpaces
return $ cmd : rest
where at_least_one_non_optional = not . null . filter (not . isOpt)
当其参数为False
, guard
基本上使解析器失败,更一般地说,它通过在参数为False
时返回empty
与任何Alternative
一起使用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.