Saltar al contenido
Topos Uranos

Resumen

Esta clase cierra la quinta unidad del curso, dedicada a las funciones. Reconstruye la cadena de conceptos que va de la declaración de una función a la recursión, pasando por la pila de llamadas, el contrato, el paso de los parámetros, el alcance y la duración de las variables y el diseño por refinamiento sucesivo; separa lo que de ello vale en cualquier lenguaje de lo que es propio de C++; reúne el comportamiento indefinido de la unidad, con lo que advierte el compilador, lo que detiene el detector y lo que no señala nadie; repite en su evaluación de salida las destrezas de la entrada; y termina con seis desafíos que combinan la unidad entera.

Objetivos de aprendizaje

  1. Descomponer un problema en funciones, cada una con su interfaz y su contrato (precondición y poscondición), y probarlas por separado.
  2. Elegir el modo de paso de cada parámetro y la forma de devolver los resultados, y justificar la elección.
  3. Resolver un problema por recursión y probar su corrección por inducción y su terminación con una medida que decrece, estimando su costo en llamadas.
  4. Reconocer el comportamiento indefinido de la unidad (el camino sin return, la referencia colgante, el resultado que desborda, la pila agotada) y reescribir el programa sin él.
  5. Separar, en lo aprendido en la unidad, lo que vale en cualquier lenguaje de lo que es propio de C++.

Lo logrado en la unidad

La unidad se propuso enseñar a descomponer un problema en partes con nombre, cada una con una interfaz y un contrato, y a resolver problemas por recursión. El propósito se ha cumplido apoyándose solo en lo que la introducción declaró por sabido: los bucles de la unidad 4, con su invariante y su variante, la especificación de la clase 1.1 y el modelo de la memoria de la unidad 2. El programa de los números amigos, cuyo bucle copiado sumaba los divisores del número equivocado, se escribe ahora con una sola función, verificada una vez contra su contrato.

Funciones

La clase Funciones enseñó a declarar, definir e invocar una función, y separó tres momentos: al compilar, cada llamada se comprueba contra una declaración vista antes; al enlazar, se busca la definición; al ejecutar, se calculan los argumentos, se crea un marco en la pila de llamadas y el control salta al cuerpo. Mostró que llegar al final de una función que no es void sin un return es comportamiento indefinido, y cerró con el contrato, la precondición y la poscondición, que reparte las responsabilidades entre quien llama y la función.

Parámetros y alcance

La clase Parámetros y alcance estudió por qué caminos entran los datos y salen los resultados: el parámetro por valor es una copia; el parámetro por referencia, un segundo nombre para la casilla de quien llama; la referencia constante lee sin copiar. De ello se sigue la regla de elección (valor para lo pequeño, referencia constante para lo grande que se lee, referencia solo si modificar es el propósito) y, para varios resultados, std::pair y std::tuple con ligaduras estructuradas. Distinguió el alcance de un nombre, propiedad del texto, de la duración de una variable, propiedad de la ejecución, y terminó con la referencia que sobrevive a su casilla.

Diseñar con funciones

La clase Diseñar con funciones enseñó a decidir qué funciones escribir, por refinamiento sucesivo: un main breve que llama a funciones solo declaradas y el descenso a cada una, de modo que la descomposición se dibuja como un árbol. Fijó los criterios de una buena interfaz y la regla «una función, una tarea», con sus señales (la conjunción en el nombre, la bandera, la variable global); presentó la sobrecarga, con su orden de preferencia y la llamada ambigua; y reescribió los algoritmos de la unidad 4 como funciones probadas por separado.

Recursión

La clase Recursión enseñó que una función puede llamarse a sí misma para resolver una instancia menor del problema. Probó la corrección por inducción, suponiendo correcta la llamada menor, y la terminación con una medida natural que decrece; siguió con la pila los marcos que se apilan y se deshacen, y mostró cómo se agota; advirtió que el tipo del resultado desborda mucho antes que la pila; y comparó Fibonacci recursivo e iterativo: 2 692 5372\,692\,537 llamadas contra 29 sumas para F30F_{30}.