2014-02-13 50 views
4

給出如下的一系列項目爲:懶洋洋地分組在F#平坦的序列

[ ("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3) ] 

我如何轉換懶洋洋地成這樣:

{ ("a", { 1; 2; 3}); ("b", { 1 }); ("c", { 2; 3}) } 

你可以假設輸入數據源已經按分組鍵元素排序,例如, 「a」「b」和「c」。

我在那裏使用{}來表示這是一個懶惰評估的項目序列。

我已經得到了它的工作勢在必行的兩個while循環在源序列的IEnumerator操作,但這涉及到創建引用變量和變異等等。我相信有更好的方法做到這一點,也許使用遞歸或使用Seq庫中的一些操作,例如掃描還是展開?

+0

我之前做過類似的事情,也有一些突變:http://rojepp.wordpress.com/2012/12/20/sql-optimization-trick-in-f/ –

回答

5

如果你想實現這種過度IEnumerable<'T>(使它懶惰),那麼它必然要有些必要的,因爲所使用的IEnumerator<'T>型遍歷投入勢在必行。但其餘的可以寫成使用序列表達式的遞歸函數。

以下是第一級(它產生每組懶惰)懶惰,但它不會產生懶洋洋的族元素(我認爲這將有非常微妙的語義):

/// Group adjacent elements of 'input' according to the 
/// keys produced by the key selector function 'f' 
let groupAdjacent f (input:seq<_>) = seq { 
    use en = input.GetEnumerator() 

    // Iterate over elements and keep the key of the current group 
    // together with all the elements belonging to the group so far 
    let rec loop key acc = seq { 
    if en.MoveNext() then 
     let nkey = f en.Current 
     if nkey = key then 
     // If the key matches, append to the group so far 
     yield! loop key (en.Current::acc) 
     else 
     // Otherwise, produce the group collected so far & start a new one 
     yield List.rev acc 
     yield! loop nkey [en.Current] 
    else 
     // At the end of the sequence, produce the last group 
     yield List.rev acc 
    } 
    // Start with the first key & first value as the accumulator 
    if en.MoveNext() then 
    yield! loop (f en.Current) [en.Current] } 

不幸的是,這個(非常有用!)功能不包括在標準F#庫,因此,如果你想組相鄰元素(而不是使用Seq.groupBy列表中的任意元素),你必須自己定義...

1
Seq.groupBy fst 

會做的伎倆

+0

這會給你'(「 a「,[(」a「,1),(」a「,2)..])'。 –

+0

更重要的是,我認爲Isaac會問如何對_adjacent_元素進行分組,而不是輸入中的任何元素使用相同的密鑰... –

+0

不是,它會給你一個seq > – Lazydev

4
let p = [("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3)] 
let l = p |> Seq.groupBy fst |> Seq.map(fun x -> fst x, snd x |> Seq.map snd) 
+0

使用'Seq.groupBy'會忽略_「你可以假設輸入數據源已經在分組關鍵元素「_」上排序,結果它不能夠產生這些OP正在問的關於......的羣體,所以這產生了正確的結果,但不是懶惰地是問題的重點)。 –

+0

謝謝托馬斯解釋爲什麼這不是我以後(但感謝OP的建議) –

2

F#+有一個泛型函數chunkBy,可以用來做:

#r "FSharpPlus.dll" 
open FSharpPlus 

seq [ ("a", 1); ("a", 2); ("a", 3); ("b", 1); ("c", 2); ("c", 3) ] 
    |> chunkBy fst 
    |> map (fun (x,y) -> x, map snd y) 

而且它與seqarraylist工作。

seq的實現與Tomas的groupdAdjacent幾乎相同。

+0

FsControl看起來像一個非常好的想法..我不知道這個 – nicolas