ram*_*ion 12 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
Run Code Online (Sandbox Code Playgroud)
放入ghci,如果我根本没有评估,我可以:p成功使用:
ghci> :p pathCounts
pathCounts = (_t1::[[Integer]])
ghci> :p pathCounts'
pathCounts' = (_t2::[[Integer]])
Run Code Online (Sandbox Code Playgroud)
但如果我pathCounts'部分评估,那么:p在pathCounts继续成功的同时冻结pathCounts':
ghci> head . head $ pathCounts'
1
ghci> :p pathCounts'
pathCounts' = (1 : (_t4::[Integer])) : (_t5::[[Integer]])
ghci> :p pathCounts
^CInterrupted.
Run Code Online (Sandbox Code Playgroud)
我希望:p pathCounts打印相同:p pathCounts',因为我只是部分评估它.为什么不工作?
我希望
:p pathCounts打印相同:p pathCounts',因为我只是部分评估它.
啊,但那是有趣的一点!事实上,你已经完全评估了(无限长)的头部pathCounts.让我们用一个稍小的例子来简化讨论:
> let v = repeat 1 :: [Int]
> head v
1
> :p v
^C
Run Code Online (Sandbox Code Playgroud)
我声称在完全评估之后head v,事实上v也是完全评估的.这是因为,在存储器中,v是循环单链表.因此,如果您已经足够了解第一个元素,那么就没有什么可以评估了!
结果是,当你提出要求:print时,GHC正式尝试构造一个表示结构的所有评估部分的字符串 - 显然不能,因为它永远不会停止遍历.(:p根本没有办法表明在结构中共享.)
相比:
> let x = 1 :: Int; v = (x, x)
> fst v
1
> :p v
(1,1)
Run Code Online (Sandbox Code Playgroud)
虽然您只要求对第一部分进行评估,但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)不会导致内存表示完全评估.