2012-01-03 61 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