当解析器的一部分成功而其余部分失败时,为什么 Parsec 不回溯?

Why doesn't Parsec backtrack when one part of the parser succeeds and the rest fails?

我有这个 parsec 解析器:

a = optionMaybe $ do {try $ spaceornull *> string "hello";string "No"}                        

其中 spaceornull((:[]) <$> try space) <|> string ""

当我用输入 " " 测试 a 时,我得到:

Left (line 1, column 2):
unexpected end of input
expecting "hello"

我不明白,spaceornull *> string "hello" 应该会失败,因为没有 "hello",然后 try 秒差距回溯,现在没有消耗的输入但是 try 无论如何都会失败,所以传递给 optionMaybedo 中的解析器)完全失败,它不应该尝试消耗更多的输入,所以我们最终得到一个失败的解析器而没有消耗任何输入所以我应该得到 Right Nothing

但是错误消息说,space被消耗所以try并没有真正回溯,当部分解析器成功时try不回溯吗?以及如何让它回溯到上面的内容?

try与是否允许失败无关。它只是可能在失败的情况下回溯,但要开始回溯,您需要提供一个替代解析器以从该点开始.通常的方法是使用 <|> operator:

a = optionMaybe $ (try $ spaceornull *> string "hello") <|> string "No"

OTOH,你的代码相当于

a = optionMaybe $ (try $ spaceornull *> string "hello") >> string "No"

其中 monadic 链接 operator >> (same as *>) 将在 parsec 的情况下检查 LHS 是否成功,然后 继续并且 运行 RHS 解析器.所以它一定是,因为你也可以写:

a = optionMaybe $ do
       s <- try $ spaceornull *> string "hello"
       string $ "No"++s

这里我使用了第一个解析器的结果(你只是扔掉了,没有 <-- 将它与任何变量匹配)来决定 什么第二个应该寻找。这明明只有第一个才真正成功!


基本上,<|> 仅在 LHS 立即 一开始就失败的情况下才有效字符,或者如果您使用 try 设置回溯点。之所以需要这样做,是因为如果 parsec 需要在每个需要检查的备选方案之前留下一个回溯点,那将是非常低效的。