困惑:Haskell IO 懒惰

Confused: Haskell IO Laziness

我很难理解 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

wordsmap 被执行 在懒惰的字符流上,这个程序使用常量内存。

但是当我添加参数 t 时,情况发生了变化。 我的期望是因为 t 在惰性流上是 mapwordst 未在 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

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

Q2) (:)[,] 在惰性求值方面有什么不同吗?

欢迎任何评论。谢谢

[编辑]

Q3) 那么,是否可以先处理第4行,然后再处理 处理第二行,不增加内存消耗?

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

回答您的第一个问题,就垃圾收集器而言,t 一直有效,直到您到达 consumeList 的末尾。这没什么大不了的,因为 t 会是一个 thunk 指向工作要做的事情,但这里的问题是 thunk 现在让 y 活着,而 getContents 实际上必须阅读 y 以到达 k。在您的第一个示例中, y 可能会在读入时被垃圾收集。(作为实验,如果您在此示例中切换 yk ,我怀疑您会看到行为非常像你的第一个例子。)

对于您的第二个问题,let [x,y,z,k] = ... 表示“(无可辩驳地)匹配 正好 四个元素的列表”。这意味着当您强制 k 时,它需要(在那时)检查是否没有其他元素,这意味着它需要读入对应于 k 的所有输入,然后才能开始处理它.在较早的情况下,let (x:y:z:k:xs) = ... 它可以立即开始处理 k,因为它不必先检查 xs[] 还是 (_:_)