简体   繁体   English

如何匹配值的多个副本?

[英]How to match multiple copies of a value?

F#'s pattern matching is very powerful so it felt natural to write: F#的模式匹配非常强大,所以编写它是很自然的:

match (tuple1, tuple2) with
| ((a, a), (a, a)) -> "all values are the same"
| ((a, b), (a, b)) -> "tuples are the same"
| ((a, b), (a, c)) -> "first values are the same"
// etc

However, the first pattern match gives a compiler error: 但是,第一个模式匹配会产生编译器错误:

'a' is bound twice in this pattern

Is there a cleaner way to do it than the following? 是否有比以下更清洁的方法?

match (tuple1, tuple2) with
| ((a, b), (c, d)) when a = b && b = c && c = d -> "all values are the same"
| ((a, b), (c, d)) when a = c && b = d -> "tuples are the same"
| ((a, b), (c, d)) when a = c -> "first values are the same"
// etc

This is a perfect use case for F#'s "active patterns". 这是F#的“活动模式”的完美用例。 You can define a couple of them like this: 您可以像这样定义其中几个:

let (|Same|_|) (a, b) =
    if a = b then Some a else None

let (|FstEqual|_|) ((a, _), (c, _)) =
    if a = c then Some a else None

And then clean up your pattern matching with them; 然后清理与它们匹配的图案; note how the first case (where all values are equal) uses the nested Same pattern to check that the first and second elements of the tuple are equal: 注意第一种情况(所有值都相等)如何使用嵌套的Same模式来检查元组的第一个和第二个元素是否相等:

match tuple1, tuple2 with
| Same (Same x) ->
    "all values are the same"
| Same (x, y) ->
    "tuples are the same"
| FstEqual a ->
    "first values are the same"
| _ ->
    failwith "TODO"

Performance tip: I like to mark simple active patterns like these with inline -- since the logic within the active patterns is simple (just a few IL instructions), it makes sense to inline them and avoid the overhead of a function call. 性能提示:我喜欢使用inline标记这些简单的活动模式 - 因为活动模式中的逻辑很简单(只有几条IL指令),所以内联它们并避免函数调用的开销是有意义的。

You can use parameterized active patterns to remedy the issue. 您可以使用参数化活动模式来解决问题。

let (|TuplePairPattern|_|) ((p1, p2), (p3, p4)) ((a, b), (c, d)) =
    let matched =
        [(p1, a); (p2, b); (p3, c); (p4, d)]
        |> Seq.groupBy fst
        |> Seq.map (snd >> Set.ofSeq)
        |> Seq.forall (fun s -> Set.count s = 1)
    if matched then Some () else None

Particularly, you should define a pattern in a form of literals (chars, strings, etc). 特别是,您应该以文字(字符,字符串等)的形式定义模式。

match tuple1, tuple2 with
| TuplePairPattern(('a', 'a'), ('a', 'a')) -> "all values are the same"
| TuplePairPattern(('a', 'b'), ('a', 'b')) -> "tuples are the same"
| TuplePairPattern(("a", "b"), ("a", "c")) -> "first values are the same"
// etc

I think, the most elegant way can be accomplished by combining two excellent answers provided by @Stephen Swensen and @pad. 我认为,最优雅的方式可以通过结合@Stephen Swensen和@pad提供的两个优秀答案来实现。

The first idea is that the structure (a tuple containing two tuples) can be unpacked once, instead of doing it in every match case. 第一个想法是结构(包含两个元组的元组)可以解包一次,而不是在每个match情况下都这样做。
The second idea is working with sequences of values, all of which must be equal to each other. 第二个想法是使用值序列,所有这些值必须彼此相等。

Here's the code: 这是代码:

let comparer ((a,b),(c,d)) =
    let same = Set.ofSeq >> Set.count >> ((=) 1)
    if   same[a; b; c; d]         then "all values are the same"
    elif same[a; c] && same[b; d] then "tuples are the same"
    elif same[a; c]               then "first values are the same"
    else                                "none of above"

You may change elif 's into a match , but does not seem feasible to me. 你可以将elif变成一场match ,但对我来说似乎不太可行。

In practice, I would probably unpack the tuples up-front and then do a series of if / then / else expressions: 在实践中,我可能会在前面解包元组,然后执行一系列if / then / else表达式:

let a,b = tuple1
let c,d = tuple2

if a = b && b = c && c = d then "all values are the same"
elif a = c && b = d then "tuples are the same"
elif a = c then "first values are the same"
...

If you find yourself doing this frequently, an active pattern might be warranted (and in the case of 2-tuples, a complete active pattern would be doable and likely preferable - exhaustive matches are "safer" than non-exhaustive matches). 如果你发现自己经常这样做,可能需要一个活跃的模式(在2元组的情况下,一个完整的活动模式是可行的并且可能更可取 - 穷举匹配比非详尽匹配更“安全”)。 Or, perhaps you need a more sophisticated data structure. 或者,您可能需要更复杂的数据结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM