La principal limitación de un array es que todos los datos que contiene deben ser del mismo tipo. Pero a veces nos interesa agrupar datos de distinta naturaleza, como pueden ser el nombre y la edad de una persona, que serían del tipo string y byte, respectivamente. En ese caso, podemos emplear los records o registros, que se definen indicando el nombre y el tipo de cada dato individual (cada campo), y se accede a estos campos indicando el nombre de la variable y el nombre del campo, separados por un punto:
(* RECORD1.PAS, Contacto con los "record" *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program Record1;
var
dato: record
nombre: string[30];
edad: byte;
end;
begin
dato.nombre := 'Ignacio';
dato.edad := 23;
write('El nombre es ', dato.nombre );
write(' y la edad ', dato.edad);
end.
(* Resultado:
El nombre es Ignacio y la edad 23
*)
La peculiaridad en la definición de un "record" es la aparición de una palabra end después de los nombres de los campos, lo que indica que hemos terminado de enumerar éstos.
Ejercicio propuesto 4.3.1.1: Crea un programa que defina una variable que sea un registro con dos campos: X e Y, ambos números enteros. El campo X debe valer 20, e Y debe valer 30. Después deberá mostrar en pantalla la suma de X e Y.
Ejercicio propuesto 4.3.1.2: Crea un programa que use un "record" para almacenar el nombre de una ciudad y su cantidad de habitantes. Ambos datos se deben pedir al usuario y deben ser mostrados a continuación.
Ejercicio propuesto 4.3.1.3: Crea un programa que use un "record" para almacenar las coordenadas X, Y, Z de un punto 3D, números reales. Debe pedir los tres datos al usuario, almacenarlos y luego mostrar la distancia desde ese punto al origen (que será la raíz cuadrada de x2+y2+z2).
Puede parecer engorroso el hecho de escribir "dato." antes de cada campo. También hay una forma de solucionarlo: cuando vamos a realizar varias operaciones sobre los campos de un mismo registro (record), empleamos la orden with, con la que el programa anterior quedaría
(* RECORD2.PAS, "record" y "with" *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program Record2;
var
dato: record
nombre: string[30];
edad: byte;
end;
begin
with dato do
begin
nombre := 'Ignacio';
edad := 23;
write('El nombre es ', nombre );
write(' y la edad ', edad);
end;
end.
(* Resultado:
El nombre es Ignacio y la edad 23
*)
En este caso tenemos un nuevo bloque en el cuerpo del programa, delimitado por el "begin" y el "end" situados más a la derecha, y equivale a decir "en toda esta parte del programa me estoy refiriendo a la variable dato". Así, podemos nombrar los campos que queremos modificar o escribir, sin necesidad de repetir a qué variable pertenecen.
Nota: aquí vuelve a aparecer la escritura indentada: para conseguir una mayor legibilidad, escribimos un poco más a la derecha todo lo que depende de la orden "with". No es algo obligatorio, pero sí recomendable.
Ejercicio propuesto 4.3.2.1: Empleando "with", crea una nueva versión del programa que define un registro con dos campos X e Y, enteros, con valores X=20, e Y=30 y muestra la suma de X e Y.
Ejercicio propuesto 4.3.2.2: Empleando "with", crea una nueva versión del programa que usa un "record" para almacenar el nombre de una ciudad y su cantidad de habitantes, pide ambos datos al usuario y luego los muestra.
Ejercicio propuesto 4.3.2.3: Empleando "with", crea una nueva versión del programa que emplea un "record" para almacenar las coordenadas X, Y, Z de un punto 3D, pide los tres datos al usuario y muestra la distancia desde ese punto al origen.
Es habitual no usar un único registro, sino un conjunto de ellos, de modo que tendríamos un array de registros. Por ejemplo, podríamos guardar los datos de 5 personas con una estructura como ésta:
(* ARR_REC.PAS, "array" de varios "record" *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program RecordArray;
var
datos: array [1..10] of
record
nombre: string[30];
edad: byte;
end;
begin
datos[1].nombre := 'Ignacio';
datos[1].edad := 23;
write('El primer nombre es ', datos[1].nombre );
write(' y la primera edad ', datos[1].edad);
end.
(* Resultado:
El primer nombre es Ignacio y la primera edad 23
*)
Ejercicio propuesto 4.3.3.1: Crea un array de 4 "puntos3D", pide al usuario los valores correspondientes y luego muestra la media de X, la media de Y y la media de Z.
Ejercicio propuesto 4.3.3.2: Crea un array de hasta 20 "puntos3D", y permite al usuario añadir los datos de un nuevo punto, mostrar todos los valores introducidos, o mostrar la media de X, Y y Z.
Ejercicio propuesto 4.3.3.3: Crea un programa que sirva al usuario para guardar datos de hasta 30 ciudades (nombre y cantidad de habitantes). Debe mostrar un menú que permita: añadir los datos de una nueva ciudad, mostrar los datos de todas las ciudades existentes, o mostrar los dato de una ciudad concreta cuyo nombre introduzca el usuario.
Podemos usar un array de registros para crear una pequeña agenda, que nos permita guardar datos de personas y ver los datos almacenados, empleando un menú básico:
(* AGENDA0.PAS, Ejemplo de "Agenda": *)
(* Permite añadir datos y mostrarlos *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program Agenda0;
var
gente: array [1..1000] of { Los datos }
record
nombre: string;
email: string;
anyoNacimiento: integer;
end;
cantidad: integer; { Cantidad de datos existentes }
opcion: integer; { Opción escogida }
i: integer; { Para bucles "for" }
{Cuerpo del programa principal}
begin
cantidad := 0;
repeat
WriteLn('Agenda');
WriteLn;
WriteLn('1- Añadir una nueva persona');
WriteLn('2- Ver nombres de todos');
WriteLn('0- Salir');
Write('Escoja una opción: ');
ReadLn(opcion);
WriteLn;
case opcion of
1: { Añadir datos de una persona }
if cantidad < 1000 then
begin
cantidad := cantidad + 1;
WriteLn('Introduciendo la persona ', cantidad);
Write('Introduzca el nombre: ');
ReadLn(gente[cantidad].nombre);
Write('Introduzca el correo electrónico: ');
ReadLn(gente[cantidad].email);
Write('Introduzca el año de nacimiento: ');
ReadLn(gente[cantidad].anyoNacimiento);
WriteLn;
end
else
WriteLn('Base de datos llena');
2: { Ver nombres de todos }
begin
if cantidad = 0 then
WriteLn('No hay datos')
else
for i := 1 to cantidad do
WriteLn(i, ' ', gente[i].nombre);
WriteLn;
end;
0: { Salir de la aplicación }
begin
WriteLn;
WriteLn('Saliendo...');
WriteLn;
end;
else
begin
WriteLn;
WriteLn('Opción incorrecta!');
WriteLn;
end;
end; { Fin de "case" }
until opcion = 0;
end.
Este esqueleto es fácil de ampliar. Por ejemplo, podemos añadir la opción de buscar cualquier ficha que contenga un cierto texto, usando "pos" para ver si aparece en cualquier posición y un "boolean" para llevar la cuenta de si hemos encontrado alguna ficha correcta o no:
(* AGENDA0B.PAS, Ejemplo de "Agenda" *)
(* Permite añadir datos, mostrarlos y buscar *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program Agenda0b;
var
gente: array [1..1000] of { Los datos }
record
nombre: string;
email: string;
anyoNacimiento: integer;
end;
cantidad: integer; { Cantidad de datos existentes }
opcion: integer; { Opción escogida }
i: integer; { Para bucles "for" }
textoBuscar: string; { Para búsquedas }
encontrado: boolean; { Idem }
{Cuerpo del programa principal}
begin
cantidad := 0;
repeat
WriteLn('Agenda');
WriteLn;
WriteLn('1- Añadir una nueva persona');
WriteLn('2- Ver nombres de todos');
WriteLn('3- Buscar una persona');
WriteLn('0- Salir');
Write('Escoja una opción: ');
ReadLn(opcion);
WriteLn;
case opcion of
1: { Añadir datos de una persona }
if cantidad < 1000 then
begin
cantidad := cantidad + 1;
WriteLn('Introduciendo la persona ', cantidad);
Write('Introduzca el nombre: ');
ReadLn(gente[cantidad].nombre);
Write('Introduzca el correo electrónico: ');
ReadLn(gente[cantidad].email);
Write('Introduzca el año de nacimiento: ');
ReadLn(gente[cantidad].anyoNacimiento);
WriteLn;
end
else
WriteLn('Base de datos llena');
2: { Ver nombres de todos }
begin
if cantidad = 0 then
WriteLn('No hay datos')
else
for i := 1 to cantidad do
WriteLn(i, ' ', gente[i].nombre);
WriteLn;
end;
3: { Buscar una persona }
begin
Write('¿Qué texto busca? ');
ReadLn( textoBuscar );
encontrado := false;
for i := 1 to cantidad do
if pos (textoBuscar, gente[i].nombre) > 0 then
begin
encontrado := true;
WriteLn( i,' - Nombre: ', gente[i].nombre,
', Email: ', gente[i].email,
', Nacido en: ', gente[i].anyoNacimiento);
end;
if not encontrado then
WriteLn('No se ha encontrado.');
WriteLn;
end;
0: { Salir de la aplicación }
begin
WriteLn;
WriteLn('Saliendo...');
WriteLn;
end;
else
begin
WriteLn;
WriteLn('Opción incorrecta!');
WriteLn;
end;
end; { Fin de "case" }
until opcion = 0;
end.
Hasta ahora hemos visto los registros (records), utilizando campos fijos, pero no tiene por qué ser necesariamente así. Tenemos a nuestra disposición los registros variantes, en los que con un "case" podemos elegir unos campos u otros. La mejor forma de entenderlos es con un ejemplo.
(* REGVAR.PAS, registros variantes *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program RegistrosVariantes;
var
punto: record
case cartesiano: boolean of
true : (X,Y,Z : real);
false : (R,theta,phi : real);
end;
begin
punto.cartesiano := true;
punto.x := 5;
punto.y := -1;
punto.z := 2.3;
writeLn('Con coordenadas cartesianas, x vale ',punto.x:2:1);
punto.cartesiano := false;
punto.r := 12.3;
punto.theta := -15;
punto.phi := 45;
writeLn('Con coordenadas esfericas, phi vale ',punto.phi:2:1);
end.
(* Resultado:
Con coordenadas cartesianas, x vale 5.0
Con coordenadas esfericas, phi vale 45.0
*)
Realmente, no es necesario usar un campo para cambiar entre un juego de valores y otro, porque realmente se trata de varios conjuntos de capas que "se solapan", ocupando el mismo espacio en memoria:
(* REGVAR2.PAS, registros variantes (2) *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program RegistrosVariantes2;
var
punto: record
case boolean of
true : (X,Y,Z : real);
false : (R,theta,phi : real);
end;
begin
punto.x := 5;
punto.y := -1;
punto.z := 2.3;
writeLn('Con coordenadas cartesianas, x vale ',punto.x:2:1);
punto.r := 12.3;
punto.theta := -15;
punto.phi := 45;
writeLn('Con coordenadas esfe ricas, phi vale ',punto.phi:2:1);
writeLn('Como curiosidad, x vale ',punto.x:2:1);
end.
(* Resultado:
Con coordenadas cartesianas, x vale 5.0
Con coordenadas esfericas, phi vale 45.0
Como curiosidad, x vale 12.3
*)
El riesgo, como se ve en este último ejemplo, es que al modificar un juego de valores, el otro se altera a la vez, ya que están solapados en memoria, pero los datos posiblemente no tendrán sentido si accedemos a ellos usando campos distintos de los que habíamos empleado para guardar la información.
Ejercicio propuesto 4.3.4.1: Crea un registro variante "puntos2D", que permita anotar los datos de un punto usando coordenadas cartesianas (x,y) o coordenadas polares (radio, ángulo).
Ejercicio propuesto 4.3.4.2: Crea un registro variante "fecha", que permita guardar una fecha como string o como bloque de 3 números enteros (día, mes, año).