2010-12-03 9 views
4

同じコンストラクタを持つ値のタプルを照合する必要があることがよくあります。キャッチオール_,_は常に最後に巻き上げられます。これはもちろん壊れやすく、型に追加された追加のコンストラクタは完全にコンパイルされます。私の現在の考えは、第一引数ではなく第二引数を結ぶマッチを持つことです。しかし、他の選択肢はありますか?例えば脆弱なパターンマッチングを解決するための提案

type data = | States of int array 
      | Chars of (char list) array 

let median a b = match a,b with 
    | States xs, States ys -> 
     assert((Array.length xs) = (Array.length ys)); 
     States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i))) 
    | Chars xs, Chars ys -> 
     assert((Array.length xs) = (Array.length ys)); 
     let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in 
     Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i))) 
    (* inconsistent pairs of matching *) 
    | Chars _, _ 
    | States _, _ -> assert false 

答えて

8

あなたは以下のやや短いパターンを使用することができます実際には

| (Chars _| States _), _ -> assert false 

を、あなたはそれがまだ少し面倒なので、コンパイラは、あなたのためにそれを発生させることができます。 型は、以下とコンパイル:

let median a b = match a,b with 
| States xs, States ys -> 
    assert((Array.length xs) = (Array.length ys)); 
    States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i))) 
| Chars xs, Chars ys -> 
    assert((Array.length xs) = (Array.length ys)); 
    let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in 
    Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i))) 

警告8:このパターンマッチングが 網羅的ではありません。ここで の例一致しない値である:(文字数_は、 アメリカ_)は

今、あなたのコードに戻って提案パターンをコピー&ペーストすることができます。これは、通常、何十というコンストラクタを持つ型に対して非壊れやすいキャッチオールパターンを生成する方法です。コンパイラーを何度も起動する必要があるかもしれませんが、それは自分で入力するよりも速いです。

+0

興味深い!この戦略はおそらく、Haskell IDEに組み込むべきものでしょうか? –

+0

まあ、私はむしろそれらのコンパイルの警告をすべて持っていないだろう - 追加のものが生産されるせん断数で失われるだろう。私は実際にあなたがそのようなタプルの中で 'または'マッチをすることができるのかどうか分からなかった。それはいくつかの行を保存するはずです、ありがとう。 – nlucaroni

+0

@nlucaroniもし私がそれを明確にしていない場合は、コードにコピー・ペースト・バックするためのキャッチオールの壊れにくいパターンが含まれているため、一時的に警告を出すことを提案しています。 –

1

これは非常にハイキッシュなので(警告になります)、Objを使用して、タグが等しいかどうかを確認できます。これは、aおよびbは異なる値を持つすべてのケースをキャッチする必要があります(それは守ら句は休息と一致するかどうかを言うことができないので)

type data = | States of int array 
      | Chars of (char list) array 

let median a b = match a,b with 
    | States xs, States ys -> 
     assert((Array.length xs) = (Array.length ys)); 
     States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i))) 
    | Chars xs, Chars ys -> 
     assert((Array.length xs) = (Array.length ys)); 
     let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in 
     Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i))) 
    (* inconsistent pairs of matching *) 
    | x, y when (Obj.tag (Obj.repr x)) <> (Obj.tag (Obj.repr y)) -> assert false 

警告は非網羅的パターンマッチングのためです。

EDIT:あなたはまったくのObjを使用する必要はありません、あなただけの直接のxとyを比較することができます:

| x, y when x <> y -> assert false 

これはまだ残念ながら、警告になりますが。

+2

私の意見では、これらの警告のポイントは、実行が正しかったことを保証することではなく、データ型を変更した場合にこのパターンを再考し、変更に合わせて新しい動作を実装するよう警告されることです。あなたのテクニックはダイナミックに巧妙ですが、保守性に非常に役立つ有用な警告を出す(またはノイズに変える)わけではありません。 – gasche

+3

私の解決策はそれよりも悪いです!定数コンストラクターは考慮されていません。私はそれを編集するつもりですが、ええ、警告の価値を失うことは、ほぼ確実にそれに値するものではありません。 –

1

これは味覚/スタイルの問題でしかありませんが、私は最初にすべての有用な節を持つのではなく、すべての "ばかげた場合"を一緒にまとめる方が好きです。これは、与えられたコンストラクタに対していくつかの "便利な"節を書くことができ、あなたが何かを忘れていないことを確認したいときに非常に役に立ちます。

let median a b = match a,b with 
    | States xs, States ys -> 
    assert((Array.length xs) = (Array.length ys)); 
    States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i))) 
    | States _, _ -> assert false 

    | Chars xs, Chars ys -> 
    assert((Array.length xs) = (Array.length ys)); 
    let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in 
    Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i))) 
    | Chars _, _ -> assert false 
関連する問題