为什么这个Haskell代码能够成功地使用无限列表?

时间:2021-10-27 17:06:09

I have some Haskell code that does work correctly on an infinite list, but I do not understand why it can do so successfully. (I modified my original code -- that did not handle infinite lists -- to incorporate something from some other code online, and suddenly I see that it works but don't know why).

我有一些Haskell代码在无限列表上正常工作,但我不明白为什么它可以成功地这样做。 (我修改了我原来的代码 - 没有处理无限列表 - 在网上加入其他代码的东西,突然间我发现它有效,但不知道为什么)。

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

My understanding of foldr is that it will loop through every item in the list (and perhaps that understanding is incomplete). If so, it should not matter how the "step" function is phrased ... the code should be unable to handle infinite loops.

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

However, the following works:

但是,以下工作:

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

Please help me understand: why??

请帮我理解:为什么?

4 个解决方案

#1


Let's do a little trace in our heads of how Haskell will evaluate your expression. Substituting equals for equals on each line, the expression pretty quickly evaluates to True:

让我们来看看Haskell如何评估你的表达。在每一行上用等于等于等于,表达式很快就会计算为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

This works because acc is passed as an unevaluated thunk (lazy evaluation), but also because the || function is strict in its first argument.

这是有效的,因为acc作为未评估的thunk(延迟评估)传递,但也因为||函数在第一个参数中是严格的。

So this terminates:

所以这终止了:

True || and (repeat True)

But this does not:

但这不是:

and (repeat True) || True

Look at the definition of || to see why this is the case:

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

True  || _ =  True
False || x =  x

#2


My understanding of foldr is that it will loop through every item in the list (and perhaps that understanding is incomplete).

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

foldr (unlike foldl) does not have to loop through every item of the list. It is instructive to look at how foldr is defined.

foldr(与foldl不同)不必遍历列表中的每个项目。看看如何定义foldr是有益的。

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

When a call to foldr is evaluated, it forces the evaluation of a call to the function f. But note how the recursive call to foldr is embedded into an argument to the function f. That recursive call is not evaluated if f does not evaluate its second argument.

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

#3


The key point here is that Haskell is a non-strict language. "Non-strict" means that it allows non-strict functions, which in turn means that function parameters may not be fully evaluated before they may be used. This obviously allows for lazy evaluation, which is "a technique of delaying a computation until the result is required".

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

Start from this Wiki article

从这篇Wiki文章开始

#4


I do not know Haskell, but I suspect that in your case, it works because of lazy evaluation. Because it allows you to work with list infinitely long, when you access it, it will compute the result as you need it.

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

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

#1


Let's do a little trace in our heads of how Haskell will evaluate your expression. Substituting equals for equals on each line, the expression pretty quickly evaluates to True:

让我们来看看Haskell如何评估你的表达。在每一行上用等于等于等于,表达式很快就会计算为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

This works because acc is passed as an unevaluated thunk (lazy evaluation), but also because the || function is strict in its first argument.

这是有效的,因为acc作为未评估的thunk(延迟评估)传递,但也因为||函数在第一个参数中是严格的。

So this terminates:

所以这终止了:

True || and (repeat True)

But this does not:

但这不是:

and (repeat True) || True

Look at the definition of || to see why this is the case:

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

True  || _ =  True
False || x =  x

#2


My understanding of foldr is that it will loop through every item in the list (and perhaps that understanding is incomplete).

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

foldr (unlike foldl) does not have to loop through every item of the list. It is instructive to look at how foldr is defined.

foldr(与foldl不同)不必遍历列表中的每个项目。看看如何定义foldr是有益的。

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

When a call to foldr is evaluated, it forces the evaluation of a call to the function f. But note how the recursive call to foldr is embedded into an argument to the function f. That recursive call is not evaluated if f does not evaluate its second argument.

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

#3


The key point here is that Haskell is a non-strict language. "Non-strict" means that it allows non-strict functions, which in turn means that function parameters may not be fully evaluated before they may be used. This obviously allows for lazy evaluation, which is "a technique of delaying a computation until the result is required".

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

Start from this Wiki article

从这篇Wiki文章开始

#4


I do not know Haskell, but I suspect that in your case, it works because of lazy evaluation. Because it allows you to work with list infinitely long, when you access it, it will compute the result as you need it.

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

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