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パターンを使用して、タプルの最初と 2 番目の要素が等しいことを確認する方法に注意してください。

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が提供する2つの優れた回答を組み合わせることで、最もエレガントな方法を実現できると思います。

最初のアイデアは、構造体 (2 つのタプルを含むタプル) を毎回ではなく、1 回だけアンパックできるというものmatchです。
2 番目のアイデアは、すべてが互いに等しくなければならない一連の値を操作することです。

コードは次のとおりです。

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"

を に変更することもできますがelifmatch私には実行可能ではないようです。

于 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 に答える