|
| 1 | +# Inducción Estructural — Semana 6 |
| 2 | + |
| 3 | +**Unidad 2 · Tema 2.3 — Recursión e Inmutabilidad** |
| 4 | +**Lenguaje:** Haskell |
| 5 | +**Nivel Bloom:** Evaluar → Crear |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## ¿Por qué demostrar que el código es correcto? |
| 10 | + |
| 11 | +En paradigma funcional existe una garantía que el imperativo no puede dar fácilmente: |
| 12 | +si una función es **pura** (sin efectos secundarios), podemos razonar sobre ella |
| 13 | +matemáticamente, igual que en álgebra. |
| 14 | + |
| 15 | +Cuando optimizamos `reverse` con un acumulador para lograr O(n) en lugar de O(n²), |
| 16 | +¿cómo sabemos que el resultado es *idéntico* a la versión original y no solo "parece funcionar"? |
| 17 | + |
| 18 | +La respuesta es **inducción estructural** — la misma técnica que usa Haskell internamente |
| 19 | +para garantizar sus abstracciones, y que CMU, MIT y Cambridge exigen en sus cursos de FP. |
| 20 | + |
| 21 | +--- |
| 22 | + |
| 23 | +## Las dos implementaciones |
| 24 | + |
| 25 | +```haskell |
| 26 | +-- Versión ingenua: O(n²) — append es O(n), se llama n veces |
| 27 | +reverseNaive :: [a] -> [a] |
| 28 | +reverseNaive [] = [] |
| 29 | +reverseNaive (x:xs) = reverseNaive xs ++ [x] |
| 30 | + |
| 31 | +-- Versión con acumulador: O(n) — tail-recursive |
| 32 | +reverseAcc :: [a] -> [a] -> [a] |
| 33 | +reverseAcc [] acc = acc |
| 34 | +reverseAcc (x:xs) acc = reverseAcc xs (x:acc) |
| 35 | + |
| 36 | +-- API pública — oculta el acumulador |
| 37 | +myReverse :: [a] -> [a] |
| 38 | +myReverse xs = reverseAcc xs [] |
| 39 | +``` |
| 40 | + |
| 41 | +**Pregunta:** ¿`myReverse xs == reverseNaive xs` para toda lista `xs`? |
| 42 | + |
| 43 | +QuickCheck puede dar evidencia, pero no certeza. La inducción da certeza. |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +## Paso 0: Propiedades algebraicas que usaremos |
| 48 | + |
| 49 | +Antes de la prueba principal, necesitamos tres lemas básicos de listas: |
| 50 | + |
| 51 | +**L1 — Identidad del append:** |
| 52 | +``` |
| 53 | +[] ++ ys = ys (definición de ++) |
| 54 | +(x:xs) ++ ys = x : (xs ++ ys) (definición de ++) |
| 55 | +``` |
| 56 | + |
| 57 | +**L2 — Asociatividad del append:** (demostrada por inducción, la usamos sin redeprobar) |
| 58 | +``` |
| 59 | +(xs ++ ys) ++ zs = xs ++ (ys ++ zs) |
| 60 | +``` |
| 61 | + |
| 62 | +**L3 — Elemento singleton:** |
| 63 | +``` |
| 64 | +x : ys = [x] ++ ys (por L1 con xs := [x]) |
| 65 | +``` |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## Lema Principal |
| 70 | + |
| 71 | +Para poder probar el teorema, necesitamos un lema más fuerte que generaliza el acumulador. |
| 72 | + |
| 73 | +**Lema:** Para toda lista `xs` y toda lista `acc`: |
| 74 | + |
| 75 | +``` |
| 76 | +reverseAcc xs acc = reverseNaive xs ++ acc |
| 77 | +``` |
| 78 | + |
| 79 | +### Prueba por inducción estructural sobre `xs` |
| 80 | + |
| 81 | +**Caso base:** `xs = []` |
| 82 | + |
| 83 | +``` |
| 84 | +reverseAcc [] acc |
| 85 | += acc (def. de reverseAcc, cláusula 1) |
| 86 | += [] ++ acc (por L1: [] ++ acc = acc) |
| 87 | += reverseNaive [] ++ acc (def. de reverseNaive: reverseNaive [] = []) |
| 88 | +``` |
| 89 | +✓ El caso base se sostiene. |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +**Hipótesis inductiva (HI):** Supongamos que para alguna lista `zs` y *toda* lista `acc`: |
| 94 | + |
| 95 | +``` |
| 96 | +reverseAcc zs acc = reverseNaive zs ++ acc |
| 97 | +``` |
| 98 | + |
| 99 | +**Caso inductivo:** Debemos probar que para `xs = z : zs` y toda lista `acc`: |
| 100 | + |
| 101 | +``` |
| 102 | +reverseAcc (z:zs) acc = reverseNaive (z:zs) ++ acc |
| 103 | +``` |
| 104 | + |
| 105 | +Desarrollamos el lado izquierdo: |
| 106 | + |
| 107 | +``` |
| 108 | +reverseAcc (z:zs) acc |
| 109 | += reverseAcc zs (z:acc) (def. de reverseAcc, cláusula 2) |
| 110 | += reverseNaive zs ++ (z:acc) (por HI, con acc := z:acc) |
| 111 | += reverseNaive zs ++ ([z] ++ acc) (por L3: z:acc = [z] ++ acc) |
| 112 | += (reverseNaive zs ++ [z]) ++ acc (por L2: asociatividad) |
| 113 | += reverseNaive (z:zs) ++ acc (def. de reverseNaive, cláusula 2, invertida) |
| 114 | +``` |
| 115 | + |
| 116 | +✓ El caso inductivo se sostiene. |
| 117 | + |
| 118 | +**Por el principio de inducción estructural**, el lema es verdadero para toda lista `xs`. □ |
| 119 | + |
| 120 | +--- |
| 121 | + |
| 122 | +## Teorema Principal |
| 123 | + |
| 124 | +**Teorema:** Para toda lista `xs`: |
| 125 | + |
| 126 | +``` |
| 127 | +myReverse xs = reverseNaive xs |
| 128 | +``` |
| 129 | + |
| 130 | +**Prueba:** |
| 131 | + |
| 132 | +``` |
| 133 | +myReverse xs |
| 134 | += reverseAcc xs [] (def. de myReverse) |
| 135 | += reverseNaive xs ++ [] (por el Lema, con acc := []) |
| 136 | += reverseNaive xs (por L1 aplicado a la derecha: xs ++ [] = xs) |
| 137 | +``` |
| 138 | + |
| 139 | +✓ **QED** — `myReverse` es extensionalmente equivalente a `reverseNaive`. □ |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +## Implicación práctica |
| 144 | + |
| 145 | +Haskell no necesita ejecutar millones de casos de prueba para saber que `myReverse` |
| 146 | +es correcto. La prueba anterior es una **garantía absoluta** válida para listas de |
| 147 | +cualquier longitud y cualquier tipo — lo que ningún test puede dar. |
| 148 | + |
| 149 | +Esta es la razón por la que lenguajes como Haskell, Coq, Lean y Agda se usan en |
| 150 | +sistemas de alta confiabilidad: cohetes SpaceX, kernels seL4, compiladores de criptografía. |
| 151 | + |
| 152 | +--- |
| 153 | + |
| 154 | +## Verificación computacional con QuickCheck |
| 155 | + |
| 156 | +La prueba matemática dice que son equivalentes. QuickCheck lo confirma empíricamente: |
| 157 | + |
| 158 | +```bash |
| 159 | +ghc -package QuickCheck induccion_estructural.hs -o prueba && ./prueba |
| 160 | +``` |
| 161 | + |
| 162 | +Ver código: [`induccion_estructural.hs`](induccion_estructural.hs) |
| 163 | + |
| 164 | +--- |
| 165 | + |
| 166 | +## Práctica — Tu turno |
| 167 | + |
| 168 | +**Entregable:** Completa las siguientes pruebas por inducción estructural. |
| 169 | +Escribe cada paso con su justificación (como en el ejemplo anterior). |
| 170 | + |
| 171 | +### Ejercicio 1 — `length` y `append` |
| 172 | + |
| 173 | +Demuestra que para toda lista `xs` y `ys`: |
| 174 | + |
| 175 | +``` |
| 176 | +length (xs ++ ys) = length xs + length ys |
| 177 | +``` |
| 178 | + |
| 179 | +Definiciones relevantes: |
| 180 | +```haskell |
| 181 | +length [] = 0 |
| 182 | +length (_:xs) = 1 + length xs |
| 183 | + |
| 184 | +[] ++ ys = ys |
| 185 | +(x:xs) ++ ys = x : (xs ++ ys) |
| 186 | +``` |
| 187 | + |
| 188 | +*Pista: inducta sobre `xs`.* |
| 189 | + |
| 190 | +--- |
| 191 | + |
| 192 | +### Ejercicio 2 — `map` y `append` |
| 193 | + |
| 194 | +Demuestra que para toda función `f` y listas `xs`, `ys`: |
| 195 | + |
| 196 | +``` |
| 197 | +map f (xs ++ ys) = map f xs ++ map f ys |
| 198 | +``` |
| 199 | + |
| 200 | +Definiciones relevantes: |
| 201 | +```haskell |
| 202 | +map _ [] = [] |
| 203 | +map f (x:xs) = f x : map f xs |
| 204 | +``` |
| 205 | + |
| 206 | +--- |
| 207 | + |
| 208 | +### Ejercicio 3 — `reverse` doble (reto) |
| 209 | + |
| 210 | +Demuestra que para toda lista `xs`: |
| 211 | + |
| 212 | +``` |
| 213 | +reverseNaive (reverseNaive xs) = xs |
| 214 | +``` |
| 215 | + |
| 216 | +*Pista: necesitarás el Lema de esta sesión más un lema adicional sobre `reverse` y `append`.* |
| 217 | + |
| 218 | +--- |
| 219 | + |
| 220 | +## Formato de entrega |
| 221 | + |
| 222 | +Sube tu prueba como archivo `induccion_MATRICULA.md` en un Pull Request a |
| 223 | +`unidad2/tema2.3/entregas/`. Cada paso debe tener su justificación en paréntesis, |
| 224 | +igual que la prueba demostrativa de esta sesión. |
| 225 | + |
| 226 | +**Criterio de evaluación:** |
| 227 | + |
| 228 | +| Criterio | Puntos | |
| 229 | +|----------|--------| |
| 230 | +| Caso base correcto con justificación | 25 | |
| 231 | +| Hipótesis inductiva enunciada correctamente | 25 | |
| 232 | +| Caso inductivo con cada paso justificado | 40 | |
| 233 | +| Conclusión formal (QED) | 10 | |
| 234 | + |
| 235 | +--- |
| 236 | + |
| 237 | +## Referencias |
| 238 | + |
| 239 | +- Bird, R. (2014). *Thinking Functionally with Haskell*. Cambridge University Press. Cap. 6. |
| 240 | +- Harper, R. (2016). *Practical Foundations of Mathematics for Computer Science*. (libre en línea) |
| 241 | +- CMU 15-150 Lecture Notes on Structural Induction: https://www.cs.cmu.edu/~15150/ |
0 commit comments