Tema 9: Otros tipos de datos.
Curso: Curso de Pascal, por Nacho Cabanes
Tema 9: Otros tipos de datos.
Comenzamos a ver los tipos de datos que podíamos manejar en el tema 2. En aquel momento tratamos los siguientes:
- Byte. Entero, 0 a 255. Ocupa 1 byte de memoria.
- Integer. Entero con signo, -32768 a 32767. Ocupa 2 bytes.
- Char. Carácter, 1 byte.
- String[n]. Cadena de n caracteres (hasta 255). Ocupa n+1 bytes.
- Real. Real con signo. De 2.9e-39 a 1.7e38, 11 o 12 dígitos significativos, ocupa 6 bytes.
- Boolean. TRUE o FALSE.
- Array. Vectores o matrices.
- Record. Con campos de distinto tamaño.
Pues esta vez vamos a ampliar con otros tipos de datos. :
- Enteros.
- Correspondencia byte-char.
- Reales del 8087.
- Tipos enumerados.
- Más detalles sobre Strings.
- Registros variantes.
- Conjuntos.
Vamos allá:
Comencemos por los demás tipos de números enteros.
Estos son:
- Shortint. Entero con signo, de -128 a 127, ocupa 1 byte.
- Word. Entero sin signo, de 0 a 65535. Ocupa 2 bytes.
- Longint. Con signo, de -2147483648..2147483647. Ocupa 4 bytes.
Estos tipos, junto con "char" (y "boolean" y otros para los que
no vamos a entrar en tanto detalle) son tipos ordinales, existe
una relación de orden entre ellos y cada elemento está precedido
y seguido por otro que podemos conocer (cosa que no ocurre en los reales).
Para ellos están definidas las funciones:
- pred - Predecesor de un elemento : pred(3) = 2
- succ - Sucesor: succ(3) = 4
- ord - Número de orden (posición) dentro de todo el conjunto.
El uso más habitual de "ord" es para convertir de "char"
a "byte". Los caracteres se almacenan en memoria de tal forma que
a cada uno se le asigna un número entre 0 y 255, su "código
ASCII" (ej: A=65, a=96, 0=48, _=178). La forma de hallar el código
ASCII de una letra es simplemente ord(letra), como ord('_').
El paso contrario, la letra que corresponde a cierto número, se hace con la función "chr". Así, podemos escribir los caracteres "imprimibles" de nuestro ordenador sabiendo que van del 32 al 255 (los que están por debajo de 32 suelen ser caracteres de control, que en muchos casos no se podrán mostrar en pantalla; en algunos sistemas ocurre lo mismo con los caracteres que están por encima del 127):
var
bucle: byte;
begin
for bucle := 32 to 255 do
write( chr(bucle) );
end.
Si tenemos coprocesador matemático, podemos utilizar también los siguientes tipos de números reales del 8087:
Nombre | Rango | Dígitos | Bytes |
single | 1.5e-45 a 3.4e38 | 7-8 | 4 |
double | 5.0e-324 a 1.7e308 | 15-16 | 8 |
extended | 3.4e-4932 a 1.1e4932 | 19-20 | 10 |
comp | -9.2e18 a 9.2e18 | 19-20 | 8 |
El tipo "comp" es un entero (no real, sin decimales) de 8 bytes, que sólo tenemos disponible si usamos el coprocesador.
Nosotros podemos crear nuestros propios tipos de datos enumerados:
type DiasSemana = (Lunes, Martes, Miercoles, Jueves, Viernes,
Sabado, Domingo);
Declaramos las variables igual que hacíamos con cualquier otro tipo:
var dia: DiasSemana;
Y las empleamos como siempre: podemos darles un valor, utilizarlas en comparaciones, etc.
begin
dia := Lunes;
[...]
if dia = Viernes then
writeln( 'Se acerca el fin de semana!' );
[...]
Los tipos enumerados también son tipos ordinales, por lo que podemos usar pred, succ y ord con ellos. Así, con los datos del ejemplo anterior tendríamos
pred(Martes) = Lunes, succ(Martes) = Miercoles, ord(Martes) = 1
(el número de orden de Martes es 1, porque es el segundo elemento, y se empieza a numerar en cero).
Nota: también podemos definir los valores que puede tomar una variable, indicándolo en forma de subrango, al igual que se hace con los índices de un array:
var
dia: 1..31;
Volvamos a los strings. Habíamos visto que se declaraban de la forma "string[n]" y ocupaban n+1 bytes (si escribimos sólo "string", es válido en las últimas versiones de Pascal y equivale a "string[255]"). ¿Por qué n+1 bytes? Pues porque también se guarda la longitud de la cadena.
Ya que estamos, vamos a ver cómo acceder a caracteres individuales
de una cadena. Nada más fácil. A lo mejor a alguien
la definición anterior, indicando el tamaño entre corchetes
le recuerda a la de un Array. Así es. De hecho, la definición
original en Pascal del tipo String[x] era "Packed Array[1..x] of char"
("packed" era para que el compilador intentase "empaquetar" el array,
de modo que ocupase menos; esto no es necesario en Turbo Pascal).
Así, con nombre[1] accederíamos a la primera letra del nombre,
con nombre[2] a la segunda, y así sucesivamente.
Una última curiosidad: habíamos dicho que se guarda también
la longitud de la cadena. ¿Donde? Pues en la posición
0. Va programita de ejemplo:
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Longitud de una }
{ cadena; posiciones }
{ POSCAD.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.2.0w }
{ - Turbo Pascal 7.0 }
{ - Turbo Pascal 5.0 }
{ - Surpas 1.00 }
{--------------------------}
program PosCad;
var
linea: string [20]; (* Cadena inicial: limitada a 20 letras *)
pos: byte; (* Posición que estamos mirando *)
begin
writeln( 'Introduce una línea de texto...' );
readln( linea );
for pos := 1 to ord(linea[0]) do
writeln(' La letra número ', pos,' es una ', linea[pos]);
end.
Comentarios:
- "linea[0]" da la longitud de la cadena, pero es un carácter, luego debemos convertirlo a byte con "ord".
- Entonces, recorremos la cadena desde la primera letra hasta la última.
- Si tecleamos más de 20 letras, las restantes se desprecian.
Tema 9.2: Otros tipos de datos (2).
También habíamos visto ya los registros (records), pero con unos campos fijos. 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. {--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Registros variantes }
{ REGVAR.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - Free Pascal 2.2.0w }
{ - Turbo Pascal 7.0 }
{ - Turbo Pascal 5.0 }
{ - Surpas 1.00 }
{ - Tmt Pascal Lt 1.01 }
{--------------------------}
program RegistrosVariantes;
type
TipoDato = (Num, Fech, Str);
Fecha = record
D, M, A: Byte;
end;
Ficha = record
Nombre: string[20]; (* Campo fijo *)
case Tipo: TipoDato of (* Campos variantes *)
Num: (N: real); (* Si es un número: campo N *)
Fech: (F: Fecha); (* Si es fecha: campo F *)
Str: (S: string[30]); (* Si es string: campo S *)
end;
var
UnDato: Ficha;
begin
UnDato.Nombre := 'Nacho'; (* Campo normal de un record *)
UnDato.Tipo := Num; (* Vamos a almacenar un número *)
UnDato.N := 3.5; (* que vale 3.5 *)
Writeln('Ahora el tipo es numérico, y el nombre es ',
UnDato.Nombre, '.');
Writeln('El campo N vale: ', UnDato.N);
UnDato.Nombre := 'Nacho2'; (* Campo normal *)
UnDato.Tipo := Fech; (* Ahora almacenamos una fecha *)
UnDato.F.D := 7; (* Día: 7 *)
UnDato.F.M := 11; (* Mes: 11 *)
UnDato.F.A := 93; (* Año: 93 *)
Writeln('Ahora el tipo es Fecha, y el nombre es ',
UnDato.Nombre, '.');
Writeln('El campo F.D (día) vale: ', UnDato.F.D);
UnDato.Nombre := 'Nacho3'; (* Campo normal *)
UnDato.Tipo := Str; (* Ahora un string *)
UnDato.S := 'Nada'; (* el texto "Nada" *)
Writeln('Ahora el tipo es string, y el nombre es ',
UnDato.Nombre, '.');
Writeln('El campo S vale: ', UnDato.S);
end.
Tema 9.3: Otros tipos de datos (3).
Finalmente, tenemos los conjuntos (sets). Un conjunto está formado por una serie de elementos de un tipo base, que debe ser un ordinal de no más de 256 valores posibles, como un "char", un "byte" o un enumerado.
type
Letras = set of Char;
type DiasSemana = (Lunes, Martes, Miercoles, Jueves, Viernes,
Sabado, Domingo);
Dias = set of DiasSemana;
Para construir un "set" utilizaremos los corchetes ([]), y dentro de ellos enumeramos los valores posibles, uno a uno o como rangos de valores separados por ".." :
var
LetrasValidas : Letras;
Fiesta : Dias;
begin
LetrasValidas = ['a'..'z', 'A'..'z', '0'..'9'];
Fiesta = [ Sabado, Domingo ];
end.
Un conjunto vacío se define con [ ].
Las operaciones que tenemos definidas sobre los conjuntos son:
Operac | Nombre |
+ | Unión |
- | Diferencia |
* | Intersección |
in | Pertenencia |
Así, podríamos hacer cosas como
VocalesPermitidas := LetrasValidas * Vocales;
if DiaActual in Fiesta then
writeln( 'No me diras que estas trabajando... ;-D ' );
En el primer ejemplo hemos dicho que el conjunto de vocales permitidas (que deberíamos haber declarado) es la intersección de las vocales (que también debíamos haber declarado) y las letras válidas.
En el segundo, hemos comprobado si la fecha actual (que sería de tipo DiasSemana) pertenece al conjunto de los días de fiesta.
Vamos a ver un ejemplito sencillo "que funcione":
{--------------------------}
{ Ejemplo en Pascal: }
{ }
{ Conjuntos (sets) }
{ EJSET.PAS }
{ }
{ Este fuente procede de }
{ CUPAS, curso de Pascal }
{ por Nacho Cabanes }
{ }
{ Comprobado con: }
{ - FreePascal 2.0.2 }
{--------------------------}
program EjSet;
var
minusculasValidas,
mayusculasValidas,
letrasValidas: set of char;
letra: char;
begin
minusculasValidas := ['a', 'b', 'c', 'd'];
mayusculasValidas := ['F', 'H', 'K', 'M'];
letrasValidas := minusculasValidas
+ mayusculasValidas;
repeat
writeln( 'Introduce una letra...' );
readln( letra );
if not (letra in letrasValidas) then
writeln('No aceptada!');
until letra in letrasValidas;
end.
Pues eso es todo por hoy. Si es denso (eso creo), pues a releerlo un par de veces y a experimentar...
No propongo ejercicios porque es un tema árido y habrá
gente que no use casi nada de esto. Quien le vea la utilidad, que
se los proponga él sólo según la aplicación
que les quiera dar. De todas formas, a medida que vayamos haciendo
programas más grandes, iremos usando más de una de las cosas
que hemos visto hoy.
Actualizado el: 15-04-2006 17:40