困惑: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
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
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
可能会在读入时被垃圾收集。(作为实验,如果您在此示例中切换 y
和 k
,我怀疑您会看到行为非常像你的第一个例子。)
对于您的第二个问题,let [x,y,z,k] = ...
表示“(无可辩驳地)匹配 正好 四个元素的列表”。这意味着当您强制 k
时,它需要(在那时)检查是否没有其他元素,这意味着它需要读入对应于 k
的所有输入,然后才能开始处理它.在较早的情况下,let (x:y:z:k:xs) = ...
它可以立即开始处理 k
,因为它不必先检查 xs
是 []
还是 (_:_)
。
我很难理解 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
时,情况发生了变化。
我的期望是因为 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
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
可能会在读入时被垃圾收集。(作为实验,如果您在此示例中切换 y
和 k
,我怀疑您会看到行为非常像你的第一个例子。)
对于您的第二个问题,let [x,y,z,k] = ...
表示“(无可辩驳地)匹配 正好 四个元素的列表”。这意味着当您强制 k
时,它需要(在那时)检查是否没有其他元素,这意味着它需要读入对应于 k
的所有输入,然后才能开始处理它.在较早的情况下,let (x:y:z:k:xs) = ...
它可以立即开始处理 k
,因为它不必先检查 xs
是 []
还是 (_:_)
。