我试图找出如何以最好的方式解析 Haskell 中的和数据类型。这是我尝试的摘录
type Value = Int
data Operator = ADD | SUB | MUL | DIV | SQR deriving (Show)
toOperator :: String -> Maybe Operator
toOperator "ADD" = Just ADD
toOperator "SUB" = Just SUB
toOperator "MUL" = Just MUL
toOperator "DIV" = Just DIV
toOperator "SQR" = Just SQR
toOperator _ = Nothing
parseOperator :: ParsecT String u Identity () Operator
parseOperator = do
s <- choice $ map (try . string) ["ADD", "SUB", "MUL", "DIV", "SQR"]
case toOperator s of
Just x -> return x
Nothing -> fail "Could not parse that operator."
这段代码实现了我想要的功能,但有一个明显的问题:它检查数据两次。一旦进入 choice $ map (try . string) ["ADD", "SUB", "MUL", "DIV", "SQR"]
行,然后通过 toOperator
。
我想要的是,如果字符串出现在列表中,则希望将其解析为 Operator
,否则失败。但我不知道如何以“干净”的方式做到这一点。
最佳答案
如果让 toOperator
直接参与 Parsec 解析过程,而不是让它成为一个单独发生的步骤,会更简单,因为这样“这个东西是否是有效的运算符”可以提供反馈解析过程。
对于这种特定情况,您正在解析的内容是一个零字段枚举,其构造函数名称与您正在解析的字符串完全匹配,已经发布了几个很好的快捷方式,向您展示如何简洁地解析这些构造函数。在这个答案中,我将展示一种替代方法,它更容易适应“匹配几种情况之一”的一般情况,并处理更奇特的东西,例如“三个构造函数之一具有 Int 参数,但其他构造函数没有”
operator :: StringParser Operator
operator = string "ADD" *> pure ADD
<|> string "DIV" *> pure DIV
<|> string "MUL" *> pure MUL
<|> try (string "SUB") *> pure SUB
<|> string "SQR" *> pure SQR
现在假设您有一个额外的构造函数 VAR
,它采用 String 参数。向此解析器添加对该构造函数的支持很容易:
operator :: StringParser Operator
operator = ...
<|> string "VAR" *> (VAR <$> var)
var :: StringParser String
var = spaces *> anyChar `manyTill` space
关于使用 parsec 解析 sum 数据类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49758970/