35

我有一些 Haskell 代码可以在无限列表上正常工作,但我不明白为什么可以成功。(我修改了我的原始代码 - 没有处理无限列表 - 以合并来自其他在线代码的内容,突然我发现它可以工作但不知道为什么)。

myAny :: (a -> Bool) -> [a] -> Bool
myAny p list = foldr step False list
   where
      step item acc = p item || acc

我对 foldr 的理解是它将遍历列表中的每个项目(也许这种理解是不完整的)。如果是这样,那么“step”函数的措辞应该无关紧要......代码应该无法处理无限循环。

但是,以下工作:

*Main Data.List> myAny even [1..]
True

请帮助我理解:为什么?

4

4 回答 4

50

让我们对 Haskell 将如何评估您的表达式进行一些跟踪。在每一行上用 equals 替换 equals,表达式很快就会计算为 True:

myAny even [1..]
foldr step False [1..]
step 1 (foldr step False [2..])
even 1 || (foldr step False [2..])
False  || (foldr step False [2..])
foldr step False [2..]
step 2 (foldr step False [3..])
even 2 || (foldr step False [3..])
True   || (foldr step false [3..])
True

这是有效的,因为acc它作为未评估的 thunk(惰性评估)传递,还因为该函数在其第一个参数||中是严格的。

所以这终止:

True || and (repeat True)

但这不会:

and (repeat True) || True

看||的定义 看看为什么会这样:

True  || _ =  True
False || x =  x
于 2009-05-07T06:52:05.140 回答
19

我对 foldr 的理解是它将遍历列表中的每个项目(也许这种理解是不完整的)。

foldr(不像foldl)不必遍历列表的每个项目。看看如何foldr定义是有启发性的。

foldr f z []     = z
foldr f z (x:xs) = f x (foldr f z xs)

评估调用时foldr,它会强制评估对函数的调用f。但请注意递归调用foldr是如何嵌入到函数的参数中的ff如果不评估其第二个参数,则不会评估该递归调用。

于 2009-05-07T07:40:01.157 回答
1

这里的关键是 Haskell 是一种非严格的语言。“非严格”意味着它允许非严格函数,这反过来意味着函数参数在使用之前可能不会被完全评估。这显然允许延迟评估,这是“一种延迟计算直到需要结果的技术”。

这篇 Wiki 文章开始

于 2009-05-07T06:43:31.713 回答
1

我不知道 Haskell,但我怀疑在你的情况下,它是因为惰性评估而起作用的。因为它允许您无限长地使用列表,所以当您访问它时,它将根据您的需要计算结果。

http://en.wikipedia.org/wiki/Lazy_evaluation

于 2009-05-07T06:44:09.950 回答