AnteriorPosterior

8. Uso y creación de unidades

  Curso: Programación en Pascal (v5), por Nacho Cabanes

8.1. Uso de unidades existentes

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:

  • ClrScr, para borrar la pantalla.
  • TextColor, para cambiar el color en que se escribirá el texto.
  • TextBackground, para cambiar el color de fondo.
  • GotoXY, para mover el cursor a ciertas coordenadas.
  • Delay, para detener el programa una cierta cantidad de milisegundos.
  • ReadKey, para detener el programa hasta que se pulse cualquier tecla (y saber qué tecla se había pulsado).
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.

8.2. Creación de unidades

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:

  • La primera es que los programas serán más modulares. Podremos dejar aparte las funciones que se encargan de controlar el teclado, por ejemplo, y en nuestro programa principal sólo estará lo que realmente tenga este programa que lo diferencie de los otros, la "lógica de negocio". Esto facilita la legibilidad del programa y también las posibles correcciones o ampliaciones.
  • La segunda ventaja es que no tendremos distintas versiones de los mismos procedimientos o funciones. Esto ayuda a ganar espacio en el disco duro, pero eso es lo menos importante. Lo realmente interesante es que si se nos ocurre una mejora para un procedimiento, todos los programas que lo usen se van a beneficiar de él automáticamente sólo con recompilar.

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:

  • Al compilar una unidad se crea un fichero con extensión .TPU (.PPU para Free Pascal). Ese fichero se podría usar desde nuestros programas, aunque no tengamos todo el código fuente la "unit", pero sólo en caso de que se cumplan dos condiciones: que empleemos la misma versión de compilador (porque el formato de estos ficheros puede ser distinto si se ha creado con versiones distintas del compilador, por ejemplo con Turbo Pascal 7 en vez de Turbo Pascal 5.5)), y que sepamos cómo era la parte pública (interface) de esa unidad.
  • En Turbo Pascal 7 para MsDos, cada unidad tiene su propio segmento de código, así que cada unidad puede almacenar hasta 64k de procedimientos o funciones, lo que permite crear programas más grandes (para MsDos) que cuando no usamos unidades. Por el contrario, los datos son comunes a todas las unidades, con la limitación 64k en total (un segmento de memoria) para todos los datos (estáticos) de todo el programa. Si queremos almacenar datos de más de 64k en un programa de Turbo Pascal para MsDos, tenga una o más unidades, deberemos emplear variables dinámicas, que veremos más adelante.
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.

Actualizado el: 21-07-2014 00:28

AnteriorPosterior