Что работает
Если вы вложите определение точки фиксации в списки внутри определения точки фиксации на деревьях, результат будет хорошо напечатан. Это общий принцип, когда у вас есть вложенная рекурсия в индуктивном типе, т.е. когда рекурсия проходит через конструктор типа 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].
Начиная с руководства Coq, в нотации 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))
Для того чтобы определение с фиксированной точкой было принято, «если встречается [в ], то… аргумент должен быть синтаксически распознан как структурно меньший, чем [аргумент, переданный в ]» (упрощение, потому что функции имеют единственный аргумент). Здесь мы должны проверить, чтот I е Ifjtifi
- j = 2i=1 , : должен быть структурно меньше, чем в , хорошо.j=2
l
t
size
- j = 1i=2 , : должен быть структурно меньше, чем в , выглядит хорошо, но не так!j=1
h
l
size_l
- j = 2i=2 , : должен быть структурно меньше, чем в , хорошо.j=2
r
l
size_l
Причина, по которой h
он не является структурно меньше, чем l
согласно интерпретатору Coq, мне не ясна. Насколько я понимаю из обсуждений по списку Coq-club [1] [2], это ограничение в интерпретаторе, которое в принципе можно снять, но очень осторожно, чтобы не допустить несоответствия.
Ссылки
Cocorico, неопределенный Coq wiki: Взаимная индукция
Список рассылки Coq-Club:
Команда разработчиков Coq. Coq Proof Assistant: Справочное руководство . Версия 8.3 (2010). [ веб ] гл. 4 .
Эдуардо Хименес. Кодификация охраняемых определений с помощью рекурсивных схем . В Types'94: Типы для доказательств и программ , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]
Эдуардо Хименес. Структурно-рекурсивные определения в теории типов . В ICALP'98: Материалы 25-го Международного коллоквиума по автоматам, языкам и программированию. Springer-Verlag, 1998. [ PDF ]