2016-12-23 2 views
4

대용량 파일에서 각 문자의 발생 횟수를 계산하고 싶습니다. 하스켈에서 엄격한 방식으로 계산을해야한다는 것을 알고 있지만 (foldl '을 사용하여 달성하려고 시도 했음) 여전히 메모리가 부족합니다. 비교를 위해 파일 크기는 약 2GB이고 컴퓨터에는 100GB의 메모리가 있습니다. 그 파일에 다른 문자가 많지 않습니다. 어쩌면 20. 내가 뭘 잘못하고 있니?대용량 파일의 문자 계산 중 메모리 부족 문제가 발생했습니다.

ins :: [(Char,Int)] -> Char -> [(Char,Int)] 
ins [] c = [(c,1)] 
ins ((c,i):cs) d 
    | c == d = (c,i+1):cs 
    | otherwise = (c,i) : ins cs d 

main = do 
    [file] <- getArgs 
    txt <- readFile file 
    print $ foldl' ins [] txt 
+0

에 대한 [? 무엇 약한 헤드 정규형 (http://stackoverflow.com/questions/6872898/iskell-what-is-weak head-normal-form) – freestyle

+0

이것은'insmc = Map.insertWith '(+) c (1 :: Int) m' – Michael

답변

7

귀하의 ins 기능은 memory leak의 많은 원인이 thunks의 톤을 만드는 것입니다. foldl'은 여기서 충분하지 않은 weak head normal form으로 평가됩니다. 필요한 것은 deepseq부터 Control.DeepSeq까지이며, 정상형이됩니다.

또는 연결 목록 대신 카운트 용으로 Data.Map.Strict을 사용하십시오. 또한 입출력이 2GB의 순서 인 경우 일반 문자열 대신 lazy ByteString을 사용하는 것이 좋습니다.

벨로 코드를 입력의 크기에 관계없이 일정한 메모리 공간에서 수행해야합니다 : 난 당신이 읽을 것을 권장

import System.Environment (getArgs) 
import Data.Map.Strict (empty, alter) 
import qualified Data.ByteString.Lazy.Char8 as B 

main :: IO() 
main = getArgs >>= B.readFile . head >>= print . B.foldl' go empty 
    where 
    go = flip $ alter inc 
    inc :: Maybe Int -> Maybe Int 
    inc Nothing = Just 1 
    inc (Just i) = Just $ i + 1 
+0

으로 잘 동작합니다. 일반적으로'deepseq' 병렬 프로그래밍을 제외하고 이 경우에는 정상적으로 작동하지만, 여기에도 잔인합니다 - 필요한만큼 두 배의 힘을 가할 것으로 예상 할 수 있습니다! 다른 맥락에서는 훨씬 더 심각합니다. 또한, 'Data.Bytestring.Char8'은 입력이 ASCII와 같은 것으로 제한되지 않는다면 * 문자 *를 계산하는 데는 적합하지 않습니다. 마지막으로, 'Data.Map'은 작은 키가있는지도에는 이상적이지 않습니다. 일반적으로'IntMap'을 사용하고'Int'와'Char'를 변환하는 것이 훨씬 낫습니다. – dfeuer

+0

정말 도움이되었습니다! 고마워요! – Dune