Co działa
Jeśli zagnieździsz definicję punktu stałego na listach w definicji punktu stałego na drzewach, wynik jest dobrze wpisany. Jest to ogólna zasada, gdy zagnieżdżasz rekurencję w typie indukcyjnym, tj. Gdy rekurencja przechodzi przez konstruktor podobny 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.
Lub jeśli wolisz napisać to bardziej zwięźle:
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.
(Nie mam pojęcia, od kogo go usłyszałem; z pewnością wiele razy odkryto to niezależnie).
Ogólny predykat rekurencyjny
Mówiąc bardziej ogólnie, można LTree
ręcznie zdefiniować „właściwą” zasadę indukcji . Automatycznie generowana zasada indukcji LTree_rect
pomija hipotezę z listy, ponieważ generator zasad indukcji rozumie tylko nie zagnieżdżone ściśle pozytywne zdarzenia typu indukcyjnego.
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
Dodajmy hipotezę indukcyjną do list. Aby spełnić go w wywołaniu rekurencyjnym, wywołujemy zasadę indukcji listy i przekazujemy ją jako zasadę indukcji drzewa na mniejszym drzewie w liście.
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.
Dlaczego
Odpowiedź na pytanie dlaczego leży w precyzyjnych zasadach akceptacji funkcji rekurencyjnych. Reguły te są bardzo subtelne, ponieważ istnieje delikatna równowaga między dopuszczaniem złożonych przypadków (takich jak ten z zagnieżdżoną rekurencją w typie danych) a nieuzasadnieniem. Podręcznik referencyjny Coq wprowadza język (rachunek konstrukcji indukcyjnych, który jest językiem sprawdzającym Coq), głównie z formalnie precyzyjnymi definicjami, ale jeśli chcesz dokładnych zasad dotyczących indukcji i koindukcji, musisz przejść do artykułów naukowych, na ten temat Eduardo Giméneza [1].
Fix
Fixfi{f1:A1:=t1;f2:A2:=t2}
Γ1Γ2=(x:LTree)=(l:listLTree)A1A2=nat=natt1t2=case(x,LTree,λy.g1(f2y))=case(l,listLTree,λhr.g2(f1h)(f2r))
fjtifi
- i=1j=2
l
t
size
- i=2j=1
h
l
size_l
- i=2j=2
r
l
size_l
Powód, dla którego h
nie jest strukturalnie mniejszy niż l
według interpretera Coq, nie jest dla mnie jasny. O ile rozumiem z dyskusji na liście klubów Coq [1] [2], jest to ograniczenie tłumacza, które w zasadzie można znieść, ale bardzo ostrożnie, aby uniknąć wprowadzenia niespójności.
Bibliografia
Cocorico, niekończąca się wiki Coq: Mutual Induction
Lista mailingowa Coq-Club:
Zespół programistów Coq. Coq Proof Assistant: Podręcznik referencyjny . Wersja 8.3 (2010). [ web ] ch. 4 .
Eduardo Giménez. Kodyfikacja strzeżonych definicji za pomocą schematów rekurencyjnych . W Types'94: Typy dla dowodów i programów , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]
Eduardo Giménez. Strukturalne definicje rekurencyjne w teorii typów . W ICALP'98: Materiały z 25. Międzynarodowego Kolokwium na temat automatów, języków i programowania. Springer-Verlag, 1998. [ PDF ]