推理懒惰

时间:2011-03-11 15:16:57

标签: haskell lazy-evaluation

我有以下代码段:

import qualified Data.Vector as V
import qualified Data.ByteString.Lazy as BL
import System.Environment
import Data.Word
import qualified Data.List.Stream as S

histogram ::  [Word8] -> V.Vector Int
histogram c = V.accum (+) (V.replicate 256 0) $ S.zip (map fromIntegral c) (S.repeat 1)

mkHistogram file = do
  hist <- (histogram . BL.unpack) `fmap` BL.readFile file
  print hist

我这样看:打印之前什么都没做。通过首先解压缩打印thunk时,然后一次从一个Word8映射。这些word8中的每一个都用1压缩,一次一个值。这个元组然后由累加器函数获取,该函数一次更新数组,一个元组/ Word8。然后我们移动到下一个thunk并重复,直到不再有内容为止。

这将允许在常量内存中创建直方图,但是这没有发生,但它会因堆栈溢出而崩溃。如果我尝试对其进行分析,我会看到它运行到最后,但是记忆很多(对于2.5 Mb文件,需要300-500 Mb)。记忆是线性获得的,直到结束,直到它被释放,形成一个“漂亮的”三角形图形。

我的推理在哪里出错,我应该采取什么步骤让它在恒定的记忆中运行?

1 个答案:

答案 0 :(得分:12)

我认为问题在于Data.Vector的元素并不严格。因此,虽然你的推理是正确的,但在累积直方图时,你的thunk看起来像:

<1+(1+(1+0)) (1+(1+0)) 0 0 (1+(1+(1+(1+0)))) ... >

而不是

<3 2 0 0 4 ...>

只有当你打印时才计算出这些总和。我没有在文档(耻辱)中看到严格的accum函数,并且没有任何地方可以挂钩seq。摆脱这种困境的一种方法可能是使用Data.Vector.Unboxed代替,因为未装箱的类型是解除严格的。也许您可以使用您的示例作为用例请求严格的accum函数。