Que funciona
Si anida la definición del punto de fijación en las listas dentro de la definición del punto de fijación en los árboles, el resultado está bien escrito. Este es un principio general cuando tiene recursión anidada en un tipo inductivo, es decir, cuando la recursión pasa por un constructor 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.
O si prefiere escribir esto más brevemente:
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.
(No tengo idea de quién lo escuché primero; esto ciertamente se descubrió independientemente muchas veces).
Un predicado de recursión general
De manera más general, puede definir el principio de inducción "adecuado" LTree
manualmente. El principio de inducción generado automáticamente LTree_rect
omite la hipótesis en la lista, porque el generador del principio de inducción solo comprende las ocurrencias estrictamente positivas no anidadas del tipo inductivo.
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
Agreguemos la hipótesis de inducción en las listas. Para cumplirlo en la llamada recursiva, llamamos al principio de inducción de la lista y lo pasamos al principio de inducción del árbol en el árbol más pequeño dentro de la 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é
La respuesta a por qué radica en las reglas precisas para aceptar funciones recursivas. Estas reglas son forzosamente sutiles, porque hay un delicado equilibrio entre permitir casos complejos (como este, con recursión anidada en el tipo de datos) y falta de solidez. El manual de referencia de Coq presenta el lenguaje (el cálculo de las construcciones inductivas, que es el lenguaje de prueba de Coq), principalmente con definiciones formalmente precisas, pero si desea las reglas exactas con respecto a la inducción y la coinducción necesitará ir a los trabajos de investigación, sobre este tema de Eduardo Giménez [1].
Fix
F i x fyo{ f1: A1: = t1;F2: A2: = t2}
Γ1Γ2= ( x : L T r e e )= ( l : l i s tL T r e e )UNA1UNA2= n a t= n a tt1t2= c a s e ( x , L T r e e , λ y. sol1( f2y) )= c a s e ( l , l i s tL T r e e ,λhr . sol2( f1h ) ( f2r ) )
FjtyoFyo
- i = 1j = 2
l
t
size
- i = 2j = 1
h
l
size_l
- i = 2j = 2
r
l
size_l
La razón por la cual h
no es estructuralmente más pequeña que l
según el intérprete de Coq no me resulta clara. Según tengo entendido por las discusiones sobre la lista de Coq-club [1] [2], esta es una restricción en el intérprete, que en principio podría eliminarse, pero con mucho cuidado para evitar la introducción de una inconsistencia.
Referencias
Cocorico, el wiki de Coq no terminante: inducción mutua
Lista de correo de Coq-Club:
El equipo de desarrollo de Coq. The Coq Proof Assistant: Manual de referencia . Versión 8.3 (2010). [ web ] cap. 4 .
Eduardo Giménez. Codificación de definiciones guardadas con esquemas recursivos . En Tipos'94: Tipos de pruebas y programas , LNCS 996. Springer-Verlag, 1994. doi: 10.1007 / 3-540-60579-7_3 [ Springer ]
Eduardo Giménez. Definiciones recursivas estructurales en la teoría de tipos . En ICALP'98: Actas del 25º Coloquio internacional sobre autómatas, idiomas y programación. Springer-Verlag, 1998. [ PDF ]