模式匹配和无限列表

时间:2014-11-02 22:10:03

标签: list haskell lazy-evaluation infinite fold

我无法理解这段简单的代码片段:

-- This works:     foldr go1 [] [1..]
-- This doesn't:   foldr go2 [] [1..]

go1 a b = a : b

go2 a [] = a : []
go2 a b  = a : b

使用go1折叠会立即开始返回值,但go2似乎在等待列表的结尾。

显然,模式匹配导致某些事情的处理方式不同。有人能解释一下究竟发生了什么吗?

4 个答案:

答案 0 :(得分:3)

go1不同,go2检查其第二个参数是否为空。为了做到这一点,需要评估第二个参数,至少足以确定它是否为空。

因此,对于foldr的来电,这意味着以下内容:

首先使用两个参数调用go1go2:1和foldr go [] [2 ..]的结果。在go1的情况下,第二个参数保持不变,因此foldr的结果只是1 :: foldr go [] [2 ..]而没有进一步评估尾部,直到它被访问。

但是,在go2的情况下,需要评估foldr go [] [2 ..]以检查它是否为空。要做到这一点foldr go [] [3 ..]然后需要进行评估,原因相同。等等无限期。

答案 1 :(得分:1)

要测试表达式是否满足某种模式,您至少需要将其评估为弱头正常形式。因此模式匹配迫使评估。 一个常见的例子是interleave函数,它交织两个列表。它可以定义为

interleave :: [a] -> [a] -> [a]
interleave  xs     []    = xs
interleave  []     ys    = ys
interleave (x:xs) (y:ys) = x : y : interleave xs ys

但是这个函数在第二个参数中是严格的。更懒的版本是

interleave  []    ys = ys
interleave (x:xs) ys = x : interleave ys xs

您可以在此处阅读更多内容:http://en.wikibooks.org/wiki/Haskell/Laziness

答案 2 :(得分:0)

这是因为懒惰......由于在此示例中定义go1go2的方式,它们的行为与b==[]的行为完全相同,但是编译器不知道这个。

对于go1,最左边的折叠将使用尾递归来立即输出a的值,然后计算b的值。

go1 a b -> create and return the value of a, then calculate b 

对于go2,编译器甚至不知道要匹配哪种情况,直到计算出b的值....这将永远不会发生。

go2 a b -> wait for the value of b, pattern match against it, then output a:b

答案 3 :(得分:0)

要看到差异,请在GHCi中尝试:

> head (go1 1 (error "urk!"))
1
> head (go2 1 (error "urk!"))
*** Exception: urk!

问题是go2将在返回列表头部之前评估其第二个参数。也就是说,go2在其第二个参数中是 strict ,与 lazy go1不同。

当你折叠无限列表时,这很重要:

fold1 go1 [] [1..] =
go1 1 (go1 2 (go1 3 ( ..... =
1 : (go1 2 (go1 3 ( ..... =
1 : 2 : (go1 3 ( ...

工作正常,但

fold1 go1 [] [1..] =
go2 1 (go2 2 (go2 3 ( .....

不能简化为1:...,因为go2坚持评估它的第二个参数,这是对go2的另一个调用,后者又需要对其自己的第二个参数进行求值,是另一个......

嗯,你明白了。第二个不会停止。