2012-07-03 29 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