Saltar al contenido
Topos Uranos

Resumen

Hasta aquí, cada colección del curso guardaba datos de un solo tipo: notas, estaturas, casillas de una tabla. Las entidades de un problema, en cambio, tienen varios rasgos de tipos distintos: un estudiante tiene un nombre y una nota; un libro, un título, un autor y un año. Esta clase presenta el registro, el tipo compuesto que define el propio programador con struct para agrupar esos rasgos bajo un solo nombre. Empieza por la razón de ser del registro, la fragilidad de los vectores paralelos; sigue con la definición de un struct, sus campos con valor inicial y la creación de registros con llaves y con inicializadores designados; después, los registros como parámetros y como resultados de funciones; los vectores de registros, que se recorren con ligaduras estructuradas y se ordenan y se buscan por un campo, con la cuestión del ordenamiento estable; y cierra con el arte de modelar un problema con tipos que reflejen su dominio.

Objetivos de aprendizaje

  1. Definir un struct con campos y valores iniciales, y crear registros con inicialización con llaves, también con inicializadores designados.
  2. Escribir funciones que reciban y devuelvan registros, eligiendo el modo de paso.
  3. Ordenar y buscar en un vector de registros por un campo con std::ranges::sort, std::ranges::stable_sort y std::ranges::find con una proyección, y distinguir un ordenamiento estable de uno que no lo es.
  4. Modelar con registros los datos de un problema y justificar qué campos y tipos se eligen.

Agrupar datos

Vectores paralelos

Supóngase que un programa debe guardar, para cada estudiante de un grupo, su nombre, su nota y su edad. Con lo visto hasta la clase Tablas, la solución a mano son tres vectores, uno por rasgo, con el convenio de que el índice ii designa a la misma persona en los tres: names[i], grades[i] y ages[i]. Se llaman vectores paralelos. Una tabla no sirve, porque sus casillas deben ser todas del mismo tipo, y un nombre no es un número.

Programa en C++
#include <algorithm>#include <cstddef>#include <print>#include <string>#include <vector>​int main(){    const std::vector<std::string> names{"Ana", "Luis", "Eva"};    std::vector<int> grades{6, 4, 7};    const std::vector<int> ages{19, 22, 20};    std::ranges::sort(grades);    for (std::size_t i{0}; i < names.size(); ++i) {        std::println("{}: nota {}, {} años", names[i], grades[i], ages[i]);    }}
Salida
Ana: nota 4, 19 añosLuis: nota 6, 22 añosEva: nota 7, 20 años

El programa quería ordenar a los estudiantes por nota, y ordenó solo las notas. Compila sin avisos y no falla en ningún detector; sin embargo, su salida es falsa: Ana tenía un 6 y figura con un 4. El convenio que unía los tres vectores no está escrito en ningún lugar que el compilador pueda leer, de modo que nada impide romperlo. De ello se sigue la fragilidad de los vectores paralelos: toda operación que mueva, añada o quite un elemento debe repetirse, igual y en el mismo orden, en todos ellos; y basta olvidar uno para corromper los datos en silencio.

EjemploFilas que se desalinean y fichas que se mueven enteras

Tres vectores paralelos frente a un vector de fichas, cada una con los tres datos de una persona.

Demostración

  1. La columna 0 describe a una persona: names[0], grades[0] y ages[0] son el nombre, la nota y la edad de Ana. El vínculo es solo el índice.

  2. std::ranges::sort(grades) reordena una sola fila. Las otras dos no se enteran: la columna 0 junta ahora el nombre de Ana, la nota de Luis y la edad de Ana, que no son de nadie.

  3. La alternativa: un solo vector, cuyos elementos son fichas. Cada ficha contiene los tres datos de una persona, y el vínculo entre ellos ya no es un convenio sobre índices, sino la ficha misma.

  4. Ordenar por nota mueve fichas enteras: la de Luis pasa al primer lugar con su nombre y su edad. Ningún dato puede separarse de los demás, porque la operación no los ve por separado.

Si esto es así, lo que el problema pide es un tipo cuyos valores sean fichas: un valor que contenga un nombre, una nota y una edad, y que se copie, se pase a una función, se guarde en un vector y se ordene como un todo. C++ no lo trae hecho, porque las fichas de cada problema son distintas; pero permite que el programador lo defina. Ese tipo es el registro.