La mayoría de lenguajes de programación modernos nos permiten incluir una serie de bibliotecas externas (en ingles, library) de funciones y procedimientos, que amplían las posibilidades del lenguaje base.
En Turbo Pascal, estas bibliotecas reciben el nombre de "unidades"
(unit), y existen a partir de Turbo Pascal 5. También existen en otras
versiones de Pascal recientes, como Free Pascal.
Por ejemplo, una de las "unidades" básicas que incluyen Turbo Pascal y Free Pascal es la unidad CRT, que nos da una serie de facilidades para manejar la pantalla en modo texto, el teclado y la generación de sonidos sencillos. Vamos a ver un primer ejemplo de su uso, y más adelante profundizaremos en las posibilidades adicionales que nos aporta:
(* CRT1.PAS, Ejemplo simple de la unidad CRT *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program Crt1;
uses crt;
var
tecla : char;
begin
ClrScr; { Borra la pantalla }
TextColor( Yellow ); { Color amarillo }
TextBackground( Red ); { Fondo azul }
GotoXY( 40, 12 ); { Vamos al centro de la pantalla }
Write('Hola... '); { Saludamos }
Delay( 2000 ); { Esperamos dos segundos }
WriteLn( 'Pulse una tecla... ');
tecla := ReadKey; { Esperamos que se pulse una tecla }
GotoXY( 1, 24 ); { Vamos a la penúltima línea }
Write( 'Ha pulsado ', tecla ); { E informamos de la tecla pulsada }
end.
Como se puede ver en este ejemplo, esta "unidad CRT" nos añade algunas órdenes adicionales, que no tendríamos si no incluyéramos esa biblioteca adicional (y que pueden no estar disponibles en otros compiladores de Pascal, que no incluyan dicha biblioteca). Entre esas órdenes están:
Ejercicio propuesto 8.1.1: Crea un programa emplee la biblioteca CRT y un bucle "for" para mostrar el texto "Hola" usando todos los colores (desde el 1 hasta el 15).
Ejercicio propuesto 8.1.2: Crea un programa emplee la biblioteca CRT para crear un procedimiento "EscribirTeletipo", que recibirá como parámetros una coordenada X, una coordenada Y, un texto, y escribirá el texto en esas coordenadas, letra a letra, haciendo una pausa de 100 milisegundos entre cada letra y la siguiente.
Ejercicio propuesto 8.1.3: Crea un programa emplee la biblioteca CRT y la orden GotoXY para dibujar un rectángulo hueco, cuyo borde sean letras X, cuya anchura sea 10 y cuya altura sea 5. Debe estar a 4 líneas del borde superior de la pantalla y a 12 columnas del borde izquierdo.
Ahora vamos a ver cómo podemos crear nuestras propias bibliotecas.
¿Para qué? Nos podría bastar con teclear en un programa todas las funciones que nos interesen. Si creamos otro programa que las necesite, pues las copiamos también en ese y ya está... ¿no?
No es la forma ideal de trabajar. El hecho de emplear unidades nos ayuda a conseguir dos cosas:
Por ejemplo, imaginemos que estamos haciendo un programa de rotación de objetos en tres dimensiones. Creamos nuestra biblioteca de funciones, y la aprovechamos para todos los proyectos que vayamos a hacer en tres dimensiones. No solo evitamos reescribir en cada programa el procedimiento RotaPunto (y otros muchos) que ahora se tomará de nuestra unidad "MiGraf3D", sino que, además, si descubrimos una forma más rápida de rotar puntos, todos los programas que utilicen el procedimiento RotaPunto se verán beneficiados en cuanto los recompilemos.
Vamos a lo práctico...
Una "unit" tiene dos partes: una pública, que es aquella a la que podremos acceder, y una privada, que es el desarrollo detallado de los procedimientos y funciones, y a esta parte no se puede acceder desde otros programas.
La parte pública se denota con la palabra "interface", y la privada con "implementation".
Debajo de interface basta indicar los nombres de los procedimientos que queremos "exportar", así como las variables públicas, si nos interesase crear alguna. Debajo de implementation escribimos realmente estos procedimientos o funciones, con todas sus órdenes, tal como haríamos en un programa normal.
Vamos a ver un ejemplo:
(* NCRT1.PAS, Ejemplo de unidad *)
(* Parte de CUPAS5, por Nacho Cabanes *)
unit nCrt1;
interface { Parte "pública", que se exporta }
{ Escribir un texto en ciertas coordenadas }
procedure EscribeXY( X, Y: byte ; texto: string );
{ ----------------------------------}
implementation { Parte "privada", detallada }
uses crt; { Usa a su vez la unidad CRT }
procedure EscribeXY( X, Y: byte ; texto: string );
begin
GotoXY( X, Y );
Write( texto );
end;
end. { Final de la unidad }
Cuidado: este ejemplo no se puede ejecutar. Hay que recordar que una Unit es algo auxiliar, una biblioteca de funciones y procedimientos que nosotros utilizaremos desde otros programas, como el que veremos en un instante.
Esta "unit" declara un procedimiento "EscribeXY", que escribe en unas ciertas coordenadas de pantalla (usando primero un GotoXY y luego un Write).
Un programa que cargase esta "unit" y utilizase este procedimiento podría ser:
(* USONCRT1.PAS, Ejemplo de uso de la unidad nCrt1 *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program UsoNCrt1;
uses nCrt1;
begin
EscribeXY( 7, 5, 'Texto en la posición 7,5.' );
end.
Este programa no necesita llamar a la unidad CRT original, sino que nuestra unidad ya lo hace por él.
Ahora vamos a mejorar ligeramente nuestra unidad, añadiéndole un procedimiento "Pausa". Aprovecharemos (aunque no será necesario) para crear una variable dentro de la parte privada:
(* NCRT2.PAS, Segundo ejemplo de unidad *)
(* Parte de CUPAS5, por Nacho Cabanes *)
unit nCrt2;
{-------------------}
interface
procedure EscribeXY( X, Y: byte ; texto: string );
procedure Pausa;
{-------------------}
implementation
uses crt; { Usa a su vez la unidad CRT }
var
tecla: char; { variable privada: el usuario no
puede utilizarla }
procedure EscribeXY( X, Y: byte ; texto: string );
begin
GotoXY( X, Y );
Write( texto );
end;
procedure Pausa; { Pausa, llamando a ReadKey }
begin
tecla := ReadKey; { El valor de "tecla" se pierde }
end;
{-------------------}
end. { Final de la unidad }
Un programa que usase esta unidad, junto con la CRT original podría ser:
(* USONCRT2.PAS, Ejemplo de uso de la unidad nCrt2 *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program UsoNCrt2;
uses crt, nCrt2; { Usamos la nuestra y también la original }
begin
ClrScr; { De Crt }
EscribeXY( 7, 5, 'Texto en la posición 7,5.' ); { De nCrt2 }
pausa; { De nCrt2 }
end.
Finalmente, hay que destacar que las unidades pueden contener más cosas además de funciones y procedimientos: pueden tener un "trozo de programa", su código de inicialización, como por ejemplo:
(* NCRT3.PAS, Tercer ejemplo de unidad *)
(* Parte de CUPAS5, por Nacho Cabanes *)
unit miCrt3;
{-------------------}
interface
var
EraMonocromo: boolean; { Variable pública, el usuario puede
acceder a ella }
procedure EscribeXY( X, Y: byte ; texto: string );
procedure Pausa;
{-------------------}
implementation
uses crt;
var
tecla: char; { Variable privada: el usuario no
puede utilizarla }
procedure EscribeXY( X, Y: byte ; texto: string );
begin
GotoXY( X, Y );
Write( texto );
end;
procedure Pausa;
begin
tecla := ReadKey;
end;
{-------------------} { Inicialización }
begin
if lastmode = 7 then { Si el modo de pantalla era monocromo }
EraMonocromo := true { EraMonocromo será verdadero }
else EraMonocromo := false; { si no => falso }
end. { Final de la unidad }
y el programa podría usar la variable EraMonocromo, sin necesidad de haberla declarado:
(* USONCRT3.PAS, Ejemplo de uso de la unidad nCrt3 *)
(* Parte de CUPAS5, por Nacho Cabanes *)
program UsoNCrt3;
uses crt, nCrt3;
begin
ClrScr;
EscribeXY( 7, 5, 'Texto en la posición 7,5.' );
if not EraMonocromo then
EscribetXY ( 10, 10, 'Modo de color ' );
pausa;
end.
Para terminar, veamos otros dos detalles más sobre unidades, más avanzados pero que pueden resultar útiles en ocasiones puntuales:
Ejercicio propuesto 8.2.1: Crea una biblioteca llamada "TEXTOS", que incluya una función "IZQUIERDA(texto, n)", que devolverá la subcadena formada por las primeras N letras de una cadena de texto, y otra función "DERECHA(texto, n)", que devolverá la subcadena formada por las últimas N letras de la cadena de texto que se le indique como parámetro,
Ejercicio propuesto 8.2.2: Amplía la biblioteca TEXTOS con una función INVERTIR(texto), que devuelva invertida (de la última letra a la primera) la cadena de texto que se le pase como parámetro.