Haskell 上的 foldr1 和无限列表
foldr1 and infinite list on Haskell
阅读有关 folds
的 wonderful 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)
就是这样。
阅读有关 folds
的 wonderful 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)
就是这样。