Haskell 为什么:p在我给它这个corecursive值时会冻结在GHCi中?
我已经定义了无限列表的无限列表Haskell 为什么:p在我给它这个corecursive值时会冻结在GHCi中?,haskell,ghci,corecursion,Haskell,Ghci,Corecursion,我已经定义了无限列表的无限列表pathCounts和有限列表的无限列表pathCounts': import Data.Function (fix) nextRow xs = fix $ \ys -> zipWith (+) xs (0:ys) pathCounts = repeat 1 : map nextRow pathCounts pathCounts' = map (take 100) pathCounts ghci> head . head $ pathCounts'
pathCounts
和有限列表的无限列表pathCounts'
:
import Data.Function (fix)
nextRow xs = fix $ \ys -> zipWith (+) xs (0:ys)
pathCounts = repeat 1 : map nextRow pathCounts
pathCounts' = map (take 100) pathCounts
ghci> head . head $ pathCounts'
1
ghci> :p pathCounts'
pathCounts' = (1 : (_t4::[Integer])) : (_t5::[[Integer]])
ghci> :p pathCounts
^CInterrupted.
进入ghci,如果我根本没有评估过其中任何一项,我可以在以下任一项上成功使用:p
:
ghci> :p pathCounts
pathCounts = (_t1::[[Integer]])
ghci> :p pathCounts'
pathCounts' = (_t2::[[Integer]])
但是如果我对pathCounts'
进行部分计算,那么:p
在pathCounts
上冻结,而在pathCounts'
上仍然成功:
import Data.Function (fix)
nextRow xs = fix $ \ys -> zipWith (+) xs (0:ys)
pathCounts = repeat 1 : map nextRow pathCounts
pathCounts' = map (take 100) pathCounts
ghci> head . head $ pathCounts'
1
ghci> :p pathCounts'
pathCounts' = (1 : (_t4::[Integer])) : (_t5::[[Integer]])
ghci> :p pathCounts
^CInterrupted.
我希望:p pathCounts
打印的内容与:p pathCounts'
相同,因为我只对其进行了部分评估。为什么不起作用
我希望:p pathCounts
打印的内容与:p pathCounts'
相同,因为我只对其进行了部分评估
啊,但这才是有趣的一点!事实上,您已经完全评估了路径计数的(无限长)头。让我们举一个稍微小的例子来简化讨论:
> let v = repeat 1 :: [Int]
> head v
1
> :p v
^C
我声称在全面评估头部v
之后,实际上v
也得到了全面评估。这是因为,在内存中,v
是一个循环单链表。因此,如果您已经计算了足够多的值来了解第一个元素,那么就没有什么可计算的了
结果是,当您要求打印它时,GHC会适时地尝试构造一个字符串来表示结构的所有求值部分——显然不能,因为它永远不会停止遍历。(:p
只是没有一种方式来表示结构中的共享。)
比较:
> let x = 1 :: Int; v = (x, x)
> fst v
1
> :p v
(1,1)
虽然您只请求对v
的第一部分进行评估,但实际上所有v
都已在此处进行评估,因此:p
会打印所有内容,并且不会指示第一部分和第二部分之间存在的共享
现在,为什么pathCounts'
没有同样的问题?好的,问题是,尽管map f(repeat x)=repeat(f x)
是一个外延正确的等式,但在Haskell的GHC实现中,这个等式在操作上并不可靠,:p
完全是关于操作语义的,它对外延语义嗤之以鼻。特别是,map
没有(不能)观察到repeat x
中存在的共享;因此,它产生一个非循环无限列表。由于map f(repeat x)
的共享较少,因此强制执行map f(repeat x)
的一部分不会导致内存中的表示形式得到完全评估。我不确定这是为什么造成的,但上次调用:p pathCounts
会产生空间泄漏。在你的系统监视器中检查你的RAM使用情况当你调用它时,我的RAM很快就达到了完全使用。我的猜测是,无论出于何种原因,pathCounts'
的部分求值会使:p pathCounts
尝试求值pathCounts
的重复1
项。如果您尝试head,会发生什么。头。尾部$pathCounts'
?我假设你又出现空间泄漏。是的,pathCounts'
有点分散注意力-我只是想证明:p
适用于有限列表的部分计算无限列表。如果我直接或间接检查pathCounts
的任何部分,就会发生泄漏。更简单的示例ghci>let pcs=repeat$repeat 1::[Int]
,ghci>pcs!!0 !! 0
,ghci>:p pcs
顺便说一句,在常见的lisp和scheme(我想,至少是一些方言)中,甚至在prolog中,它们确实有办法显示结构中的循环性…@Willenss我们在Haskell中也有办法做到这一点-只是:p
没有使用它们。例如,请参阅。感谢链接(我知道我曾经看到过类似的东西!)--旁注:因此,这里的问题只存在于路径计数
的头部,原因与zipWith
和地图
相同。…@WillNess-Aha,感谢您的评论。现在我更了解里德·巴顿的抱怨了。当然,你(和他)是对的,我先前的反驳现在看起来很愚蠢=D