[英]Haskell, foldr on infinite list
我在阅读LYAH时遇到问题。
这是我对无限列表上的文件夹的想法:
foldr (:) [] [1..] = 1:2:...:**∞:[]**
我认为GHCi在评估∞:[]之前不知道它是列表。
但是GHCI确实知道。
因此,我认为它可以识别文件夹(:) [] [无限列表] = [无限列表本身]。
Prelude> [1..10] == (take 10 $ foldr (:) [] [1..])
True
Prelude> [1..] == (foldr (:) [] [1..])
Interrupted.
但是事实并非如此。
我想知道在评估∞:[]之前,当GHCi识别为[1 ..]时实际会发生什么。
只是在评估之前键入推断?
我想知道GHCi在评估
∞:[]
之前认识到[1..]
时会发生什么。
GHCI 不承认,这是[1...]
这是只有懒评价的结果。
foldr
实现为:
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
如果你喜欢写东西foldr (:) [] [1..]
然后Haskell 没有 evalautes这(直接),它只是要计算出店。
现在假设您要print (take 3 (foldr (:) [] [1..]))
该列表,然后Haskell被迫对其进行评估,它将通过计算:
take 3 (foldr (:) [] [1..])
-> take 3 ((:) 1 (foldr (:) [] [2..]))
-> (:) 1 (take 2 (foldr (:) [] [2..]))
-> (:) 1 (take 2 ((:) 2 (foldr (:) [] [3..]))
-> (:) 1 ((:) 2 (take 1 (foldr (:) [] [3..])))
-> (:) 1 ((:) 2 (take 1 ((:) 3 (foldr (:) [] [4..]))))
-> (:) 1 ((:) 2 ((:) 3 (take 0 (foldr (:) [] [4..]))))
-> (:) 1 ((:) 2 ((:) 3 [])
因此它派生[1, 2, 3]
,并且由于Haskell的惰性,它对什么是文件foldr (:) [] [4..]
并不感兴趣。 即使该列表最终将停止,也不会对其进行评估。
如果计算类似[1..] = foldr (:) [] [1..]
,则Haskell将检查列表是否相等,列表相等定义为:
[] == [] = True
(x:xs) == (y:ys) = x == y && xs == ys
[] == (_:_) = False
(_:_) == [] = False
所以Haskell是被迫放松右边的列表foldr
,但它会继续这样做,直到发现不相等的物品,或列表中的一个到达终点。 但是由于每次元素都是相等的,并且两个列表都不会结束,所以它将永远不会结束,因为它将像下面这样评估它:
(==) [1..] (foldr (:) [] [1..])
-> (==) ((:) 1 [2..]) ((:) 1 (foldr (:) [] [2..]))
它看到两者相等,因此递归调用:
-> (==) ((:) 1 [2..]) ((:) 1 (foldr (:) [] [2..]))
-> (==) [2..] foldr (:) [] [2..])
-> (==) ((:) 2 [3..]) ((:) 2 (foldr (:) [] [3..]))
-> (==) [3..] foldr (:) [] [3..])
-> ...
但是正如您所看到的,它永远不会停止评估。 Haskell不知道 foldr (:) [] [1..]
等于[1..]
,它的目的是求值,并且由于相等性迫使它求值整个列表,因此它会陷入无限循环。
是的,有可能在编译器中添加某种模式,例如将foldr (:) [] x
替换为x
,因此将来Haskell编译器可能会针对这些返回True
,但这不能解决问题。 从根本上说 ,如果Haskell可以针对任何类型的函数(在这里(:)
派生此类事物,那么它将解决一个不确定的问题,因此是不可能的。
Ghc(至少在理论上)不知道有限列表和无限列表之间的区别。 通过计算列表的长度可以知道列表是有限的。 如果您尝试找到一个无限列表的长度,那么您的时间将会很糟糕,因为您的程序将永远不会终止。
这个问题实际上是关于惰性评估的。 在像C或python这样的严格语言中,您需要在每一步都知道某物的全部价值。 如果要添加列表中的元素,则在开始之前已经需要知道列表中有什么以及有多少东西。
Haskell中的所有数据具有以下形式:
True
, Left 7
, (,) 5 'f'
(与(5,'f')
)或(:) 3 []
但是在Haskell中,价值有两种“形状”
在Haskell中,有一个称为弱头范式的概念,其中:
让我们看一下文件foldr (:) [] [1..]
的评估过程。 首先定义文件foldr
foldr f a [] = a
foldr f a (x:xs) = f x (foldr xs)
现在什么是文件foldr (:) [] [1..]
?
foldr (:) [] [1..]
看来这只是一个笨拙。 我们对此一无所知。 因此,让我们将其评估为WHNF。 首先,我们需要将参数[1..]
(实际上是enumFrom 1
)转换为WHNF,以便可以对其进行模式匹配:
foldr (:) [] (1:[2..])
现在我们可以评估文件夹:
(:) 1 (foldr [] [2..])
1 : (foldr [] [2..])
因此,我们计算了列表的第一个元素,而不必查看其整个无限长度。 同样,我们可以计算出第二个元素,依此类推。
那么,如果我们执行[1..] == [1..]
什么? 好,列表的==
的定义是(省略了三种情况)
(x:xs) == (y:ys) = x == y && xs == ys
因此,尝试减少到WHNF,我们得到:
[1..] == [1..]
(1 == 1) && ([2..] == [2..])
True && ([2..] == [2..])
[2..] == [2..]
... and so on
因此,我们一直坚持下去,永远都不会找到可以用来对结果进行模式匹配(即检查)的构造函数。
需要注意的是,我们可以抵消True && ...
因为定义&&
不看它的第二个参数:
True && x = x
False && _ = False
如果我们用完整的四向真值表定义&&
,则程序可能比上述速度更快(如果编译器没有做任何聪明的事情)用完内存(相反,您将没有耐心(或宇宙射线命中)您的ram并使程序返回False
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.