我们看一下相关的定义(和上面的不完全一样)Prelude http://hackage.haskell.org/package/base-4.7.0.2/docs/Prelude.html,但与此分析等效)。
(&&) :: Bool -> Bool -> Bool
True && x = x
False && _ = False
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
看看每个人的机会foldr
and foldl
必须产生一个结果。它们在给出时都会立即产生结果[]
。在里面(x:xs)
case, foldr
也有机会产生结果,如果f
立即返回而不评估其正确的参数(这是递归调用)。foldl
没有这个,因为它最外层的调用是对它自己的,所以唯一的时间foldl
可以返回任何信息[]
情况,对于无限列表永远不会达到。
在这样的例子中,我发现进行一些手动评估很有帮助。回想一下,Haskell 的求值顺序是由外向内的:我们尽可能少地求值以获得最外层函数应用程序的适用模式匹配。我将在每个步骤中将要评估的下一个函数标记为斜体。foldr
很简单:
foldr (&&) False (repeat False)
= foldr (&&) False (False : repeat False)
= False && foldr (&&) False (repeat False)
= False
And foldl
揭示了问题:
foldl (&&) False (repeat False)
= foldl (&&) False (False : repeat False)
= foldl (&&) (False && False) (repeat False)
= foldl (&&) (False && False) (False : repeat False)
= foldl (&&) ((False && False) && False) (repeat False)
= foldl (&&) ((False && False) && False) (False : repeat False)
= foldl (&&) (((False && False) && False) && False) (repeat False)
等等。请注意,即使(&&)
有能力通过检查任一侧来简化,但我们仍然没有机会返回它,因为我们从未达到[]
case.
然而,该命令(&&)
评估其论点does仍然很重要(它首先评估左侧,由模式匹配语义确定)。我们可以flip
参数的顺序,看看会发生什么foldr
does:
ghci> foldr (flip (&&)) False (repeat False)
^CInterrupted
(锻炼)为什么是这样?