我有一个生成表单列表的函数:[(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
我已经为此搜索了一个合适的解决方案,并尝试使用模式匹配而没有任何运气。有一个简单的解决方案,我错过了吗?谢谢!
答案 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
之后你需要比较两个长度:
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