使用无限列表管理内存使用

时间:2017-02-07 23:58:06

标签: haskell

所以我试图找到使用数字15

制作数字5的方法
import Data.List
import Control.Monad
import Control.Monad.Omega
import System.IO

data Expr = X | Sub Expr Expr | Div Expr Expr | Pow Expr Expr
          deriving Show

eval x X = x
eval x (Sub a b) = eval x a -  eval x b
eval x (Div a b) = eval x a /  eval x b
eval x (Pow a b) = eval x a ** eval x b

exprs | v <- each exprs
      = (X:) . concat $ transpose
      [ runOmega $ liftM2 Div v v
      , runOmega $ liftM2 Sub v v
      , runOmega $ liftM2 Pow v v
      ]

main = do
  hSetBuffering stdout LineBuffering
  mapM_ print $ filter ((==5).eval 15) exprs

但是,第一个结果是列表中的118588079th元素,并且Haskell在到达之前很久就耗尽了内存。我怎么知道第一个是118588079?因为如果我只计算它并要求索引,Haskell根本就不使用内存:

run x | v <- each $ run x
      = (x:) . concat $ transpose
      [ runOmega $ liftM2 (/ ) v v
      , runOmega $ liftM2 (- ) v v
      , runOmega $ liftM2 (**) v v
      ]

main = print . map snd . filter ((==5).fst) $ zip (run 15) [0..]

在第一种情况下我的记忆究竟在哪里,我该如何解决它?

2 个答案:

答案 0 :(得分:5)

顶级绑定,如

list = [1..1000000]

将懒惰地生成一次列表,并将其保存在内存中以供后续使用。

一个功能,而不是

fun x = [1..1000000]

会在每次通话时分配一个新列表,每次都从头开始重新计算(懒惰)。因为它是由顶级绑定引用的,所以它永远不会被垃圾收集。

请注意,这不是Haskell规定的 - 它只是以这种方式工作的GHC。

为了进行比较,请尝试以下变体:

run x | v <- each $ run x
      = (x:) . concat $ transpose
      [ runOmega $ liftM2 (/ ) v v
      , runOmega $ liftM2 (- ) v v
      , runOmega $ liftM2 (**) v v
      ]
run15 = run 15
main = print . map snd . filter ((==5).fst) $ zip run15 [0..]

您应该会看到大量内存被消耗,因为不会发生垃圾回收。取而代之的是,

main = print . map snd . filter ((==5).fst) $ zip run15 [0..]
   where 
   run15 = run 15

应该允许垃圾收集并在少量内存中运行。

(顺便说一下,使用模式绑定代替let / where困惑了我一段时间。)

答案 1 :(得分:2)

@chi是完全正确的。没有参数的函数实际上不是一个函数,并且将保存在内存中,无论它是如何递归的,它是否产生无限的数据结构。这会导致你的内存不足:

run' | v <- each run'
     = (15:) . concat $ transpose
     [ runOmega $ liftM2 (/ ) v v
     , runOmega $ liftM2 (- ) v v
     , runOmega $ liftM2 (**) v v
     ]

main = print $ run' !! 1000000000

虽然第一个run函数不会。同样的说明 - 尽管看起来很傻 - 这不会耗尽内存:

exprs' x | v <- each $ exprs' x
         = (x:) . concat $ transpose
         [ runOmega $ liftM2 Div v v
         , runOmega $ liftM2 Sub v v
         , runOmega $ liftM2 Pow v v
         ]

main = print $ exprs' X !! 1000000000


PS

如果有人好奇,列表中的118588079个元素是((((x**x)/x)-(x**(x-(x/x))))-((x-x)-(x/x)))