Смущенный: Haskell IO Laziness
У меня возникают трудности в понимании ленивых оценок Хаскелла.
Я написал простую тестовую программу. Он читает 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) являются (:)
а также [,]
отличается с точки зрения ленивой оценки?
Любые комментарии приветствуются. Спасибо
[РЕДАКТИРОВАТЬ]
В3) Тогда можно ли обрабатывать 4-ю строку первой, а 2-ю строку без увеличения потребления памяти?
Эксперимент под руководством Дерека заключается в следующем. С переключением 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
1 ответ
Чтобы ответить на ваш первый вопрос, t
жив, насколько сборщик мусора обеспокоен, пока вы не достигнете конца consumeList
, Это не было бы таким большим делом, так как t
было бы громогласным указателем на работу, но проблема здесь в том, что громогласный теперь держит y
жив и getContents
должен на самом деле читать в y
чтобы добраться до k
, В вашем первом примере y
может быть сборка мусора во время чтения. (Как эксперимент, если вы переключились y
а также k
в этом примере я подозреваю, что вы увидите поведение, очень похожее на ваш первый пример.)
На ваш второй вопрос let [x,y,z,k] = ...
означает "(неопровержимо) сопоставить список ровно из четырех элементов". Это означает, что когда вы заставляете k
он должен (на этом этапе) проверить, что больше нет элементов, что означает, что он должен прочитать все входные данные, соответствующие k
прежде чем он может начать обрабатывать его. В предыдущем случае let (x:y:z:k:xs) = ...
он может начать обработку k
немедленно, потому что не нужно сначала проверять, xs
является []
или же (_:_)
,