为什么以下Haskell代码不会终止:
foldr (||) True $ repeat False -- never terminates
当这样的事情发生时:
foldr (||) False $ repeat True -- => True
对我来说,这是第二个看起来更难以终止的表达方式。我对Haskell懒惰评估的看法有什么问题?
答案 0 :(得分:18)
我认为你对懒惰的理解是正确的,但不适用于foldr
。我们来看看它的“specification”
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
然后看看你的表达
foldr (||) True $ repeat False -- never terminates
正如您所见,True
(z
)我们正在“寻找”让||
终止,直到整个列表被消耗为止。这不会发生,因为列表是无限的。
这也应该解释实际终止的例子。
答案 1 :(得分:13)
第一个扩展为False || (False || (False || ...))
,第二个扩展为True || (True || (True || ...))
。 foldr
的第二个参数是红色鲱鱼 - 它出现在||
的最里面的应用程序中,而不是最外层的应用程序中,因此它实际上永远无法到达。
答案 2 :(得分:9)
如果您手动展开foldr
:
foldr (||) True $ repeat False == False || (False || (False (False || ... True)))
因此,为了获得最终的False
,代码必须评估列表直到其(不存在)结束。在第二个示例中,您重复True
,因此可以进行短路评估。不要期待懒惰评价的魔法!
答案 3 :(得分:3)
另一个有用的见解是||
在Haskell中不是可交换的,它是有偏见的:
Prelude> undefined || True
*** Exception: Prelude.undefined
Prelude> True || undefined
True
与数学不同,||
和flip (||)
是不同的功能。例如。比较
foldr (||) False $ repeat True
和foldr (flip (||)) False $ repeat True
前者终止,但后者不会。