Chu*_*ang 3 haskell lazy-evaluation space-leak
我在理解Haskell懒惰评估方面遇到了困难.
我写了简单的测试程序.它读取4行数据,第二和第四输入行有很多数字.
consumeList :: [Int] -> [Int] -> [Int]
consumeList [] _ = error "hi" -- to generate heap debug
consumeList (x:xs) y = consumeList xs y
main = do
inputdata <- getContents
let (x:y:z:k:xs) = lines inputdata
s = map (read ::String->Int) $ words $ k
t = []
print $ consumeList s t
Run Code Online (Sandbox Code Playgroud)
words并且map在懒惰的字符流上执行,该程序使用常量内存.

但是当我添加论点时t,情况就会发生变化.我的期望是,因为t是map和words懒惰流,而t不是在使用consumeList,这种变化不应该改变的内存使用情况.但不是.
consumeList :: [Int] -> [Int] -> [Int]
consumeList [] _ = error "hi" -- to generate heap debug
consumeList (x:xs) y = consumeList xs y
main = do
inputdata <- getContents
let (x:y:z:k:xs) = lines inputdata
s = map (read ::String->Int) $ words $ k
t = map (read ::String->Int) $ words $ y
print $ consumeList s t -- <-- t is not used
Run Code Online (Sandbox Code Playgroud)
Q1)为什么这个程序在t没有使用时会继续分配内存?
我有另一个问题.当我模式匹配延迟流时[,],不会(:)改变内存分配行为.
consumeList :: [Int] -> [Int] -> [Int]
consumeList [] _ = error "hi" -- to generate heap debug
consumeList (x:xs) y = consumeList xs y
main = do
inputdata <- getContents
let [x,y,z,k] = lines inputdata -- <---- changed from (x:y:..)
s = map (read ::String->Int) $ words $ k
t = []
print $ consumeList s t
Run Code Online (Sandbox Code Playgroud)
Q2)是(:)和[,]在懒惰evalutation方面有什么不同?
欢迎任何评论.谢谢
[编辑]
Q3)那么,是否可以先处理第4行和第2行处理,而不增加内存消耗?
Derek指导的实验如下.通过从第二个例子切换y和k,我得到了相同的结果:
consumeList :: [Int] -> [Int] -> [Int]
consumeList [] _ = error "hi"
consumeList (x:xs) y = consumeList xs y
main = do
inputdata <- getContents
let (x:y:z:k:xs) = lines inputdata
s = map (read ::String->Int) $ words $ y -- <- swap with k
t = map (read ::String->Int) $ words $ k -- <- swap with y
print $ consumeList s t
Run Code Online (Sandbox Code Playgroud)
要回答你的第一个问题,t就垃圾收集器而言,直到你到达终点为止consumeList.这不会是一个大问题,因为t这将是一个指向工作要做的事,但这里的问题是thunk现在保持y活着并且getContents必须实际读入y才能进入k.在你的第一个例子中,y可能是垃圾收集,因为它被读入.(作为一个实验,如果你切换y,k在这个例子中,我怀疑你看到的行为非常像你的第一个例子.)
对于你的第二个问题,let [x,y,z,k] = ...意味着"(无可辩驳地)匹配恰好四个元素的列表".这意味着当你强制k它需要(在那时)检查没有其他元素,这意味着它需要读入对应的所有输入k才能开始处理它.在前面的情况下,let (x:y:z:k:xs) = ...就可以开始处理k立即使用,因为它没有首先检查是否xs是[]或(_:_).