2016-04-21 28 views
2

我想通過交替行將文件分成兩個單獨的文件。 (即寫入文件1的行1,3,5,7 ...和寫入文件2的行2,4,6,8 ...)。代碼中的空間泄漏在哪裏?

我正在使用的文件是〜700MB,所以當我看到內存使用情況超過6GB時,我知道有什麼地方是錯誤的。

main :: IO() 
main = withFile splitFile ReadMode splitData 
    where 
    splitData h = do 
     dataSet <- lines <$> hGetContents h 
     let (s1,s2) = foldl' (\(l,r) x -> (x:r,l)) ([],[]) dataSet 
     writeFile testFile $ unlines s1 
     writeFile trainingFile $ unlines s2 

我最初使用與foldl的懶惰版本,但經過一番研究,它似乎是用嚴格的版本會有所幫助。但唉,它並沒有明顯的區別。我也試着用-O2編譯,但是也沒有做任何事。

我使用GHC 7.10.2

我沒有得到一個堆棧溢出,那麼,什麼是使用全部內存呢?

+1

另外,'foldl'和'foldl''都不會做你想要的東西。如果你願意,你可以使用'foldr',但user2407038的'zipWithM_'方法更清晰和簡單。 – dfeuer

回答

7

正如@dfeuer的評論所述,使用writeFile會迫使整個字符串被寫入計算,這也迫使讀取整個輸入。空間泄漏是由於在寫入第一個文件時必須將整個第二個文件保存在內存中,這一點很明顯,一次只能在內存中保留一行。實際上,解決方案是逐行寫入:

import Control.Monad 
import System.IO 

main :: IO() 
main = 
    withFile splitFile ReadMode $ \hIn -> 
    withFile testFile WriteMode $ \hOdd -> 
    withFile trainingFile WriteMode $ \hEven ->   
    zipWithM_ hPutStrLn (cycle [hOdd, hEven]) . lines =<< hGetContents hIn 

該程序在恆定空間中運行。

+0

我不會在這裏使用'<$>',因爲運算符優先級不明顯(或者我會使用括號,但很可能我會使用'fmap')。 –