2013-10-01 35 views
0

我有一個函數,它會生成一個形式列表:[(String1,exp1); (String2,exp2); ...等]確定F#列表重複組件

exp是我以前定義的類型。

我現在需要一種方法來確定這樣的列表是否無效。如果一個列表有一個重複的字符串,但是一個不同的exp與每個列表配對,這個列表就是無效的。即:

[("y", exp1); ("y", exp2); ("x", exp3)] //Invalid, as "y" is repeated with different exps 

[("y", exp1); ("y", exp1); ("x", exp3)] //Valid, as "y" is repeated with the same exps 

我已經尋找一個適當的解決方案,嘗試使用模式匹配沒有任何運氣。有沒有一個簡單的解決方案,我錯過了?謝謝!

回答

4

一個簡單的解決方案是使用groupBy

let hasNoRepeatedComponents xs = 
    xs   
    |> Seq.groupBy fst 
    |> Seq.map snd 
    |> Seq.forall (fun s -> Set.count (Set.ofSeq s) = 1) 

模式匹配也不會有多大效果,除非你認爲重複的成分是連續的。

2

如果您想要進行模式匹配,您需要某種結構來存儲以前看過的項目。 Map對此很有用,因爲我們需要查找。這裏有一種模式匹配方法:

let isValid source = 
    let rec loop source (m : Map<_,_>) = 
    match source with 
    | [] -> (true, "") 
    | (s,e) :: xs -> 
     match m.TryFind s with 
     | Some v when v <> e -> (false, sprintf "Key %s is repeated with different expressions" s) 
     | Some v -> loop xs m 
     | _ -> loop xs (m.Add (s,e)) 
    loop source Map.empty 

Pad的解決方案非常優雅。但是,對於平均無效情況,這會稍微快一點,因爲它會停在遇到的第一個無效重複項目上。

+0

釷阿克斯,兩個答案都很好 – user1618840

1

@ pad的答案是一個很好的起點,但它並不適用於我(根據需要)(即最後一個樣本工作不正確)。

如果我正確理解問題,則非連續重複仍被視爲重複,因此它們會使列表無效。

基本上,groupBy之後你需要比較兩個長度:

  • 用「Y」,「X」相關聯的序列元素的原始長度等從由創建的序列的
  • 長度應用Seq.distinct

下面的代碼:

let isValid xs = 
    xs 
    |> Seq.groupBy fst 
    |> Seq.map snd // we no longer need the key 
    // compare two lengthes: original and filtered/distinct 
    |> Seq.forall (fun x -> (Seq.length x) = Seq.length(Seq.distinct x)) 

[("y", 5); ("y", 6); ("x", 7)] |> isValid |> printfn "%A" // true 
[("y", 5); ("y", 5); ("x", 7)] |> isValid |> printfn "%A" // false 
[("y", 5); ("y", 6); ("x", 7); ("y", 7); ("y", 6)] |> isValid |> printfn "%A" // false