------------------------------------------------------------- 8.- Unidades Una cosa que no se menciono durante todo el texto para no preocupar al principiante es que un programa normal de pascal solo puede ocupar 64k de codigo y 64k en datos. Las unidades cumplen una doble funcion, permiten que un programa se divida en varias partes independientes y tambien permiten romper la barrera del limite del codigo, mas no asi de los datos que deben siempre limitarse a 64k (podemos sobrepasar los 64k de datos usando los punteros), aun cuando el codigo que exista en cada unidad debe tener una longitud menor de 64k se pueden usar muchas de ellas teniendo cientos de k ejecutandose. Antiguamente la modularidad se gestionaba unicamente atraves de los procedimientos incluidos. Estos permiten que un procedimiento se copie en cualquier parte del programas desde el cual a sido llamado. Veamos: {$I C:\MIPROC.SRC} lo cual hace que el archivo miproc.src se compile en el momento en que el editor encuentra esta directiva. El resultado real es que se usara el procedimiento en el lugar donde se puso la directiva de inclusion, el problema de los archivos incluidos es que no se puede superar el limite de los 64k de codigo. Usando las unidades se superan ampliamente a los includes puesto que podemos compilar muchas rutinas que se pueden considerar seguras y ya no tendremos que volver a compilarlas mas. Cuando llegue el momento, hacemos un enlace (link) muy rapido con el programa principal acelerando la ya superveloz velocidad de compilacion de turbo pascal, ademas si disponemos de suficiente memoria ram creando un disco virtual en ella (o usando smartdrive mejor!) y poniendo las unidades alli, la velocidad de la compilacion hara que se nos pongan los pelos de punta. Como tercera ventaja la modularidad que producen las unidades hacen los programas mas faciles de leer asi que un programa grande no deberia carecer de ellas aunque no superase los 64k. Una unidad viene a ser exactamente como un programa pero las unidades no pueden ejecutarse independientemente, pueden contener absolutamente todo lo que contiene un programa turbo pascal, contienen programa principal (seccion de inicializacion) procedimientos, funciones, constantes tipos y todo lo que puede tener un programa normal pero por lo general solo se colocan procedimientos dentro de ella. Para que vea el poder de abstraccion de los procedimientos y la modularidad que poseen las unidades les mostrare el programa principal uno de mis programas:uno que juega ajedrez. Veamos como es: PROGRAM AJEDREZ; USES DIBUJOS,RUTINAS,PIENSA; { USA LAS UNIDADES DIBUJOS } { RUTINAS Y PIENSA. } BEGIN INICIALIZACION; REPEAT IF NOT (GANA_MAQUINA OR TABLAS) THEN BEGIN ACTUALIZA_TABLERO; REPEAT PREGUNTA_JUGADA; UNTIL JUGADA_SEA_LEGAL; JUGADAS_ESPECIALES; ACTUALIZA_TABLERO; IF NOT (GANA_HUMANO OR TABLAS) THEN BEGIN PIENSA_RESPUESTA; ACTUALIZA_TABLERO; END; END; UNTIL (GANA_MAQUINA) OR (GANA_HUMANO) OR (TABLAS); MENSAJE_FINAL; END. Aparte de la linea que dice jugadas_especiales todo el programa parece escrito no para una computadora sino para un ser humano, y aun sin saber como funciona cada procedimiento ya se sabe como funciona el programa en general y siguiendolo se entiende perfectamente, de hecho se parece a un algoritmo bastante general, normalmente el mantenimiento de un programa construido de esta manera resulta sencillo. Teniendo una herramienta como esta, no utilizarla seria como cortarse un brazo. Vemos aqui la clausula uses mencionando dibujos,rutinas y piensa. Bueno cada una de estas unidades es grande (recuerde que el maximo tamanio de una unidad es 64k) y algunas casi se acercan a los 64k asi que no puedo listarlas por cuestion de espacio pero el programa completo se suministra con el disco que acompania al libro, en cada una existen muchos procedimientos por ejemplo en la unidad "dibujos" hay rutinas que dibujas las fichas el tablero, las mueven etc, en la unidad "rutinas" hay procedimientos generales de entrada de datos de busqueda y generacion de jugadas y finalmente la unidad endiablaba "piensa" que es donde la maquina genera su jugada. Aqui voy a poner lo necesario para comprender lo que es una unidad y algunas rutinas interesantes. UNIT RUTINAS; INTERFACE (* ------------------------------------------------------ *) (* EN ESTA PARTE SE NOMBRAN TODOS LOS PROCEDIMIENTOS Y *) (* Y FUNCIONES QUE SERAN CONOCIDOS Y POR CONSIGUIENTE *) (* PODRAN SER LLAMADOS DESDE FUERA DE LA UNIDAD, E *) (* INCLUSO DE CUALQUIER OTRA UNIDAD O DESDE EL PROGRAMA *) (* PRINCIPAL, ADEMAS LAS VARIABLES, CONSTANTES Y TIPOS *) (* QUE SE UTILIZAN AQUI PODRAN SER USADAS EN OTRO LUGAR, *) (* DE EXISTIR REPETICIONES VALDRA LO PRIMERO QUE HALLE *) (* EL COMPILADOR SEGUN LA DECLARACION DE USO DE UNIDADES *) (* (ORDEN DE USO EN LA SENTENCIA USES) *) (* ------------------------------------------------------ *) PROCEDURE INICIALIZACION; PROCEDURE PREGUNTA_JUGADA; PROCEDURE JUGADAS_ESPECIALES; PROCEDURE MENSAJE_FINAL; FUNCTION JUGADA_SEA_LEGAL : BOOLEAN; FUNCTION GANA_MAQUINA : BOOLEAN; FUNCTION GANA_HUMANO : BOOLEAN; FUNCTION ATACA_MAQUINA (CASILLA : BYTE) : BOOLEAN; FUNCTION ATACA_HUMANO (CASILLA : BYTE) : BOOLEAN; FUNCTION TABLAS : BOOLEAN; CONST TABLERO : ARRAY [0..119] OF BYTE = {AQUI SE DEFINE EL TABLERO} (10,10,10,10,10,10,10,10,10,10, { LISTA DE VALORES } 10,10,10,10,10,10,10,10,10,10, 10,04,02,03,05,06,03,02,04,10, {16,15=REY Y DAMA NEGROS } 10,01,01,01,01,01,01,01,01,10, {14,13=TORRE Y ALFIL NEGROS } 10,00,00,00,00,00,00,00,00,10, {12,11=CABALLO Y PEON NEGROS } 10,00,00,00,00,00,00,00,00,10, {10 =FUERA DEL TABLERO REAL} 10,00,00,00,00,00,00,00,00,10, {06,05=REY Y DAMA BLANCOS } 10,00,00,00,00,00,00,00,00,10, {04,03=TORRE Y ALFIL BLANCOS } 10,11,11,11,11,11,11,11,11,10, {02,01=CABALLO Y PEON BLANCOS} 10,14,12,13,15,16,13,12,14,10, {00 =CASILLA VACIA } 10,10,10,10,10,10,10,10,10,10, 10,10,10,10,10,10,10,10,10,10); MOVIDAS_CABALLO:ARRAY[1..8]OF SHORTINT= (8,12,19,21,-8,-12,-19,-21); MOVIDAS_REY :ARRAY[1..8]OF SHORTINT= (9,11,-9,-11,10,1,-10,-1); TYPE JUGADA = RECORD SALIDA : BYTE; LLEGADA : BYTE; EVALUACION : BYTE; END; VAR JUGADAS_POSIBLES : ARRAY [0..255] OF JUGADA; ENLARN,ENCORN,ENLARB,ENCORB : BOOLEAN; POSREB,POSREN,ALPASB,ALPASN, CASILLA_SALIDA,CASILLA_LLEGADA, NUM_MOV_LICITOS,SALIDA,LLEGADA : BYTE; IMPLEMENTATION (* ------------------------------------------------ *) (* EN ESTA SECCION SE TIENEN QUE COLOCAR TODOS LOS *) (* PROCEDIMIENTOS Y FUNCIONES QUE FUEROM NOMBRADOS *) (* EN LA SECCI N DE INTERFACE, M S ALGUNOS OTROS *) (* PROCEDIMIENTOS QUE NO SEAN CONOCIDOS EN NINGUN *) (* LUGAR APARTE QUE EN ESTA UNIDAD, ELLO PARA HACER *) (* MEJOR DISEĽO DE SOFTWARE, (FACILITA OCULTACI N *) (* DE LA INFORMACI N Y MEJORA LA INTERFAZ CON OTRAS *) (* UNIDADES). *) (* ------------------------------------------------ *) USES GRAPH,CRT,PIENSA; FUNCTION JUGADA_SEA_LEGAL : BOOLEAN; { SI LA JUGADA NO SE ENCUENTRA EN LA LISTA } VAR A : BYTE; { DE TODAS LAS JUGADAS POSIBLES ENTONCES } { DICHA JUGADA ES ILEGAL. } BEGIN JUGADA_SEA_LEGAL := FALSE; FOR A := 1 TO NUM_MOV_LICITOS DO IF (JUGADAS_POSIBLES [A].SALIDA = CASILLA_SALIDA) AND (JUGADAS_POSIBLES [A].LLEGADA=CASILLA_LLEGADA) THEN BEGIN JUGADA_SEA_LEGAL := TRUE; TABLERO [CASILLA_LLEGADA] := TABLERO [CASILLA_SALIDA]; TABLERO [CASILLA_SALIDA] := 0; WRITELN ('JUGADA LEGAL'); END; END; PROCEDURE MENSAJE_FINAL; { PARA FIN DEL PROGRAMA } BEGIN IF GANA_MAQUINA THEN WRITELN ('PERDISTE HUMANO'); IF GANA_HUMANO THEN WRITELN ('HE PERDIDO...'); IF TABLAS THEN WRITELN ('ESTO ES UN EMPATE'); END; FUNCTION GANA_MAQUINA : BOOLEAN; BEGIN GENERA_JUGADAS_HUMANO; IF NUM_MOV_LICITOS = 0 THEN GANA_MAQUINA := TRUE ELSE GANA_MAQUINA := FALSE; END; { FALTAN MUCHOS PROCEDIMIENTOS Y FUNCIONES QUE NO SE } { INCLUYERON POR FALTA DE ESPACIO. } PROCEDURE VER_SI_ES_LEGAL_MAQUINA; VAR FICHA_COMIDA : BYTE; BEGIN FICHA_COMIDA := TABLERO [LLEGADA]; TABLERO [LLEGADA] := TABLERO [SALIDA]; TABLERO [SALIDA] := 0; IF NOT ATACA_HUMANO (POSREN) THEN BEGIN INC (NUM_MOV_LICITOS); JUGADAS_POSIBLES [NUM_MOV_LICITOS].SALIDA := SALIDA; JUGADAS_POSIBLES [NUM_MOV_LICITOS].LLEGADA := LLEGADA; END; TABLERO [SALIDA] := TABLERO [LLEGADA]; TABLERO [LLEGADA] := FICHA_COMIDA; END; (*-------------------------------------------------------------*) (* LA FUNCION QUE SIGUE NO PUEDE SER LLAMADA DESDE EL PROGRAMA *) (* PRINCIPAL DEBIDO A QUE NO ESTA DECLARADA EN LA SECCION DE *) (* INTERFACE, PERO PUEDEN LLAMARLA (Y PARA ESO FUE DISEĽADA) *) (* DENTRO DE ESTA UNIDAD, CUALQUIER RUTINA QUE LA NECESITE. *) (* ----------------------------------------------------------- *) FUNCTION POSIBLE_ENROQUE_NEGRO (INICIAL : BYTE) : BOOLEAN; VAR A : BYTE; BEGIN POSIBLE_ENROQUE_NEGRO := TRUE; FOR A := INICIAL DOWNTO INICIAL-1 DO IF (ATACA_HUMANO (A)) OR (TABLERO [A] <> 0) THEN POSIBLE_ENROQUE_NEGRO := FALSE; END; { PROCEDIMIENTO SE SALIDA } PROCEDURE SALIDA;FAR; { NO HAY FORMA FACIL DE SABER CUAL ES, SE } BEGIN { IDENTIFICA SOLO POR EL CAMBIO DE LOS } EXITPROC := CADENA; { PUNTEROS DE SALIDA AL SISTEMA OPERATIVO } CLOSEGRAPH; { (EXITPROC). } END; { SECCION DE INICIALIZACION } BEGIN { SE IDENTIFICA POR EL BEGIN } CADENA := EXITPROC; EXITPROC := @SALIDA; { CAMBIO DEL PUNTERO DE SALIDA A NUESTRO } DRIVER := VGA; { A NUESTRO PROCEDIMIENTO DE SALIDA. } MODO := VGAHI; INITGRAPH (DRIVER,MODO,'C:\TP\BGI'); END. En primer lugar debemos aclarar que una unidad no puede correr, el que corre es el programa principal, este siempre se compila por lo tanto hay que procurar hacerlo lo mas pequenio posible, terminar definitivamente algunas unidades y poner todas las rutinas en desarrollo en una sola unidad, asi se optimiza el trabajo en gran manera. La sentencia unit define que usamos una unidad el nombre es clave pues es el mismo con el que se llamara a la unidad en la sentencia uses. .