我正在阅读编程在Haskell中,在第8章中,作者给出了编写解析器的示例。
完整的来源是:http://www.cs.nott.ac.uk/~gmh/Parsing.lhs
我无法理解以下部分:many
允许p
的零个或多个应用程序,
而many1
至少需要一次成功申请:
many :: Parser a → Parser [a ]
many p = many1 p +++ return [ ]
many1 :: Parser a → Parser [a ]
many1 p = do v ← p
vs ← many p
return (v : vs)
递归调用如何在
处发生vs <- many p
vs
是many p
的结果值,但很多名为many1 p
的p,所有many1
在其定义中都有一个符号,并且再次具有结果值{ {1}}和v
,递归调用什么时候返回?
为什么以下代码段可以返回vs
?
[("123","abc")]
答案 0 :(得分:6)
递归在v <- p
行停止。当[]
无法再解析时,解析器的monadic行为只会将p
传播到计算的结尾。
p >>= f = P (\inp -> case parse p inp of
[] -> [] -- this line here does not call f
[(v,out)] -> parse (f v) out)
第二个函数是用do-notation编写的,这只是一个很好的语法:
many1 p = p >>= (\v -> many p >>= (\vs -> return (v : vs)))
如果解析p产生一个空列表[]
,则不会调用函数\v -> many p >>= (\vs -> return (v : vs))
,停止递归。
答案 1 :(得分:2)
关于最后一个问题:
> parse (many digit) "123abc"
[("123", "abc")]
表示解析已成功,因为答案列表中至少返回了一个结果。 Hutton解析器总是返回一个列表 - 空列表意味着解析失败。
结果(“123”,“abc”)表示解析已找到三个数字“123”并停在'a'处,这不是一个数字 - 因此“输入的其余部分”是“abc”。 / p>
请注意many
表示“尽可能多”而不是“一个或多个”。如果它是“一个或多个”,你会得到这个结果:
[("1", "23abc"), ("12", "3abc"), ("123", "abc")]
这种行为对于确定性解析不是很好,尽管有时可能需要自然语言解析。
答案 2 :(得分:1)
让我把它剥离到最基本的骨头,以便清楚地说明为什么do
- 如果块被简单地理解为命令式代码,则可能会被误解。请考虑以下代码段:
doStuff :: Maybe Int
doStuff = do
a <- Nothing
doStuff
看起来doStuff
将永远递归,毕竟,它被定义为以doStuff
结尾的一系列事物。但是do
- 块中的行序列不仅仅是按顺序执行的一系列操作。如果您处于do
- 块中的某个点,则处理块的其余部分的方式由>>=
的定义决定。在我的示例中,>>=
的第二个参数仅在第一个参数不是Nothing
时使用。所以递归永远不会发生。
许多不同的monad会发生类似的事情。你的例子稍微复杂一点:当没有更多的方法来解析某些东西时,忽略>>=
之后的东西。