2012-07-03 27 views
1

懒惰的评估对于像处理大文件这样的东西来说非常有用,因为这些文件一次不会放在主内存中。但是,假设序列中有一些元素需要立即进行评估,而其余元素可以进行延迟计算 - 有什么方法可以指定?如何评估一个懒惰序列的一部分?

具体问题:(万一,有助于回答这个问题)

具体来说,我使用了一系列IEnumerables的迭代器为多个序列 - 这些序列使用BinaryReader在流从文件中读取数据打开(每个序列负责从其中一个文件读入数据)。这些MoveNext()将以特定的顺序被调用。例如。 iter0然后iter1然后iter5然后iter3 ....等等。此订单在另一个序列中指定index = {0,1,5,3,....}。然而,序列是懒惰的,评估自然只在需要的时候完成。因此,当序列的IEnumerables正在移动时,文件读取(对于开始时从磁盘上的文件读取的序列)。这导致非法文件访问 - 一个进程正在读取的文件被再次访问(根据错误消息)。

确实,非法文件访问可能是出于其他原因,并且在尝试尽我所能调试其他原因之后,部分懒惰的评估可能值得尝试。

+0

如果您只是从文件中读取数据,那么在同时读取文件的多个流中应该没有问题。你试着使用一些缓存机制(急切地评估序列的一部分),但不应该有这种需要,因为文件系统总是使用缓冲区。 –

+1

你是如何打开文件的?您可能需要使用['FileShare.Read'](http://msdn.microsoft.com/en-us/library/System.IO.FileShare.aspx)。 – Daniel

+0

'|> Seq.cache' ...? –

回答

3

虽然我同意托马斯评论:如果文件共享妥善处理,你不应该需要这个,这里有一个方法来急切地评估第一ñ元素:

let cacheFirst n (items: seq<_>) = 
    seq { 
    use e = items.GetEnumerator() 
    let i = ref 0 
    yield! 
     [ 
     while !i < n && e.MoveNext() do 
      yield e.Current 
      incr i 
     ] 
    while e.MoveNext() do 
     yield e.Current 
    } 

let items = Seq.initInfinite (fun i -> printfn "%d" i; i) 

items 
|> Seq.take 10 
|> cacheFirst 5 
|> Seq.take 3 
|> Seq.toList 

输出

0 
1 
2 
3 
4 
val it : int list = [0; 1; 2] 
1

丹尼尔的解决方案是合理的,但我不认为我们需要其他运营商,大多数情况下只需Seq.cache

首先缓存您的序列:

let items = Seq.initInfinite (fun i -> printfn "%d" i; i) |> Seq.cache 

急于评价,然后从一开始就偷懒访问:

let eager = items |> Seq.take 5 |> Seq.toList 
let cached = items |> Seq.take 3 |> Seq.toList 

这将评估前5个元素一次(期间eager),但让他们缓存辅助访问。

+1

如果他的文件很大,Seq.cache会使用大量内存,因为它会保留所有枚举项。 – Daniel

+0

'Seq.cache'解决了文件读取错误问题。但是,是的,文件大小非常大 - 比物理内存容量大。 – AruniRC