2011-06-04 59 views
3

我有一個接受Stream的函數。我的數據列在一個大列表中,涉及數百萬個項目。在F中包裝流中的序列#

是否有一種簡單的方法可以將一個序列封裝在一個Stream中,並返回序列在流中的塊?一種明顯的方法是實現我自己的流類,它返回序列的塊。例如:

type SeqStream(sequence:seq<'a>) = 
    inherit Stream() 
    default x.Read(buf, offset, count) = 
     // get next chunk 
     // yield chunk 

有沒有更簡單的方法呢?我沒有辦法改變接受流的目標函數。

回答

6

我認爲你的方法看起來不錯。唯一的問題是,Stream是一個相對複雜的類,有相當多的成員,你可能不想實現其中的大部分 - 如果你想將它傳遞給一些使用一些額外成員的代碼,你會需要使實現更復雜。總之,只實現Read一個簡單的流可以是這樣的:

type SeqStream<'a>(sequence:seq<'a>, formatter:'a -> byte[]) = 
    inherit Stream() 

    // Keeps bytes that were read previously, but were not used  
    let temp = ResizeArray<_>() 
    // Enumerator for reading data from the sequence 
    let en = sequence.GetEnumerator() 

    override x.Read(buffer, offset, size) = 
    // Read next element and add it to temp until we have enough 
    // data or until we reach the end of the sequence 
    while temp.Count < size && en.MoveNext() do 
     temp.AddRange(formatter(en.Current)) 

    // Copy data to the output & return count (may be less then 
    // required (at the end of the sequence) 
    let ret = min size temp.Count 
    temp.CopyTo(0, buffer, offset, ret) 
    temp.RemoveRange(0, ret) 
    ret 

    override x.Seek(offset, dir) = invalidOp "Seek" 
    override x.Flush() = invalidOp "Flush" 
    override x.SetLength(l) = invalidOp "SetLength" 
    override x.Length = invalidOp "Length" 
    override x.Position 
    with get() = invalidOp "Position" 
    and set(p) = invalidOp "Position" 
    override x.Write(buffer, offset, size) = invalidOp "Write" 
    override x.CanWrite = false 
    override x.CanSeek = false 
    override x.CanRead = true 

注意,我增加了一個額外的參數 - 對一般類型的值轉換爲字節數組的功能。一般來說,很難將任何東西轉換爲字節(可以使用一些序列化),所以這可能更容易。例如,對於整數,您可以編寫:

let stream = new SeqStream<_>([ 1 .. 5 ], System.BitConverter.GetBytes)