Rozważ typ indukcyjny, który ma pewne rekurencyjne zdarzenia w zagnieżdżonej, ale ściśle dodatniej lokalizacji. Na przykład drzewa ze skończonymi rozgałęzieniami z węzłami używającymi ogólnej struktury danych listy do przechowywania elementów potomnych.
Inductive LTree : Set := Node : list LTree -> LTree.
Naiwny sposób definiowania funkcji rekurencyjnej nad tymi drzewami przez rekurencję nad drzewami i listami drzew nie działa. Oto przykład z size
funkcją obliczającą liczbę węzłów.
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.
Ta definicja jest źle sformułowana (fragment komunikatu o błędzie):
Error:
Recursive definition of size_l is ill-formed.
Recursive call to size has principal argument equal to
"h" instead of "r".
Dlaczego definicja jest źle sformułowana, mimo że r
jest to oczywiste pojęcie l
? Czy istnieje sposób na zdefiniowanie funkcji rekurencyjnych w takiej strukturze danych?
Jeśli nie znasz biegle składni Coq: LTree
jest typem indukcyjnym odpowiadającym następującej gramatyce.
Próbujemy zdefiniować size
funkcję przez indukcję nad drzewami i listami. W OCaml byłoby to:
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
źródło
Odpowiedzi:
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
.Lub jeśli wolisz napisać to bardziej zwięźle:
(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 indukcjiLTree_rect
pomija hipotezę z listy, ponieważ generator zasad indukcji rozumie tylko nie zagnieżdżone ściśle pozytywne zdarzenia typu indukcyjnego.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.
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
l
t
size
h
l
size_l
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 ]
źródło
Jest to oczywiście problem specyficzny dla Coq, ponieważ uważam, że są lepsze sposoby na obejście tego z innymi asystentami dowodowymi (patrzę na Agdę)
Na początku myślałem, że
r
nie został rozpoznany jako strukturalnie mniejszy, ponieważ struktura dotyczy tylko definicji indukcyjnej, którą obecnie obsługujeFixpoint
: więc nie jest toLTree
subterm, nawet jeśli jestlist
subtermem.Ale jeśli rozszerzysz przetwarzanie listy, to zadziała:
Lub ponieważ funkcja pomocnicza już istnieje w standardowej bibliotece:
Szczerze mówiąc, nie jestem pewien, dlaczego są akceptowane przez Coq, ale jestem pewien, że tak.
Istnieje również rozwiązanie, które działa częściej i nie tylko w przypadku list: definiowanie niezależnego typu indukcyjnego. W takim przypadku możesz
size
ręcznie zdefiniować swoją funkcję. (z dwoma punktami stałymi)Zauważ, że jeśli masz problemy z bardziej złożonymi definicjami indukcyjnymi, możesz użyć argumentu zmniejszającego rozmiar. Jest to możliwe, ale kłopotliwe dla tego problemu (i odważyłbym się powiedzieć w przypadku większości problemów)
źródło