入れ子になったコンポーネントを持つ帰納的なタイプの再帰的な定義


21

入れ子になっているが厳密に正の場所で再帰的に発生する誘導型を考えてください。たとえば、一般的なリストデータ構造を使用して子を格納するノードを使用した有限分岐を持つツリー。

Inductive LTree : Set := Node : list LTree -> LTree.

これらのツリーとツリーのリストを再帰的に再帰的に定義する単純な方法は機能しません。以下sizeは、ノードの数を計算する関数の例です。

Fixpoint size (t : LTree) : nat := match t with Node l => 1 + (size_l l) end
with size_l (l : list LTree) : nat := match l with
    | nil => 0
    | cons h r => size h + size_l r
  end.

この定義は不正な形式です(エラーメッセージの抜粋):

Error:
Recursive definition of size_l is ill-formed.
Recursive call to size has principal argument equal to
"h" instead of "r".

r明確にサブタームであるにもかかわらず、なぜ定義が不適切なのlですか?そのようなデータ構造で再帰関数を定義する方法はありますか?


Coq構文に堪能でない場合:LTreeは、次の文法に対応する帰納的な型です。

LTree:: =|lstLTree

size木とリストの帰納法によって関数を定義しようとします。OCamlでは、次のようになります。

type t = Node of t list
let rec size = function Node l -> 1 + size_l l
and size_l = function [] -> 0
                    | h::r -> size h + size_l r

これは話題ですか?よく分かりません; これについてMetaで議論しましょう
ジル「SO-悪であるのをやめる」

Coqyより少なく、より多くのものに同等の関数定義を追加できますか?構文を理解できません。
ラファエル

1
私が試した@ラファエル、それは今より良いですか?正直なところ、この質問はCoqに特有のものです。
ジル「SO-悪であるのをやめる」

回答:


14

動作するもの

ツリー上のフィックスポイントの定義内のリストにフィックスポイントの定義をネストすると、結果は適切に型付けされます。これは、帰納的型で再帰をネストしている場合、つまり再帰がのようなコンストラクターを通過する場合の一般的な原則ですlist

Fixpoint size (t : LTree) : nat :=
  let size_l := (fix size_l (l : list LTree) : nat :=
                  match l with
                    | nil => 0
                    | h::r => size h + size_l r
                  end) in
  match t with Node l =>
    1 + size_l l
  end.

または、もっと簡潔に記述したい場合:

Fixpoint size (t : LTree) : nat :=
  match t with Node l =>
    1 + (fix size_l (l : list LTree) : nat :=
          match l with
            | nil => 0
            | h::r => size h + size_l r
          end) l
  end.

(最初に誰から聞いたかわからない;これは確かに何度も独立して発見された。)

一般的な再帰述語

より一般的には、「適切な」誘導原理をLTree手動で定義できます。自動的に生成された誘導原理LTree_rectは、リスト上の仮説を省略します。これは、誘導原理ジェネレータが、誘導型のネストされていない厳密に正の出現のみを理解するためです。

LTree_rect = 
fun (P : LTree -> Type) (f : forall l : list LTree, P (Node l)) (l : LTree) =>
match l as l0 return (P l0) with
| Node x => f x
end
     : forall P : LTree -> Type,
       (forall l : list LTree, P (Node l)) -> forall l : LTree, P l

リストに帰納仮説を追加しましょう。再帰呼び出しでそれを実現するために、リスト誘導の原理を呼び出し、リスト内の小さなツリーでツリー誘導の原理を渡します。

Fixpoint LTree_rect_nest (P : LTree -> Type) (Q : list LTree -> Type)
                         (f : forall l, Q l -> P (Node l))
                         (g : Q nil) (h : forall t l, P t -> Q l -> Q (cons t l))
                         (t : LTree) :=
  match t as t0 return (P t0) with
    | Node l => f l (list_rect Q g (fun u r => h u r (LTree_rect_nest P Q f g h u)) l)
  end.

なぜ

理由の答えは、再帰関数を受け入れるための正確なルールにあります。これらのルールは、複雑な場合(データ型にネストされた再帰がある場合など)と不健全さの間に微妙なバランスがあるため、微妙です。Coqのリファレンスマニュアル紹介言語ほとんどが正式に正確な定義で、しかし、(コックの証明言語である誘導構造の微積分、)あなたは研究論文に行く必要があります誘導とcoinductionに関する正確な規則が必要な場合は、このトピックについては、EduardoGiménezの[1]。

Coqマニュアルから始めて、Fixルールの表記では、不動点定義があります。Fバツf{f1A1:=t1;f2A2:=t2}

Γ1=バツLTreeA1=natt1=caseバツLTreeλyg1f2yΓ2=llstLTreeA2=natt2=casellstLTreeλhrg2f1hf2r

fjtf

  • =1j=2ltsize
  • =2j=1hlsize_l
  • =2j=2rlsize_l

Coqインタープリターによるhよりも構造的に小さくない理由はl、私には明らかではありません。Coq-clubリスト[1] [2]の議論から理解できる限り、これは通訳者の制限であり、原則として解除することができますが、矛盾の導入を避けるために非常に慎重です。

参照資料

Cocorico、非終了Coq wiki:相互誘導

Coq-Clubメーリングリスト:

Coq開発チーム。Coq Proof Assistant:リファレンスマニュアル。バージョン8.3(2010)。[ web ] ch。4

エドゥアルド・ギメネス。再帰スキームを使用した保護された定義の体系化。では証明とプログラムのタイプ:Types'94、LNCS 996シュプリンガー・フェアラーク、1994 DOI:10.1007 / 3-540-60579-7_3 [ スプリンガー ]

エドゥアルド・ギメネス。型理論における構造的再帰的定義。でICALP'98:オートマトンの第25回国際コロキアムの議事録、言語とプログラミング。スプリンガー出版、1998年。 [ PDF ]


7

これは明らかにCoqに固有の問題です。他の証明アシスタントを使って回避するより良い方法があると信じているためです(Agdaを見ています)。

r構造は、現在処理されている帰納的定義のみに関するものであるため、最初は構造的に小さいと認識されていないと考えましたFixpoint。したがって、LTreeサブタームであってもサブタームではないlist

しかし、リストの処理を拡張すると、動作します:

Fixpoint size t :=
  match t with
  | Node l => S
     ((fix size_l l :=
     match l with
     | nil => 0
     | cons t l => size_l l + size t
     end) l)
 end.

または、補助関数が標準ライブラリに既に存在するため:

Require Import List.

Fixpoint size t :=
  match t with
  | Node l => S (fold_left (fun a t => a + size t) l 0)
  end.

正直に言って、なぜこれらがCoqに受け入れられるのかはわかりませんが、喜んでいると思います。

リストだけでなく、より頻繁に機能するソリューションもあります。自己完結型の誘導型を定義します。この場合、size関数を手動で定義できます。(2つのフィックスポイントを使用)

Inductive LTree : Set :=
  | Node : list_LTree -> LTree
with list_LTree : Set :=
  | LTree_nil : list_LTree
  | LTree_cons : LTree -> list_LTree -> list_LTree.

より複雑な帰納的定義に問題がある場合は、サイズを減らす引数を使用できることに注意してください。それは可能ですが、この問題にとっては面倒です(そして、ほとんどの問題についてあえて言うでしょう)


今日私がまだ理解していないのは、単純なアプローチがうまくいかない理由です。原則として、これはEduardoGiménezの論文の結果であるはずですが、控除がどこで中断するかわかりません。これは、基礎となる計算ではなくCoqエンジンの制限である可能性があります。
ジル「SO-悪であるのをやめる」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.