O que funciona
Se você aninhar a definição do ponto de correção nas listas dentro da definição do ponto de correção nas árvores, o resultado será bem digitado. Este é um princípio geral quando você aninha a recursão em um tipo indutivo, ou seja, quando a recursão passa por um construtor como 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.
Ou se você preferir escrever isso de forma mais concisa:
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.
(Não tenho idéia de quem o ouvi primeiro; isso certamente foi descoberto de forma independente várias vezes.)
Um predicado geral de recursão
De um modo mais geral, você pode definir o princípio de indução “adequado” LTree
manualmente. O princípio de indução gerado automaticamente LTree_rect
omite a hipótese da lista, porque o gerador do princípio de indução apenas entende ocorrências estritamente positivas não aninhadas do tipo indutivo.
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
Vamos adicionar a hipótese de indução nas listas. Para cumpri-lo na chamada recursiva, chamamos o princípio de indução de lista e passamos a ele o princípio de indução de árvore na árvore menor dentro da lista.
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.
Por quê
A resposta para o motivo está nas regras precisas para aceitar funções recursivas. Essas regras são forçosamente sutis, porque existe um delicado equilíbrio entre permitir casos complexos (como este, com recursão aninhada no tipo de dados) e insatisfação. O manual de referência da Coq apresenta a linguagem (o cálculo de construções indutivas, que é a linguagem de prova da Coq), principalmente com definições formalmente precisas, mas se você deseja as regras exatas sobre indução e coindução, precisará ir aos documentos de pesquisa, neste tópico de Eduardo Giménez [1].
Fix
F i x fEu{ f1: A1: = t1;f2: A2: = t2}
Γ1Γ2= ( x : L T r e e )= ( l : l i s tL T r e e )UMA1UMA2= n a t= n a tt1t2= c a s e ( x , L T r e e , λ y. g1( f2y) ))= c a s e ( l , l i s tL T r e e ,λhr . g2( f1h ) ( f2r ) )
fjtEufEu
- i = 1j = 2
l
t
size
- i = 2j = 1
h
l
size_l
- i = 2j = 2
r
l
size_l
A razão pela qual h
não é estruturalmente menor do que, l
segundo o intérprete Coq, não está clara para mim. Tanto quanto eu entendo das discussões na lista Coq-club [1] [2], esta é uma restrição no intérprete, que em princípio poderia ser levantada, mas com muito cuidado para evitar a introdução de uma inconsistência.
Referências
Cocorico, o wiki Coq não-determinante: Indução Mútua
Lista de discussão Coq-Club:
A equipe de desenvolvimento da Coq. O Coq Proof Assistant: Manual de Referência . Versão 8.3 (2010). [ web ] cap. 4 .
Eduardo Giménez. Codificando definições guardadas com esquemas recursivos . Em Types'94: Types for Proofs and Programs , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]
Eduardo Giménez. Definições estruturais recursivas na teoria dos tipos . Na ICALP'98: Anais do 25º Colóquio Internacional sobre Autômatos, Idiomas e Programação. Springer-Verlag, 1998. [ PDF ]