在haskell中读取大文件?

Joe*_*oel 18 io haskell lazy-evaluation bytestring

我一直在尝试读取haskell中的大文件.

我需要使用自定义算法为大学项目压缩它.一切正常,直到我开始压缩大文件.

我从我的程序中提取出错了,我在这里以"Hello大文件"的形式公开它:

import System
import qualified Data.ByteString.Lazy as BL
import Data.Word

fold_tailrec :: (a -> b -> a) -> a -> [b] -> a
fold_tailrec _ acc [] =
    acc
fold_tailrec foldFun acc (x : xs) =
    fold_tailrec foldFun (foldFun acc x) xs

fold_tailrec' :: (a -> b -> a) -> a -> [b] -> a
fold_tailrec' _ acc [] =
    acc
fold_tailrec' foldFun acc (x : xs) =
    let forceEval = fold_tailrec' foldFun (foldFun acc x) xs in
    seq forceEval forceEval

main :: IO ()
main =
    do
        args <- System.getArgs
        let filename = head args
        byteString <- BL.readFile filename
        let wordsList = BL.unpack byteString
        -- wordsList is supposed to be lazy (bufferized)
        let bytesCount = fold_tailrec (\acc word -> acc + 1) 0 wordsList
        print ("Total bytes in " ++ filename ++ ": " 
               ++ (show bytesCount))
Run Code Online (Sandbox Code Playgroud)

我将此文件命名为Test.hs,然后执行以下操作:

$ ls -l toto
-rwxrwxrwx 1 root root 5455108 2011-03-23 19:08 toto
$ ghc --make -O Test.hs
[1 of 1] Compiling Main             ( Test.hs, Test.o )
Linking Test ...
$ ./Test toto
Stack space overflow: current size 8388608 bytes.
Use `+RTS -Ksize -RTS' to increase it.
$ ./Test toto +RTS -K50M -RTS
Stack space overflow: current size 50000000 bytes.
Use `+RTS -Ksize -RTS' to increase it.
$ ./Test toto +RTS -K500M -RTS
"Total bytes in toto: 5455108"
$ time ./Test toto +RTS -K500M -RTS
"Total bytes in toto: 5455108"

real    0m33.453s
user    0m8.917s
sys 0m10.433s
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释为什么我需要500兆字节的RAM和30秒的CPU才能浏览一个可怜的5兆字节文件?请问我做错了什么?为什么不将[word8]缓冲为ByteString文档说明.以及如何解决这个问题?

我试图定义自己的尾递归折叠而不是foldl,foldr或foldl'.我尝试用seq来解冻thunk.到目前为止我没有结果.

感谢任何帮助,因为我被困住了.

Chr*_*icz 34

构造"seq x x"总是无用的.如果y = seq xx并且我强制y则强制x然后返回x.这相当于y = x并强制y.因此"seq forceEval forceEval"只能执行"forceEval".

使用折叠的错误是常见的.

您正在使用折叠来执行输入中的字节计数.你应该使用一个严格的左折叠这样的总和,但你的手写折叠是一个懒惰的左折叠.(acc + 1)没有得到评估,因此它构建了500万个嵌套应用程序:(((...(0 + 1)+1)+1)+ 1)+1)+1)... + 1 ).然后在打印时强制它,评估试图下降到500万个括号.

因此,挂起的堆栈为每个Word8都有一个条目.对于短输入,它到达终点并看到0.对于长输入,它用尽GHC的堆栈空间,因为GHC的创建者和大多数用户认为尝试分配500万个堆栈帧通常是程序员的设计错误.

我预测你可以使用"seq"来解决这个问题:

fold_tailrec' foldFun acc (x : xs) =
    let acc' = foldFun acc x
    in seq acc' (fold_tailrec' foldFun acc' xs)
Run Code Online (Sandbox Code Playgroud)

  • 他们可能会为此类案件添加警告. (2认同)