В замешательстве: лень Haskell IO

У меня возникают трудности с пониманием ленивой оценки 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-ю строку, а затем 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

введите здесь описание изображения


person Chul-Woong Yang    schedule 22.01.2016    source источник


Ответы (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 [] или (_:_).

person Derek Elkins left SE    schedule 22.01.2016
comment
Я немного понял. Спасибо. Не могли бы вы ответить мне еще на несколько вопросов? (1) Тогда возможно ли обработать 4-ю строку, а затем обработать 2-ю строку без увеличения потребления памяти? Если простое удержание указателя y из (x:y:z:k:xs) вызывает чтение и обработку потока, возможно ли это? (2) Указывает ли графы ghc-prim.GHC.Types: на необработанную строку, прочитанную getContents, или на список, обработанный map . words? Я вижу, что объем памяти составляет ~ 100 МБ, но фактические входные данные составляют 5 МБ. поэтому я подозреваю, что выделенная память может быть map . words обработана. - person Chul-Woong Yang; 22.01.2016
comment
возможно, вам следует начать новые вопросы, но обычно в таких случаях рекомендуется отказаться от ленивого ввода-вывода (как вы видели, иногда это немного больно) - person Random Dev; 22.01.2016
comment
Понимаю. Я начну новый вопрос для первого вопроса комментария выше. - person Chul-Woong Yang; 22.01.2016