作業の余分な日の後、私は今この質問に対するより完全な答えを持っています。それはまだthis linkに見えます。この解決策にはいくつかのコメントが必要です。
まず、Fix
という名前の関数コンストラクタを使用しています(長い名前はCoq.Init.Wf.Fix
です。これは、よく定義された再帰によって関数を定義するために使用できる上位関数です)。順序はorder
と呼ばれている。まあは2000年代初頭に集中的に研究し、彼らはProgram Fixpoint
コマンドの基礎で残っているし、受注を設立しました。
第二に、あなたが行うケースを書いたコードが同時にタイプregex
の2つの値を分析するので、この36ケースにつながります(最初のケースがEmpty
である場合、2番目の引数についてケース分析がないため、多少なりともあります).36ケースはyには表示されませんなぜなら、いくつかのコンストラクタは、パターンが単なる変数である同じルールでカバーされているからです。このようなケースの倍数化を避けるために、私はケース分析を に対して具体的な誘導型を考案しました。私はこの特定のタイプarT
と呼んだ。次に、タイプregex
の任意の要素をarT
の対応する要素にマップする関数ar
を定義します。タイプarT
には6個ではなく3個のコンストラクターがあるため、パターンマッキング式のコード数は大幅に少なくなり、プルーフはあまり冗長になりません。
次に、Fix
を使用してnorm_union
を定義しました。 Coq(そしてIsabelleを含むほとんどの定理証明者)の通常のように、 の言語は再帰関数が常に終了するようにします。この場合、再帰呼び出しは、関数の入力よりも小さいより小さい引数の場合にのみ発生します。この場合、これは、再帰関数の本体を、最初の引数として第1引数として、第2引数として再帰呼び出しを表すために使用される関数をとる関数で記述することによって行われます。この関数の名前はnorm_union_F
で、次のようにそのタイプがある:このタイプの説明では
forall p : regex * regex,
forall g : (forall p', order p' p ->
{r : regex | size_regex r <= size_2regex p'}),
{r : regex | size_regex r <= size_2regex p}
、再帰呼び出しを表すために使用される関数の名前はg
であり、我々はg
の種類は、それができることを課していることがわかりorder
という名前の最初の引数p
より小さいregex
の語のペアでのみ使用できます。このタイプの説明では、再帰呼び出しの戻り型がregex
ではなく{r : regex | size_regex r <= size_2regex p'}
ではないことを表現することも選択しました。これは、ネストされた再帰を処理する必要があるためです。再帰呼び出しの出力は、他の再帰呼び出しの入力として使用されます。 これは、この回答の主なトリックです。このコードで
Definition norm_union_F : forall p : regex * regex,
forall g : (forall p', order p' p ->
{r : regex | size_regex r <= size_2regex p'}),
{r : regex | size_regex r <= size_2regex p} :=
fun p norm_union =>
match ar (fst p) with
arE _ eq1 => exist _ (snd p) (th1 p)
| arU _ u v eq1 =>
match ar (snd p) with
arE _ eq2 => exist _ (Union u v) (th2' _ _ _ eq1)
| _ => exist _ (proj1_sig
(norm_union (u,
proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
_)))
(th5' _ _ _ eq1
(proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(proj1_sig
(norm_union
(u, proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p) (th3' _ _ _ eq1))))))
(proj2_sig
(norm_union
(u, proj1_sig (norm_union (v, snd p)
(th3' _ _ _ eq1)))
(th4' _ _ _ eq1 (th3' _ _ _ eq1)
(proj1_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))
(proj2_sig (norm_union (v, snd p) (th3' _ _ _ eq1)))))))
end
| arO _ d1 d2 =>
match ar (snd p) with
arE _ eq2 => exist _ (fst p) (th11' _)
| arU _ v w eq2 =>
if eq_regex_dec (fst p) v then
exist _ (Union v w) (th7' _ _ _ eq2)
else if le_regex (fst p) v then
exist _ (Union (fst p) (Union v w)) (th8' _ _ _ eq2)
else exist _ (Union v (proj1_sig (norm_union (fst p, w)
(th9' _ _ _ eq2))))
(th10' _ _ _ eq2
(proj1_sig (norm_union (fst p, w)
(th9' _ _ _ eq2)))
(proj2_sig (norm_union (fst p, w)
(th9' _ _ _ eq2))))
| arO _ d1 d2 =>
if eq_regex_dec (fst p) (snd p) then
exist _ (fst p) (th11' _)
else if le_regex (fst p) (snd p) then
exist _ (Union (fst p) (snd p)) (th12' _)
else exist _ (Union (snd p) (fst p)) (th13' _)
end
end.
、すべての出力値はexist _
コンテキスト内で、次のとおりです:
はその後、我々はnorm_union_F
関数の本体を持っていないだけで、我々は、出力値を生成しますが、我々はまた、サイズがあることを示してやりますこの値の入力ペアの合計サイズよりも小さくなります。さらに、すべての再帰呼び出しはproj1_sig
コンテキスト内にあるため、出力値を構築する瞬間のサイズ情報は忘れてしまいます。また、ここではすべての再帰呼び出し(ここではnorm_union
という名前の関数を呼び出すことで表されます)は、再帰呼び出しへの入力が最初の入力よりも実際に小さいという証拠もあります。すべての証明はthe complete developmentです。
refine
のような戦術を使用してnorm_union_F
を定義することはおそらく可能ですが、あなたは探索に招待されます。
その後、我々はnorm_union_1
の出力が{x | size_regex x <= size_2regex p}
を入力したことをnorm_union_1
Definition norm_union_1 : forall p : regex*regex,
{x | size_regex x <= size_2regex p} :=
Fix well_founded_order (fun p => {x | size_regex x <= size_2regex p})
norm_union_F.
注本当に再帰関数を定義します。これはあなたが求めているタイプではありません。したがって、出力が入力よりも小さい論理情報を忘れることによって、あなたが望むものである新しい関数を定義します。
Definition norm_union u v : regex := proj1_sig (norm_union_1 (u, v)).
あなたが依頼した機能であることは、まだ疑問に思うかもしれません。自分自身を納得させるために、私たちはあなたが定義したことを正確に表現する補助詞を証明しようとしています。
まず、norm_union_1
の対応する補題を証明します。これは、Fix
関数に関連付けられた定理、名前Fix_eq
に依存しています。証明が必要なのはかなり日常的なことです(それはいつも自動的に行うことができますが、私はそのための自動ツールを開発することに決して慣れていません)。
そして、私たちは最も興味深い見出しを完成させます。これはnorm_union
のものです。 (私は単にそれをコピー&ペースト)
Lemma norm_union_eqn u v :
norm_union u v =
match u, v with
| Empty , v => v
| u , Empty => u
| Union u v, w => norm_union u (norm_union v w)
| u , Union v w => if eq_regex_dec u v
then Union v w
else if le_regex u v
then Union u (Union v w)
else Union v (norm_union u w)
| u , v => if eq_regex_dec u v
then u
else if le_regex u v
then Union u v
else Union v u
end.
は、この式の右辺は、あなたの最初の質問に与えたコードが正確であることに注意してください:ここでは文です。この最終定理の証明は、かなり体系的です。
私はあなたの要求に厳密に従って努力しましたが、実際には3つの再帰関数を使用して同じ機能を簡単に実装していることがわかりました。最初のものはUnion
のバイナリツリーを平らにしてリストのように見せ、他の2つはこれらのユニオンを注文le_regex
に関してソートし、重複が解消されるとすぐに削除します。そのような実装は、ネストされた再帰の必要性を回避するだろう。
ネストされた再帰に固執してここに記載されているテクニックを参照する必要がある場合は、TPHOLs2000のBalaaとBertotの論文に最初に掲載されました。この論文は、Coqが別の構文を使用していた時点で書かれているため、読みにくいです。
正規表現の定義を質問の一部としてCoq構文で追加してください。 – Yves