Acciones

Diferencia entre revisiones de «Razonamiento por casos y por inducci¢n»

De Lógica matemática y fundamentos (2018-19)

 
 
(No se muestran 4 ediciones intermedias del mismo usuario)
Línea 1: Línea 1:
 
<source lang="isabelle">
 
<source lang="isabelle">
chapter {* Tema 6: Razonamiento estructurado sobre programas *}
+
chapter ‹Tema 7: Razonamiento por casos y por inducción›
  
theory T6_Razonamiento_sobre_programas
+
theory T7_Razonamiento_por_casos_y_por_induccion
imports Main  
+
imports Main HOL.Parity
 
begin
 
begin
  
text {*
+
text ‹En este tema se amplían los métodos de demostración por casos y  
  En este tema se demuestra con Isabelle las propiedades de los
+
   por inducción iniciados en el tema anterior.
  programas funcionales como se expone en el tema 2a y se demostraron
 
   automáticamente en el tema 2b. A diferencia del tema 2b, ahora
 
  nos fijamos no sólo en el método de demostración sino en la estructura
 
  de la prueba resaltando su semejanza con las del tema 2a. *}
 
  
declare [[names_short]]
+
section ‹Razonamiento por distinción de casos›
  
section {* Razonamiento ecuacional *}
+
subsection ‹Distinción de casos booleanos›
  
text {* ----------------------------------------------------------------
+
text ‹Ejemplo de demostración por distinción de casos booleanos:
  Ejemplo 1. Definir, por recursión, la función
+
   Demostrar "¬A ∨ A".
    longitud :: 'a list ⇒ nat
 
   tal que (longitud xs) es la longitud de la listas xs. Por ejemplo,
 
    longitud [a,c,d] = 3
 
  ------------------------------------------------------------------- *}
 
  
fun longitud :: "'a list ⇒ nat" where
+
― ‹La demostración estructurada es›
   "longitud []    = 0"
+
lemma "¬A ∨ A"
| "longitud (x#xs) = 1 + longitud xs"
+
proof cases
 
+
  assume "A"
value "longitud [a,c,d] = 3"
+
  then show "¬A ∨ A" ..
 +
next
 +
  assume "¬A"
 +
  then show "¬A ∨ A" ..
 +
qed
 +
 
 +
text ‹Comentarios de la demostración anterior:
 +
  · "proof cases" indica que el método de demostración será por
 +
    distinción de casos.
 +
  · Se generan 2 casos:
 +
      1. ?P ⟹ ¬A ∨ A
 +
      2. ¬?P ⟹ ¬A ∨ A
 +
    donde ?P es una variable sobre las fórmulas.
 +
  · (assume "A") indica que se está usando "A" en lugar de la variable
 +
    ?P.
 +
   · "then" indica usando la fórmula anterior.
 +
  · ".." indica usando la regla lógica necesaria (las reglas lógicas se
 +
    estudiarán en los siguientes temas).
 +
  · "next" indica el siguiente caso (se puede observar cómo ha
 +
    sustituido ¬?P por ¬A.›
  
text {* ---------------------------------------------------------------
+
― ‹La demostración aplicativa es›
   Ejemplo 2. Demostrar que
+
lemma "¬A ∨ A"
    longitud [a,c,d] = 3
+
   apply cases
   ------------------------------------------------------------------- *}
+
  apply simp_all
 +
   done
  
lemma "longitud [a,c,d] = 3"
+
― ‹La demostración automática es›
by simp
+
lemma "¬A ∨ A"  
 +
  by cases simp_all
  
text {* ---------------------------------------------------------------
+
lemma "¬A ∨ A"
   Ejemplo 3. Definir la función
+
   by auto
    fun intercambia :: 'a × 'b ⇒ 'b × 'a
 
  tal que (intercambia p) es el par obtenido intercambiando las
 
  componentes del par p. Por ejemplo,
 
    intercambia (u,v) = (v,u)
 
  ------------------------------------------------------------------ *}
 
  
fun intercambia :: "'a × 'b ⇒ 'b × 'a" where
+
text ‹Ejemplo de demostración por distinción de casos booleanos con
   "intercambia (x,y) = (y,x)"
+
  nombres:  
 +
   Demostrar "¬A ∨ A".›
  
value "intercambia (u,v) = (v,u)"
+
― ‹La demostración estructurada es›
 +
lemma "¬A ∨ A"
 +
proof (cases "A")
 +
  case True
 +
  then show "¬A ∨ A" ..
 +
next
 +
  case False
 +
  then show "¬A ∨ A" ..
 +
qed
  
text {*
+
text ‹Comentarios sobre la demostración anterior:
   La definición de la función intercambia genera una regla de
+
   · (cases "A") indica que la demostración se hará por casos según los
   simplificación
+
    distintos valores de "A".
   · intercambia.simps: intercambia (x,y) = (y,x)
+
   · Como "A" es una fórmula, sus posibles valores son verdadero o falso.
 
+
   · "case True" indica que se está suponiendo que A es verdadera. Es
   Se puede ver con  
+
    equivalente a "assume A".
  · thm intercambia.simps
+
  · "case False" indica que se está suponiendo que A es falsa. Es
*}
+
    equivalente a "assume ¬A".
 +
  · En general,  
 +
    · el método (cases F) es una abreviatura de la aplicación de la regla
 +
        ⟦F ⟹ Q; ¬F ⟹ Q⟧ ⟹ Q 
 +
    · La expresión "case True" es una abreviatura de F.
 +
    · La expresión "case False" es una abreviatura de ¬F.
 +
   · Ventajas de "cases" con nombre:
 +
    · reduce la escritura de la fórmula y
 +
    · es independiente del orden de los casos.
  
text {* ---------------------------------------------------------------
+
subsection ‹Distinción de casos sobre otros tipos de datos›
  Ejemplo 4. (p.6) Demostrar que
 
    intercambia (intercambia (x,y)) = (x,y)
 
  ------------------------------------------------------------------- *}
 
  
(* Demostración aplicativa *)
+
text ‹Ejemplo de distinción de casos sobre listas:
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
  Demostrar que la longitud del resto de una lista es la longitud de la
   apply (simp only: intercambia.simps)
+
   lista menos 1.
  done
 
  
(* Demostración declarativa *)
+
― ‹La demostración estructurada es›
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
lemma "length (tl xs) = length xs - 1"
proof -
+
proof (cases xs)
   have "intercambia (intercambia (x,y)) = intercambia (y,x)"
+
  assume "xs = []"
     by (simp only: intercambia.simps)
+
  then have "length (tl xs) = 0"
   also have "... = (x,y)"  
+
    by simp
     by (simp only: intercambia.simps)
+
  also have "… = 0 - 1"
   finally show "intercambia (intercambia (x,y)) = (x,y)"
+
    by simp
 +
   also have "… = length xs - 1"
 +
    using ‹xs = []›
 +
    by simp
 +
  finally show "length (tl xs) = length xs - 1"
 +
    by this
 +
next
 +
  fix y ys
 +
  assume "xs = y#ys"
 +
  then have "length (tl xs) = length ys"
 +
     by simp
 +
   also have "= (1 + length ys) - 1"
 +
     by simp
 +
   also have "… = length (y#ys) - 1"
 +
    by simp
 +
  also have "… = length xs - 1"
 +
    using ‹xs = y#ys›
 
     by simp
 
     by simp
 +
  finally show "length (tl xs) = length xs - 1"
 +
    by this
 
qed
 
qed
  
text {*
+
text ‹Comentarios sobre la demostración anterior:
  Notas sobre el lenguaje: En la demostración anterior se ha usado
+
   · "(cases xs)" indica que la demostración se hará por casos sobre los
   · "proof" para iniciar la prueba,
+
     posibles valores de xs.
  · "-" (después de "proof") para no usar el método por defecto,
+
   · Como xs es una lista, sus posibles valores son la lista vacía ([]) o
  · "have" para establecer un paso,
+
     una lista no vacía (de la forma (y#ys)).
  · "by (simp only: intercambia.simps)" para indicar que sólo se usa
+
   · Se generan 2 casos:
     como regla de escritura la correspondiente a la definición de
+
      1. xs = [] ⟹ length (tl xs) = length xs - 1
    intercambia,
+
      2. ⋀a list. xs = a # list ⟹ length (tl xs) = length xs - 1›
   · "also" para encadenar pasos ecuacionales,
 
  · "..." para representar la derecha de la igualdad anterior en un
 
     razonamiento ecuacional,
 
  · "finally" para indicar el último pasa de un razonamiento ecuacional,
 
  · "show" para establecer la conclusión.
 
   · "by simp" para indicar el método de demostración por simplificación y
 
  · "qed" para terminar la pruebas,
 
*}
 
  
(* Demostración declarativa simplificada *)
+
― ‹La demostración detallada es›
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
lemma "length (tl xs) = length xs - 1"
proof -
+
proof (cases xs)
   have "intercambia (intercambia (x,y)) = intercambia (y,x)" by simp
+
  assume "xs = []"
   also have "... = (x,y)" by simp  
+
  then have "length (tl xs) = 0"
   finally show "intercambia (intercambia (x,y)) = (x,y)" by simp
+
    by (simp only: list.sel(2)
 +
                  list.size(3))
 +
  also have "… = 0 - 1"
 +
    by (simp only: natural_zero_minus_one)
 +
  also have "… = length xs - 1"
 +
    using ‹xs = []›
 +
    by (simp only: list.size(3))
 +
  finally show "length (tl xs) = length xs - 1"
 +
    by this
 +
next
 +
  fix y ys
 +
  assume "xs = y#ys"
 +
   then have "length (tl xs) = length ys"
 +
    by (simp only: list.sel(3))
 +
  also have "… = length (y#ys) - 1"
 +
    by (simp only: length_Cons)
 +
   also have "… = length xs - 1"
 +
    using ‹xs = y#ys›
 +
    by (simp only:)
 +
   finally show "length (tl xs) = length xs - 1"  
 +
    by this
 
qed
 
qed
  
text {*
+
― ‹La demostración simplificada es›
   Nota: La diferencia entre las dos demostraciones es que en los dos
+
lemma "length (tl xs) = length xs - 1"
   primeros pasos no se explicita la regla de simplificación.
+
proof (cases xs)
*}
+
  case Nil
 +
  then show ?thesis by simp
 +
next
 +
   case Cons
 +
   then show ?thesis by simp
 +
qed
  
(* Demostración automática *)
+
text ‹Comentarios sobre la demostración anterior:
lemma "intercambia (intercambia (x,y)) = (x,y)"
+
  · "case Nil" es una abreviatura de
   by simp
+
      "assume xs =[]".
 +
  · "case Cons" es una abreviatura de
 +
      "fix y ys assume xs = y#ys"
 +
   · ?thesis es una abreviatura de la conclusión del lema.›
  
text {* ---------------------------------------------------------------
+
― ‹La demostración aplicativa es›
  Ejemplo 5. Definir, por recursión, la función
+
lemma "length (tl xs) = length xs - 1"
    inversa :: 'a list ⇒ 'a list
+
   apply (cases xs)
  tal que (inversa xs) es la lista obtenida invirtiendo el orden de los
+
  apply simp_all
   elementos de xs. Por ejemplo,
+
   done
    inversa [a,d,c] = [c,d,a]
 
   ------------------------------------------------------------------ *}
 
  
fun inversa :: "'a list ⇒ 'a list" where
+
― ‹La demostración automática es›
  "inversa []    = []"
+
lemma "length (tl xs) = length xs - 1"  
| "inversa (x#xs) = inversa xs @ [x]"
+
  by (cases xs) simp_all
  
value "inversa [a,d,c] = [c,d,a]"
+
text ‹En el siguiente ejemplo vamos a demostrar una propiedad de la
 +
  función drop que está definida en la teoría List de forma que
 +
  (drop n xs) es la lista obtenida eliminando en xs los n primeros
 +
  elementos. Su definición es la siguiente 
 +
    drop_Nil:  "drop n []     = []"  
 +
    drop_Cons: "drop n (x#xs) = (case n of
 +
                                    0 => x#xs |
 +
                                    Suc(m) => drop m xs)" ›
  
text {* ---------------------------------------------------------------
+
text ‹Ejemplo de análisis de casos:
   Ejemplo 6. (p. 9) Demostrar que  
+
   Demostrar que el resultado de eliminar los n+1 primeros elementos de
    inversa [x] = [x]
+
   xs es el mismo que eliminar los n primeros elementos del resto de xs.›
   ------------------------------------------------------------------- *}
 
  
(* La demostración aplicativa es *)
+
― ‹La demostración estructurada es›
lemma "inversa [x] = [x]"
+
lemma "drop (n + 1) xs = drop n (tl xs)"
   apply simp
+
proof (cases xs)
   done
+
  case Nil
 +
   then show ?thesis by simp
 +
next
 +
  case Cons
 +
   then show ?thesis by simp
 +
qed
  
text {*
+
― ‹La demostración aplicativa es›
  En la demostración anterior se usaron las siguientes reglas:
+
lemma "drop (n + 1) xs = drop n (tl xs)"
  · inversa.simps(1): inversa [] = []
+
   apply (cases xs)
  · inversa.simps(2): inversa (x#xs) = inversa xs @ [x]
+
  apply simp_all
  · append_Nil:      [] @ ys = ys
 
  Vamos a explicitar su aplicación.
 
*}
 
 
 
(* La demostración aplicativa detallada es *)
 
lemma "inversa [x] = [x]"
 
   apply (simp only: inversa.simps(2))
 
  apply (simp only: inversa.simps(1))
 
  apply (simp only: append_Nil)
 
 
   done
 
   done
  
(* La demostración declarativa es *)
+
― ‹La demostración automática es›
lemma "inversa [x] = [x]"
+
lemma "drop (n + 1) xs = drop n (tl xs)"
proof -
+
   by (cases xs) simp_all
  have "inversa [x] = inversa (x#[])" by simp
 
  also have "... = (inversa []) @ [x]" by (simp only: inversa.simps(2))
 
  also have "... = [] @ [x]" by (simp only: inversa.simps(1))
 
  also have "... = [x]" by (simp only: append_Nil)
 
  finally show "inversa [x] = [x]" by simp
 
qed
 
 
 
(* La demostración declarativa simplificada es *)
 
lemma "inversa [x] = [x]"
 
proof -
 
  have "inversa [x] = inversa (x#[])" by simp
 
  also have "... = (inversa []) @ [x]" by simp
 
  also have "... = [] @ [x]" by simp
 
   also have "... = [x]" by simp
 
  finally show "inversa [x] = [x]" by simp
 
qed
 
 
 
(* La demostración automática es *)
 
lemma "inversa [x] = [x]"
 
  by simp
 
  
section {* Razonamiento por inducción sobre los naturales *}
+
section ‹Inducción matemática›
  
text {*
+
text [Principio de inducción matemática]
  [Principio de inducción sobre los naturales] Para demostrar una
+
  Para demostrar una propiedad P para todos los números naturales basta
  propiedad P para todos los números naturales basta probar que el 0
+
  probar que el 0 tiene la propiedad P y que si n tiene la propiedad P,
  tiene la propiedad P y que si n tiene la propiedad P, entonces n+1
+
  entonces n+1 también la tiene.  
  también la tiene.
 
 
     ⟦P 0; ⋀n. P n ⟹ P (Suc n)⟧ ⟹ P m
 
     ⟦P 0; ⋀n. P n ⟹ P (Suc n)⟧ ⟹ P m
  
   En Isabelle el principio de inducción sobre los naturales está
+
   En Isabelle el principio de inducción matemática está formalizado en
   formalizado en el teorema nat.induct y puede verse con
+
   el teorema nat.induct y puede verse con
 
     thm nat.induct
 
     thm nat.induct
*}
+
  
text {* ---------------------------------------------------------------
+
text ‹Ejemplo de demostración por inducción: Usaremos el principio de
  Ejemplo 7. Definir la función
+
   inducción matemática para demostrar que  
    repite :: nat ⇒ 'a ⇒ 'a list
+
     1 + 3 + ... + (2n-1) = n^2
   tal que (repite n x) es la lista formada por n copias del elemento
 
  x. Por ejemplo,
 
     repite 3 a = [a,a,a]
 
  ------------------------------------------------------------------ *}
 
  
fun repite :: "nat ⇒ 'a ⇒ 'a list" where
+
   Definición. [Suma de los primeros impares]  
   "repite 0 x      = []"
+
  (suma_impares n) la suma de los n números impares. Por ejemplo,
| "repite (Suc n) x = x # (repite n x)"
+
    suma_impares 3  = 9
 +
  
value "repite 3 a = [a,a,a]"
+
fun suma_impares :: "nat ⇒ nat" where
 +
  "suma_impares 0 = 0"
 +
| "suma_impares (Suc n) = (2*(Suc n) - 1) + suma_impares n"
  
text {* ---------------------------------------------------------------
+
value "suma_impares 3"
  Ejemplo 8. (p. 18) Demostrar que
 
    longitud (repite n x) = n
 
  ------------------------------------------------------------------- *}
 
  
(* La demostración aplicativa es *)
+
text ‹  Ejemplo de demostración por inducción matemática:
lemma "longitud (repite n x) = n"
+
  Demostrar que la suma de los n primeros números impares es n^2.›
  apply (induct n)
 
  apply simp_all
 
  done
 
  
(* La demostración estructurada es *)
+
― ‹Demostración estructurada del lema anterior por inducción y
lemma "longitud (repite n x) = n"
+
  razonamiento ecuacional›
 +
lemma "suma_impares n = n * n"
 
proof (induct n)
 
proof (induct n)
   show "longitud (repite 0 x) = 0" by simp
+
   show "suma_impares 0 = 0 * 0"  
next
 
  fix n
 
  assume HI: "longitud (repite n x) = n"
 
  have "longitud (repite (Suc n) x) = longitud (x # (repite n x))"  
 
 
     by simp
 
     by simp
  also have "... = 1 + longitud (repite n x)" by simp
 
  also have "... = 1 + n" using HI by simp
 
  finally show "longitud (repite (Suc n) x) = Suc n" by simp
 
qed
 
 
text {*
 
  Comentarios sobre la demostración anterior:
 
  · A la derecha de proof se indica el método de la demostración.
 
  · (induct n) indica que la demostración se hará por inducción en n.
 
  · Se generan dos subobjetivos correspondientes a la base y el paso de
 
    inducción:
 
    1. longitud (repite 0 x) = 0
 
    2. ⋀n. longitud (repite n x) = n ⟹ longitud (repite (Suc n) x) = Suc n
 
    donde ⋀n se lee "para todo n". 
 
  · "next" indica el siguiente subobjetivo.
 
  · "fix n" indica "sea n un número natural cualquiera"
 
  · assume HI: "longitud (repite n x) = n" indica «supongamos que
 
    "longitud (repite n x) = n" y sea HI la etiqueta de este supuesto».
 
  · "using HI" usando la propiedad etiquetada con HI.
 
*}
 
 
(* La demostración automática es *)
 
lemma "longitud (repite n x) = n"
 
  by (induct n) auto
 
 
section {* Razonamiento por inducción sobre listas *}
 
 
text {*
 
  Para demostrar una propiedad para todas las listas basta demostrar
 
  que la lista vacía tiene la propiedad y que al añadir un elemento a
 
  una lista que tiene la propiedad se obtiene otra lista que también
 
  tiene la propiedad.
 
 
  En Isabelle el principio de inducción sobre listas está formalizado
 
  mediante el teorema list.induct
 
    ⟦P [];
 
      ⋀x xs. P xs ⟹ P (x#xs)⟧
 
    ⟹ P xs
 
*}
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 9. Definir la función
 
    conc :: 'a list ⇒ 'a list ⇒ 'a list
 
  tal que (conc xs ys) es la concatención de las listas xs e ys. Por
 
  ejemplo,
 
    conc [a,d] [b,d,a,c] = [a,d,b,d,a,c]
 
  ------------------------------------------------------------------ *}
 
 
fun conc :: "'a list ⇒ 'a list ⇒ 'a list" where
 
  "conc []    ys = ys"
 
| "conc (x#xs) ys = x # (conc xs ys)"
 
 
value "conc [a,d] [b,d,a,c] = [a,d,b,d,a,c]"
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 10. (p. 24) Demostrar que
 
    conc xs (conc ys zs) = (conc xs ys) zs
 
  ------------------------------------------------------------------- *}
 
 
(* La demostración estructurada es *)
 
lemma "conc xs (conc ys zs) = conc (conc xs ys) zs"
 
proof (induct xs)
 
  show "conc [] (conc ys zs) = conc (conc [] ys) zs" by simp
 
 
next
 
next
   fix x xs
+
   fix n assume HI: "suma_impares n = n * n"
  assume HI: "conc xs (conc ys zs) = conc (conc xs ys) zs"  
+
   have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n"  
   have "conc (x # xs) (conc ys zs) = x # (conc xs (conc ys zs))" by simp
+
    by simp
   also have "... = x # (conc (conc xs ys) zs)" using HI by simp
+
   also have "= (2 * (Suc n) - 1) + n * n"  
   also have "... = conc (conc (x # xs) ys) zs" by simp
+
    using HI by simp
   finally show "conc (x # xs) (conc ys zs) = conc (conc (x # xs) ys) zs"  
+
   also have "= n * n + 2 * n + 1"  
 +
    by simp
 +
   also have "… = (Suc n) * (Suc n)"  
 
     by simp
 
     by simp
 +
  finally show "suma_impares (Suc n) = (Suc n) * (Suc n)"
 +
    by this
 
qed
 
qed
  
text {*
+
― ‹Demostración del lema anterior con patrones y razonamiento
  Comentario sobre la demostración anterior
+
  ecuacional›
  · (induct xs) genera dos subobjetivos:
+
lemma "suma_impares n = n * n" (is "?P n")
    1. conc [] (conc ys zs) = conc (conc [] ys) zs
+
proof (induct n)
    2. ⋀a xs. conc xs (conc ys zs) = conc (conc xs ys) zs ⟹
+
   show "?P 0"  
              conc (a#xs) (conc ys zs) = conc (conc (a#xs) ys) zs
+
    by simp
*}
 
 
 
(* La demostración automática es *)
 
lemma "conc xs (conc ys zs) = conc (conc xs ys) zs"
 
  by (induct xs) auto
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 11. Refutar que
 
    conc xs ys = conc ys xs
 
  ------------------------------------------------------------------- *}
 
 
 
lemma "conc xs ys = conc ys xs"
 
  quickcheck
 
  oops
 
 
 
text {* Encuentra el contraejemplo,
 
  xs = [a2]
 
  ys = [a1] *}
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 12. (p. 28) Demostrar que
 
    conc xs [] = xs
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma "conc xs [] = xs"
 
proof (induct xs)
 
   show "conc [] [] = []" by simp
 
next
 
  fix x xs
 
  assume HI: "conc xs [] = xs"
 
  have "conc (x # xs) [] = x # (conc xs [])" by simp
 
  also have "... = x # xs" using HI by simp
 
  finally show "conc (x # xs) [] = x # xs" by simp
 
qed
 
 
 
(* La demostración automática es *)
 
lemma "conc xs [] = xs"
 
  by (induct xs) simp_all
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 13. (p. 30) Demostrar que
 
    longitud (conc xs ys) = longitud xs + longitud ys
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma "longitud (conc xs ys) = longitud xs + longitud ys"
 
proof (induct xs)
 
  show "longitud (conc [] ys) = longitud [] + longitud ys" by simp
 
 
next
 
next
   fix x xs
+
   fix n
   assume HI: "longitud (conc xs ys) = longitud xs + longitud ys"
+
   assume HI: "?P n"
   have "longitud (conc (x # xs) ys) = longitud (x # (conc xs ys))"  
+
  have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n"  
 +
    by simp
 +
   also have "… = (2 * (Suc n) - 1) + n * n"
 +
    using HI by simp
 +
  also have "… = n * n + 2 * n + 1"
 +
    by simp
 +
  also have "… = Suc n * Suc n"
 
     by simp
 
     by simp
  also have "... = 1 + longitud (conc xs ys)" by simp
+
   finally show "?P (Suc n)"
  also have "... = 1 + longitud xs + longitud ys" using HI by simp
+
    by this
  also have "... = longitud (x # xs) + longitud ys" by simp
 
   finally show "longitud (conc (x # xs) ys) =
 
                longitud (x # xs) + longitud ys" by simp
 
 
qed
 
qed
  
(* La demostración automática es *)
+
text ‹Comentario sobre la demostración anterior:
lemma "longitud (conc xs ys) = longitud xs + longitud ys"
+
  · Con la expresión
   by (induct xs) auto
+
      "suma_impares n = n * n" (is "?P n")
 +
    se abrevia "suma_impares n = n * n" como "?P n". Por tanto,
 +
      "?P 0"      es una abreviatura de "suma_impares 0 = 0 * 0"
 +
      "?P (Suc n)" es una abreviatura de
 +
                    "suma_impares (Suc n) = (Suc n) * (Suc n)"
 +
   · En general, cualquier fórmula seguida de (is patrón) equipara el
 +
    patrón con la fórmula. ›
  
section {* Inducción correspondiente a la definición recursiva *}
+
― ‹La demostración usando patrones es›
 
+
lemma "suma_impares n = n * n" (is "?P n")
text {* ---------------------------------------------------------------
+
proof (induct n)
  Ejemplo 14. Definir la función
+
   show "?P 0" by simp
    coge :: nat ⇒ 'a list ⇒ 'a list
 
  tal que (coge n xs) es la lista de los n primeros elementos de xs. Por
 
  ejemplo,
 
    coge 2 [a,c,d,b,e] = [a,c]
 
  ------------------------------------------------------------------ *}
 
 
 
fun coge :: "nat ⇒ 'a list ⇒ 'a list" where
 
  "coge n []          = []"
 
| "coge 0 xs          = []"
 
| "coge (Suc n) (x#xs) = x # (coge n xs)"
 
 
 
value "coge 2 [a,c,d,b,e] = [a,c]"
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 15. Definir la función
 
    elimina :: nat ⇒ 'a list ⇒ 'a list
 
  tal que (elimina n xs) es la lista obtenida eliminando los n primeros
 
  elementos de xs. Por ejemplo,
 
    elimina 2 [a,c,d,b,e] = [d,b,e]
 
  ------------------------------------------------------------------ *}
 
 
 
fun elimina :: "nat ⇒ 'a list ⇒ 'a list" where
 
  "elimina n []          = []"
 
| "elimina 0 xs          = xs"
 
| "elimina (Suc n) (x#xs) = elimina n xs"
 
 
 
value "elimina 2 [a,c,d,b,e] = [d,b,e]"
 
 
 
text {*
 
  La definición coge genera el esquema de inducción coge.induct:
 
    ⟦⋀n. P n [];
 
      ⋀x xs. P 0 (x#xs);
 
      ⋀n x xs. P n xs ⟹ P (Suc n) (x#xs)⟧
 
    ⟹ P n x
 
 
 
  Puede verse usando "thm coge.induct". *}
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 16. (p. 35) Demostrar que
 
    conc (coge n xs) (elimina n xs) = xs
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma "conc (coge n xs) (elimina n xs) = xs"
 
proof (induct rule: coge.induct)
 
  fix n
 
  show "conc (coge n []) (elimina n []) = []" by simp
 
next
 
  fix x xs
 
   show "conc (coge 0 (x#xs)) (elimina 0 (x#xs)) = x#xs" by simp
 
 
next
 
next
   fix n x xs
+
   fix n  
   assume HI: "conc (coge n xs) (elimina n xs) = xs"
+
   assume "?P n"
  have "conc (coge (Suc n) (x#xs)) (elimina (Suc n) (x#xs)) =
+
   then show "?P (Suc n)" by simp
        conc (x#(coge n xs)) (elimina n xs)" by simp
 
  also have "... = x#(conc (coge n xs) (elimina n xs))" by simp
 
  also have "... = x#xs" using HI by simp 
 
   finally show "conc (coge (Suc n) (x#xs)) (elimina (Suc n) (x#xs)) =
 
                x#xs"
 
    by simp
 
 
qed
 
qed
 +
 
 +
― ‹La demostración aplicativa es›
 +
lemma "suma_impares n = n * n"
 +
  apply (induct n)
 +
  apply simp_all
 +
  done
  
text {*
+
― ‹La demostración automática es›
  Comentario sobre la demostración anterior:
+
lemma "suma_impares n = n * n"
  · (induct rule: coge.induct) indica que el método de demostración es
+
  by (induct n) simp_all
    por el esquema de inducción correspondiente a la definición de la
 
    función coge.
 
  · Se generan 3 subobjetivos:
 
    · 1. ⋀n. conc (coge n []) (elimina n []) = []
 
    · 2. ⋀x xs. conc (coge 0 (x#xs)) (elimina 0 (x#xs)) = x#xs
 
    · 3. ⋀n x xs.
 
            conc (coge n xs) (elimina n xs) = xs ⟹
 
            conc (coge (Suc n) (x#xs)) (elimina (Suc n) (x#xs)) = x#xs
 
*}
 
  
(* La demostración automática es *)
+
text ‹Ejemplo de definición con existenciales.
lemma "conc (coge n xs) (elimina n xs) = xs"
+
  Un número natural n es par si existe un natural m tal que n=m+m.
  by (induct rule: coge.induct) auto
 
  
section {* Razonamiento por casos *}
+
definition par :: "nat ⇒ bool" where
 +
  "par n ≡ ∃m. n=m+m"
  
text {* ---------------------------------------------------------------
+
text ‹Ejemplo de inducción y existenciales:  
  Ejemplo 17. Definir la función
+
   Demostrar que para todo número natural n, se verifica que n*(n+1)
    esVacia :: 'a list ⇒ bool
+
   es par.›
   tal que (esVacia xs) se verifica si xs es la lista vacía. Por ejemplo,
 
    esVacia []  = True
 
    esVacia [1] = False
 
   ------------------------------------------------------------------ *}
 
  
fun esVacia :: "'a list ⇒ bool" where
+
― ‹Demostración estructurada por inducción›
  "esVacia []    = True"
+
lemma "par (n*(n+1))"
| "esVacia (x#xs) = False"
+
proof (induct n)
 
+
   show "par (0*(0+1))"
value "esVacia []  = True"
+
    by (simp add: par_def)
value "esVacia [a] = False"
 
 
 
text {* ---------------------------------------------------------------
 
  Ejemplo 18 (p. 39) . Demostrar que
 
    esVacia xs = esVacia (conc xs xs)
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma "esVacia xs = esVacia (conc xs xs)"
 
proof (cases xs)
 
   assume "xs = []"
 
  then show "esVacia xs = esVacia (conc xs xs)" by simp
 
 
next
 
next
   fix y ys
+
   fix n
   assume "xs = y#ys"
+
   assume "par (n*(n+1))"
   then show "esVacia xs = esVacia (conc xs xs)" by simp
+
  then have "∃m. n*(n+1) = m+m"  
 +
    by (simp add: par_def)
 +
  then obtain m where m: "n*(n+1) = m+m"
 +
    by (rule exE)
 +
  then have "(Suc n)*((Suc n)+1) = (m+n+1)+(m+n+1)"
 +
    by simp
 +
  then have "∃p. (Suc n)*((Suc n)+1) = p+p"
 +
    by (rule exI)
 +
   then show "par ((Suc n)*((Suc n)+1))"  
 +
    by (simp add: par_def)
 
qed
 
qed
  
text {*
+
― ‹Demostración casi detallada por inducción›
  Comentarios sobre la demostración anterior:
+
lemma "par (n*(n+1))"
   · "(cases xs)" es el método de demostración por casos según xs.
+
proof (induct n)
  · Se generan dos subobjetivos  correspondientes a los dos
+
   have "(0::nat) = 0 + 0"
     constructores de listas:
+
     by (simp only: add_0)
    · 1. xs = [] ⟹ esVacia xs = esVacia (conc xs xs)
+
  then have "∃m. (0::nat) = m + m"
     · 2. ⋀y ys. xs = y#ys ⟹ esVacia xs = esVacia (conc xs xs)
+
     by (rule exI)
   · "then" indica "usando la propiedad anterior"
+
   then have "par 0"
*}
+
    by  (simp only: par_def)
 
+
   then show "par (0*(0+1))"
(* La demostración estructurada simplificada es *)
+
    by (simp only: mult_0)
lemma "esVacia xs = esVacia (conc xs xs)"
 
proof (cases xs)
 
  case Nil
 
   then show "esVacia xs = esVacia (conc xs xs)" by simp
 
 
next
 
next
   case Cons
+
   fix n
   then show "esVacia xs = esVacia (conc xs xs)" by simp
+
  assume "par (n*(n+1))"
 +
  then have "∃m. n*(n+1) = m+m"
 +
    by (simp only: par_def)
 +
  then obtain m where m: "n*(n+1) = m+m"
 +
    by (rule exE)
 +
  then have "(Suc n)*((Suc n)+1) = (m+n+1)+(m+n+1)"
 +
    by simp
 +
  then have "∃p. (Suc n)*((Suc n)+1) = p+p"
 +
    by (rule exI)
 +
   then show "par ((Suc n)*((Suc n)+1))"  
 +
    by (simp only: par_def)
 
qed
 
qed
  
text {*
+
― ‹Demostración aplicativa por inducción›
  Comentarios sobre la demostración anterior:
+
lemma "par (n*(n+1))"
  · "case Nil" es una abreviatura de "assume xs = []"
+
  apply (induct n)
  · "case Cons" es una abreviatura de "fix y ys assume xs = y#ys"
+
  apply (simp add: par_def)
  · "thus" es una abreviatura de "then show".
+
   apply (simp add: par_def)
*}
+
   apply arith
 
+
  done
(* La demostración con el patrón sugerido es *)
 
lemma "esVacia xs = esVacia (conc xs xs)"
 
proof (cases xs)
 
  case Nil
 
  then show ?thesis by simp
 
next
 
   case (Cons x xs)
 
   then show ?thesis by simp
 
qed
 
  
(* La demostración automática es *)
+
― ‹Demostración automática›
lemma "esVacia xs = esVacia (conc xs xs)"
+
lemma "par (n*(n+1))"
   by (cases xs) auto
+
   by (induct n) (auto simp add: par_def, arith)
  
section {* Heurística de generalización *}
+
text ‹En Isabelle puede demostrarse de manera más simple un lema
 +
  equivalente usando en lugar de la función "par" la función "even"
 +
  definida en la teoría Parity por
 +
    even x ⟷ x mod 2 = 0 ›
  
text {*
+
lemma
   Heurística de generalización: Cuando se use demostración estructural,
+
   fixes n :: "nat"
   cuantificar universalmente las variables libres (o, equivalentemente,
+
   shows "even (n*(n+1))"
   considerar las variables libres como variables arbitrarias). *}
+
   by auto
  
text {* ---------------------------------------------------------------
+
text ‹Comentarios sobre la demostración anterior:
   Ejemplo 19. Definir la función
+
   · Para poder usar la función "even" de la librería Parity es necesario
    inversaAc :: 'a list ⇒ 'a list
+
    importar dicha librería. Por ello, antes del inicio de la teoría
  tal que (inversaAc xs) es a inversa de xs calculada usando
+
    aparece
  acumuladores. Por ejemplo,  
+
      imports Main HOL.Parity
    inversaAc [a,c,b,e] = [e,b,c,a]
+
  ------------------------------------------------------------------ *}
 
  
fun inversaAcAux :: "'a list ⇒ 'a list ⇒ 'a list" where
+
text ‹Para completar la demostración basta demostrar la equivalencia de
   "inversaAcAux [] ys    = ys"
+
   las funciones "par" y "even".›
| "inversaAcAux (x#xs) ys = inversaAcAux xs (x#ys)"
 
  
fun inversaAc :: "'a list ⇒ 'a list" where
+
lemma "par n = even n"
  "inversaAc xs = inversaAcAux xs []"
+
proof -
 
+
  have "par n = (∃m. n = m+m)"  
value "inversaAc [a,c,b,e] = [e,b,c,a]"
+
    by (simp only: par_def)
 
+
   then show "par n = even n"  
text {* ---------------------------------------------------------------
+
     (* try *)  
  Ejemplo 20. (p. 44) Demostrar que
+
     by presburger
    inversaAcAux xs ys = (inversa xs) @ ys
 
  ------------------------------------------------------------------- *}
 
 
 
(* La demostración estructurada es *)
 
lemma inversaAcAux_es_inversa:
 
  "inversaAcAux xs ys = (inversa xs) @ ys"
 
proof (induct xs arbitrary: ys)
 
   show "⋀ys. inversaAcAux [] ys = inversa [] @ ys" by simp
 
next
 
  fix a xs
 
  assume HI: "⋀ys. inversaAcAux xs ys = inversa xs@ys"
 
  show "⋀ys. inversaAcAux (a#xs) ys = inversa (a#xs)@ys"
 
  proof -
 
    fix ys
 
     have "inversaAcAux (a#xs) ys = inversaAcAux xs (a#ys)" by simp
 
     also have "… = inversa xs@(a#ys)" using HI by simp
 
    also have "… = inversa (a#xs)@ys" by simp
 
    finally show "inversaAcAux (a#xs) ys = inversa (a#xs)@ys" by simp
 
  qed
 
 
qed
 
qed
  
text {*
+
text ‹Comentarios sobre la demostración anterior:
  Comentarios sobre la demostración anterior:
+
   · "by presburger" indica que se use como método de demostración el
   · "(induct xs arbitrary: ys)" es el método de demostración por
+
     algoritmo de decisión de la aritmética de Presburger.
     inducción sobre xs usando ys como variable arbitraria.
 
  · Se generan dos subobjetivos:
 
    · 1. ⋀ys. inversaAcAux [] ys = inversa [] @ ys
 
    · 2. ⋀a xs ys. (⋀ys. inversaAcAux xs ys = inversa xs @ ys) ⟹
 
                    inversaAcAux (a # xs) ys = inversa (a # xs) @ ys
 
  · Dentro de una demostración se pueden incluir otras demostraciones.
 
  · Para demostrar la propiedad universal "⋀ys. P(ys)" se elige una
 
    lista arbitraria (con "fix ys") y se demuestra "P(ys)".  
 
*}
 
  
(* La demostración automática es *)
+
― ‹Demostración declarativa›
lemma "inversaAcAux xs ys = (inversa xs)@ys"
+
lemma "par n = even n"
   by (induct xs arbitrary: ys) auto
+
   apply (unfold par_def)
 +
  apply presburger
 +
  done
  
text {* ---------------------------------------------------------------
+
― ‹Demostración automática›
   Ejemplo 21. (p. 43) Demostrar que
+
lemma "par n = even n"
    inversaAc xs = inversa xs
+
   by (simp only: par_def, presburger)
  ------------------------------------------------------------------- *}
 
  
(* La demostración automática es *)
+
section ‹Recursión general. La función de Ackermann›
corollary "inversaAc xs = inversa xs"
 
  by (simp add: inversaAcAux_es_inversa)
 
  
text {*
+
text ‹El objetivo de esta sección es mostrar el uso de las definiciones
   Comentario de la demostración anterior:
+
   recursivas generales y sus esquemas de inducción. Como ejemplo se usa
   · "(simp add: inversaAcAux_es_inversa)" es el método de demostración
+
  la función de Ackermann (se puede consultar información sobre dicha
    por simplificación usando como regla de simplificación la propiedad
+
   función en http://en.wikipedia.org/wiki/Ackermann_function).
    inversaAcAux_es_inversa.  
 
*}
 
  
section {* Demostración por inducción para funciones de orden superior *}
+
  Definición.  La función de Ackermann se define por
 +
    A(m,n) = n+1,            si m=0,
 +
            A(m-1,1),        si m>0 y n=0,
 +
            A(m-1,A(m,n-1)), si m>0 y n>0
 +
  para todo los números naturales.
  
text {* ---------------------------------------------------------------
+
   La función de Ackermann es recursiva, pero no es primitiva recursiva.
   Ejemplo 22. Definir la función
 
    sum :: nat list ⇒ nat
 
  tal que (sum xs) es la suma de los elementos de xs. Por ejemplo,
 
    sum [3,2,5] = 10
 
  ------------------------------------------------------------------ *}
 
  
fun sum :: "nat list ⇒ nat" where
+
fun ack :: "nat ⇒ nat ⇒ nat" where
   "sum []    = 0"
+
   "ack 0      n      = n+1"
| "sum (x#xs) = x + sum xs"
+
| "ack (Suc m) 0       = ack m 1"  
 +
| "ack (Suc m) (Suc n) = ack m (ack (Suc m) n)"
  
value "sum [3,2,5] = 10"
+
― ‹Ejemplo de evaluación›
 +
value "ack 2 3" (* devuelve 9 *)
  
text {* ---------------------------------------------------------------
+
text ‹Esquema de inducción correspondiente a una función:
   Ejemplo 23. Definir la función
+
   · Al definir una función recursiva general se genera una regla de
    map :: ('a ⇒ 'b) ⇒ 'a list ⇒ 'b list
+
    inducción. En la definición anterior, la regla generada es
  tal que (map f xs) es la lista obtenida aplicando la función f a los
+
    ack.induct:  
  elementos de xs. Por ejemplo,
+
      ⟦⋀n. P 0 n;
    map (λx. 2*x) [3,2,5] = [6,4,10]
+
        ⋀m. P m 1 ⟹ P (Suc m) 0;
  ------------------------------------------------------------------ *}
+
        ⋀m n. ⟦P (Suc m) n; P m (ack (Suc m) n)⟧ ⟹ P (Suc m) (Suc n)
 +
      ⟹ P a b
 +
  
fun map :: "('a ⇒ 'b) ⇒ 'a list ⇒ 'b list" where
+
text ‹Ejemplo de demostración por la inducción correspondiente a una
   "map f []    = []"
+
   función:
| "map f (x#xs) = (f x) # map f xs"
+
  Demostrar que para todos m y n, A(m,n) > n.›
  
value "map (λx. 2*x) [3::nat,2,5] = [6,4,10]"
+
― ‹La demostración detallada es›
 
+
lemma "ack m n > n"
text {* ---------------------------------------------------------------
+
proof (induct m n rule: ack.induct)
  Ejemplo 24. (p. 45) Demostrar que
+
  fix n
    sum (map (λx. 2*x) xs) = 2 * (sum xs)
+
   show "ack 0 n > n"
  ------------------------------------------------------------------- *}
+
    by (simp only: ack.simps(1))
 
 
(* La demostración estructurada es *)
 
lemma "sum (map (λx. 2*x) xs) = 2 * (sum xs)"
 
proof (induct xs)
 
   show "sum (map (λx. 2*x) []) = 2 * (sum [])" by simp
 
 
next
 
next
   fix a xs
+
   fix m
   assume HI: "sum (map (λx. 2*x) xs) = 2 * (sum xs)"
+
   assume "ack m 1 > 1"
   have "sum (map (λx. 2*x) (a#xs)) = sum ((2*a)#(map (λx. 2*x) xs))"  
+
   then show "ack (Suc m) 0 > 0"  
     by simp
+
     by (simp only: ack.simps(2))
  also have "... = 2*a + sum (map (λx. 2*x) xs)" by simp
+
next 
   also have "... = 2*a + 2*(sum xs)" using HI by simp
+
  fix m n
  also have "... = 2*(a + sum xs)" by simp
+
   assume "n < ack (Suc m) n"  
  also have "... = 2*(sum (a#xs))" by simp
+
    and "ack (Suc m) n < ack m (ack (Suc m) n)"
   finally show "sum (map (λx. 2*x) (a#xs)) = 2*(sum (a#xs))" by simp
+
   then show "Suc n < ack (Suc m) (Suc n)"
 +
    by (simp only: ack.simps(3))
 
qed
 
qed
  
(* La demostración automática es *)
+
text ‹Comentarios sobre la demostración anterior:
lemma "sum (map (λx. 2*x) xs) = 2 * (sum xs)"
+
   · (induct m n rule: ack.induct) indica que el método de demostración
   by (induct xs) auto
+
    es el esquema de recursión correspondiente a la definición de
 
+
    (ack m n).
text {* ---------------------------------------------------------------
+
   · Se generan 3 casos:
  Ejemplo 25. (p. 48) Demostrar que  
+
    1. ⋀n. n < ack 0 n
    longitud (map f xs) = longitud xs
+
    2. ⋀m. 1 < ack m 1 ⟹ 0 < ack (Suc m) 0
  ------------------------------------------------------------------- *}
+
    3. ⋀m n. ⟦n < ack (Suc m) n;
 
+
              ack (Suc m) n < ack m (ack (Suc m) n)
(* La demostración estructurada es *)
+
            ⟹ Suc n < ack (Suc m) (Suc n)
lemma "longitud (map f xs) = longitud xs"
+
proof (induct xs)
 
  show "longitud (map f []) = longitud []" by simp
 
next
 
  fix a xs
 
   assume HI: "longitud (map f xs) = longitud xs"
 
  have "longitud (map f (a#xs)) = longitud (f a # (map f xs))" by simp
 
  also have "... = 1 + longitud (map f xs)" by simp
 
  also have "... = 1 + longitud xs" using HI by simp
 
  also have "... = longitud (a#xs)" by simp
 
  finally show "longitud (map f (a#xs)) = longitud (a#xs)" by simp
 
qed
 
 
 
(* La demostración automática es *)
 
lemma "longitud (map f xs) = longitud xs"
 
  by (induct xs) auto
 
 
 
section {* Referencias *}
 
  
text {*
+
― ‹La demostración automática es›
  · J.A. Alonso. "Razonamiento sobre programas" http://goo.gl/R06O3
+
lemma "ack m n > n"
  · G. Hutton. "Programming in Haskell". Cap. 13 "Reasoning about
+
   by (induct m n rule: ack.induct) auto
    programms".
 
   · S. Thompson. "Haskell: the Craft of Functional Programming, 3rd
 
    Edition. Cap. 8 "Reasoning about programms".
 
  · L. Paulson. "ML for the Working Programmer, 2nd Edition". Cap. 6.  
 
    "Reasoning about functional programs".
 
*}
 
  
 
end
 
end
 
</source>
 
</source>

Revisión actual del 19:00 6 may 2020

chapter Tema 7: Razonamiento por casos y por inducción

theory T7_Razonamiento_por_casos_y_por_induccion
imports Main HOL.Parity
begin

text En este tema se amplían los métodos de demostración por casos y 
  por inducción iniciados en el tema anterior.

section Razonamiento por distinción de casos

subsection Distinción de casos booleanos

text Ejemplo de demostración por distinción de casos booleanos:
  Demostrar "¬A ∨ A".

 La demostración estructurada es
lemma "¬A ∨ A" 
proof cases
  assume "A" 
  then show "¬A ∨ A" ..
next
  assume "¬A" 
  then show "¬A ∨ A" ..
qed

text Comentarios de la demostración anterior:
  · "proof cases" indica que el método de demostración será por
    distinción de casos. 
  · Se generan 2 casos:
       1. ?P  ¬A  A
       2. ¬?P  ¬A  A
    donde ?P es una variable sobre las fórmulas.
  · (assume "A") indica que se está usando "A" en lugar de la variable
    ?P.
  · "then" indica usando la fórmula anterior.
  · ".." indica usando la regla lógica necesaria (las reglas lógicas se
    estudiarán en los siguientes temas).
  · "next" indica el siguiente caso (se puede observar cómo ha
    sustituido ¬?P por ¬A.

 La demostración aplicativa es
lemma "¬A ∨ A" 
  apply cases
   apply simp_all
  done

 La demostración automática es
lemma "¬A ∨ A" 
  by cases simp_all

lemma "¬A ∨ A"
  by auto

text Ejemplo de demostración por distinción de casos booleanos con 
  nombres: 
  Demostrar "¬A ∨ A".

 La demostración estructurada es
lemma "¬A ∨ A" 
proof (cases "A")
  case True 
  then show "¬A ∨ A" ..
next
  case False 
  then show "¬A ∨ A" .. 
qed

text Comentarios sobre la demostración anterior:
  · (cases "A") indica que la demostración se hará por casos según los
    distintos valores de "A".
  · Como "A" es una fórmula, sus posibles valores son verdadero o falso.
  · "case True" indica que se está suponiendo que A es verdadera. Es
    equivalente a "assume A".
  · "case False" indica que se está suponiendo que A es falsa. Es
    equivalente a "assume ¬A".
  · En general, 
    · el método (cases F) es una abreviatura de la aplicación de la regla
         F  Q; ¬F  Q  Q  
    · La expresión "case True" es una abreviatura de F.
    · La expresión "case False" es una abreviatura de ¬F.
  · Ventajas de "cases" con nombre: 
    · reduce la escritura de la fórmula y
    · es independiente del orden de los casos.

subsection Distinción de casos sobre otros tipos de datos

text Ejemplo de distinción de casos sobre listas: 
  Demostrar que la longitud del resto de una lista es la longitud de la
  lista menos 1.

 La demostración estructurada es
lemma "length (tl xs) = length xs - 1" 
proof (cases xs)
  assume "xs = []"
  then have "length (tl xs) = 0"
    by simp
  also have "… = 0 - 1"
    by simp
  also have "… = length xs - 1"
    using xs = []
    by simp
  finally show "length (tl xs) = length xs - 1" 
    by this
next
  fix y ys
  assume "xs = y#ys"
  then have "length (tl xs) = length ys"
    by simp
  also have "… = (1 + length ys) - 1"
    by simp
  also have "… = length (y#ys) - 1"
    by simp 
  also have "… = length xs - 1"
    using xs = y#ys
    by simp
  finally show "length (tl xs) = length xs - 1" 
    by this
qed

text Comentarios sobre la demostración anterior:
  · "(cases xs)" indica que la demostración se hará por casos sobre los
    posibles valores de xs.
  · Como xs es una lista, sus posibles valores son la lista vacía ([]) o
    una lista no vacía (de la forma (y#ys)).
  · Se generan 2 casos:
       1. xs = []  length (tl xs) = length xs - 1
       2. a list. xs = a # list  length (tl xs) = length xs - 1

 La demostración detallada es
lemma "length (tl xs) = length xs - 1" 
proof (cases xs)
  assume "xs = []"
  then have "length (tl xs) = 0"
    by (simp only: list.sel(2)
                   list.size(3))
  also have "… = 0 - 1"
    by (simp only: natural_zero_minus_one)
  also have "… = length xs - 1"
    using xs = []
    by (simp only: list.size(3))
  finally show "length (tl xs) = length xs - 1" 
    by this
next
  fix y ys
  assume "xs = y#ys"
  then have "length (tl xs) = length ys"
    by (simp only: list.sel(3))
  also have "… = length (y#ys) - 1"
    by (simp only: length_Cons)
  also have "… = length xs - 1"
    using xs = y#ys
    by (simp only:)
  finally show "length (tl xs) = length xs - 1" 
    by this
qed

 La demostración simplificada es 
lemma "length (tl xs) = length xs - 1" 
proof (cases xs)
  case Nil 
  then show ?thesis by simp
next
  case Cons 
  then show ?thesis by simp 
qed

text Comentarios sobre la demostración anterior:
  · "case Nil" es una abreviatura de 
       "assume xs =[]".
  · "case Cons" es una abreviatura de 
       "fix y ys assume xs = y#ys"
  · ?thesis es una abreviatura de la conclusión del lema.

 La demostración aplicativa es
lemma "length (tl xs) = length xs - 1" 
  apply (cases xs)
   apply simp_all
  done

 La demostración automática es
lemma "length (tl xs) = length xs - 1" 
  by (cases xs) simp_all

text En el siguiente ejemplo vamos a demostrar una propiedad de la 
  función drop que está definida en la teoría List de forma que 
  (drop n xs) es la lista obtenida eliminando en xs los n primeros 
  elementos. Su definición es la siguiente   
     drop_Nil:  "drop n []     = []" 
     drop_Cons: "drop n (x#xs) = (case n of 
                                    0 => x#xs | 
                                    Suc(m) => drop m xs)" 

text Ejemplo de análisis de casos:
  Demostrar que el resultado de eliminar los n+1 primeros elementos de
  xs es el mismo que eliminar los n primeros elementos del resto de xs.

 La demostración estructurada es
lemma "drop (n + 1) xs = drop n (tl xs)"
proof (cases xs)
  case Nil 
  then show ?thesis by simp
next
  case Cons 
  then show ?thesis by simp
qed

 La demostración aplicativa es
lemma "drop (n + 1) xs = drop n (tl xs)"
  apply (cases xs)
   apply simp_all
  done

 La demostración automática es
lemma "drop (n + 1) xs = drop n (tl xs)"
  by (cases xs) simp_all

section Inducción matemática

text [Principio de inducción matemática]
  Para demostrar una propiedad P para todos los números naturales basta
  probar que el 0 tiene la propiedad P y que si n tiene la propiedad P,
  entonces n+1 también la tiene. 
     P 0; n. P n  P (Suc n)  P m

  En Isabelle el principio de inducción matemática está formalizado en
  el teorema nat.induct y puede verse con
     thm nat.induct


text Ejemplo de demostración por inducción: Usaremos el principio de
  inducción matemática para demostrar que 
     1 + 3 + ... + (2n-1) = n^2

  Definición. [Suma de los primeros impares] 
  (suma_impares n) la suma de los n números impares. Por ejemplo,
     suma_impares 3  =  9


fun suma_impares :: "nat ⇒ nat" where
  "suma_impares 0 = 0" 
| "suma_impares (Suc n) = (2*(Suc n) - 1) + suma_impares n"

value "suma_impares 3"

text   Ejemplo de demostración por inducción matemática:
  Demostrar que la suma de los n primeros números impares es n^2.

 Demostración estructurada del lema anterior por inducción y 
  razonamiento ecuacional
lemma "suma_impares n = n * n"
proof (induct n)
  show "suma_impares 0 = 0 * 0" 
    by simp
next
  fix n assume HI: "suma_impares n = n * n"
  have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n" 
    by simp
  also have "… = (2 * (Suc n) - 1) + n * n" 
    using HI by simp
  also have "… = n * n + 2 * n + 1" 
    by simp
  also have "… = (Suc n) * (Suc n)" 
    by simp
  finally show "suma_impares (Suc n) = (Suc n) * (Suc n)" 
    by this
qed

 Demostración del lema anterior con patrones y razonamiento 
   ecuacional
lemma "suma_impares n = n * n" (is "?P n")
proof (induct n)
  show "?P 0" 
    by simp
next
  fix n 
  assume HI: "?P n"
  have "suma_impares (Suc n) = (2 * (Suc n) - 1) + suma_impares n" 
    by simp
  also have "… = (2 * (Suc n) - 1) + n * n" 
    using HI by simp
  also have "… = n * n + 2 * n + 1" 
    by simp
  also have "… = Suc n * Suc n"
    by simp
  finally show "?P (Suc n)" 
    by this
qed

text Comentario sobre la demostración anterior:
  · Con la expresión
       "suma_impares n = n * n" (is "?P n")
    se abrevia "suma_impares n = n * n" como "?P n". Por tanto, 
       "?P 0"       es una abreviatura de "suma_impares 0 = 0 * 0"
       "?P (Suc n)" es una abreviatura de 
                    "suma_impares (Suc n) = (Suc n) * (Suc n)"
  · En general, cualquier fórmula seguida de (is patrón) equipara el
    patrón con la fórmula. 

 La demostración usando patrones es
lemma "suma_impares n = n * n" (is "?P n")
proof (induct n)
  show "?P 0" by simp
next
  fix n 
  assume "?P n"
  then show "?P (Suc n)" by simp
qed
  
 La demostración aplicativa es
lemma "suma_impares n = n * n"
  apply (induct n)
   apply simp_all
  done

 La demostración automática es
lemma "suma_impares n = n * n"
  by (induct n) simp_all

text Ejemplo de definición con existenciales. 
  Un número natural n es par si existe un natural m tal que n=m+m.

definition par :: "nat ⇒ bool" where
  "par n ≡ ∃m. n=m+m"

text Ejemplo de inducción y existenciales: 
  Demostrar que para todo número natural n, se verifica que n*(n+1) 
  es par.

 Demostración estructurada por inducción
lemma "par (n*(n+1))"
proof (induct n)
  show "par (0*(0+1))"
    by (simp add: par_def) 
next
  fix n 
  assume "par (n*(n+1))"
  then have "∃m. n*(n+1) = m+m" 
    by (simp add: par_def)
  then obtain m where m: "n*(n+1) = m+m" 
    by (rule exE)
  then have "(Suc n)*((Suc n)+1) = (m+n+1)+(m+n+1)" 
    by simp
  then have "∃p. (Suc n)*((Suc n)+1) = p+p" 
    by (rule exI)
  then show "par ((Suc n)*((Suc n)+1))" 
    by (simp add: par_def)
qed

 Demostración casi detallada por inducción
lemma "par (n*(n+1))"
proof (induct n)
  have "(0::nat) = 0 + 0"
    by (simp only: add_0)
  then have "∃m. (0::nat) = m + m"
    by (rule exI)
  then have "par 0"
    by  (simp only: par_def)
  then show "par (0*(0+1))"
    by (simp only: mult_0) 
next
  fix n 
  assume "par (n*(n+1))"
  then have "∃m. n*(n+1) = m+m" 
    by (simp only: par_def)
  then obtain m where m: "n*(n+1) = m+m" 
    by (rule exE)
  then have "(Suc n)*((Suc n)+1) = (m+n+1)+(m+n+1)" 
    by simp
  then have "∃p. (Suc n)*((Suc n)+1) = p+p" 
    by (rule exI)
  then show "par ((Suc n)*((Suc n)+1))" 
    by (simp only: par_def)
qed

 Demostración aplicativa por inducción
lemma "par (n*(n+1))"
  apply (induct n)
   apply (simp add: par_def)
  apply (simp add: par_def)
  apply arith
  done

 Demostración automática
lemma "par (n*(n+1))"
  by (induct n) (auto simp add: par_def, arith)

text En Isabelle puede demostrarse de manera más simple un lema
  equivalente usando en lugar de la función "par" la función "even" 
  definida en la teoría Parity por
     even x  x mod 2 = 0 

lemma 
  fixes n :: "nat"
  shows "even (n*(n+1))"
  by auto

text Comentarios sobre la demostración anterior:
  · Para poder usar la función "even" de la librería Parity es necesario
    importar dicha librería. Por ello, antes del inicio de la teoría
    aparece 
       imports Main HOL.Parity 


text Para completar la demostración basta demostrar la equivalencia de 
  las funciones "par" y "even".

lemma "par n = even n"
proof - 
  have "par n = (∃m. n = m+m)" 
    by (simp only: par_def)
  then show "par n = even n" 
    (* try *) 
    by presburger
qed

text Comentarios sobre la demostración anterior:
  · "by presburger" indica que se use como método de demostración el
    algoritmo de decisión de la aritmética de Presburger.

 Demostración declarativa
lemma "par n = even n"
  apply (unfold par_def)
  apply presburger
  done

 Demostración automática
lemma "par n = even n"
  by (simp only: par_def, presburger)

section Recursión general. La función de Ackermann

text El objetivo de esta sección es mostrar el uso de las definiciones
  recursivas generales y sus esquemas de inducción. Como ejemplo se usa 
  la función de Ackermann (se puede consultar información sobre dicha 
  función en http://en.wikipedia.org/wiki/Ackermann_function).

  Definición.  La función de Ackermann se define por
    A(m,n) = n+1,             si m=0,
             A(m-1,1),        si m>0 y n=0,
             A(m-1,A(m,n-1)), si m>0 y n>0
  para todo los números naturales. 

  La función de Ackermann es recursiva, pero no es primitiva recursiva.

fun ack :: "nat ⇒ nat ⇒ nat" where
  "ack 0       n       = n+1" 
| "ack (Suc m) 0       = ack m 1" 
| "ack (Suc m) (Suc n) = ack m (ack (Suc m) n)"

 Ejemplo de evaluación
value "ack 2 3" (* devuelve 9 *)

text Esquema de inducción correspondiente a una función:
  · Al definir una función recursiva general se genera una regla de
    inducción. En la definición anterior, la regla generada es
    ack.induct: 
       ⟦⋀n. P 0 n; 
        m. P m 1  P (Suc m) 0;
        m n. P (Suc m) n; P m (ack (Suc m) n)  P (Suc m) (Suc n)
        P a b


text Ejemplo de demostración por la inducción correspondiente a una 
  función:
  Demostrar que para todos m y n, A(m,n) > n. 

 La demostración detallada es
lemma "ack m n > n"
proof (induct m n rule: ack.induct)
  fix n
  show "ack 0 n > n" 
    by (simp only: ack.simps(1))
next
  fix m 
  assume "ack m 1 > 1"
  then show "ack (Suc m) 0 > 0" 
    by (simp only: ack.simps(2))
next  
  fix m n
  assume "n < ack (Suc m) n" 
     and "ack (Suc m) n < ack m (ack (Suc m) n)"
  then show "Suc n < ack (Suc m) (Suc n)" 
    by (simp only: ack.simps(3))
qed

text Comentarios sobre la demostración anterior:
  · (induct m n rule: ack.induct) indica que el método de demostración
    es el esquema de recursión correspondiente a la definición de 
    (ack m n).
  · Se generan 3 casos:
    1. n. n < ack 0 n
    2. m. 1 < ack m 1  0 < ack (Suc m) 0
    3. m n. n < ack (Suc m) n; 
              ack (Suc m) n < ack m (ack (Suc m) n)
              Suc n < ack (Suc m) (Suc n)


 La demostración automática es
lemma "ack m n > n"
  by (induct m n rule: ack.induct) auto

end