2014-12-03 5 views
1

Этот код используется только 1Мб ОЗУ:памяти бесконечных списков в Haskell

main = putStrLn $ show $ length (take 2000000 [1..]) 

Хотя этот код использует 90Mb of RAM:

nums :: [Int] 
nums = nextInts 0 
    where 
    nextInts x = x : nextInts (succ x) 

main = putStrLn $ show $ length (take 2000000 nums) 

Если изменить это так, то это будет используя снова 1Мб ОЗУ:

nums :: [Int] 
nums = nextInts 0 
    where 
    nextInts x 
     |x == 90000000 = [] -- some large number 
     |otherwise = x : nextInts (succ x) 

main = putStrLn $ show $ length (take 2000000 nums) 

Вопрос: C кто-то объясняет, почему второй образец кода хранит весь список в ОЗУ, а третий не делает этого. Также опишите, как я должен изменить второй образец, чтобы использовать O (1) RAM и быть списком бесконечности.

+0

'putStrLn. show' is 'print' – nponeccop

+0

' nums' - это точно такой же код, как 'iterate' (источники для библиотечных функций могут быть найдены Hoogle) – nponeccop

+0

Это не то, что происходит в вашем случае: но иногда GHCi будет хранить весь список в ОЗУ только потому, что он находится в корневой области. –

ответ

13

Во втором случае использование памяти происходит не от хранения списка, а от создания неоцененных громкоговорителей с использованием succ x.

succ ленив, поэтому звонок succ x просто выделяет новый кусок на кучу. Этот thunk никогда не оценивается, потому что вам никогда не нужно знать значение любого из элементов списка.

В третьем случае, если вы пользуетесь x, используя защитный код x == 9000000, и поэтому трюки не растут.

+1

Да, вы правы, используя «seq» во вторых образцовых работах –

+0

Почему GC не собирает эти трюки так, как они сделаны? –

+2

Поскольку трюки образуют цепочку, то есть thunk для x = 4 относится к thunk для x = 3, который относится к thunk для x = 2 и т. Д. Когда 'take' проходит через элемент списка x = 4, мы можем ' t мусор собирает его, потому что новая глава списка x = 5, которая относится к thunk x = 4. – ErikR

Смежные вопросы