Що працює
Якщо ввести визначення точки фіксації у списки всередині визначення точки фіксації на деревах, результат буде добре набрано. Це загальний принцип, коли ви вклали рекурсію індуктивного типу, тобто коли рекурсія проходить через такий конструктор 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 Довідкове керівництво вводить мову (обчислення індуктивних конструкцій, що є доказом мову Coq), в основному , з формально точними визначеннями, але якщо ви хочете точні правила щодо індукції і коіндукціі вам потрібно піти в науково - дослідних робіт, з цієї теми [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
Причина, чому h
вона не є структурно меншою, ніж l
за словами перекладача Coq, мені не зрозуміла. Наскільки я розумію з обговорень списку клубів Кок [1] [2], це обмеження в перекладачі, яке в принципі можна було б зняти, але дуже обережно, щоб уникнути неузгодженості.
Список літератури
Кокоріко, Вікі Coq, що не знищує: Взаємна індукція
Список розсилки Coq-Club:
Команда розвитку Coq Помічник доказу Coq: Довідковий посібник . Версія 8.3 (2010). [ веб ] гл. 4 .
Едуардо Гіменес. Кодифікація захищених визначень за допомогою рекурсивних схем . In Types'94: Types для доказів та програм , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]
Едуардо Гіменес. Структурно-рекурсивні визначення в теорії типів . В ICALP'98: Матеріали 25-го Міжнародного колоквіуму з питань автоматів, мов та програмування. Springer-Verlag, 1998. [ PDF ]