Haskell 上的 foldr1 和无限列表

foldr1 and infinite list on Haskell

阅读有关 foldswonderful book 我有一个关于 foldr1 和那里提出的 head' 实施的问题,相关代码是:

head' = foldr1 (\x _ -> x)

此代码适用于无限列表,而 foldl1 不适用。 this answer.

是一个很好的视觉解释

我不太明白 为什么 它有效,考虑到 foldr1 使用 last 元素作为累加器.例如:

foldr1 (\x _ -> x) [1..]

这是有效的,因为(我认为)延迟评估,即使 foldr 是从列表的最后一个元素开始的(它是无限的),我假设是因为函数没有使用任何中间结果,只是 return 第一个元素。

那么,编译器是否足够聪明,可以知道,因为在 lambda 函数内部只使用了 x,只是 return 是列表的第一个元素?即使它应该从最后开始?

相反,做

scanr1 (\x _ -> x) [1..]

将不结束地打印无限列表的所有元素,我想这就是 foldr 正在做的事情,只是编译器足够聪明,不会评估它和 return 头部。

提前致谢。

更新

我找到了一个非常好的答案,它帮助我更深入地了解 foldr 的工作原理:

foldr1 使用最后一个元素作为初始累加器值,但是组合函数(\x _ -> x)在它的第二个参数中是懒惰的。

因此,只要列表非空(更不用说无限),就永远不需要“累加器”值,因此永远不需要。

foldr 并不意味着它应该 从右边开始 ,只是操作在右边分组/关联/括号。如果组合函数在其第二个参数中是严格的,那么确实需要从右边开始计算,但如果不是——那么就不是。

所以不,这与编译器的智能无关,这是关于 Haskell 的惰性语义要求的。 foldr 的定义是

foldr g z [x1,x2,...,xn]  =  g x1 (foldr g z [x2,...,xn])

foldr1 g xs  =  foldr g (last xs) (init xs)

就是这样。