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