如何在haskell中处理没有堆栈溢出错误的大文件?

时间:2012-04-01 13:07:27

标签: haskell

这是我的代码:(获取文件行号和字数)

import System.IO
import Data.Maybe
readL::(Int,Int,Int)->IO()
readL (w,l,-1) = do
                putStrLn $ "word:" ++(show w )++"\nline:"++(show l)
readL (w,l,0) = do 
                s<-hIsEOF stdin
                if s 
                        then readL (w,l,-1)
                        else 
                                do
                                f<-getLine
                                readL (w+length f,l+1,0)

main = do
        hSetBinaryMode stdin True
        readL (0,0,0)

当我处理大小为100米的文件时,它只是崩溃,但有错误: 堆栈空间溢出:当前大小为8388608字节

我写错了吗?

我还有另一个版本:

import System.IO
import Data.List
main = do
        hSetBinaryMode stdin True
        interact $ (\(w,l)->"line:"++(show l)++"\nwords:"++(show w)++"\n"). foldl' (\(w,l) r-> (w + length r,l+1) ) (0,0)   .lines

这也有同样的问题......并且有很多记忆,所以,任何人都可以解决这个问题吗?我只是哈斯克尔的新学习者。

1 个答案:

答案 0 :(得分:2)

问题是,在达到输入结束之前,wl readL参数都不会被评估。因此,对于具有多行的输入,您构建了大的(((0 + length line1) + length line2) ... + length lastline),类似于l,并且对于超过五十行左右,评估该thunk将不适合可用堆栈。此外,length f保持读取行直到被评估,从而导致不必要的大量内存使用。

你必须在循环中保持累积参数的评估,最简单的方法是使用爆炸模式

readL !(!w,!l,-1) = ...

seq

readL (w,l,c)
    | w `seq` l `seq` (c == -1) = putStrLn $ "word:" ++(show w )++"\nline:"++(show l)
readL (w,l,0) = do 
                s<-hIsEOF stdin
                if s 
                        then readL (w,l,-1)
                        else 
                                do
                                f<-getLine
                                readL (w+length f,l+1,0)

foldl'版本存在同样的问题,

foldl' (\(w,l) r-> (w + length r,l+1) ) (0,0)

仅将累加器对评估为弱头法线形式,即到最外层构造函数,此处为(,)。它不会强制评估组件。要做到这一点,你可以

  • 对折叠使用严格的对类型

    data P = P !Int !Int
    
    foo = ... . foldl' (\(P w l) r -> P (w + length r) (l+1)) (P 0 0) . lines
    
  • 或在折叠功能中使用seq

    ... . foldl' (\(w,l) r -> w `seq` l `seq` (w + length r, l+1)) . lines