确定F#列表重复的组件

时间:2013-10-01 06:19:36

标签: .net list f# pattern-matching tuples

我有一个生成表单列表的函数:[(String1,exp1); (String2,exp2); ......等等]

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

我已经为此搜索了一个合适的解决方案,并尝试使用模式匹配而没有任何运气。有一个简单的解决方案,我错过了吗?谢谢!

3 个答案:

答案 0 :(得分:4)

一个简单的解决方案是使用groupBy

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

除非您假设重复的组件是连续的,否则模式匹配不会有太大帮助。

答案 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的解决方案非常优雅。但是,对于平均无效的情况,这会稍微快一些,因为它会在遇到的第一个无效重复项时停止。

答案 2 :(得分: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