2012-01-03 52 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消耗整個字符串時纔有效。

一個更優雅的解決辦法是遠離懶惰IO完全移動,並使用iteratees或類似的東西。但這對於這樣一個簡單的用例可能不值得。

+0

事實證明,你的例子中的最後兩個字符串可以用'evaluate $ parseDF2 s'替換,不需要'length s'。所以我猜想必須有一個非常緊湊的解決方案。 – Yrogirg 2012-01-03 18:33:07

+0

我相信'getDF2'中的最後兩行可以用'return $!替換! parseDF2 s'。至少如果涉及的'Vector's被解除封裝。 – 2012-01-03 18:43:28

+0

@Yrogirg:這並不完全正常:'evaluate'只評估一個級別,WHNF,並且您的解析器可以很容易地在'DF2'的惰性字段中執行一些解析工作。如果你使用deepseq包(這很常見),那麼我認爲你可以使用'getDF2 i = force。 parseDF2 <$> readFile(「DF2/DF2_」++ show i)'。 – ehird 2012-01-03 18:47:01