Programación en Pascal/Los tipos básicos

En todos los lenguajes de programación hay que manejar tipos de datos. Puede que en algunos lenguajes el uso sea más explícito que en otros (por ejemplo, en Pascal hay que declararlos y en Python no), pero está claro que aún así el programador tiene que saber que tipo de datos está manejando en cada momento.

Como en la vida real, no es correcto mezclar unos tipos con otros. Por ejemplo, no es correcto mezclas las operaciones que puedes hacer con la fruta que las operaciones que puedes hacer con los automóviles... son dos cosas muy distintas. Aún hay cosas que puedes hacer que son similares como moverlas, comprarlas, etc, pero hay algunos principios que las diferencian, que son las operaciones que puedes hacer con uno que no puedes hacer con otro. Así por ejemplo, es normal comer fruta, pero no lo es tanto comer coches. Tampoco es normal conducir naranjas. ;-)

De la misma manera, en cualquier lenguaje, no es recomendable mezclar números con cadenas de caracteres (strings), con clases, etc, a menos que se quiera convertir de uno a otro. Aún así, Pascal permite esas mezclas, siempre y cuando definas la forma exacta en que pueden ser realizadas (esto es bastante avanzado, y se tratará en el apartado de Object Pascal sobre polimorfismo y sobrecarga de operadores).

Algunos lenguajes como Python, Perl, Ruby y Lisp, son más flexibles a la hora de mezclar tipos. Pero ello trae como consecuencia errores inesperados en tiempo de ejecución, las dichosas "excepciones". No obstante, y a pesar de esa dificultad inicial, saber y controlar que tipos de datos usas en cada momento te hará un mejor programador, y a la larga, no solo reportará en programas con menos errores y más rápidos, sino también en mejores programas porque sabes mejor que estás haciendo.

Los tipos numéricos

editar

En Pascal existen varios tipos de números, con diferentes tamaños (espacio que ocupan en memoria), y diferentes capacidades (el número máximo y mínimo y la precisión que pueden manejar). Estos son:

Tipo Rango Bits Precisión
Shortint -128..127 8 entero
Integer -32768..32767 16 entero
Longint -2147483648..2147483647 32 entero
Byte 0..255 8 entero
Word 0..65535 16 entero
Cardinal 0..2147483647 32 entero
Real   ..   48 36bits
Single   ..   32 24bits
Double   ..   64 48bits
Extended   ..   80 60bits
Comp -9223372036854775809..9223372036854775807 64 entero

Como puede comprobarse, hay un amplísimo rango de tipos númericos entre los que elegir. La cuestión es, precisamente, ¿cuál elegir?

La respuesta es: depende.

Normalmente se intentará elegir el tipo de dato más pequeño con el que sea suficiente hacer las operaciones para las que se esté programando en ese momento. Como normal general, un tipo numérico pequeño proporcionará más rápidez y ahorro de memoria. Sin embargo, y debido a como funcionan las actuales computadoras de 32 y 64 bits, es posible que el compilador decida utilizar 32 bits para un tipo de datos de 16 bits, sin que el programador se entere realmente. Esto es debido a que las operaciones con tipos de datos alienados en direciones equivalentes a las del procesador en uso suelen estar más optimizadas. No conviene abusar de los tipos de datos de mayor tamaño, como el Double y el Extended, que son capaces de almacenar números de dimensiones astronómicas pero a cambio de ser lentos y ocupar mucha memoria. Estos tipos son solamente recomendables para aplicaciones científicas que exijan manejar esos números y precisiones.

 
Recuerda: no uses un tipo grande de números a menos que lo necesites.

Los tipos de datos de precisión no entera, tales como Real, Single, etc, empiezan a perder precisión cuando se sobrepasa el valor de sus bits de mantisa (en la tabla está marcado en la columna Precisión). Así, como por ejemplo, en 24 bits solo se puede almacenar un número de  , esto es, aproximadamente 16.7 millones, cuando se pasa de esos 16.7 millones la variable que contiene el número empieza a perder precisión y solo almacena un dato aproximado. Lo mismo sucede al sobrepasar cierto número de decimales. Cuanto mayor es la precisión, menos pérdidas de datos habrá. Recomiendo echar un vistazo al artículo en Wikipedia sobre coma flotante para comprender esto mejor.

Así mismo, hoy en día la velocidad de los tipos de coma flotante es tan rápida o casi como los tipos enteros. Sin embargo, usar tipos en coma flotante no está permitido en muchas partes del programa (por ejemplo, para indexar cadenas, como veremos más adelante), además de que, debido al redondeo, pueden producir serios bugs (errores) difíciles de detectar en algunos casos. Por lo tanto, se debe evitar su uso excepto para cuando realmente son útiles y de verdad se necesiten decimales.

Ejemplos práctico de uso de tipos numéricos

editar

Es interesante comprobar hasta que punto se pueden mezclar unos datos numéricos con otros, y como los imprime writeln en la consola. Veamos el ejemplo de la lección anterior, retocado con nuevos tipos:

program leccion2;

var
  numero1 : Integer;
  numero2 : Byte;
  resultado : Double;

begin
  numero1 := 5;
  numero2 := 6;
  resultado := numero1 + numero2;
  writeln (resultado);
end.

Al ejecutarlo, tenemos esto:

1.10000000000000E+001

¿Por qué?

Porque la variable resultado es de tipo Double, con todos esos decimales de precisión. El E+001 del final significa "por 10 elevado a más 1". Efectivamente, es lo mismo que   o lo que es lo mismo, simplemente 11.

Este ejemplo muestra bien como Pascal es capaz de convertir unos números en otros. Al fin y al cabo, se ha mezclado un Integer de 32bits con un Byte de solo 8bits, y ha puesto el resultado en un Double de 64bits en coma flotante.

Pero no siempre vamos a ser afortunados. Si el tipo de dato en donde quieres almacenar el resultado tiene un rango más pequeño que del que extraes el valor y es entero, entonces la conversión es, como es lógico, no correcta. Por ejemplo, ¿cómo almacenarías el valor 48484 en un byte? No se puede, un byte solo almacena números hasta 255, como se ha visto en la anterior tabla.

Prueba el mismo programa, modificado:

program leccion2;

var
  numero1 : Integer;
  numero2 : Real;
  resultado : Byte;

begin
  numero1 := 5;
  numero2 := 6;
  resultado := numero1 + numero2;
  writeln (resultado);
end.

Inevitablemente obtienes un error:

leccion2.pas(11,24) Error: Incompatible types: got "Real" expected "Byte"
leccion2.pas(15) Fatal: There were 1 errors compiling module, stopping
 
Recuerda: no mezcles tipos de datos a menos que estés absolutamente seguro que no habrá desbordamiento de rango.

Sin embargo, si eres libre de convertir un Double en un Single, ya que ambos son en coma flotante. Eso si, es muy posible que obtengas una "bonita" excepción de desbordamiento de operación, en tiempo de ejecución, si sobrepasas los límites de rango mostrados en la tabla anterior. Pruébalo para comprobarlo:

program leccion2;

var
  numero1 : Double;
  numero2 : Real;
  resultado : Single;

begin
  numero1 := 78e40;
  numero2 := 6;
  resultado := numero1 + numero2;
  writeln (resultado);
end.

El terrible resultado:

Runtime error 207 at $080480D0
  $080480D0  main,  line 12 of leccion2.pas
  $08061407


 
Recuerda: asegúrate de no pasarte de su rango u obtendrás una excepción que se presentará en el momento menos apropiado (generalmente cuando tu programa esté ya en la calle).

Instrucciones matemáticas permitidas en pascal

editar
Operador Operación Válido entre
+ adición enteros y reales
- resta enteros y reales
* multiplicación enteros y reales
/ división enteros y reales
div división entera enteros
mod resto enteros
<> diferenciación enteros y reales

Por ejemplo:

program leccion3;

var
  PI : Single;
  r : Single;

begin
  PI := 3.14156;
  r := 6.7;
  write ('La circunferencia es:');
  writeln (2 * PI * r);
end.

Obtenemos:

La circunferencia es: 4.209690396E+01

Nota como hemos puesto el resultado directamente en la llamada a la función writeln. También nota como hay otra nueva función: write. Es igual que writeln pero no escribe un retorno de carro. De esa manera, lo siguiente que se escriba saldrá a continuación.

Los tipos char y string

editar

Hasta ahora, hemos estado usando caracteres y cadenas de caracteres (strings) directamente, sin asignarlos a variables, para la función writeln. Pero se pueden usar en variables, y hacer algunas operaciones con ellos:

program leccion3;

var
  nombre : string;
  resultado : string;

begin
  write('Dime tu nombre: ');
  readln(nombre);
  resultado := 'Hola ' + nombre + ', ¿qué tal estás?';
  writeln (resultado);
end.

Al ejecutarlo, el programa espera que introduzcas un nombre y pulses intro. A continuación, y ofrece lo siguiente:

Dime tu nombre: Javier
Hola Javier, ¿qué tal estás?

readln es por lo tanto una función que sirve para que el usuario del programa introduzca datos, estos se guardan en la variable especificada, y con esa variable se hacen las operaciones oportunas, en este caso, concatenar (sumar) cadenas de caracteres.

El tipo char es simplemente un carácter solo. El tipo string es una cadena (array) de caracteres. Más adelante aprenderemos a manejar cadenas.

Quizá te estés preguntando: ¿puedo introducir números con readln? Prueba:

program leccion3;

var
  r : Single;
  resultado : Single;

begin
  write('Dime el radio del círculo: ');
  readln(r);
  resultado := 2 * 3.14 * r;
  writeln (resultado);
end.

Si lo ejecutas te pide el radio del círculo e imprime el resultado. Desgraciadamente también si lo que escribes no es un número te premiará con una bonita excepción. Aprenderás un poco más adelante a hacer las conversiones de forma más segura.

¿Y qué sucede si sumas un string y un número? Prueba:

program leccion3;

var
  r : Single;
  resultado : string;

begin
  write('Dime el radio del círculo: ');
  readln(r);
  resultado := 'El resultado es: ' + 2 * 3.14 * r;
  writeln (resultado);
end.

Pues no es de extrañar que suceda lo que tiene que suceder:

leccion2.pas(10,36) Error: Operator is not overloaded
leccion2.pas(14) Fatal: There were 1 errors compiling module, stopping

No sé pueden mezclar naranjas con coches... o quizá si. Más en la siguiente sección:

Conversión de tipos

editar