困惑:Haskell IO Laziness

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,情况就会发生变化.我的期望是,因为tmapwords懒惰流,而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)

在此输入图像描述

Der*_*ins 5

要回答你的第一个问题,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[](_:_).