2012-01-03 4 views
2

5000 개의 파일이 5000 개의 파일로 저장되어 있습니다. 나는 그들의 합계를 찾아야한다. DF2 유형은 Vector Double의 동의어이며 Num의 인스턴스가됩니다. 그래서 읽고 [IO DF2]를 나열하고 그것을 접어 모든 파일을 구문 분석 : 나는 오류가 발생하지만리소스 소모 (열려있는 파일이 너무 많음)

getFinal :: IO DF2 
getFinal = foldl1' (liftA2 (+)) $ map getDF2 [1..(sdNumber runParameters)] 
    where getDF2 i = fmap parseDF2 $ readFile ("DF2/DF2_" ++ show i) 

을 :

DF2: DF2/DF2_1022: openFile: resource exhausted (Too many open files) 

구글은 매우 일반적인 것으로이 문제를 밝혀 :

그러나 게으른 IO 문제는 무엇인지 알지 못했습니다. 게으른 경우 파일이 필요하기 전에 파일을 여는 이유는 무엇입니까? Duncan Coutts가 내 경우에 elegant solution을 적용하는 방법을 이해하지 못했습니다.

답변

6

필요하기 전에 파일을 열지는 않습니다. 그것은 전체 문자열을 강제로 때까지 그들을 닫지 않는 것입니다. 이 문제를 해결하는 간단한 방법은 문자열을 읽은 후 전체 문자열을 강제로 읽는 것입니다. 벡터가 엄격하기 때문에,이 작업을 수행하는 가장 간단한 방법은 구문 분석 후 평가하는 벡터를 강제하는 것입니다 :

getFinal :: IO DF2 
getFinal = foldl1' (liftA2 (+)) $ map getDF2 [1..(sdNumber runParameters)] 
    where getDF2 i = readFile ("DF2/DF2_" ++ show i) >>= evaluate . parseDF2 

Control.Exception.evaluate 사용; evaluate을 인수를 강제로 반환 한 다음 반환하는 것으로 생각할 수 있습니다. 그러나 parseDF2이 전체 문자열을 사용하는 경우에만 작동합니다.

좀 더 우아한 해결책은 lazy IO에서 완전히 벗어나 iteratees 나 그와 비슷한 것을 사용하는 것입니다. 하지만 그렇게 간단한 사용의 경우 가치가 없을 것입니다.

+0

예제에서 마지막 두 문자열은'evaluate $ parseDF2 s'로 바꿀 수 있으며'length s'는 필요하지 않습니다. 그래서 정말로 컴팩트 한 솔루션이 있어야합니다. – Yrogirg

+0

'getDF2'의 두 줄은'return $! '로 바꿀 수 있다고 생각합니다. parseDF2 s'라고합니다. 적어도 관련된 'Vector'가 언 박싱되어 있다면 적어도. –

+0

@Yrogirg : 그건 잘 작동하지 않습니다 :'evaluate'은 WHNF에 한 수준 만 평가하고 파서는'DF2'의 게으른 필드에서 파싱 작업을 쉽게 할 수 있습니다. deepseq 패키지를 사용했다면 (보통), getDF2 i = force를 사용할 수 있다고 생각합니다. parseDF2 <$> readFile ("DF2/DF2_"++ show i)'. – ehird

관련 문제