15

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

但是,第一个模式匹配会产生编译器错误:

'a' is bound twice in this pattern

有没有比以下更清洁的方法?

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
4

4 回答 4

11

这是 F# 的“活动模式”的完美用例。您可以像这样定义其中的几个:

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

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

然后清理与它们匹配的模式;注意第一种情况(所有值都相等)如何使用嵌套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"

性能提示:我喜欢用这些标记简单的活动模式inline——因为活动模式中的逻辑很简单(只有几条 IL 指令),因此内联它们并避免函数调用的开销是有意义的。

于 2013-01-28T12:49:28.317 回答
4

您可以使用参数化的活动模式来解决问题。

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

特别是,您应该以文字(字符、字符串等)的形式定义模式。

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
于 2013-01-28T12:52:03.090 回答
3

我认为,最优雅的方式可以通过结合@Stephen Swensen 和@pad 提供的两个出色答案来完成。

第一个想法是结构(一个包含两个元组的元组)可以解包一次,而不是在每种match情况下都这样做。
第二个想法是处理值序列,所有这些值必须彼此相等。

这是代码:

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"

您可以将elif' 更改为match,但对我来说似乎不可行。

于 2013-01-28T19:49:14.737 回答
0

在实践中,我可能会预先解包元组,然后执行一系列 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"
...

如果您发现自己经常这样做,则可能需要一个活动模式(在 2 元组的情况下,一个完整的活动模式将是可行的并且可能更可取 - 穷举匹配比非穷举匹配“更安全”)。或者,也许您需要更复杂的数据结构。

于 2013-01-28T13:48:02.653 回答