0

我有一个生成表单列表的函数: [(String1, exp1); (字符串2,exp2);... ETC]

exp 是我之前定义的类型。

我现在需要一种方法来确定这样的列表是否无效。如果一个列表有一个重复的字符串,但每一个都有不同的 exp 配对,则它是无效的。IE:

[("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

3 回答 3

4

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

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

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

于 2013-10-01T07:17:55.413 回答
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 的解决方案非常优雅。但是,对于平均无效案例,这会稍微快一些,因为它会在遇到的第一个无效重复项处停止。

于 2013-10-01T07:39:05.783 回答
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
于 2013-10-01T21:56:36.433 回答