Programación en Pascal/Tipos avanzados

Es bastante usual que se necesite agrupar datos distintos. Imagina por ejemplo que necesitamos llevar las fichas de una biblioteca; necesitamos almacenar, en cada ficha, el nombre y la dirección del miembro, los libros que se ha llevado, si los ha devuelto, etc. Esto puede ser un caos si cada cosa se almacena en variables distintas. Por eso se crearon los Records.



Tabla de contenidos

Tipos en Pascal

   Clasificación de tipos
   Tipos estructurados

Tipo Registro

   Motivación
   Definición de un registro
   Ejemplo(1)
   Ejemplo(2)
   Ejemplo(3)
   Acceso a campos de registros
   Lectura de un registro
   Escritura de un registro
   Ejemplo
   El tipo Fecha
   Comparación de Fechas
   La instrucción with
   Ambigüedad con with
   Asignación de registros
   Estructuras complejas

Registros variantes

   Motivación
   Ejemplo
   Ejemplo (cont)
   Ejemplo. Figuras
   Creando una figura
   Área de una figura
   Comparación de posición

Array con tope

   Definición
   Cómo funciona
   Crear conjunto vacío
   Insertar un elemento
   Eliminar un elemento
   Búsqueda de un elemento

Tipos en Pascal Clasificación de tipos


     Elementales
         
           Ordinales o escalares: Integer, Char, Boolean, subrangos, enumerados.
         
           Otros: real
   
     Estructurados:
         
           arreglos (array)
         
           conjuntos (set)
         
           registros (record)

Tipos estructurados


     Arreglos: Secuencias homogéneas de tamaño fijo. El orden es relevante y puede haber repetidos.
   
     Conjuntos: Conjuntos en el sentido matemático, sin orden, no hay repetidos. El tamaño es variable pero acotado.
   
     Registros: Colección heterogénea y ordenada de datos. Conocidos como tuplas. Tamaño fijo.

Tipo Registro Motivación

Es usual la representación de objetos o entidades de la realidad mediante estructuras de datos.

Una de las estrucutras más utilizadas con este fin es el record.

Un record es una tupla de datos de diferente tipo, cada uno de los cuales se accede mediante un nombre de campo.

Por ejemplo,

                  CI        Nombre        Direc.     Tel.
              +---------+---------------+---------+---------+
   estudiante | 4235678 | Juan Gonzalez | Yi 2345 | 6718990 |
              +---------+---------------+---------+---------+

Definición de un registro


type 
      T = record
             campo1 : tipo1;
             campo2 : tipo2;
             ...
             campok : tipok
          end;

Donde campo-i es un identificador que da nombre a un campo y tipo-i es su correspondiente tipo. Ejemplo(1)

Datos de un estudiante.

type
  TCedula   = array [1..8] of 0..9;
  TNombre   = array [1..45] of char;
  TTelefono = array [1..7] of 0..9;
  TDirec    = array [1..40] of char;
  TEstudiante = record
                   cedula    : TCedula;
                   nombre    : TNombre;
                   telefono  : TTelefono;
                   direccion : TDirec
                end;

Ejemplo(2)

Los puntos del plano pueden representarse como una pareja de reales:


 type
      punto = record
             coordenadaX,
             coordenadaY : real
          end;
  

Ejemplo(3)

Los números racionales pueden representarse así:


 type
      TSigno   = (mas,menos);
      natural  = 0..MaxInt;
      positivo = 1..MaxInt;
   
       racional = record
           signo       : TSigno;
           numerador   : natural;
           denominador : positivo;
      end;


Acceso a campos de registros

El operador . (punto) seguido del nombre del campo se utiliza para acceder a los componentes de un registro.


var q : racional
    ...
    q.signo:= mas;
    q.numerador:= 4;
  
    WriteLn(q.denominador);
    ...

Lectura de un registro

No es posible aplicarle read a una variable de un tipo registro.

procedure LeerRacional(var q : racional);
var
   signo : -1..+1;
begin
   Write('Ingrese signo(-1,0,+1): ');
   ReadLn(signo);
   if signo = -1 then
      q.signo:= menos
   else
      q.signo:= mas;
   Write('Ingrese denominador: ');
   ReadLn(q.denominador);
   Write('Ingrese numerador: ');
   ReadLn(q.numerador);
end; {LeerRacional}

Escritura de un registro

Para mostrar un registro se debe desplegar campo por campo.

procedure MostrarRacional(q :racional);
begin
   if q.signo = menos
      then Write('-');      (* signo *)
       
   Write(q.numerador);      (* numerador *)
   
   Write('/');              (* separador *)
   
   Write(q.denominador);    (* denominador *)
end; {MostrarRacional}

Ejemplo

La suma de dos racionales.

function SignoRac(q: racional): integer;
begin
  case q.signo of
     mas   : SignoRac:= +1;
     menos : SignoRac:= -1;
  end
end; {SignoRac}
procedure SumaRacionales(p,q: racional; var resultado: racional);
var 
  num: integer;
begin
   resultado.denominador:= q.denominador * p.denominador;
   
   num:= SignoRac(p) * p.numerador * q.denominador
         +
         SignoRac(q) * q.numerador * p.denominador;
   
   resultado.numerador:= abs(num);
   
   if num < 0 then
      resultado.signo:= menos
   else
      resultado.signo:= mas
end; {SumaRacionales}

El tipo Fecha

No existe un tipo fecha predefinido en Pascal.

type
   TFecha = record
       anio: 0..10000;
       mes : 0..12;
       dia : 1..31;
   end;

Comparación de Fechas

function AnteriorFecha(f1,f2: TFecha) : boolean;
begin
   if f1.anio = f2.anio then
      if f1.mes = f2.mes then
         AnteriorFecha:= f1.dia < f2.dia
      else
         AnteriorFecha:= f1.mes < f2.mes
   else
      AnteriorFecha:= f1.anio < f2.anio;
end; {AnteriorFecha}

La instrucción with

La instrucción with permite simplificar la forma de referenciar los campos de un registro.


procedure MostrarRacional(q :racional);
begin
   with q do
   begin
       if signo = menos
          then Write('-');    (* signo *)       
       Write(numerador);      (* numerador *)
       Write('/');            (* separador *)
       Write(denominador);    (* denominador *)
   end; {with} 
end; {MostrarRacional}

Ambigüedad con with

var
  q    : racional;
  signo: integer;
  ...
   with q do
   begin
     ...
     signo:= 1;  (* error!  representa q.signo *)
     ...
     signo:= mas; (* correcto *)
     ...
   end;

Asignación de registros

Una variable de tipo registro se puede asignar a otra del mismo tipo.

var 
  p1,p2: punto;
   ...
   p1 := p2;  (* asignación campo a campo *)
   ...

Estructuras complejas

Anidamiento de tipos estructurados.

type
   TFechas = array [1..Max] of fecha;
   ...
begin
   a[7].anio := 1968;  (* campo anio de la séptima celda *)
   ...

Registros variantes Motivación

Se quiere representar una entidad que puede pertenecer a diferentes categorías.

Según la categoría hay diferentes datos.

Existe un conjunto de datos comunes a todas las categorías (eventualmente vacío). Ejemplo

Categorías:


     Estudiante:
         
           Año de Ingreso
         
           Cantidad de materias
   
     Docente
         
           Carga horaria
         
           Grado
   
     Egresado
         
           Año de egreso
         
           Título

Datos comunes a todas las categorías:


     Cédula.
   
     Credencial cívica.

Ejemplo (cont)

type
   TOrden = (docente,estudiante,egresado);
   ...
   TUniveritario = record           
       cedula     : TCedula;
       credencial : TCredencial;
       case orden:TOrden of
       docente : (
           grado: 1..5;
           carga: 0..40
       );
       estudiante : (
           semestre: 1..15;
           materias: integer
       );
       egresado : (
           egreso: 1900..3000;
           titulo: TTitulo
       );
   end; 
   

Ejemplo. Figuras

const
  MaxFig =  30;
type
     RGBColor = record
                  red,
                  green,
                  blue  : 0..255;
                end;    
     TipoFigura = (circulo,cuadrado,rectangulo);
     punto = record 
               x,y: real;
             end;
             
     figura = record
       color: RGBColor;
       case clase: TipoFigura of
         circulo : (
           radio: real;
           centro: punto
         );
         cuadrado: (
           lado: real;
           verticeSupIzq: punto
         );
         rectangulo: (
           base,altura: real;
           verticeInfIzq: punto
         );
     end;

Creando una figura

Para crear una figura se deben asignar los campos correspondientes de acuerdo a la categoría.

(* creación de un rectángulo *)

   r.color.red:= 10;
   r.color.green:= 121;
   r.color.blue:= 203;
   
   r.clase:= rectangulo;
   
   r.base:= 12.4;
   r.altura:= 345.90;
   
   r.verticeInfIzq.x:= 0.9;
   r.verticeInfIzq.y:= 19.78;

Área de una figura

El campo discriminante es el que permite identificar cada categoría.

function areaFigura(fig :  Figura): real;
begin
  with fig do begin
     case fig.clase of
       circulo    : areaFigura := PI * sqr(radio);
       rectangulo : areaFigura:= base * altura;
       cuadrado   : areaFigura:= sqr(lado);
     end; { case }
  end; { with }
end; {areaFigura}

Comparación de posición


function masArriba(fig: figura; alt: real): boolean;
begin
  with fig do begin
     case clase of
       circulo    : masArriba:= (centro.y - radio) > alt;
       rectangulo : masArriba:= verticeInfIzq.y  > alt;
       cuadrado   : masArriba:= (verticeSupIzq.y - lado) > alt;
     end; { case }
  end; {with}
end; {masArriba}

Array con tope Definición

Otra posible representación de conjuntos es la siguiente:

       Type 
   
         Conjunto = record
            elems : array [1..N] of T0;
            tope  : 0..N
         end;

donde ahora T0 no necesita ser un tipo escalar. Solo se requiere que los valores de tipo T0 sean comparables por igualdad. Cómo funciona


     El array elems almacena los elementos del conjunto.
   
     Los elementos se almacenan en las celdas 1 a tope
   
     El campo tope apunta a la última posición ocupada del array.
   
     El conjunto vacío se representa mediante el campo tope en 0.
   
     Pueden representarse conjuntos que contengan de 0 a N elementos

Crear conjunto vacío

type
    Conj = record
             elems : array [1..N] of T;
             tope : 0..N
           end;
Procedure CrearConjuntoVacio(var S : Conj);
begin
   S.tope := 0
end; 

Insertar un elemento

procedure Insertar(e : T; var S : Conj);
{ pre condicion: (e no pertenece a S) y (S.tope < N)}
begin
   with S do 
   begin
       tope := tope + 1;
       elems[tope] := e
   end;
end;

Eliminar un elemento

Si el elemento no está no hace nada.

procedure Eliminar(e : T; var S : Conj);
var i : integer;
begin
  i := 1;
  { evaluacion por circuito corto }
  while (i <= S.tope) and (S.elems[i] <> e) do         
     i : = i + 1;
     
  if i <= S.tope then
  begin
    S.elems[i] := S.elems[tope];
    S.tope := S.tope - 1
  end
end;

Búsqueda de un elemento

function pertenece(e : T; S : Conj) : boolean;
var
   i: integer;
begin
   i:= 1;
  { evaluacion por circuito corto }
  while (i <= S.tope) and (S.elemns[i] <> e) do
     i:= i+1;
     
  pertenece:= i<=S.tope;
end;