我无法理解这段简单的代码片段:
-- 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
似乎在等待列表的结尾。
显然,模式匹配导致某些事情的处理方式不同。有人能解释一下究竟发生了什么吗?
答案 0 :(得分:3)
与go1
不同,go2
检查其第二个参数是否为空。为了做到这一点,需要评估第二个参数,至少足以确定它是否为空。
因此,对于foldr
的来电,这意味着以下内容:
首先使用两个参数调用go1
和go2
: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)
这是因为懒惰......由于在此示例中定义go1
和go2
的方式,它们的行为与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
的另一个调用,后者又需要对其自己的第二个参数进行求值,是另一个......
嗯,你明白了。第二个不会停止。