3

我在理解 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

words并且map在字符流上懒惰地执行,该程序使用常量内存。 持续使用内存

但是当我添加参数t时,情况发生了变化。我的期望是因为tismap并且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

内存在增加

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 行,再处理第 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

在此处输入图像描述

4

1 回答 1

5

要回答您的第一个问题,t就垃圾收集器而言,直到您到达consumeList. 这没什么大不了的,因为t这将是一个指向工作要做的 thunk,但这里的问题是 thunk 现在保持y活动状态并且getContents必须实际读取y才能到达k. 在您的第一个示例中,y可能会在读入时进行垃圾收集。(作为实验,如果您切换y并且k在此示例中,我怀疑您会看到与您的第一个示例非常相似的行为。)

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

于 2016-01-22T05:20:38.033 回答