无法理解相互递归

时间:2011-05-21 09:58:42

标签: haskell recursion mutual-recursion

我正在阅读编程在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

vsmany p的结果值,但很多名为many1 p的p,所有many1在其定义中都有一个符号,并且再次具有结果值{ {1}}和v,递归调用什么时候返回? 为什么以下代码段可以返回vs

[("123","abc")]

3 个答案:

答案 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会发生类似的事情。你的例子稍微复杂一点:当没有更多的方法来解析某些东西时,忽略>>=之后的东西。