Vous êtes sur la page 1sur 188

CONTENIDOS TEMATICOS.

http://jm00092.freehostia.com/cqb/indetal.htm

0.0 Introduccin ....................................................................................................................... 1 TEMA 1.1 PRIMEROS PASOS ............................................................................................... 3 1.1.1 PRIMER PROGRAMA EN QBASIC .......................................................................... 3 1.1.2 ENTENDIENDO LO QUE HEMOS HECHO ............................................................. 4 1.1.3 ESTRUCTURA BSICA? ........................................................................................ 4 1.1.4 ESCRIBIR COMENTARIOS ...................................................................................... 5 TEMA 1.2 VARIABLES ......................................................................................................... 6 1.2.1 - INTRODUCCIN A LAS VARIABLES .................................................................. 6 1.2.2 - TIPOS DE DATOS ................................................................................................... 7 1.2.3 - NOMBRES DE VARIABLES ................................................................................... 8 1.2.4 - DECLARACIN DE VARIABLES .......................................................................... 9 1.2.5 - CUIDADO! PELIGRO EN QBASIC! ..................................................................... 10 #T_1_3TEMA 1.3 CONSTANTES .........................................................................................11 1.3.1 - INTRODUCCIN A LAS CONSTANTES ............................................................. 11 1.3.2 - DEFINICIN DE CONSTANTES .......................................................................... 12 1.3.3 - RECOMENDACIN DE USAR CONSTANTES ................................................... 12 TEMA 1.4 ENTRADA Y SALIDA BSICA ..........................................................................13 1.4.1 - INTRODUCCIN A E/S ........................................................................................ 13 1.4.2 - ENTRADA DE DATOS. LEER DESDE EL TECLADO......................................... 13 1.4.3 - CONTROL DE SABOTAJES ................................................................................. 14 1.4.4 - SALIDA. ESCRIBIR EN PANTALLA ................................................................... 16 TEMA 1.5 OPERADORES Y EXPRESIONES.......................................................................18 1.5.1 - Introduccin a los operadores .................................................................................. 19 1.5.2 - Operador de asignacin ........................................................................................... 19 1.5.3 - Contadores y acumuladores ..................................................................................... 20 1.5.4 - Operadores aritmticos ............................................................................................ 20 1.5.5 - Operadores relacionales o de comparacin ............................................................... 21 1.5.6 - Operadores lgicos .................................................................................................. 22 TEMA 1.6 FUNCIONES INCLUIDAS EN EL LENGUAJE ...................................................24 TEMA 1.7 ESTRUCTURAS CONDICIONALES IF ..............................................................27 1.7.1 - INTRODUCCIN A LOS BLOQUES DE CONTROL ........................................... 27 1.7.3 - SANGRA............................................................................................................... 28 1.7.4 - ANIDACIN .......................................................................................................... 28 1.7.5 - INSTRUCCIN IF SIMPLIFICADA ...................................................................... 29 1.7.6 - ALTERNATIVA DOBLE. INSTRUCCIN IF-THEN-ELSE ................................. 29 1.7.7 - ALTERNATIVA MLTIPLE. INSTRUCCIN ELSEIF ........................................ 31

TEMA 1.8 ESTRUCTURA DE SELECCIN SELECT ..........................................................34 TEMA 1.9 ESTRUCTURA DE REPETICIN FOR...NEXT .................................................37 1.9.1 - Introduccin a estructuras de control repetitivas ....................................................... 37 1.9.2 - Cmo detener un programa ...................................................................................... 37 1.9.3 - Instruccin FOR...NEXT ......................................................................................... 38 TEMA 1.10 ESTRUCTURA DE REPETICIN WHILE...WEND ..........................................42 Tema 1.11 ESTRUCTURA DE REPETICIN DO...LOOP ....................................................44 1.11.1 - ESTRUCTURA DO...LOOP ................................................................................. 44 1.11.2 - DEPURACIN DE DATOS DE ENTRADA ........................................................ 45 TEMA 1.12 ARRAYS ............................................................................................................46 1.12.1 - INTRODUCCIN A LOS ARRAYS .................................................................... 46 1.12.2 - VECTORES .......................................................................................................... 47 1.12.3 - MATRICES .......................................................................................................... 51 1.12.4 - POLIEDROS ......................................................................................................... 53 1.12.5 - AHORRAR ESPACIO EN LOS ARRAYS DE CADENAS .................................. 53 1.12.6 - MATRICES ORLADAS ....................................................................................... 54 1.12.7 - FORMACIONES DINMICAS ............................................................................ 55 TEMA 1.13 PROGRAMACIN MODULAR.........................................................................57 1.13.1 - INTRODUCCIN A LA PROGRAMACIN MODULAR ................................... 57 1.13.2 - LA PROGRAMACIN MODULAR EN QBASIC ............................................... 58 1.13.3 - LLAMADA A PROCEDIMIENTOS Y FUNCIONES........................................... 58 1.13.4 - DEFINICIN DE PROCEDIMIENTOS................................................................ 60 1.13.5 - VARIABLES LOCALES ...................................................................................... 61 1.13.6 - VARIABLES GLOBALES ................................................................................... 62 1.13.7 - PASO DE PARMETROS ................................................................................... 63 1.13.8 - DEFINICN DE FUNCIONES ............................................................................. 66 1.13.9 - RECUSIVIDAD .................................................................................................... 68 1.13.10 - DIFERENCIA ENTRE PROCEDIMIENTOS Y FUNCIONES............................ 71 1.13.11 - PROTOTIPOS..................................................................................................... 72 TEMA 1.14 TIPOS DE DATOS DEFINIDOS POR EL USUARIO ........................................73 1.14.1 - DEFINICIN DE NUEVOS TIPOS DE DATOS .................................................. 73 1.14.2 - UNA APROXIMACIN A LA PROGRAMACIN ORIENTADA A OBJETOS . 77 TEMA 1.15 FICHEROS SECUENCIALES ............................................................................78 1.15.1 - INTRODUCCIN AL USO DE FICHEROS......................................................... 78 1.15.2 - INTRODUCCIN A LOS FICHEROS SECUENCIALES .................................... 79 1.15.3 - ESCRIBIR INFORMACIN EN FICHEROS SECUENCIALES .......................... 80 1.15.4 - LEER INFORMACIN DE FICHEROS SECUENCIALES .................................. 82 1.15.5 - ANEXAR INFORMACIN A FICHEROS SECUENCIALES .............................. 84 TEMA 1.16 FICHEROS DE ACCESO DIRECTO O ALEATORIO .......................................85

1.16.1 - INTRODUCCIN A LOS FICHEROS DIRECTOS .............................................. 85 1.16.2 - LECTURA Y ESCRITURA EN FICHEROS DIRECTOS ..................................... 86 1.16.3 MANTENIMIENTO DE FICHEROS DIRECTOS................................................ 89 1.16.3.1 INTRODUCCIN AL MANTENIMIENTO DE FICHEROS ............................ 89 1.16.3.2 - ALTAS. AADIR REGISTROS ........................................................................ 89 1.16.3.3 - CONSULTAS. BSQUEDA SECUENCIAL DE REGISTROS ......................... 90 1.16.3.4 - MODIFICACIONES DE REGISTROS .............................................................. 91 1.16.3.5 - BAJAS. BORRAR REGISTROS ........................................................................ 92 1.16.3.6 - COMPACTACIN DE FICHEROS DIRECTOS ............................................... 93 1.16.3.7 - ELABORACIN DE LISTADOS DE DATOS .................................................. 95 1.16.4 - BREVE IDEA SOBRE BASES DE DATOS ......................................................... 99 1.16.5 - OTRAS INSTRUCCIONES RELACIONADAS CON FICHEROS ......................100 TEMA 1.17 CONTROL DE ERRORES................................................................................103 1.17.1 - INSTRUCCIN GOTO. LA MS CONOCIDA Y ODIADA DE BASIC ............103 1.17.2 - INTRODUCCIN A LOS ERRORES..................................................................104 1.17.3 - CONTROL DE ERRORES...................................................................................106 TEMA 1.18 LLAMADA A PROGRAMAS EXTERNOS ....................................................109 TEMA 2.1 DETECCIN DE TECLAS PULSADAS. LA FUNCIN INKEY$ ...................110 2.1.1 - USO DE LA FUNCIN INKEY$ ..........................................................................111 2.1.2 - RECONOCIMIENTO DE TECLAS ESPECIALES................................................112 2.1.3 - HACER PAUSAS EN LOS PROGRAMAS ...........................................................114 TEMA 2.2 NMEROS ALEATORIOS Y CONTROL DE TIEMPO ...................................115 2.2.1 - MMEROS ALEATORIOS. FUNCIN RND.......................................................115 2.2.2 - CONTROL DE TIEMPO .......................................................................................118 TEMA 2.3 MS SOBRE LAS CADENAS DE CARACTERES ...........................................120 2.3.1 - ACERCA DE LAS CADENAS DE CARACTERES ..............................................120 2.3.2 - LA FUNCIN E INSTRUCCIN MID$ ...............................................................121 2.3.3 - OTRAS FUNCIONES DE MANEJO DE CADENAS ............................................124 TEMA 2.4 MANEJO DE LA PANTALLA EN MODO TEXTO ...........................................126 2.4.1 - INTRODUCCIN A LOS INTERFACES DE TEXTO ..........................................127 2.4.2 - POSICIONAR EL CURSOR EN LA PANTALLA.................................................127 2.4.3 - TEXTO EN COLORES..........................................................................................130 2.4.4 - REDEFINIR COLORES ........................................................................................132 2.4.5 - CARACTERES SEMIGRFICOS .........................................................................133 2.4.6 - CARACTERES ESPECIALES DE RELLENO ......................................................137 2.4.7 - CARACTERES NO IMPRIMIBLES ......................................................................139 2.4.8 - ESMILIS y ASCII-ART ......................................................................................140 TEMA 2.5 GRFICOS .........................................................................................................143 2.5.1 - INTRODUCCIN A LOS INTERFACES GRFICOS ..........................................143

2.5.2 - LOS MODOS DE PANTALLA EN QBASIC.........................................................143 2.5.3 - RENDIMIENTO EN MODO GRFICO ................................................................147 TEMA 2.6 TRAZADO DE FIGURAS GRFICAS ..............................................................147 2.6.1 - EL CURSOR GRFICO ........................................................................................148 2.6.2 - PUNTOS. INSTRUCCIN PSET ..........................................................................148 2.6.3 - LNEAS Y RECTNGULOS. INSTRUCCIN LINE ...........................................149 2.6.4 - CRCULOS Y VALOS. INSTRUCCIN CIRCLE .............................................151 2.6.5 - RELLENAR REAS. INSTRUCCIN PAINT .....................................................152 2.6.6 - DIBUJAR FIGURAS COMPLEJAS. INSTRUCCIN DRAW ..............................154 TEMA 2.7 OTRAS OPERACIONES CON GRFICOS .......................................................157 2.7.1 - DEFINIR MARCOS DE VISUALIZACIN. INSTRUCCIN VIEW ...................157 2.7.2 - SISTEMA DE COORDENADAS PERSONALIZADO ..........................................158 2.7.3 - COPIAR Y PEGAR. INSTRUCCIONES GET Y PUT ...........................................159 2.7.4 - VER EL COLOR DE UN PIXEL. INSTRUCCIN POINT ...................................162 2.7.5 - ESCRIBIR TEXTO EN MODO GRFICO............................................................166 TEMA 2.8 IMPRESIN .....................................................................................................167 TEMA 2.9 EFECTOS DE SONIDO Y MSICA .................................................................170 TEMA 3.1 REFERENCIA RPIDA DEL LENGUAJE DE QBASIC .................................171 TEMA 3.2 CDIGOS DE CARACTERES ASCII ................................................................174 TEMA 3.3 USO DE QBASIC CON EL EMULADOR DOSBOX ........................................175 3.3.1 - Introduccin a los emuladores y a DOSBox ............................................................175 3.3.2 - Instalacin del emulador DOSBox ..........................................................................177 3.3.3 - Instalacin del entorno grfico D-Fend ...................................................................178 3.3.4 - Configuracin del perfil de QBasic .........................................................................179 3.3.5 - Uso de QBasic en el emulador ................................................................................182 3.3.6 - Aprovechando el emulador .....................................................................................183

0.0 Introduccin

Los ordenadores son mquinas electrnicas capaces de hacer clculos a gran velocidad y con gran precisin. Para resolver un problema usando una calculadora tenemos que tocar las teclas necesarias y anotar resultados intermedios hasta llegar al resultado final. Para volver a resolver el mismo problema, ya sea con los mismos valores de entrada o con otros, tendremos que volver a tocar todas las teclas de antes y volver a anotar resultados intermedios cuando haga falta, teniendo que entender en todo momento todos los pasos necesarios para dar con la solucin del problema. Esta serie de pasos necesarios para resolver un problema es lo que se conoce con el nombre de Algoritmo. En un ordenador ms avanzado que una calculadora, como es el caso de un PC, podemos introducir una vez nada ms los pasos necesarios para resolver el problema, el algoritmo, y en sucesivas ocasiones que haya que hacer el clculo ser suficiente con introducir los datos de entrada para que el ordenador resuelva el problema siguiendo las instrucciones del programa y nos devuelva como datos de salida la solucin al problema. El usuario del programa no necesita conocer todos los pasos intermedios que han sido necesarios para encontrarla. El ordenador almacena todas las instrucciones y datos en memoria. Como la memoria del ordenador es un dispositivo electrnico digital, solo es capaz de trabajar con nmeros y hacer operaciones muy simples como sumar, comparar si dos nmeros son iguales o copiar

datos a otras partes de la memoria o a los registros de datos del microprocesador. Un algoritmo o programa formado por estas instrucciones tan simples sera terriblemente largo y complicado, ya que cualquier proceso informtico como dibujar una letra en la pantalla o calcular una raz cuadrada necesitara muchas de estas operaciones bsicas y por lo tanto construir un programa directamente usando estas instrucciones sera bastante complicado. Para solucionar este inconveniente existen los intrpretes y los compiladores que son programas capaces de entender instrucciones ms o menos fciles y claras para nosotros y convertirlas en secuencias de instrucciones bsicas en cdigo mquina que son las nicas que entiende el ordenador. Para dar las instrucciones a los intrpretes o compiladores no podemos hacerlo de cualquier manera, sino que tenemos que seguir una serie de normas de sintaxis y usar determinadas palabras y smbolos. Esto es lo que se conoce como lenguaje de programacin. Basic es un lenguaje de programacin diseado en la dcada de 1960 en los Estados Unidos originalmente para grandes ordenadores, pero que despus se ha utilizado principalmente como medio de aprendizaje de la programacin, ms que para el desarrollo de aplicaciones comerciales. Las siglas B.A.S.I.C significan "Beginners all-purpose symbolic instruction code", es decir, lenguaje de programacin simblico multipropsito para principiantes. Hay muchas versiones, desde las primitivas que utilizaban nmeros de lneas hasta las ms modernas usadas en Microsoft Visual Basic que incluyen programacin orientada a objetos para entornos Windows. QBasic es un Intrprete del lenguaje Basic que se incluye con los sistemas operativos MS-DOS a partir de las versiones 5.5 sustituyendo a otros ms antiguos como GW-Basic. Se compone del editor de cdigo, del intrprete de Basic y de un depurador sencillo todo integrado en el entorno de programacin QBasic. La versin de Basic que soporta es bastante avanzada y no necesita que escribamos nmeros de lneas, al mismo tiempo que cuenta con las instrucciones de control necesarias como para que podamos programar respetando los principios de la Programacin Estructurada. El lenguaje de QBasic se ha quedado obsoleto por ser para el sistema MS-DOS y no para Windows, adems de que al ser un intrprete no nos permite distribuir las aplicaciones que hagamos de forma independiente de QBasic. Aunque no nos vale para escribir programas comerciales, nos va a servir para aprender a programar de la forma ms sencilla posible y sin complicarnos teniendo que escribir cosas que no entendemos todava. Adems el entorno est en castellano y es muy estable, por lo que nuestros programas nunca van a bloquear el ordenador. QBasic funciona perfectamente en Windows. Una vez terminado el curso todo lo aprendido lo podremos ampliar para empezar con Visual Basic, o bien nos servir para empezar a

programar en Pascal o en C, lenguajes mucho ms completos que Basic, pero algo ms complicados. Los fundamentos de programacin que aprendamos durante el curso van a ser los mismos en la mayora de lenguajes.

TEMA 1.1 PRIMEROS PASOS


y y y y

1.1.1 1.1.2 1.1.3 1.1.4

Primer programa en QBasic Entender lo que hemos hecho Estructura bsica Escribir comentarios

1.1.1 PRIMER PROGRAMA EN QBASIC Es tradicional en programacin que al empezar con un nuevo lenguaje para empezar a entenderlo se escriba un programa que borre la pantalla y escriba las palabras "Hola mundo", all vamos paso por paso. Desde MS-DOS entraramos en el directorio donde tenemos QBasic y a continuacin cargamos el programa. CD DOS[Enter] QBasic[Enter] O lo ms comn en estos tiempos, desde Windows buscamos el programa QBasic.exe o un acceso directo que apunte a l y hacemos doble clic sobre su icono. (Muchas veces encontraremos los accesos directos a QBasic con el nombre de Microsoft Quick Basic) En el editor podemos empezar a escribir todo el cdigo de nuestro programa. Esto es como un procesador de textos, solo que en vez de escribir lo que queramos slo se puede escribir en lenguaje Basic. Si escribimos otra cosa, cuando pasemos a la siguiente lnea pulsando "Enter" el programa se dar cuanta de que algo no va bien y nos sacar un mensaje de error. Vamos a escribir nuestro programa. cls print "Hola Mundo" Ya est! Ahora que ya nos hemos hartado de escribir todo el cdigo del programa vamos a probar a ver si funciona. Pulsamos la tecla F5 La pantalla se pondr toda negra y en la esquina superior izquierda aparecer de color gris el rtulo "Hola Mundo". Ya est. Nuestro programa hace lo que queramos. Abajo aparecen unas letras que dicen "Presione cualquier tecla y contine". Este rtulo lo escribe automticamente QBasic para indicarnos que nuestro programa ha terminado y que cuando pulsemos cualquier tecla volveremos al editor de cdigo. Pulsa cualquier tecla y ya ves otra vez la pantalla azul del editor con las instrucciones del programa.

Para no tener que hartarnos otro da de escribir nuestro pedazo de programa vamos a grabarlo en el sistema de archivos de nuestro ordenador. Abrimos el men "Archivo" y seleccionamos "Guardar". Aparece un cuadro de dilogo donde se nos pide el nombre del archivo. El manejo del cuadro de dilogo es muy sencillo, pero hay que tener en cuenta que como QBasic funciona en MS-DOS los nombres de los archivos no pueden tener ms de 8 caracteres ni pueden incluir espacios en blanco. Tampoco es conveniente usar caracteres especiales ni letras acentuadas ni . Los programas que hagamos en QBasic se guardan en un nico fichero de texto ASCII con extensin ".BAS" que contiene el cdigo fuente del programa, es decir, las instrucciones en lenguaje Basic que hemos escrito para que el programa funcione. 1.1.2 ENTENDIENDO LO QUE HEMOS HECHO Ahora vamos a ver recordar el Cdigo de nuestro programa. CLS PRINT "Hola Mundo" Vemos que hay dos instrucciones: CLS borra la pantalla (Igual que en MS-DOS) y PRINT "Hola Mundo" escribe en la pantalla "Hola Mundo". Las dos instrucciones se han ejecutado una detrs de otra, por eso podemos decir que un programa es un listado de instrucciones que se ejecutan de forma secuencial una detrs de otra desde la primera hasta la ltima. Al escribir el programa habrs observado que seguramente escribiste cls y al pasar de lnea se puso en maysculas como CLS. Y despus con print pas lo mismo, pero "Hola Mundo" se ha quedado como lo escribiste. Esto es porque CLS y PRINT son instrucciones del lenguaje Basic que se ponen en maysculas automticamente para dar mayor legibilidad al programa. "Hola Mundo" va entre comillas porque es un Literal o una expresin de cadena y se va a quedar siempre como t lo has escrito. 1.1.3 ESTRUCTURA BSICA? Veamos en un momento como sera un programa en lenguaje PASCAL que haga lo mismo que el nuestro: Program Hola; uses crt; begin clrscr; writeln("Hola Mundo"); end. Y lo mismo en lenguaje C: #include < stdio.h > int main(void) { clrscr(); cprintf("Hola Mundo");

return(0); } Podemos ver que en los dos estn las instrucciones para borrar la pantalla (clrscr) y la de escribir (writeln en PASCAL y cprintf en C), pero Por que llevan punto y coma al final? y Qu son esos smbolos y esas otras palabras tan raras?... En otros lenguajes de programacin hace falta escribir un "esqueleto bsico" donde montar las instrucciones, en QBasic no. Nos limitamos a escribir lo que queremos que haga el programa y ya est. Estas estructuras bsicas son necesarias en los otros lenguajes ms avanzados, pero en QBasic como slo vamos a aprender as lo tenemos ms fcil sin tener que escribir cosas que todava no entendemos. 1.1.4 ESCRIBIR COMENTARIOS Los programas que veamos al principio de este curso van a ser muy sencillos y se explican por s solos, pero una vez vayamos aprendiendo a programar y hagamos programas cada vez ms largos nos encontraremos con el problema de que pasado un tiempo tengamos que revisar el programa y no nos acordemos de lo que hacen algunas instrucciones, o que nuestro programa lo vea otra persona y no sepa por dnde cogerlo. Para solucionar este problema los lenguajes de programacin nos dan la posibilidad de escribir comentarios dentro del listado del programa para explicar su funcionamiento. Lo que pongamos en los comentarios no altera para nada el funcionamiento del programa. En QBasic para escribir un comentario ponemos un apstrofo ('). La tecla del apstrofo est en el bloque alfanumrico del teclado en la fila de arriba, a la derecha del cero. Todo lo que escribamos a continuacin del apstrofo ya no ser tenido en cuenta a la hora de ejecutar el programa. Veamos nuestro ejemplo con comentarios: ' PROGRAMA HOLA MUNDO ' Escribe en la pantalla "Hola Mundo" ' ' Este ejemplo forma parte del curso ' de introduccin a la programacin ' CLS 'Borra la pantalla PRINT "Hola Mundo" 'Escribe "Hola mundo" Aqu hemos puesto una cabecera describiendo el programa y a continuacin a la derecha de algunas instrucciones, en este caso de todas, hemos explicado para que sirven. En los programas ya terminados es conveniente poner la cabecera siempre y explicar las instrucciones ms complicadas. El formato de la cabecera vendr especificado en el proyecto, por ejemplo que contenga el nombre del programa, una descripcin corta, el autor y la fecha. En nuestros programas no es necesario ser tan estrictos.

Otra utilidad que se da a los comentarios en los entornos de programacin es la de conseguir que un trozo de programa no se ejecute. Por ejemplo podemos poner como comentario la parte de un programa dnde se nos pide la clave de acceso y que ya hemos comprobado que funciona, para que no nos la pida cada vez que entremos a comprobar otra cosa. Al final nos tendremos que acordar de quitar estos comentarios para que todo funcione bien.

TEMA 1.2 VARIABLES


y y y y y

1.2.1 1.2.2 1.2.3 1.2.4 1.2.5

Introduccin a las variables Tipos de datos Nombres de variables Declaracin en variables Cuidado, peligro en QBasic!

1.2.1 - INTRODUCCIN A LAS VARIABLES Nuestro programa anterior funciona muy bien, pero siempre que lo ejecutemos va a suceder lo mismo, borrar la pantalla y escribir un rtulo que diga "Hola Mundo". Vamos a escribir un programa un poco ms complicado que nos pregunte nuestro nombre y nos salude. El resultado (lo que se ve en pantalla al ejecutarlo) podra ser el siguiente: Cmo te llamas? juanma Hola juanma El programa borrar la pantalla (Esto lo vamos a hacer siempre para no confundirlo con lo anterior que pueda haber), nos preguntar por nuestro nombre, nos dejar escribirlo con el teclado y cuando lo hagamos lo guardar en "algn sitio", despus escribir "Hola " seguido de nuestro nombre que lo habr sacado del "sitio" donde lo guard antes. Vamos a ver cmo ser el cdigo: CLS INPUT "Cmo te llamas? ", nombre$ PRINT "hola "; nombre$ Ahora tenemos una instruccin nueva, INPUT, que lleva dos parmetros separados por una coma. Esta instruccin lo que hace es dejar al usuario del programa que escriba algo con el teclado y cuando pulse la tecla Enter lo guarda en memoria. La primera parte "Cmo te llamas?" la escribe en pantalla tal como est para que el usuario sepa lo que se le pregunta. La segunda parte es una Variable, es decir el nombre que le hemos puesta a un "trozo" de la memoria del ordenador dnde vamos a guardar lo que ha escrito el usuario y que le hemos llamado nombre$ para despus poder localizarlo. La siguiente instruccin, PRINT, es la misma que en el anterior programa. Lo que hace es escribir lo que lleva detrs. En esta ocasin va a escribir dos cosas, primero "Hola " con su espacio detrs tal como aparece dentro de las comillas, y a continuacin todo seguido

escribir el valor que haya guardado en la variable nombre$. No escribir Hola nombre$ porque nombre$ no est entre comillas. Esto de las variables es una de las cosas ms importantes de los lenguajes de programacin. Los programas van a manejar datos que estarn grabados en la memoria del ordenador. Si no existieran las variables tendramos que saber en que posicin exacta los guardamos y despus para recuperarlos habra que escribir los nmeros de las direcciones de memoria y saber exactamente cuanto largo son los datos. Esto es muy complicado (Esto es lo que se hace en los Lenguajes Ensambladores), para hacerlo aqu ms sencillo usaremos los nombres de variables y QBasic se encargar de ver en que lugar fsico de la memoria mete la informacin y despus de saber recuperarla cuando se la pidamos. 1.2.2 - TIPOS DE DATOS Nuestros programas van a poder trabajar con varios tipos de informacin, como son letras, nmeros con o sin decimales, etc. Nuestro programa anterior usaba una variable de texto o de cadena de caracteres para almacenar nuestro nombre, pero en otros casos habr que almacenar nmeros ms o menos grandes, por ejemplo. Veamos ahora los tipos de datos que hay en QBasic.
NOMBRE SUFIJO DESCRIPCIN Texto con cualquier carcter Nmeros enteros Nmeros enteros ms grandes Nmeros con decimales Nmeros con ms decimales MNIMO Y MXIMO 0 a 32767 caracteres -32768 a 32767 2.147.483.647 a2.147.483.648 2,8x1045 a 2,8x1045 4,9x10324 a 4,9x10324

STRING

INTEGER

LONG

&

SINGLE

DOUBLE

Vamos ahora a ver lo que significa esta tabla. En QBasic las variables van a ser de uno de estos cinco tipos. Si queremos almacenar palabras, nombres, fechas, horas, cdigos postales, nmeros de telfono, DNIs, matrculas, etc. tendremos que usar una variable de tipo Cadena (STRING) cuyo nombre llevar al final el carcter $.

Para guardar nmeros tendremos que usar alguno de los otros cuatro tipos de variables. Si nuestros nmeros no van a llevar decimales usaremos una variable de tipo entero (INTEGER) que ocupa poca memoria (2 bytes) y adems el ordenador trabaja con ellas muy rpido. Si los nmeros enteros van a llegar a ser mayores de 32.767 tendremos que utilizar una variable de tipo Entero largo (LONG) en la que "caben" hasta nmeros mayores que dos mil millones. Si vamos a usar nmeros reales (que pueden llevar decimales) tendremos que usar variables de precisin sencilla (SINGLE). Para algn clculo que necesite obtener resultados con muchos decimales usaremos las variables de doble precisin (DOUBLE) que son ms lentas de manejar y ocupan ms memoria que las anteriores. Si intentamos guardar en una variable un valor ms grande (o ms pequeo si es negativo) de lo que permite su tipo de datos, se producir un "Error de Tiempo de Ejecucin" y el programa se parar. 1.2.3 - NOMBRES DE VARIABLES Cada variable llevar un nombre que nosotros decidamos para usarla en nuestro programa. Tenemos que elegir nombres que no sean demasiado largos y que tengan algo que ver con lo que va a contener la variable. Los nombres de variables deben cumplir estas condiciones: y Deben tener entre 1 y 40 caracteres. y Pueden incluir letras y nmeros. y No pueden llevar la ni letras acentuadas. y El primer carcter tiene que ser una letra. Estos nombres de variable son vlidos. y Edad y fechaNacimiento y jugador1 y iva16porCiento y vidasRestantes Estos otros nombres no son vlidos
y y y y

2jugadores (No empieza por una letra) ao (Contiene la ) elPatioDeMiCasaEsParticularYCuandoLlueveSeMoja (Muy Largo) puntuacin (Lleva acento)

Hay que tener en cuenta que QBasic no distingue de maysculas y minsculas, por lo tanto una variable que se llame nivel ser la misma que Nivel, NIVEL o niVEL. De hecho cuando cambiemos las maysculas y minsculas de una variable se cambiarn todas las que ya haya escritas en el resto del programa, es decir, si hemos escrito en un sitio puntos y despus escribimos PuntoS en otro sitio, la anterior tambin se cambiar a PuntoS.

Por convenio los nombres de las variables se escriben todo en minsculas para diferenciarlos visualmente de las Palabras Claves que se van poniendo en maysculas automticamente. Si una variable lleva dos palabras se suele escribir la primera letra de la segunda palabra en maysculas para separar un poco, por ejemplo nombreJugador totalActivo o nuevoRegistro. En las variables que deberan llevar la hay varias formas: Para ao se suele escribir cosas como ano, anno o anyo. De los acentos pasamos. En Visual Basic se pueden usar nombres de variables con y acentos, pero en QBasic no. 1.2.4 - DECLARACIN DE VARIABLES En otros lenguajes de programacin (la mayora) hay que Declarar las variables al principio del programa. Esto significa escribir una instruccin que diga algo as como "Voy a usar una variable que se va a llamar nombre y va a ser de tipo cadena" y as con cada una de las variables que vayamos a usar en el programa. En QBasic NO hace falta declarar las variables. La primera vez que escribimos el nombre de una variable QBasic reserva en memoria el espacio para utilizarla y ya est disponible durante el resto del programa, pero Cmo sabe QBasic del tipo que queremos que sea la variable? En la tabla de tipos de datos haba una columna que pona Sufijo y que para cada tipo de datos tena un smbolo distinto. NOMBRE STRING INTEGER LONG SINGLE DOUBLE SUFIJO $ % & ! #

Para indicar el tipo escribiremos uno de estos sufijos detrs del nombre de la variable, por ejemplo si nombre va a contener texto escribiremos nombre$ (Como hicimos en el ejemplo). A esto se llama declaracin implcita, ya que dentro del nombre va includo el tipo de datos. Ahora Que pasa si escribimos en distintos sitios el mismo nombre de variable con distintos sufijos?, por ejemplo: variable$ = "Esto es un texto" variable% = 2000

10

PRINT variable$ PRINT variable% Lo que ocurrir es que QBasic entiende que son dos variables completamente distintas, cada una de un tipo y nos da como resultado: Esto es un texto 2000 Esto puede producir un Error de Lgica, es decir, el programa funciona perfectamente, pero a lo mejor no hace lo que esperbamos. Para evitar esto hay que poner a las variables nombres ms descriptivos, y si en este caso de verdad queramos dos variables es mejor ponerles nombres distintos para evitar confusiones. Tambin puede pasar que se nos olvide ponerle a una variable el sufijo de tipo, entonces QBasic la tratar como de tipo real de precisin sencilla, que es el tipo predeterminado. Para ver como se cambia el tipo predeterminado mira en la ayuda de QBasic la instruccin DEFINT y similares. En la prctica a partir de ahora vamos a poner el sufijo $ a las variables de cadena y las dems las dejamos sin sufijo para que sean reales que admiten nmeros muy grandes con o sin decimales. Si furamos a hacer programas muy depurados sera conveniente usar los otros tipos de variables cuando fuera necesario para que fueran ms eficientes con el consumo de memoria. Las variables al ser usadas la primera vez antes de asignarles (guardar en ellas) ningn valor contienen un texto de longitud cero si son de cadena o el nmero 0 si son de otro tipo. En otros lenguajes de programacin esto no es as y contienen cualquier valor que hubiera en esa posicin de memoria de otros programas que la hubieran usado antes, por lo que se pueden producir errores. Aqu no. Lo ltimo que queda es saber que el Tiempo de Vida de una variable es desde que se usa por primera vez (desde que se declara o se ejecuta la lnea donde su nombre aparece por primera vez) hasta que el programa termina, despus su valor se pierde. No podemos contar con que tengamos valores en memoria guardados de la vez anterior que ejecutamos el programa. 1.2.5 - CUIDADO! PELIGRO EN QBASIC! No tener que declarar las variables puede parecer una ventaja, pero por el contrario ES UN GRAVE INCONVENIENTE de QBasic. El problema menos importante es que usaremos nuevas variables cada vez que queramos sin acordarnos de las anteriores que a lo mejor ya no volvemos a usar, y por lo tanto nuestros programas van a usar ms memoria de la necesaria. El problema ms grave es que si en un sitio escribimos mal el nombre de una variable (o confundimos el sufijo de tipo) QBasic se creer que

11

estamos usando una nueva variable y nuestro programa seguir funcionando como si nada, pero los clculos no saldrn bien. Veamos el siguiente ejemplo, otra vez el programa saludador con un error de este tipo: cls input "Cmo te llamas? ", nombre$ print "hola "; nomber$ Una vez que lo ejecutemos podra pasar esto: Cmo te llamas? Juanma Hola No ha producido ningn error de Tiempo de Ejecucin (No se ha parado y ha terminado correctamente), pero no nos ha saludado con nuestro nombre!!! Nuestro nombre lo guard en la variable nombre$ cuando lo escribimos, pero al respondernos ha escrito "Hola " y a continuacin el valor de la variable nomber$ que es otra variable nueva distinta y que est vaca. Aqu es muy sencillo de ver lo que pasa, pero en un programa de mil lneas con cuarenta variables puede ser muy complicado incluso saber cual variable es la que hemos escrito mal. Por esto hay que tener ESPECIAL CUIDADO en QBasic al escribir los nombres de las variables. En otros lenguajes las variables estn declaradas al principio y si escribimos alguna mal nos da un error de compilacin de tipo "Identificador desconocido" y en muchos casos hasta nos seala la lnea donde est la variable mal escrita para que la corrijamos. En QBasic la mayora de las veces que el programa funciona bien pero no hace lo que queremos es que hemos escrito mal el nombre de alguna variable. Cuando tenemos nombres de variables largos un truco para ver si estn bien escritos todos es buscar una variable que est bien escrita seguro y poner la ltima letra en maysculas, por ejemplo si tenemos identificador lo cambiaramos por identificadoR que para QBasic sigue siendo lo mismo, pero al hacerlo se cambiarn todas las variables con nombre identificador por identificadoR en todo el programa y si al dar un repaso rpido vemos que alguna acaba con r minscula es que alguna letra est mal escrita y habr que repasarla y corregir el error.

TEMA 1.3 CONSTANTES


y y y

1.3.1 - Introduccin a las constantes 1.3.2 - Definicin de constantes 1.3.3 - Recomendacin de usar constantes

1.3.1 - INTRODUCCIN A LAS CONSTANTES Cuando hagamos programas que usen variables nos daremos cuenta de que hay variables cuyo valor realmente no cambia a lo largo de

12

todo el programa. Podemos tratar estas variables de una forma especial para que el ordenador las maneje de forma ms eficiente. Estas variables las definiremos como constantes al principio del programa dndoles un valor, y despus podremos usar este valor dentro del programa cuando nos haga falta, pero no modificarlo.

1.3.2 - DEFINICIN DE CONSTANTES Usaremos la palabra clave CONST seguida del nombre de la constante, el signo igual y el valor que le vamos a dar. Ejemplos: CONST pi = 3.14 CONST iva = 16 CONST ruta = "c:\basic\prg\" Para los nombres valen las mismas reglas que para los nombres de variables, es decir, hasta 40 caracteres sin ni acentos, etc... Tambin se suelen poner todo en minsculas. El tipo de datos lo adivina QBasic a partir del valor que le asignamos, por eso no es necesario poner los sufijos de identificacin de tipo (%, !, &, #, $), pero si usamos muchas constantes sera recomendadle hacerlo para dar ms legibilidad al programa. Al definir constantes numricas podemos hacer referencias a otras constantes definidas anteriormente, por ejemplo: CONST precio = 380 CONST dobleprecio = precio * 2 CONST mitadprecio = precio / 2 Esto no se utiliza mucho, pero puede evitar errores. En el ejemplo si el valor de precio es correcto, seguro que tambin lo son los valores de las otras dos constantes. 1.3.3 - RECOMENDACIN DE USAR CONSTANTES En los programas extensos puede ser recomendables usar constantes para determinados valores que alguna vez en el futuro puede que nos interese cambiar. Imaginemos un programa de contabilidad en el que se calculan precios con IVA del 16 por ciento. Lo ms fcil sera escribir el nmero 16 en cada operacin donde sea necesario y ya est, pero imaginemos que cambian las leyes y el IVA pasa a ser del 18 por ciento. Para seguir usando el programa habra que revisar todas las lneas del listado, con el enorme trabajo que esto puede suponer, y en todos los sitios relacionados con IVA dnde hubiera un 16 poner un 18, pero podran pasar dos cosas: y Que cambiramos un 16 por un 18 en algo que no tuviera nada que ver con el IVA. y Que en algn sitio dejemos el 16 para calcular el IVA. En cualquiera de los dos casos nuestro programa dejara de funcionar correctamente ya que hara los clculos mal.

13

Para evitar este problema basta con definir al principio del programa una constante... CONST iva = 16 ... y dentro del programa usar el nombre de la constante en vez de escribir el nmero 16. Si cambian las leyes del IVA bastar con cambiar el valor de la constante... CONST iva = 18 ... y ya est, el programa seguir funcionado perfectamente todo con el 18 por ciento. En los programas grandes bien estructurados se utilizan constantes para todos los valores que pueden cambiar alguna vez. Por ejemplo en un juego puede que en futuras versiones queramos cambiar la puntuacin mxima o el nmero de vidas disponibles.

TEMA 1.4 ENTRADA Y SALIDA BSICA


y y y y

1.4.1 1.4.2 1.4.3 1.4.4

Introduccin a E/S Entrada. Leer desde teclado Control de sabotajes Salida. Escribir en pantalla

1.4.1 - INTRODUCCIN A E/S En el tema anterior hemos visto una forma de que nuestro programa se comunique con la memoria del ordenador: usando las variables. Aqu vamos a ver la forma de que nuestro programa se comunique con el usuario. La forma ms sencilla de hacerlo es a travs de la "Consola en Modo Texto", es decir leyendo lo que el usuario escriba con el teclado y dando los resultados a travs de la pantalla en forma de texto (Como en MS-DOS). En otros temas ms adelante veremos cmo dibujar en la pantalla, cmo usar la impresora y cmo leer y escribir informacin en el sistema de archivos. Antes de seguir hay que aclarar que "Leer" significa que el programa lea los datos de entrada que el usuario ha escrito, y "Escribir" significa poner en la pantalla los resultados de el programa para que el usuario los vea. No al revs, las instrucciones son para el ordenador. 1.4.2 - ENTRADA DE DATOS. LEER DESDE EL TECLADO La orden que usaremos en QBasic para leer lo que el usuario escribe en el teclado es INPUT. Ya la hemos usado, pero vamos a verla con ms tranquilidad. Su sintaxis ms comn sera: INPUT "Pregunta al usuario", variable y "Pregunta al usuario" es una cadena de texto entre comillas que aparecer en pantalla para que el usuario la vea y sepa lo que se le est preguntando. No puede ser una variable ni una expresin,

14

solamente una cadena entre comillas. En otros lenguajes de programacin no se da esta posibilidad y hay que escribir un rtulo justo antes usando otra instruccin. y variable es el nombre de la variable dnde se almacenar la informacin que escriba el usuario. Al ejecutar esta instruccin se escribe en la pantalla el texto que hay entre comillas y a la derecha aparece el cursor intermitente para que el usuario escriba. El programa estar parado hasta que el usuario escriba lo que quiera y pulse la tecla "Enter". Entonces se almacena en la variable lo que ha escrito el usuario (El valor anterior que pudiera tener la variable se pierde) y el programa sigue con la siguiente instruccin que haya. El usuario podr desplazarse con las flechas del teclado por el texto que est escribiendo para corregir algn error antes de pulsar "Enter" Para que lo que escriba el usuario no salga justo pegado al texto de la pregunta lo que se suele hacer es escribir un espacio en blanco al final dentro de las comillas. Si escribimos... INPUT "Cmo te llamas?",variable$ El resultado despus de que el usuario termine de escribir sera Cmo te llamas?Juanma Pero si lo ponemos con el espacio al final... INPUT "Cmo te llamas? ",variable$ El resultado es parecido pero se ve mejor Cmo te llamas? Juanma La variable tiene que ser del tipo correcto, si le pedimos al usuario que escriba una palabra, la variable debe de ser de tipo cadena y si no tendr que ser de tipo numrico con o sin decimales. Si solo ponemos la variable, sin pregunta al usuario, aparecer un interrogante. Es decir, si ponemos... INPUT nombre_variable Aparecer en la pantalla ?_ Para que no aparezca indicacin alguna, solo el cursor, hay que poner las comillas vacas, por ejemplo... INPUT "", nombre_variable Esto puede ser til cuando la pregunta que hacemos al usuario incluye algn clculo o expresin de cadena. Como INPUT no permite evaluar expresiones en la pregunta al usuario, esta pregunta habr que mostrarla antes en una instruccin PRINT normal de las que veremos ms adelante con su punto y coma al final, y a continuacin poner el input de esta forma para leer la informacin del usuario. 1.4.3 - CONTROL DE SABOTAJES Con estas instrucciones puede pasar que el usuario escriba cosas no vlidas, ya sea por descuido o para intentar provocar un fallo en el programa. Ms adelante veremos como asegurarnos, por ejemplo de que si le pedimos el da del mes no pueda escribir 450, en este

15

apartado solo vamos a ver lo que hace QBasic en situaciones que en otros lenguajes podran dar un error. Qu ocurre si el usuario pulsa la tecla Enter sin haber escrito nada? Si la variable es de texto se almacenar en ella una cadena de longitud cero, es decir "". Si la variable era de cualquier tipo numrico se almacenar en ella el nmero cero. Qu ocurre si el usuario escribe letras cuando le pedimos un nmero? INPUT "Escribe un nmero: ",variable PRINT variable En otros lenguajes se producira un error. QBasic saca un rtulo que pone "Empezar nuevamente" y nos vuelve a repetir la pregunta, hasta que no escribamos un nmero no seguimos. Escribe un nmero: No quiero Empezar nuevamente Escribe un nmero: Que no! Empezar nuevamente Escribe un nmero: 2 2 Si el usuario escribe un nmero cuando le pedimos letras se guarda el nmero como texto en la variable de tipo cadena, no podr ser usado directamente para hacer operaciones matemticas. Qu ocurre si el usuario escribe un nmero muy grande? INPUT "Escribe un nmero: ",variable% PRINT variable% Si el valor escrito no cabe en la variable aparece un rtulo de "Desbordamiento" y se repite la pregunta hasta que el nmero quepa. Recordemos como ejemplo que en las variables de tipo entero el valor mximo es 32767: Escribe un nmero: 99999999999999999999999999999 Desbordamiento Empezar nuevamente Escribe un nmero: 32769 Desbordamiento Empezar nuevamente Escribe un nmero: 32000 32000 Y si el usuario escribe algo que llega al extremo derecho de la pantalla? Se sigue escribiendo en la siguiente lnea por la izquierda. QBasic no le dejar de escribir ms de 256 caracteres. Qu ocurre si el usuario escribe decimales cuando se le pide un nmero entero? INPUT "Escribe un nmero entero: ",variable% PRINT variable% QBasic redondear el valor al guardarlo en la variable entera, si la parte decimal es menor que 0,5 se quitan los decimales, si es igual o

16

mayor que 0,5 se aumenta una unidad, igual que hacamos para convertir de Pesetas a Euros. Escribe un nmero entero: 4.4 4 Escribe un nmero entero: 4.499 4 Escribe un nmero entero: 4.5 5 Escribe un nmero entero: 4.59 5 Recordemos que en MS-DOS Normalmente se utiliza el punto como separador decimal. Si escribimos una coma QBasic se creer que estamos escribiendo letras y nos repetir la pregunta. Escribe un nmero: 6,2 Empezar nuevamente Escribe un nmero: 6.2 6 1.4.4 - SALIDA. ESCRIBIR EN PANTALLA Como ya hemos dicho, la forma ms bsica de dar al usuario los resultados de nuestro programa es a travs de la pantalla. En temas posteriores se explica cmo conseguir crear las pantallas de los programas para que cada cosa aparezca en un sitio determinado y con distintos colores, recuadros, etc, pero por ahora para aprender a programar nos limitaremos a escribir cosas una debajo de otra como en MS-DOS. Usaremos la pantalla de texto de forma que cuando lleguemos a escribir en la lnea ms baja de la pantalla todo subir hacia arriba y desaparecer lo que hubiera en la primera lnea. Para aclarar la pantalla y no liarnos con lo anterior hemos puesto en todos los ejemplos CLS como primera instruccin para que se borre de la pantalla lo que hubiera de ejecuciones anteriores del programa actual o de otro, y se empiece a escribir en la parte superior. Ahora vamos a ver con ms detalle la instruccin PRINT que es la que usamos parara escribir en la pantalla. Una aclaracin. Si a alguien le parece que PRINT significa imprimir tiene razn, en informtica a escribir en la pantalla del ordenador tambin se le puede llamar imprimir en pantalla. Adems esta orden PRINT tambin se utiliza para imprimir como se ver ms adelante. La sintaxis de la instruccin es: PRINT texto Donde texto puede ser una cadena de caracteres entre comillas, que se escribir tal cual. PRINT "Esto es un texto" o el nombre de una variable. mensaje$ = "Prueba superada" PRINT mensaje$

17

En este caso se escribir el valor de que tenga la variable, en nuestro ejemplo se escribira Prueba Superada. La variable tambin puede ser de tipo numrico... PRINT total% Tambin podemos escribir el resultado de operaciones matemticas... PRINT 2+2 En este caso se escribir 4. Las operaciones matemticas (Expresiones) las veremos con detalle en el tema siguiente. Despus de escribir algo el cursor (invisible) pasa a la lnea de abajo, por ejemplo PRINT "Uno" PRINT "Dos" PRINT "Tres" Escribira... Uno Dos Tres Pero en algn caso nos puede interesar que no sea as y que se escriba lo siguiente a continuacin en la misma lnea. Para hacerlo no tenemos ms que escribir un punto y coma al final de la instruccin sobre la que queremos seguir, por ejemplo: PRINT "Uno"; PRINT "Dos"; PRINT "Tres" PRINT "Cuatro" Escribira... UnoDosTres Cuatro Normalmente en una instruccin PRINT se suelen escribir varias cosas, como vimos en el programa saludador que primero escriba la palabra Hola y despus nuestro nombre que estaba almacenado en la variable nombre$. Para hacer esto basta separar con punto y coma (;) las distintas "cosas" que queremos escribir, por ejemplo: nombrePrograma = "Super Juego" nombreUsuario = "JM" PRINT "Hola "; nombreUsuario; ", bienvenido a "; nombrePrograma Escribira... Hola JM, bienvenido a Super Juego Observa que los espacios entre palabras hay que ponerlos en algn sitio dentro de las comillas, de lo contrario saldra todo junto, incluso alguna vez es necesario hacer... PRINT unaVariable$ ; " "; otraVariable$ ...para que no salga todo junto. Ahora vamos a probar con esto: PRINT 2; 3; 4 Visto la anterior, el resultado tendra que ser... 234 Pero no, es

18

2 3 4 QBasic escribe siempre los nmeros con un espacio delante y otro detrs. Lo que ha escrito exactamente es: Espacio 2 Espacio Espacio 3 Espacio Espacio 4 Espacio, el ltimo espacio no lo vemos pero tambin lo ha escrito. Una consecuencia de esto ser que los nmeros por ahora nunca van a salir justo en la parte izquierda de la pantalla, sino una posicin ms adelante, por ejemplo... PRINT "Prueba" PRINT 25 PRINT 74 PRINT "Fin prueba" Escribira... Prueba 25 74 Fin Prueba Pero de esto no nos tenemos que preocupar. Si intentamos arreglarlo vamos a complicar nuestros programas innecesariamente. Ya habr tiempo de dibujar pantallas en los temas de ampliacin. Una cosa que s podemos hacer ya es usar lo que se llama "Posiciones de tabulacin". Esto es que QBasic divide cada lnea de la pantalla en posiciones que comienzan cada 14 caracteres, la primera en la posicin 1, la segunda en la 14, la tercera en la 28, etc. Y si nosotros separamos las expresiones de la orden PRINT con comas en vez de puntos y comas los textos se escribirn en estas posiciones en vez de justo pegado a la anterior. Vamos con un ejemplo: PRINT "Uno", "Dos", "Tres" PRINT "Cuatro", "Cinco", "Seis" PRINT "Siete", "Ocho", "Nueve" Que escribira en pantalla... Uno Dos Tres Cuatro Cinco Seis siete Ocho Nueve Esto ser muy til en los listados que veremos ms adelante. Por supuesto en una misma instruccin podemos separar unas cosas con comas y otras con puntos y comas, segn haga falta.

TEMA 1.5 OPERADORES Y EXPRESIONES


y y y y y y

1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6

Introduccin a los operadores Operador de asignacin Contadores y acumuladores Operadores aritmticos Operadores relacionales o de comparacin Operadores lgicos

19

1.5.1 - Introduccin a los operadores En este tema vamos a ver como construir las expresiones matemticas y lgicas necesarias para que nuestros programas sea capaces de hacer clculos. Una expresin sera lo equivalente a las frmulas que escribimos en una hoja de clculo (Excel), es decir una sucesin de nmeros, operadores (signos ms, menos, etc.) y nombres de variables, entre otras cosas, colocados en el orden correcto. 1.5.2 - Operador de asignacin Lo primero que vamos a ver en este tema es un operador que nos permita guardar "algo" en una variable. En el programa Saludador para guardar nuestro nombre en la variable nombre$ usbamos directamente la instruccin INPUT que se encargaba de leer los datos del teclado y guardarlos directamente en la variable. Aqu no emplebamos el operador de asignacin, pero despus hemos hecho cosas como: mensaje$ = "Prueba superada" o al declarar las constantes hacamos: CONST iva = 16 En estos ejemplos se puede ver que hemos usado el signo Igual para "Asignar" a la variable que hay a su izquierda el valor de la "Expresin" que hay a su derecha, por lo tanto ya podemos decir que el signo igual es el operador de asignacin en el lenguaje Basic. En el caso ms sencillo: total = 500 hacemos que en la variable total se almacene el nmero 500, perdindose el valor que tuviera anteriormente. Tambin podemos hacer: total = total + 100 En este caso estamos usando la variable total como un "acumulador" ya que vamos a almacenar en ella el valor que tenga antes de la asignacin ms cien. Dicho de otra forma, si la variable vala 500 ahora le vamos a almacenar su valor de 500 ms 100, con lo que al final de la asignacin pasar a valer 600. En el siguiente apartado se explican algunas cosas sobre estas asignaciones. Es importante tener claro que a una variable solo le podemos asignar los datos adecuados a su tipo, por ejemplo si a una variable de cadena le asignamos una expresin numrica o a una variable numrica le asignamos una cadena se producir un Error de Tiempo de Ejecucin y el programa se parar. Tambin hay que tener en cuenta que si a una variable de tipo entero le asignamos una expresin cuyo valor es con decimales, el nmero almacenado se redondear, por ejemplo... num% = 10 / 3 har que num% valga 3 en vez de 3.3333, igual que num% = 20 / 3

20

har que num% valga 7 en vez de 6.6666. Esta vez se ha redondeado hacia arriba. Tiene que quedar claro que la variable "destino" siempre va a la izquierda del signo igual, y la expresin a la derecha. Las expresiones nunca van a ir solas. Siempre van en una asignacin o en una estructura condicional que ya veremos ms adelante. 1.5.3 - Contadores y acumuladores Estas dos palabras se usan mucho en programacin para referirse a variables que van incrementando su valor a lo largo de la ejecucin del programa. Normalmente sern de tipo numrico y no se tratan de ninguna forma en especial, solo que al asignarles un valor se hace de forma que el anterior no se pierda, por ejemplo: nivel = nivel + 1 total = total + subtotalLinea vidas = vidas - 1 tamao = tamao * 2 Es muy importante inicializarlas de forma correcta siguiendo estas normas: y Si vamos a sumar o restar la inicializaremos a cero al principio del programa para que no empiecen con valores residuales. y Si vamos a multiplicar las inicializamos a 1, porque si valen cero todo lo que multipliquemos por ella seguir valiendo cero. La diferencia entre acumuladores y contadores es que los acumuladores se incrementan con cualquier nmero, como por ejemplo el total de una factura, mientras que los contadores se incrementan siempre con el mismo nmero, normalmente 1. 1.5.4 - Operadores aritmticos Llamamos operadores aritmticos a los signos que usaremos para hacer operaciones aritmticas como sumas, restas, etc.
OPERADOR + * / MOD ^ Suma Resta Multiplicacin Divisin Resto de divisin (Mdulo) Potencias NOMBRE

21

El operador suma lo que hace es sumar valores. Se pueden encadenar tantas sumas como queramos, por ejemplo total = 2 + 74 + 7 + 25 + 82 El operador resta resta un valor a otro, por ejemplo: neto = peso - tara El operador asterisco hace multiplicaciones, por ejemplo: elDoble = n * 2 El operador divisin divide un valor entre otro, por ejemplo: kilos = gramos / 1000 El operador MOD nos devuelve el resto de una divisn. Es especialmente til para deducir si un nmero es par, ya que al dividir un par entre 2 el resto siempre es 0. Se usa entre los dos operandos igual que los smbolos anteriores, veremos su funcionamiento y su utilizacin en los temas siguientes. El operador "acento circunflejo" sirve para calcular potencias, por ejemplo 5^2 es cinco al cuadrado o tambin 5*5. Tambin podramos calcular potencias decimales como 4^2.5. Para escribir este smbolo pulsa maysculas y la tecla correspondiente dos veces, oirs un pitido y solo entonces aparecer escrito en la pantalla. Si aparece dos veces borra uno. 1.5.5 - Operadores relacionales o de comparacin Estos operadores hacen que una expresin devuelva un valor lgico, es decir, en vez de un nmero devolver VERDADERO o FALSO. Esto nos ser muy til en las estructuras condicionales que veremos en los siguientes temas, dnde veremos muchos ejemplos de su utilizacin. OPERADOR = > < >= <= <> NOMBRE Igual Mayor que Menor que Mayor o igual que Menor o igual que Distinto de

En QBasic el signo igual sirve tanto para asignaciones como para comparaciones, pero nunca se confunden ya que QBasic los evala de una u otra forma segn dnde estn escritos.

22

1.5.6 - Operadores lgicos Estos operadores tambin devuelven un valor VERDADERO o FALSO a partir de los valores de las dos expresiones lgicas que unen. Igual que los anteriores se explicarn en los temas siguientes dedicados a estructuras condicionales. OPERADOR AND OR NOT XOR EQV IMP NOMBRE Operador Y Operador O Operador monario de negacin Operador O exclusivo Operador de equivalencia Operador de implicacin

Los tres primeros son las puertas lgicas elementales del lgebra de Boole, los otros se pueden construir a partir de las anteriores y por eso no suelen estar en otros lenguajes de programacin y no hablaremos de ellos aqu. AND devuelve verdadero si las dos expresiones que une son verdaderas, en caso contrario devuelve falso. Por ejemplo: esNavidad = ((mes = 12) AND (dia = 25)) Podemos asegurar que es Navidad si el mes es 12 y el da es 25. Si el mes no es diciembre no ser navidad aunque estemos a 25, tampoco si es un da de diciembre distinto de 25 y mucho menos si ni es diciembre ni es 25. Usamos por primera vez los parntesis para dar ms claridad, ms adelante se explica cmo hacerlo. OR devuelve verdadero si alguna de las dos expresiones que une es verdadera, o las dos lo son. Si las dos son falsas devuelve falso, por ejemplo: puedesComprar = ((tuDinero > 0) OR (precio = 0)) En este caso la variable puedesComprar sera verdadero si tu dinero es mayor que cero (aprovechamos para dar un ejemplo del operador >) o si el precio es gratis, o las dos cosas. Solo sera falso si no tienes dinero y el producto a comprar vale dinero, con lo que las dos partes de la expresin seran falsas y el resultado tambin. NOT es un operador "monario". Esto significa que slo tiene un operando, a diferencia de los otros que siempre estn entre dos operandos. Lo que hace NOT es invertir el resultado de una expresin, es decir, si es verdadera devuelve falso y si es falsa devuelve verdadero.

23

Para detallar los posibles valores que devuelven los operadores lgicos se construyen las llamadas "Tablas de la verdad" que representan todas las combinaciones posibles y los valores devueltos por cada operador.

TABLA DE LA VERDAD PARA AND VERDADERO VERDADERO FALSO FALSO AND AND AND AND VERDADERO FALSO VERDADERO FALSO = = = = VERDADERO FALSO FALSO FALSO

TABLA DE LA VERDAD PARA OR VERDADERO VERDADERO FALSO FALSO OR OR OR OR VERDADERO FALSO VERDADERO FALSO = = = = VERDADERO VERDADERO VERDADERO FALSO

TABLA DE LA VERDAD PARA NOT NOT NOT VERDADERO FALSO = = FALSO VERDADERO

Observa que la variable puedesComprar y la esNavidad del ejemplo anterior pueden ser de cualquier tipo numrico para poder ser verdaderas o falsas. QBasic entiende que una variable (o el resultado de una expresin) es falsa si vale 0 y verdadera en cualquier otro

24

caso. Otros lenguajes tienen un tipo de datos especfico para estas situaciones, pero aqu puede valer cualquier tipo de datos numrico. Es normal declarar constantes al principio de los programas para poder usar las palabras VERDADERO y FALSO en las expresiones y darle ms claridad. Se hara: CONST FALSO = 0 pero para verdadero podemos hacer... CONST VERDADERO = NOT FALSO con lo que hacemos que VERDADERO sea justo lo contrario de FALSO. Muy lgico.

TEMA 1.6 FUNCIONES INCLUIDAS EN EL LENGUAJE


El lenguaje BASIC incluye un montn de funciones que nos harn algunos clculos sin necesidad de tener que programar nosotros todo lo necesario. Una funcin es una palabra que, insertada dentro de una expresin, llama a un pequeo "programita" que hace los clculos y nos devuelve un resultado. Veamos este ejemplo: CLS INPUT "Escribe un nmero: ", num raiz = SQR(num) PRINT "Su raz cuadrada es ";raiz Este programa nos pide que escribamos un nmero y lo guarda en la variable num. A continuacin calcula la raz cuadrada del nmero usando la funcin SQR y gualda el resultado en la variable raiz para al final escribirlo en pantalla. No hemos tenido que programar ni conocer las operaciones matemticas necesarias para calcular la raz cuadrada de un nmero, lo ha hecho automticamente la funcin. Observa la forma de decirle a la funcin cual es el nmero que queremos que utilice para calcular la raz cuadrada: Lo metemos entre parntesis despus del nombre de la funcin. Este nmero se dice que es un Parmetro que pasamos a la funcin. Si alguna funcin no necesita parmetros no ponemos nada, en otros lenguajes hay que poner los parntesis vacos detrs del nombre de la funcin, pero en QBasic no. El parmetro no tiene por que ser un nmero constante, puede ser a su vez otra expresin. En la instruccin PRINT podemos incluir cualquier expresin, por lo tanto en el ejemplo anterior nos podamos haber ahorrado la variable raz escribiendo directamente: CLS INPUT "Escribe un nmero: ", num PRINT "Su raz cuadrada es "; SQR(num) Las funciones tienen tipo de datos como las variables, es decir, nos devuelven un resultado que puede ser cadena, entero, etc. y por lo tanto pueden llevar un sufijo de identificacin de tipo a continuacin del nombre, por ejemplo la funcin TIMER nos devuelve el nmero de

25

segundos que han pasado desde las doce de la noche en formato numrico: segundos = TIMER mientras que la funcin TIME$ nos devuelve la hora actual en formato de cadena de texto con la hora, los minutos y los segundos separados por dos puntos: horaActual$ = TIME$ PRINT "Son las "; horaActual$ En este caso para guardar el valor devuelto por TIME$ hemos tenido que usar una variable de cadena. Ambas funciones no llevan parmetros porque si lo que hacemos es preguntar la hora no tenemos que decir nada ms, ya la propia funcin ver como saca la hora del reloj interno del ordenador. Si una funcin lleva varios parmetros se pondrn separados por comas dentro de los parntesis, por ejemplo: PRINT STRING$ (20,"Z") La funcin STRING$ devolver una cadena con veinte zetas, es decir "ZZZZZZZZZZZZZZZZZZZZ". Para hacerlo necesita saber dos cosas: cuantas y cual letra tiene que repetir, por eso le damos dos parmetros separados por una coma. Observa tambin en el ejemplo que el primer parmetro debe de ser un nmero (o una expresin numrica) y el segundo tiene que ser una cadena (o una expresin de cadenas). Para saber de qu tipo es cada parmetro y el tipo del resultado que devuelve la funcin es muy importante consultar la ayuda de QBasic todas las veces que haga falta. Si intentamos pasar parmetros de otro tipo se producir un error de tipos y el programa se parar. Tambin se producir un error de "Llamada a funcin no vlida" si la funcin no es capaz de hacer los clculos con los parmetros que le hemos pasado, aunque sean del tipo correcto. Por ejemplo, sabemos que no existe raz cuadrada para los nmeros menores que cero, por lo que se producir un error de este tipo si hacemos PRINT SQR (-14) En caso de que pasemos como parmetro una expresin, por ejemplo PRINT SQR(miVariable) hay que tener mucho cuidado de que esta expresin no pueda llegar a valer menos que cero. Estas son algunas de las funciones ms usadas. En temas posteriores irn apareciendo ms. Para verlas todas y todos los tipos de datos que necesitan y devuelven mira la ayuda del QBasic.
FUNCIN SQR(num) DESCRIPCIN Raz cuadrada Pasa a entero qitando decimales EJEMPLO SQR(16) devuelve 4

INT(decimal)

INT(1.6) devuelve 1

26

CINT(decimal)

Redondea a entero (Max = 32767) Redondea a entero largo Calcula el seno Calcula el coseno Calcula la tangente Devuelve la longitud de la cadena Devuelve una cadena sin espacios al final Devuelve la cadena pasada a maysculas (No funciona col la ni con los acentos) Devuelve una subcadena del tamao indicado a parir de la posicin indicada. Devuelve una cadena formada por un nmero de caracteres repetidos Devuelve la hora actual como cadena Devuelve la fecha actual como cadena en formato mes-dia-ao Devuelve la letra de la

CINT(1,6) devuelve 2

CLNG(decimal)

CLNG(100000.2) devuelve 100000

SIN(num)

SIN(40) devuelve 0.64278

COS(num)

COS(40) devuelve 0.76604

TAN(num)

TAN(40) devuelve 0.839009

LEN(Cadena)

LEN("RONDA") devuelve 5

RTRIM(Cadena)

RTRIM("Fin. "Fin."

") devuelve

UCASE$(Cadena)

UCASE$("Toma Castaa") devuelve "TOMA CASTAA"

MID$(Cadena, inicio, largo)

MID$("Programacin",5,4) devuelve "rama"

STRING$(largo, carcter)

STRING$(10,"#") devuelve "##########"

TIME$

Devuelve por ejemplo "16:45:08"

DATE$

Devuelve por ejemplo "0120- 2011"

INKEY$

Devolvera "A" si la ltima tecla pulsada fue la "A

27

ltima tecla pulsada antes de llegar a ejecutar la funcin.

mayscula"

En una de las ampliaciones de este curso se habla de un conjunto de funciones muy tiles para el manejo de cadenas y en otro se explica en detalle la funcin INKEY$ que es especialmente til para detectar teclas pulsadas en mens, juegos, preguntas que se responden con s o no, etc.

TEMA 1.7 ESTRUCTURAS CONDICIONALES IF


y y y y y y y

1.7.1 1.7.2 1.7.3 1.7.4 1.7.5 1.7.6 1.7.7

Introduccin a los bloques de control Alternativa simple. Instruccin IF Sangra Anidacin Instruccin IF simplificada Alternativa doble. Instruccin IF-THEN-ELSE Alternativa mltiple. Instruccin ELSEIF

1.7.1 - INTRODUCCIN A LOS BLOQUES DE CONTROL Hasta ahora al ejecutar nuestros programas se han ejecutado todas las instrucciones que aparecen en el listado del cdigo fuente del programa, desde la primera hasta la ltima. A partir de ahora vamos a poder conseguir que una o varias instrucciones solo se lleguen a ejecutar si se cumple una condicin, que si no se cumple se ejecuten otras, o incluso que algunas instrucciones se ejecuten ms de una vez (Esto lo veremos en los siguientes temas). 1.7.2- ALTERNATIVA SIMPLE. INSTRUCCIN IF Empecemos con un ejemplo. Un programa que nos pida la nota de un examen y nos felicite si est aprobado. CLS INPUT "Escribe la nota del examen: ", nota IF nota >= 5 THEN PRINT "Enhorabuena, has aprobado." END IF PRINT "Gracias por usar el programa." Ahora ejecutemos el programa. Hemos sacado un 7: Escribe la nota del examen: 7 Enhorabuena, has aprobado. Gracias por usar el programa. Volvamos a lanzar el programa, esta vez hemos sacado un 4: Escribe la nota del examen: 4 Gracias por usar el programa. Esta ltima vez se ha saltado una de las instrucciones del listado Por qu?

28

Porque hemos usado la instruccin IF (si en ingls), una de las ms importantes de todo lenguaje de programacin estructurado, y le hemos puesto como condicin que el valor almacenado en la variable nota sea mayor o igual que cinco con lo que hacemos que todas las instrucciones que haya entre IF y END IF solo se ejecuten si esta expresin es VERDADERA. Vamos a ver con ms tranquilidad la sintaxis de la instruccin IF IF condicin THEN instrucciones END IF SI la condicin es VERDADERA, ENTONCES (THEN) se ejecutarn las instrucciones hasta llegar a END IF (FIN SI). Despus se seguir con el resto del programa Si la condicin es falsa se saltar todo el bloque IF y se ejecutarn las siguientes instrucciones del programa que haya despus del END IF. 1.7.3 - SANGRA En el ejemplo anterior puedes ver que las instrucciones que hay dentro del bloque IF estn escritas un poco ms a la derecha que las dems, no justo en el margen izquierdo. Esto se hace por comodidad, para poder saber de un vistazo cuales son las instrucciones que hay dentro del bloque. En la mayora de lenguajes no es obligatorio (En COBOL s), pero siempre es muy recomendable hacerlo as para dar mayor claridad al cdigo fuente del programa. Para hacer esto basta con pulsar la tecla TABULADOR antes de escribir la instruccin, mejor que escribir espacios. En las opciones de QBasic se puede especificar el tamao de la tabulacin (Cuantos espacios se escriben). Por defecto tiene 8, pero es mejor un nmero ms bajo entre 3 y 5 como ya veremos ms adenante. Esta tcnica tambin se conoce como encolumnado o indentacin (En ingls se llama indent). 1.7.4 - ANIDACIN Como hemos visto en la sintaxis de la instruccin IF, dentro del bloque de instrucciones a ejecutar se puede usar cualquier instruccin, por lo tanto tambin se pueden usar otras instrucciones IF. A esto es a lo que se llama anidacin de instrucciones. Veamos un ejemplo que nos diga si un nmero es par (usando el operador MOD) y en este caso nos diga tambin si el nmero es mayor que 10: CLS INPUT "Escribe un nmero: ", num IF num MOD 2 = 0 THEN PRINT "Es un nmero par" IF num > 10 THEN PRINT "Es mayor que 10" END IF END IF

29

Ejemplos de la ejecucin de este programa pueden ser... Escribe un nmero: 7 Escribe un nmero: 8 Es un nmero par Escribe un nmero: 12 Es un nmero par Es mayor que 10 Aqu si la primera condicin se cumple se escribe "Es par" y adems se hace otra comprobacin que se podr cumplir o no. Nunca se va a llegar a la segunda comprobacin si la primera no se ha cumplido. Es muy importante observar que cada IF lleva su END IF correspondiente y que el bloque de instrucciones del segundo IF est encolumnado todava ms a la derecha que el anterior. Cuando escribimos un programa en papel es comn unir con una lnea cada IF con su END IF para aclarar el listado.

1.7.5 - INSTRUCCIN IF SIMPLIFICADA Si el bloque de instrucciones de una instruccin IF slo va a llevar una instruccin podemos escribirla en la misma lnea detrs del THEN y ahorrarnos el END IF. Para el ejemplo del examen podamos haber hecho: CLS INPUT "Escribe la nota del examen: ", nota IF nota >= 5 THEN PRINT "Enhorabuena, has aprobado." PRINT "Gracias por usar el programa." Esto es til en algunos casos, pero si vemos que nos vamos a liar es mejor poner el END IF como hemos visto antes, aunque si nos acostumbramos a poner siempre los encolumnados no hay por qu equivocarse. 1.7.6 - ALTERNATIVA DOBLE. INSTRUCCIN IF-THEN-ELSE En el ejemplo del examen estara bien que "si no" aprueba, decirle que ha suspendido. Lo podemos hacer de esta forma: CLS INPUT "Escribe la nota del examen: ", nota IF nota >= 5 THEN PRINT "Enhorabuena, has aprobado." ELSE PRINT "Lo siento, has suspendido." END IF PRINT "Gracias por usar el programa."

30

En este caso se comprueba la condicin del IF. Si es verdadera se ejecuta su bloque de instrucciones y despus se sigue con lo que venga detrs del END IF, igual que antes. Ahora viene lo nuevo, si la condicin no se cumple se ejecuta el bloque de instrucciones del ELSE hasta el END IF, y despus se sigue con lo que haya detrs. De esta forma nos aseguramos de que siempre se ejecuta uno y solo uno de los dos bloques de instrucciones segn la condicin sea verdadera o falsa. Dentro del bloque del ELSE tambin puede ir cualquier tipo de instruccin, incluido otro bloque IF o del tipo que sea. Veamos un ejemplo "especialmente malo" de un programa que nos diga el nombre de un mes a partir de su nmero. En temas posteriores simplificaremos bastante este problema. CLS INPUT "Escribe el nmero del mes: ", mes IF mes = 1 THEN PRINT "Enero" ELSE IF mes = 2 THEN PRINT "Febrero" ELSE IF mes = 3 THEN PRINT "Marzo" ELSE IF mes = 4 THEN PRINT "Abril" ELSE IF mes = 5 THEN PRINT "Mayo" ELSE IF mes = 6 THEN PRINT "Junio" ELSE IF mes = 7 THEN PRINT "Julio" ELSE IF mes = 8 THEN PRINT "Agosto" ELSE IF mes = 9 THEN PRINT "Septiembre" ELSE IF mes = 10 THEN PRINT "Octubre" ELSE IF mes = 11 THEN PRINT "Noviembre"

31

ELSE IF mes = 12 THEN PRINT "Diciembre" ELSE PRINT "Mes no vlido" END IF END IF END IF END IF END IF END IF END IF END IF END IF END IF END IF END IF PRINT "S'ACAB" Muy sencillo: Si es enero lo escribimos, si no miramos si es febrero, si no a ver si es marzo, etc. as hasta diciembre. Si no es diciembre sacamos un mensaje de mes no vlido. Observa que cada IF tiene su ELSE y su END IF correspondiente abajo. Normalmente nunca llegaremos a estas estructuras tan anidadas, pero aqu se puede ver la importancia de que el tamao de tabulacin no sea muy grande. Observa que si haces que la ventana del navegador sea ms estrecha podra pasar (segn los programas) que algunas lneas de las ms largas pasan abajo y se nos estropea toda nuestra jerarqua. En QBasic y en los otros editores de programacin esto no ocurre porque no tienen salto de lnea automtico para que no pase eso, pero de todas formas es muy incmodo tener que ir moviendo la barra de desplazamiento horizontal a izquierda y derecha para ver nuestro listado. 1.7.7 - ALTERNATIVA MLTIPLE. INSTRUCCIN ELSEIF Vamos a arreglar un poco el ejemplo anterior para no tener tantos END IF. CLS INPUT "Escribe el nmero del mes: ", mes IF mes = 1 THEN PRINT "Enero" ELSEIF mes = 2 THEN PRINT "Febrero"

32

ELSEIF mes = 3 THEN PRINT "Marzo" ELSEIF mes = 4 THEN PRINT "Abril" ELSEIF mes = 5 THEN PRINT "Mayo" ELSEIF mes = 6 THEN PRINT "Junio" ELSEIF mes = 7 THEN PRINT "Julio" ELSEIF mes = 8 THEN PRINT "Agosto" ELSEIF mes = 9 THEN PRINT "Septiembre" ELSEIF mes = 10 THEN PRINT "Octubre" ELSEIF mes = 11 THEN PRINT "Noviembre" ELSEIF mes = 12 THEN PRINT "Diciembre" ELSE PRINT "Mes no vlido" END IF PRINT "S'ACAB" A la Palabra Clave ELSE le hemos colocado directamente la otra condicin para simplificar un poco el Algoritmo, pero viene a ser prcticamente lo mismo. Veamos la sintaxis de forma un poco ms clara: IF condicin THEN bloqueInstrucciones ELSEIF otra condicin THEN bloqueInstrucciones ELSE bloqueInstrucciones END IF Si la condicin del IF se cumple se ejecutan sus instrucciones y ya est. Si no, se comprueba la condicin del primer ELSEIF y si es verdadera se ejecutan sus instrucciones y ya est. Si no, se comprueba la condicin del siguiente ELSEIF y si es verdadera se ejecutan sus instrucciones y ya est. Si la condicin del ltimo ELSEIF no se cumple se ejecuta el bloque ELSE si existe. Puede haber tantos bloques ELSEIF con su condicin como sea necesario, pero solo un bloque ELSE (o ninguno) al final. Si no hay bloque ELSE puede suceder que no se ejecute nada porque no se cumpla ninguna de las condiciones. Las condiciones no tienen que estar relacionadas de ninguna forma, aqu siempre hemos preguntado por el mes, pero podamos haber comprobado cualquier otra cosa, o poner los meses desordenados.

33

Esta estructura ELSEIF no se usa mucho y no existe en algunos otros lenguajes de programacin, en su lugar se usan los IF anidados como vimos en el ejemplo largusimo anterior o la estructura SELECT que veremos en el tema siguiente. Una ltima cosa antes de acabar con los IF. Si escribimos ENDIF todo junto, QBasic y Visual Basic nos lo corrigen automticamente. Esto es porque en alguna versin muy antigua del lenguaje BASIC se escriba as.

34

TEMA 1.8 ESTRUCTURA DE SELECCIN SELECT


En el ejemplo de instrucciones IF anidadas nos sala un pedazo de listado para decir el nombre del mes. Despus lo arreglamos un poco con las instrucciones ELSEIF. Ahora vamos a hacerlo todava un poco mejor. CLS INPUT "Escribe el nmero del mes: ", mes SELECT CASE mes CASE 1 PRINT "Enero" CASE 2 PRINT "Febrero" CASE 3 PRINT "Marzo" CASE 4 PRINT "Abril" CASE 5 PRINT "Mayo" CASE 6 PRINT "Junio" CASE 7 PRINT "Julio" CASE 8 PRINT "Agosto" CASE 9 PRINT "Septiembre" CASE 10 PRINT "Octubre" CASE 11 PRINT "Noviembre" CASE 12 PRINT "Diciembre" CASE ELSE PRINT "Mes no vlido" END SELECT PRINT "S'ACAB" Hemos usado una estructura nueva: La instruccin SELECT. Esta estructura es equivalente a las anteriores de IF anidados, pero es ms fcil de manejar y el programa queda ms estructurado. Si has entendido la estructura IF no te ser muy difcil entender esta. Lo primero es escribir las Palabras Clave SELECT CASE seguidas de una expresin. Esta expresin es normalmente simplemente una variable que puede ser de cadena o numrica. En este caso no tiene que devolver VERDADERO o FALSO como en los IF ya que no se usan operadores relacionales ni lgicos, solo los aritmticos cuando hace falta. A continuacin para cada resultado posible se pone la Palabra Clave CASE y la expresin a comparar con la del principio. Si la comparacin es verdadera se ejecuta el bloque de instrucciones entre

35

este CASE y el siguiente y se sale de la estructura. Si la condicin es falsa se ejecuta el bloque del CASE ELSE si existe y si no nada. Las expresiones de los cases se pueden poner de una de las siguientes formas: CASE 1 Una expresin (En este caso un nmero), igual que en el ejemplo. CASE 1, 2, 3 Varias expresiones separadas por comas. CASE 1 TO 3 Un intervalo de valores, ambos inclusive, separados por la palabra clave TO. En este caso si la expresin inicial era de tipo entero sern vlidos los resultados 1, 2 y 3, pero si era de tipo real sern vlidos todos los nmeros posibles entre el 1 y el 3 como por ejemplo el 1.517512 y el 2.17521. CASE IS > 2 CASE IS = 5 CASE IS <> 8 CASE IS <= 6 Usando operadores relacionales. En el primer ejemplo sern vlidos todos los valores mayores que 2, en el segundo slo el 5, en el tercero cualquiera menos el 8 y en el ltimo los que sean menor o igual que 6. Si se nos olvida la palabra clave IS, QBasic la escribir por nosotros. Normalmente escribiremos las expresiones de los CASE de la forma ms sencilla posible evitando intervalos muy extraos y teniendo cuidado con los operadores relacionales para no dejarnos "fuera" ningn valor posible, pero podemos llegar a escribir una estructura SELECT tan mal hecha como esta sin que el programa de ningn error de ejecucin. SELECT CASE n CASE 1 CASE 1 CASE 3 CASE 2 TO 4 CASE IS < 5 CASE IS <= 5 CASE <> 100 CASE 16 TO 34 END SELECT Hay expresiones repetidas, valores que entran en varias expresiones como el 3, un desastre. Que ocurrira aqu? Lo primero es que nunca ocurrira nada porque no hemos puesto bloques de instrucciones en los CASE, pero si las hubiramos puesto pasara lo siguiente. QBasic empieza a comprobar por los CASE hasta que encuentre uno que le venga bien. Cuando lo encuentra ejecuta su bloque de instrucciones y sale del SELECT CASE aunque otros bloques posteriores tambin hubieran servido. En este caso si el valor de n es 1 se ejecuta el primer CASE 1, el segundo no se llega a ejecutar nunca. Si el valor de n es 3 se ejecuta el CASE 3 y se sale, aunque

36

los cuatro siguientes tambin hubieran servido. Si el valor de n es 100 no se ejecuta nada porque ningn CASE sirve y tampoco hay un CASE ELSE. Las expresiones de los cases normalmente sern simplemente nmeros (o cadenas) y algn intervalo alguna vez para poder estar seguro de que se va a ejecutar siempre el bloque correcto y nuestro programa va a funcionar bien. Entre el SELECT CASE y el primer CASE no puede haber nada. Si los bloques de instrucciones van a llevar slo una instruccin sencilla podemos ponerla a continuacin de la expresin del CASE separndola con dos puntos, por ejemplo nuestro ejemplo de los meses quedara as: SELECT CASE mes CASE 1: PRINT "Enero" CASE 2: PRINT "Febrero" CASE 3: PRINT "Marzo" CASE 4: PRINT "Abril" CASE 5: PRINT "Mayo" CASE 6: PRINT "Junio" CASE 7: PRINT "Julio" CASE 8: PRINT "Agosto" CASE 9: PRINT "Septiembre" CASE 10: PRINT "Octubre" CASE 11: PRINT "Noviembre" CASE 12: PRINT "Diciembre" CASE ELSE: PRINT "Mes no vlido" END SELECT con lo que conseguimos un listado casi la mitad ms corto. Por supuesto las expresiones tambin pueden ser de cadenas: INPUT "Escribe el nombre de un perifrico del ordenador: ", perif$ SELECT CASE perif$ CASE "Teclado", "Ratn" PRINT "Es un perifrico de entrada" CASE "Monitor", "Impresora" PRINT "Es un perifrico de salida" CASE "Mdem" PRINT "Es un perifrico de entrada/salida" CASE ELSE PRINT "Este perifrico no lo conozco" END SELECT Y por supuesto los bloques de instrucciones de los CASE pueden contener cualquier tipo de instrucciones anidadas en su interior como bloques IF, otro SELECT, etc. Si estos bloques se hacen muy largos no te asustes, solucionaremos el problema cuando lleguemos a Programacin Modular donde el SELECT servir como men para dar entrada a distintos procedimientos o subprogramas.

37

Casi todos los lenguajes tienen una estructura equivalente a SELECT, pero en el caso del lenguaje C es bastante mala. Hay que tener en cuenta de que en los lenguajes de los sistemas de bases de datos (SQL) existe una instruccin SELECT que no tiene nada que ver con esto, sirve para sacar informacin de las bases de datos.

TEMA 1.9 ESTRUCTURA DE REPETICIN FOR...NEXT


y y y

1.9.1 - Introduccin a las estructuras de control repetitivas 1.9.2 - Cmo detener la ejecucin de un programa 1.9.3 - Instruccin FOR...NEXT

1.9.1 - Introduccin a estructuras de control repetitivas En los siguientes temas vamos a ver las instrucciones que existen en la Programacin Estructurada para conseguir que un bloque de instrucciones se puedan ejecutar ms de una vez sin necesidad de escribirlas repetidas en el listado del cdigo fuente del programa. En lenguaje ensamblador y en las versiones antiguas da BASIC se usan instrucciones de tipo GOTO que continan la ejecucin del programa en otra parte, pero esto da lugar a programas muy reliados (Cdigo "espagueti") que son muy difciles de depurar y pueden contener errores. Para solucionar el problema en la Programacin Estructuradas existen estructuras de control que encierran un conjunto de instrucciones (con una instruccin al principio y otra al final) y lo que hacen es ejecutar el bloque de instrucciones entero un nmero determinado de veces, mientras se cumpla una condicin o hasta que se cumpla una condicin, segn sea la estructura. A estas estructuras tambin se las conoce como "Bucles" o "Lazos". 1.9.2 - Cmo detener un programa Al usar estas estructuras nos podemos encontrar con el problema de que si el programa no est escrito correctamente nunca se salga de la estructura de control producindose el efecto llamado "Bucle infinito" que puede llegar a bloquear el ordenador. En un programa normal ya compilado y terminado que se ejecute bajo Windows puede pasar que el ordenador se bloquee y aparezca una pantalla azul recuerdo de Bill Gates de tipo "El sistema est ocupado o no responde...", con lo que casi seguro que vamos a tener que reiniciar el ordenador. En los entornos de programacin esto normalmente no llegar a ocurrir. En caso de que nuestro programa se bloquee puede que se agoten los recursos del sistema y el programa se detenga dando un error de tiempo de ejecucin y volviendo al editor de cdigo. Si el programa se queda bloqueado se puede pulsar la siguiente combinacin de teclas: Control + Pausa Para detener la ejecucin del programa y volver al editor de cdigo donde habr que repasar el cdigo para que esto no ocurra y el

38

programa funcione siempre bien. En algunos casos tras pulsar esta combinacin de teclas habr que pulsar una vez la tecla ENTER para desbloquear el programa. 1.9.3 - Instruccin FOR...NEXT Empecemos con un ejemplo como siempre. Vamos a escribir un programa que escriba los nmeros del 1 al 5 usando las instrucciones que ya conocemos. CLS PRINT 1 PRINT 2 PRINT 3 PRINT 4 PRINT 5 Como se puede ver es un programa bastante tonto. Hay cinco instrucciones casi iguales. Solo cambia el valor de la expresin que cada vez vale lo que en la instruccin anterior ms uno, por lo tanto tambin podamos haber hecho esto: CLS n = 0 n = n + 1 PRINT n n = n + 1 PRINT n n = n + 1 PRINT n n = n + 1 PRINT n n = n + 1 PRINT n Se puede comprobar que el resultado es el mismo que en el programa anterior y ahora s que tenemos cinco pares de instrucciones completamente idnticos. Vamos a hacer el mismo programa con la nueva instruccin FOR ("Para" en castellano): CLS FOR n = 1 TO 5 PRINT n NEXT Ya est. Mira que sencillo, pero ahora viene la explicacin. Esto lo que hace es que se ejecute lo que hay entre el FOR y el NEXT cinco veces siguiendo estos pasos: y La primera vez n vale 1, como pone en la instruccin. y Se ejecuta el bloque de instrucciones con n valiendo 1 y AUTOMTICAMENTE n se incrementa en 1, pasando a valer 2 y Se comprueba que n es menor o igual que 5, y como lo es se sigue. y Se ejecuta el bloque de instrucciones con n valiendo 2 y AUTOMTICAMENTE n se incrementa en 1, pasando a valer 3

39

Se comprueba que n es menor o igual que 5, y como lo es se sigue. y Se ejecuta el bloque de instrucciones con n valiendo 3 y AUTOMTICAMENTE n se incrementa en 1, pasando a valer 4 y Se comprueba que n es menor o igual que 5, y como lo es se sigue. y Se ejecuta el bloque de instrucciones con n valiendo 4 y AUTOMTICAMENTE n se incrementa en 1, pasando a valer 5 y Se comprueba que n es menor o igual que 5, y como lo es se sigue. y Se ejecuta el bloque de instrucciones con n valiendo 5 y AUTOMTICAMENTE n se incrementa en 1, pasando a valer 6 y Se comprueba que n es menor o igual que 5, y como ya no lo es se sale del bucle y se ejecuta la siguiente instruccin que venga detrs del NEXT. Todo esto puede parecer muy complicado, pero con la prctica conseguiremos que esta sea una de las instrucciones ms fciles de entender de la programacin, slo habr que detenerse a pensar en estos pasos cuando algn programa no haga lo que queremos y no demos con el error. Veamos la sintaxis de la instruccin FOR: FOR contador = inicio TO final bloqueInstrucciones NEXT contador es la variable que usaremos como contador (el FOR la modifica automticamente) y tendr que ser de tipo numrico, normalmente entero aunque tambin puede ser real. Ya hemos hablado de los contadores en el tema de los operadores de asignacin. inicio es una expresin numrica cuyo valor tomar el contador la primera vez. final es una expresin numrica cuyo valor lo usar el FOR de forma que solo entrar si el contador no supera al valor de esta expresin. En nuestro ejemplo el final era 5 y cuando el contador (n) llegaba a valer 6 ya no entrbamos. Ahora vamos a ver dos normas muy importantes que hay que seguir siempre con los contadores de los FOR y No debemos modificar el valor de esta variable dentro del bucle, ya lo hace automticamente la instruccin FOR. Dicho de otra forma: No debemos asignar ningn valor a esta variable hasta despus de terminar el FOR. y Una vez terminado el FOR no debemos leer el valor de la variable contador porque su valor queda indeterminado. Podremos usar esta variable ms adelante si previamente le asignamos un valor antes de intentar leerla. Estas normas nos las podramos saltar sin dar un error de ejecucin, pero puede que el mismo algoritmo de distintos resultados en distintas versiones de BASIC, ya que el contador es manejado
y

40

internamente por el intrprete del lenguaje de programacin y puede que no siempre se haga de la misma forma. Los valores inicio y fin no tienen por que ser expresiones constantes. En este ejemplo escribiremos los nmeros desde uno hasta donde quiera el usuario: CLS INPUT "Escribe hasta dnde quieres llegar: ", max FOR n = 1 TO max PRINT n NEXT No es necesario que tengamos que usar siempre el valor del contador para calcular algo. Este FOR escribe "Hecho en Ronda" siete veces: FOR n = 1 TO 7 PRINT "Hecho en Ronda" NEXT y este hace exactamente lo mismo: FOR n = 82 TO 88 PRINT "Hecho en Ronda" NEXT El siguiente escribe los pares del 2 al 10, es decir, 2, 4, 6, 8, 10. FOR n = 1 TO 5 PRINT n * 2 NEXT En QBasic hay una forma de hacer esto ms fcilmente: FOR n = 2 TO 10 STEP 2 PRINT n NEXT Antes veamos que el FOR incrementa automticamente al contador en 1 en cada pasada. Usando la palabra clave STEP seguida de una expresin numrica conseguimos modificar este incremento. Otro ejemplo con STEP que se explica solo. CLS INPUT "Escribe un nmero: ", s PRINT "Estos son los nmeros del 0 al 100 de "; s; " en "; s FOR n = 0 TO 100 STEP s PRINT n NEXT Todo esto funciona muy bien, espero que se entienda. Pero puede surgir una duda, supongamos que escribimos el nmero 7 y el programa escribe de siete en siete, dando este resultado: Escribe un nmero: 7 Estos son los nmeros del 0 al 100 de 7 en 7 0 7 14 21 28 35 42

41

49 56 63 70 77 84 91 98 Como se puede ver, no se ha alcanzado el 100 es porque el siguiente valor que sera el 105 ya supera al 100 que es valor final del FOR y no se ejecuta. Tambin puede ocurrir que la expresin del STEP valga 0. En este caso el FOR incrementar en cero el contador con lo que nunca se llegar al valor final y se producir un bucle infinito. Habr que pulsar Ctrl+Pausa para detener el programa y corregir el cdigo. Ahora ya podemos hacer que un FOR funcione hacia atrs, escribiendo la expresin final menor que la inicial y una expresin negativa en el STEP. Como ejemplo un FOR que escriba los nmeros del 10 al 1. FOR n = 10 TO 1 STEP -1 PRINT n NEXT Si no usamos el STEP negativo y escribimos el valor final menor que el inicial, nunca se ejecutar el bloque FOR. Si un programa no funciona bien porque un FOR no se ejecuta nunca ser conveniente revisar esto. Como siempre, dentro del bloque FOR puede ir cualquier tipo de instrucciones, incluido otro FOR. Veamos un ejemplo: FOR i = 1 TO 8 FOR j = 1 TO 5 PRINT "Hola" NEXT NEXT Cuantas veces escribir "Hola" este programa? Si el FOR interior se ejecuta entero 8 veces y cada vez escribe "Hola" 5 veces, en total lo har 8 por 5 igual a 40 veces, es decir, el producto. Este tipo de instrucciones son especialmente tiles en algoritmos que ya veremos ms adelante como el recorrido de matrices. Hay que tener cuidado de no usar la misma variable contador para los dos FOR, ya que romperamos la regla de no modificar el valor del contador del primer FOR y el programa no funcionara bien. QBasic permite escribir a continuacin del NEXT el nombre del contador del FOR, por ejemplo: FOR i = 1 TO 8 FOR j = 1 TO 5 PRINT "Hola" NEXT j NEXT i

42

Esto puede ser til para saber en un listado muy complicado a que FOR corresponde cada NEXT, pero si encolumnamos correctamente nuestro programa esto no ser necesario. El FOR en Basic es bastante flexible. En otros lenguajes funciona de otra forma o incluso puede que ni siquiera exista, ya que como veremos a continuacin no es imprescindible para construir un algoritmo.

TEMA 1.10 ESTRUCTURA DE REPETICIN WHILE...WEND


En este tema vamos a ver una estructura repetitiva ms primitiva que el PARA ya que no maneja automticamente el contador y por lo tanto es ms difcil de utilizar, pero usada corrctamente puede ser bastante ms flexible. Recordemos el ejemplo de escribir los nmeros del 1 al 5 con la instruccin FOR. FOR n = 1 TO 5 PRINT n NEXT Ahora veremos cmo se hace lo mismo en QBasic usando la instruccin WHILE (Mientras). n = 1 WHILE n <= 5 PRINT n n = n + 1 WEND Esto lo que hace es ejecutar el bloque de instrucciones (Lo que hay entre el WHILE y el WEND) una y otra vez mientras se cumpla la condicin del WHILE. Un poco ms difcil que con el FOR. Vamos a verlo paso a paso: y Usaremos como contador la variable n y por tanto la tenemos que inicializar nosotros al valor que queramos que tenga la primera vez, en este caso 1. y Se comprueba la condicin del WHILE: como n vale 1 que es menor o igual que 5, entramos. y Se ejecutan las instrucciones del bloque con n valiendo 1. La ltima instruccin incrementa n en 1, con lo que pasa a valer 2. y Volvemos al WHILE y se comprueba su condicin: como n vale 2 que es menor o igual que 5, entramos. y Se ejecutan las instrucciones del bloque con n valiendo 2. La ltima instruccin incrementa n en 1, con lo que pasa a valer 3. y Volvemos al WHILE y se comprueba su condicin: como n vale 3 que es menor o igual que 5, entramos. y Se ejecutan las instrucciones del bloque con n valiendo 3. La ltima instruccin incrementa n en 1, con lo que pasa a valer 4. y Volvemos al WHILE y se comprueba su condicin: como n vale 4 que es menor o igual que 5, entramos. y Se ejecutan las instrucciones del bloque con n valiendo 4. La ltima instruccin incrementa n en 1, con lo que pasa a valer 5.

43

Volvemos al WHILE y se comprueba su condicin: como n vale 5 que es menor o igual que 5, entramos. y Se ejecutan las instrucciones del bloque con n valiendo 5. La ltima instruccin incrementa n en 1, con lo que pasa a valer 6. y Volvemos al WHILE y se comprueba su condicin: como n vale 6 que ya no es menor o igual que 5, no entramos y pasamos a la siguiente instruccin que haya detrs del WEND. Se puede ver que el funcionamiento es parecido al del FOR, solo que aqu lo tenemos que controlar todo nosotros. Las dos reglas que dijimos sobre los contadores del FOR ya aqu no tienen sentido porque de hecho nosotros vamos a tener que incrementar el contador haciendo una asignacin y una vez terminado podemos estar seguro del valor que tiene la variable. Una norma que s conviene respetar (Aunque no siempre es necesario) es que la instruccin que incrementa el contador sea la ltima del bloque, ya que si est en otro sitio ejecutaremos unas instrucciones con un valor y las dems con el otro, con lo que nos podemos liar. Un error muy tpico es que se nos olvide de poner la instruccin de incrementar el contador, produciendo un bucle infinito que har que nuestro programa no termine nunca. Si un programa se bloquea es conveniente revisar esto. Tambin puede pasar que no lleguemos a entrar al MIENTRAS porque la condicin ya sea falsa la primera vez, por ejemplo: contador = 120 WHILE contador < 100 PRINT "Esto no se va a llegar a escribir nunca. contador = contador + 1 WEND Hasta ahora hemos hablado de contador, pero como veremos en los ejemplos podemos usar un acumulador, o ninguno de los dos, ya que la condicin del WHILE puede ser cualquiera y no hay porqu contar ni acumular algo siempre. Veamos algunos ejemplos de MIENTRAS: Rutina que escribe del 0 al 100 de 2 en 2: c = 0 WHLE c <= 100 PRINT c c = c + 2 WEND Escribir de 50 hasta 1 hacia atrs: c = 50 WHILE c >= 1 PRINT c c = c - 1 WEND Calcular el factorial de un nmero que pedimos al usuario (Este ejemplo se mejorar en otro tema ms adelante): INPUT " nmero para calcular su factorial: ", num c = 1
y

44

factorial = 1 WHILE c <= num factorial = factorial * c c = c + 1 WEND PRINT "El factorial de; num; "es; factorial Leer nmeros por teclado hasta que se escriba el 0: INPUT "Escribe nmeros (0 para salir):, num WHILE num <> 0 INPUT "Escribe nmeros (0 para salir):, num WEND Este ltimo ejemplo presenta lo que se conoce como lectura anticipada. Antes de llegar al WHILE hemos tenido que conseguir el valor de la variable num porque si no lo hacemos puede pasar que num valga cero y por lo tanto no lleguemos a entrar al bucle. Esto es til en casos como la lectura de ficheros secuenciales, pero otras veces conviene evitarlo para no repetir instrucciones. Veremos como hacerlo en el siguiente tema. Una ltima cosa es que hay una teora en informtica que dice que cualquier algoritmo puede ser programado usando solamente instrucciones MIENTRAS. Es decir, ni bloques IF, ni ELSE, ni CASE, ni FOR, ni otras estructuras que veremos ms adelante. Yo no lo he comprobado, pero si a alguien le ha gustado mucho este tema ya puede empezar a hacerlo.

Tema 1.11 ESTRUCTURA DE REPETICIN DO...LOOP


y y

1.11.1 - Estructura DO...LOOP 1.11.2 - Depuracin de datos de entrada

1.11.1 - ESTRUCTURA DO...LOOP Esta estructura es similar a la WHILE, solo que la condicin se especifica al final del bloque, con lo que puede ser ms fcil de entender y nunca tendremos que hacer "lecturas anticipadas" ya que siempre se entra por lo menos una vez. La principal novedad de este bloque es que podemos repetir las instrucciones MIENTRAS se cumpla la condicin o HASTA que se cumpla. El siguiente ejemplo escribe los nmeros del 1 al 5 usando una instruccin DO...LOOP WHILE que hace que el bucle se ejecute MIENTRAS nuestro contador sea menor que 5. n = 0 DO n = n + 1 PRINT n LOOP WHILE n < 5 Observa que el contador se incrementa al principio del bucle, y por lo tanto la primera vez que escribamos n, ya tendr el valor de 1. La

45

ltima vez escribir 5 y al llegar a la condicin se comprobar que NO es menor que 5 y ya salimos. Ahora haremos lo mismo con la instruccin DO... LOOP UNTIL que ejecutar el bloque HASTA QUE el contador llegue a valer 5. n = 0 DO n = n + 1 PRINT n LOOP UNTIL n = 5 Esto es parecido. Observa que la condicin es justo la contraria. La ltima vez n vale 5 y despus de escribirla se comprueba que la condicin es verdadera y se sale del bucle. Las instrucciones DO...LOOP UNTIL son normalmente las ms fciles de comprender. En la ayuda de QBasic recomiendan que se dejen de usar las instrucciones WHILE...WEND para usar mejor las DO...LOOP, pero algunas veces ser mejor usar las WHILE...WEND como en el caso de los ficheros secuenciales que ya veremos ms adelante. 1.11.2 - DEPURACIN DE DATOS DE ENTRADA Ahora ya estamos en condiciones de ver una forma de conseguir que un programa que requiere la intervencin del usuario para introducir datos de entrada no avance HASTA QUE el usuario no escriba los datos correctos. En el tema de Entrada/Salida vimos que QBasic es capaz de controlar que no se metan valores fuera de rango, por ejemplo que si el programa pide un entero no se pueda escribir un numero mayor de 32767. Aqu lo que vamos a ver es como conseguir que se pedimos un mes el programa no avance hasta que el usuario escriba un nmero entre 1 y 12. Esto lo deberamos hacer en cualquier programa siempre que pidamos al usuario que escriba algo. Para hacer este control lo que hacemos es meter la instruccin INPUT dentro de un bloque REPETIR, del que no salimos HASTA que la respuesta sea correcta o MIENTRAS sea incorrecta. Vamos a ver unos ejemplos que aclararn todas las posibles situaciones. Leer un nmero menor que 100 CLS DO INPUT "Escribe un nmero menor que 100: ",num LOOP UNTIL num < 100 Aqu no seguimos hasta que el nmero sea menor que 100. En el siguiente ejemplo seguiremos repitiendo la pregunta mientras el nmero sea mayor o igual que 100 CLS DO INPUT "Escribe un nmero menor que 100: ",num LOOP WHILE num >= 100

46

Puedes volver a comprobar aqu que para el mismo problema la condicin del LOOP WHILE es justo la inversa a la del LOOP UNTIL. Ahora pedimos un mes, que tiene que ser entre 1 y 12 CLS DO INPUT "Escribe un mes (0 a 12): ",mes LOOP UNTIL (mes >= 1) AND (mes <= 12) En este caso tenemos un intervalo y por lo tanto hay que controlar dos condiciones que uniremos con el operador AND, con lo que no seguimos HASTA que la primera se cumpla Y la segunda tambin. Tambin lo podamos haber hecho con un bloque DO...LOOP WHILE CLS DO INPUT "Escribe un mes (0 a 12): ",mes LOOP WHILE (mes < 1) OR (mes > 12) Ahora no salimos MIENTRAS alguna de las dos condiciones se cumpla, son justo las contrarias a la del ejemplo anterior, ya que usamos el operador OR que tambin se puede decir que es el contrario al AND. Por ltimo vamos a ver un problema muy tpico: Un programa que nos pide una clave de acceso para poder seguir. Este es el caso ms sencillo en el que no pasamos hasta que no demos con la clave, otros problemas ms complicados seran que el programa terminara tras varios intentos fallidos o que admitiera varias claves llvndonos segn la que sea a una parte del programa. CONST ClaveCorrecta$="brete Ssamo" CLS DO INPUT "Escribe la clave: ",clavePrueba$ LOOP UNTIL clavePruebas = claveCorrecta$ PRINT "Ya has entrado" Recordar tambin que en un programa terminado para explotacin la clave nunca va a estar en el listado del programa, sino que ser obtenida de algn tipo de fichero o base de datos para que el usuario tenga la posibilidad de cambiarla.

TEMA 1.12 ARRAYS


y y y y y y y

1.12.1 1.12.2 1.12.3 1.12.4 1.12.5 1.12.6 1.12.7

Introduccin a los arrays Vectores Matrices Poliedros Ahorrar espacios en arrays de cadenas Matrices orladas Formaciones dinmicas

1.12.1 - INTRODUCCIN A LOS ARRAYS Hasta ahora hemos usado variables para guardar la informacin que maneja el programa. Una variable con su nombre y su tipo para cada

47

dato (nombre, cantidad, nivel, precio, edad...) que nos haca falta. Pero Qu ocurre si nuestro programa tiene que manejar una coleccin de datos muy grande como los nombres de una agenda o los precios de un catlogo? Se podra hacer usando 100 variables distintas pero el algoritmo llegara a ser terriblemente complicado y muy poco funcional. En este apartado vamos a ver cmo podemos asignar un mismo nombre de variable para muchos datos y referirnos a cada uno de ellos de forma individual usando un nmero al que llamaremos ndice. Usaremos unas estructuras de datos llamadas arrays, de las que se dice que son "Estructuras estticas de almacenamiento interno". y Son estticas porque su tamao se declara al principio del programa y ya no se puede modificar. Si hacemos un programa que trabaje con 100 nombres siempre lo har con 100 aunque solo lleguemos a usar 20, y nunca podr trabajar con ms de 100. y Son de almacenamiento interno porque estn en la memoria RAM del ordenador, como variables que son, y su tiempo de vida slo dura mientras se ejecuta el programa. Al terminar se pierde su contenido. En programacin normalmente se conocen como "Arrays" aunque su nombre ms correcto en castellano sera "formaciones". Como veremos en los apartados siguientes, los arrays de 1 dimensin se llaman vectores, los de 2 dimensiones se llaman matrices y los de 3 o ms se llaman poliedros o arrays multidimensionales. En informtica muchas veces se llama vector a cualquier array. En la ayuda de QBasic (y de Visual Basic) siempre llaman Matriz a cualquier array, por lo que hay que tener cuidado de no liarse. 1.12.2 - VECTORES Imaginemos este bloque de pisos de cuatro plantas.

Vamos a escribir un programa de la forma que sabemos hasta ahora que nos pregunte el nombre de la persona vive en cada piso y una vez que ha recopilado toda esa informacin, nos deje preguntarle quien vive en el piso que nosotros queramos. CLS INPUT "Nombre de quien vive en el 1: ", nombre1$

48

INPUT "Nombre de quien vive en el 2: ", nombre2$ INPUT "Nombre de quien vive en el 3: ", nombre3$ INPUT "Nombre de quien vive en el 4: ", nombre4$ DO INPUT "Escribe un piso para ver quien vive en l: ",n LOOP WHILE (n < 1) OR (n > 4) SELECT CASE n CASE 1: PRINT "En el 1 vive "; nombre1$ CASE 2: PRINT "En el 2 vive "; nombre2$ CASE 3: PRINT "En el 3 vive "; nombre3$ CASE 4: PRINT "En el 4 vive "; nombre4$ END SELECT El resultado podra ser este: Nombre de quien vive en el 1: Paca Nombre de quien vive en el 2: Manolo Nombre de quien vive en el 3: Lola Nombre de quien vive en el 4: Pepe Escribe un piso para ver quien vive en l: 3 En el 3 vive Lola Un listado un poco largo para hacer algo tan sencillo. Si en vez de cuatro pisos fueran 40 tendramos un programa casi diez veces ms largo con muchas partes casi iguales, pero que no podemos meter en bucles repetitivos porque cada variable es distinta. Observa que comprobamos que el piso sea entre 1 y 4, a partir de ahora va a ser muy importante depurar los datos de entrada, ya veremos por qu. Ahora vamos a escribir un programa que haga lo mismo que el anterior, pero usando un VECTOR. DIM nombre$ (1 TO 4) FOR n = 1 TO 4 PRINT "Nombre de quien vive en el"; n; ": "; INPUT "", nombre$(n) NEXT DO INPUT "Escribe un piso para ver quien vive: ",n LOOP WHILE (n < 1) OR (n > 4) PRINT "En el";n;" vive ";nombre$(n) El resultado sera similar, pero el listado es mucho ms corto, especialmente todo el SELECT CASE anterior que se ha transformado en una sola instruccin. Vamos a ver este programa lnea a lnea. La primera instruccin es nueva. En ella lo que hacemos es DECLARAR una variable que se va a llamar nombre$ (como lleva el $ va a ser de tipo texto) y va a poder guardar cuatro valores a los que accederemos con subndices que van desde el 1 hasta el 4. Para determinar este intervalo de valores hay que usar nmeros enteros constantes, no valen expresiones matemticas. En el siguiente bloque FOR, que se ejecutar 4 veces lo que hacemos es ir pidiendo al usuario que escriba los nombres. La primera vez

49

guardamos lo que escriba en la posicin 1 del vector porque hacemos referencia al ndice 1 entre parntesis a continuacin del nombre del vector. La siguiente vez al ndice 2, la siguiente vez al 3 y la ltima vez que se ejecute el bucle hacemos referencia al ndice 4. A esto es a lo que se llama "Recorrer el vector" ya que hemos hecho algo con cada uno de sus elementos. Normalmente esto lo haremos siempre con un bucle FOR, que es lo ms cmodo. La pregunta de "nombre de quien vive en..." no est en el INPUT porque esta instruccin no evala expresiones, por eso est antes en un PRINT normal con un punto y coma al final para que el cursor no pase a la siguiente lnea. Ahora ya tenemos dentro del vector los cuatro nombres para usarlos como queramos haciendo referencia al nombre del vector y al subndice entre parntesis. Para hacer referencia a los subndices de un array se puede usar cualquier expresin, no tiene porqu ser un nmero constante, pero hay que tener cuidado de no hacer nunca referencia a ndices que no existan En el siguiente bloque pedimos al usuario que escriba el nmero de un piso y lo guardamos en la variable N. Obligamos a que sea un nmero entre 1 y 4. Al final viene lo espectacular. Para acceder a cualquiera de los ndices del vector se puede hacer directamente tomando el valor de la variable N como subndice sin necesidad de controlar cada valor por separado como antes. Vamos con otro ejemplo para que todo esto vaya quedando cada vez ms claro. En temas anteriores tenamos un programa para escribir el nombre de un mes a partir de su nmero. Lo hicimos con muchos IF anidados, despus con ELSEIF y por ltimo con SELECT CASE que ya quedaba mucho ms corto, pero de ninguna forma nos libramos de escribir todo el SELECT CASE y todos los meses en cualquier parte del programa dnde queramos que se escriba el nombre de algn ms. Ahora vamos a plantear el problema de otra forma: DIM mese$ (1 TO 12) mese$(1)="Enero" mese$(2)="Febrero" mese$(3)="Marzo" mese$(4)="Abril" mese$(5)="Mayo" mese$(6)="Junio" mese$(7)="Julio" mese$(8)="Agosto" mese$(9)="Septiembre" mese$(10)="Octubre" mese$(11)="Noviembre" mese$(12)="Diciembre" '(...) PRINT mese$(2) 'Escribe Febrero '(...)

50

n=6 PRINT mese$(n) 'Escribe Junio Hemos declarado un vector de cadenas de 12 posiciones llamado mese$. Al principio del programa llenamos el vector con los nombres de los meses, cada uno en su lugar correcto. Donde nos haga falta el nombre de un mes slo tendremos que usar el vector y referirnos al mes que queramos. De esta forma meses(3) nos devolver "Marzo" y si n vale 11 entonces meses(n) nos devolver "Noviembre". En los dos ejemplos que hemos puesto los ndices de los vectores han empezado en el 1, pero esto no tiene que ser siempre as. En QBasic pueden empezar por cualquier nmero incluso negativo, aunque lo ms normal es que siempre empiecen por 1 o por 0. El valor ms bajo posible para los ndices es -32768 y el ms alto es 32767. Por supuesto el final del intervalo no puede ser menor que el principio. Veamos algunos ejemplos ms de declaracin de vectores. DIM alumno$(1 TO 30) '30 cadenas DIM nota (1 TO 30) '30 nmeros reales DIM ventas_verano%(6 TO 9) '4 enteros Tambin podemos declarar vectores de un solo elemento, aunque esto puede que no tenga mucho sentido. DIM nmero (1 TO 1) Para determinar el tamao de cualquier vector usamos la siguiente frmula: ltimo ndice - primer ndice + 1 As en el ejemplo de los meses resulta 12 - 1 + 1 = 12 elementos, muy sencillo no? pero a veces terminaremos contando con los dedos. Para inicializar un vector (Borrarlo entero) no hace falta recorrerlo, podemos hacer: ERASE (nombre_vector) Y el vector se quedar lleno de ceros si es numrico o de cadenas vacas ("") si es de cadenas. Recordemos que en QBasic todas las variables estn a cero al principio, pero en otros lenguajes no. Ahora vamos a ver el problema ms tpico de los vectores (y en general de todos los arrays). No podemos intentar acceder a subndices que no existen. En caso de que llegue a ocurrir en QBasic se producir un error de tipo "Subndice fuera del intervalo" y el programa se detendr sin mayores consecuencias. En muchos otros lenguajes no ocurrir nada, pero si estamos leyendo sacaremos datos de otras posiciones de memoria que no son nuestras de ms all del final del vector y si estamos escribiendo lo haremos sobre escribiendo otros datos importantes para el programa o incluso para el sistema operativo (DOS o WINDOWS) con lo que casi seguro conseguiremos que el ordenador se quede bloqueado. Imagina que en un programa (No de QBasic ocurre esto y va a parar

51

a un registro estratgico del sistema operativo un nmero que equivale de alguna forma a la llamada al programa de formatear el disco duro, no veas el estropicio. De ah la importancia de depurar los datos de entrada, especialmente los que van a servir como ndices para arrays. Si en nuestro primer ejemplo no depuramos el dato del piso que pedimos al usuario y este escribe uno que no est entre cero y cuatro, al acceder al vector se producira este error. 1.12.3 - MATRICES Ahora imaginemos este otro bloque de pisos, para el que tenemos que hacer un programa similar al anterior.

Vamos a escribir el programa. Ser igual que el anterior, habr que controlar las cuatro plantas pero adems dentro de cada una habr que controlar las tres puertas que hay en cada rellano (1, 2 y 3). DIM nombre$ (1 TO 4, 1 TO 3) FOR piso = 1 TO 4 FOR puerta = 1 TO 3 PRINT "Nombre de quien vive en el"; piso; ""; puerta; ": "; INPUT "", nombre$(piso, puerta) next NEXT PRINT "Para saber quien vive en un piso..." DO INPUT " Escribe el piso: ",piso LOOP WHILE (piso < 1) OR (piso > 4) DO INPUT " Escribe la puerta: ",puerta LOOP WHILE (puerta < 1) OR (puerta > 3) PRINT "En el";piso;"";puerta;" vive ";nombre$(piso, puerta) Se puede ver que es muy parecido, pero hemos utilizado una MATRIZ, que es un array de dos dimensiones, mientras que un vector es un array de una sola dimensin. Por si hay alguna duda de lo que hace este programa vamos a ver un posible resultado.

52

Nombre de quien vive en el 1 1: Paca Nombre de quien vive en el 1 2: Gloria Nombre de quien vive en el 1 3: Fernando Nombre de quien vive en el 2 1: Mari Nombre de quien vive en el 2 2: Juan Nombre de quien vive en el 2 3: Manolo Nombre de quien vive en el 3 1: Lola Nombre de quien vive en el 3 2: Rosa Nombre de quien vive en el 3 3: Mario Nombre de quien vive en el 4 1: Pepe Nombre de quien vive en el 4 2: Nacho Nombre de quien vive en el 4 3: Luisa Para ver quien vive en un piso... Escribe la planta: 3 Escribe la puerta: 2 En el 3 2 vive Rosa Lo ms novedoso es la forma de declarar la matriz, igual que el vector pero esta vez habr que usar dos intervalos separados por una coma. Y por lo tanto siempre que nos refiramos a la matriz habr que usar dos subndices. Para recorrer la matriz hay que usar dos FOR anidados. Esta vez la hemos recorrido por filas (pisos en nuestro ejemplo) ya que el "FOR piso" est fuera y hasta que no se ejecute entero el "FOR puerta" en cada piso no pasamos al siguiente. Para recorrerla por columnas (puertas) bastara con intercambiar los FOR: FOR puerta = 1 TO 3 FOR piso = 1 TO 4 '(...) next NEXT Para el ordenador es completamente intrascendente que lo hagamos de una forma u otra, l no entiende de filas horizontales ni columnas verticales, de hecho almacena todos los elementos seguidos uno detrs de otro y hace operaciones matemticas con los dos subndices para determinar la nica posicin del elemento. Esto se puede ver fcilmente pensando en la posicin de los buzones de correos en el portal del bloque de pisos.

Cada piso tiene su buzn, el del 2 2 es el quinto porque tiene delante los tres de la primera planta y es el segundo de la segunda. Para calcular las posiciones se hace algo as como ((PLANTA1)*N_total_de_PUERTAS)+PUERTA, que sale ((2-1)*3)+2 = 5. A nosotros esto no nos interesa porque lo hace QBasic automticamente. Si programamos en otros lenguajes ms primitivos s que habra que preocuparse de esto porque slo existen vectores de una dimensin.

53

1.12.4 - POLIEDROS Lo ms normal es usar vectores (1 dimensin) o matrices (2 dimensiones), pero QBasic puede llegar a manejar arrays con hasta 60 dimensiones!!! Veamos un caso rebuscado de un array de tres dimensiones ampliando los ejemplos anteriores de los bloques de pisos.

En este caso nuestro bloque tiene tres portales (1 a 3) y cada uno de ellos tiene cuatro plantas y tres puertas igual que antes. Para declarar el poliedro habra que hacer: DIM nombre$ (1 TO 3, 1 TO 4, 1 TO 3) La primera dimensin va a ser para los portales, la segunda para los pisos y la tercera para las puertas. Para recorrer el array haramos: FOR portal = 1 TO 3 FOR piso = 1 TO 4 FOR puerta = 1 TO 3 '(...) NEXT NEXT NEXT Esta vez tenemos tres bucles FOR anidados y las instrucciones que pongamos dentro de todo se ejecutarn 36 veces que es el producto de todas las dimensiones. Ya empezamos a liarnos pensando en recorrer el array primero por portales o no, etc... Todava podemos tener una representacin visual del problema, pero si usamos arrays de cuatro, cinco o ms dimensiones ya esto no ser as y la habremos liado del todo. En estos casos lo mejor ser plantear el problema de otra forma o usar estructuras de datos ms flexibles que ya veremos ms adelante. 1.12.5 - AHORRAR ESPACIO EN LOS ARRAYS DE CADENAS Hasta ahora no hemos sido demasiado estrictos en el ahorro de memoria ya que ni siquiera estamos declarando las variables. Nuestros programas de QBasic son muy sencillos y usan muy pocas variables, pero al trabajar con arrays hay que darse cuenta que basta

54

con declarar una matriz de 20 por 20 elementos para que se gasten 400 posiciones de memoria. En las cadenas QBasic no nos limita el nmero de caracteres que vamos a poder meter en ellas y para que esto funcione se gasta cierta cantidad de memoria (unos 10 bytes), que si multiplicamos por 400 posiciones de memoria son casi 4 KB, una cantidad respetable para programas tan pequeos. Puede ocurrir que en nuestra matriz de cadenas no lleguemos a almacenar palabras ms largas de por ejemplo 15 letras con los que nos convendra limitar el tamao de las cadenas y ahorrarnos esos 10 bytes en cada posicin. Para declarar un array de cadenas de tamao limitado haramos: DIM matriz(1 TO 20, 1 TO 20) AS STRING * 15 Las palabras clave AS STRING declaran explcitamente la variable como de tipo cadena (Esta ser la forma normal de hacerlo en Visual Basic), por eso ya no es necesario poner el $ al final del nombre. Si a continuacin ponemos un asterisco y un nmero estamos limitando el tamao de las cadenas con lo que QBasic ya no tendr que determinarlo automticamente constantemente, con lo que ahorraremos memoria y el programa funcionar algo ms rpido. Si asignamos a estas variables una cadena de ms de 15 caracteres no ocurre nada, simplemente la cadena se corta y slo se almacenan los primeros 15 caracteres, los dems se pierden. En los tipos numricos no podemos ahorrar nada, pero en los arrays de cadenas deberamos hacer siempre esto, especialmente si van a contener muchos elementos. 1.12.6 - MATRICES ORLADAS Imaginemos el juego del buscaminas en el que tenemos una matriz de 8 por 8 elementos (Casillas) que van a contener un 1 si contienen una mina o un cero si estn vacas.

Cuando el usuario destape una casilla, si no contiene una mina, habr que escribir el nmero de minas que la rodean. Para hacer esto habr que sumar los valores de las casillas que hay arriba a la izquierda, arriba, arriba a la derecha, a la derecha, abajo a la derecha, abajo, abajo a la izquierda y a la izquierda. Si estamos en una casilla del interior del tablero esto funciona perfectamente, pero si estamos en el borde nos encontramos con el problema de que haramos referencia a posiciones de fuera de la matriz con lo que se producira un error de "Subndice fuera del intervalo".

55

Para evitar esto habra que comprobar que si estamos en el borde superior no se cuenten las casillas superiores, si estamos en el inferior no se cuenten las de abajo, si estamos en una esquina solo se cuenten las tres de dentro, etc. con lo que tendramos en total nueve rutinas diferentes, as como que controlar cual de ellas usar segn por donde est la casilla que estamos destapando. Para solucionar este problema lo que se suele hacer es dotar a la matriz de ms filas y columnas de forma que todos los elementos que realmente nos interesan estn rodeados por un "borde" de elementos vacos que estarn ah slo para poder hacer referencia a ellos sin salirnos de la matriz. Una representacin grfica de la matriz orlada podra ser esta, donde hay un borde de "casillas" que no vamos a usar nunca para poner minas, pero a las que podemos referirnos siempre que sea necesario sin salirnos de la matriz. En el ejemplo que nos ocupa, estas posiciones contendran el valor 0 ya que estn vacas.

Esta tcnica se aplica mucho en juegos de este tipo, en programas de imgenes y en cualquier lugar donde se tenga que acceder a posiciones de una matriz y puedan producirse problemas en los bordes. En el caso de vectores habra que aadir un elemento ms al principio y otro al final, para los poliedros sera como envolverlo completamente y en el caso de arrays de ms de cuatro dimensiones ya es difcil imaginarse como se hara. Hay que recordar que en QBasic se pueden usar subndices negativos, pero en la mayora de otros lenguajes de programacin esto no es as. El nico inconveniente de las matrices orladas es que ocupan ms memoria, exactamente (2*(alto + ancho))+4 posiciones ms. Este gasto de recursos est justificado en la mayora de los casos ya que se consigue simplificar mucho los programas. 1.12.7 - FORMACIONES DINMICAS En otros lenguajes de programacin ms avanzados como C o Pascal existen lo que se llaman "Estructuras de almacenamiento dinmicas" que son tcnicas de programacin que mediante la utilizacin de punteros (Variables especiales que contienen direcciones de memoria) permiten acceder directamente a la memoria del ordenador y colocar los datos en estructuras como listas enlazadas, rboles, pilas, colas, tablas hash, etc cuyo tamao cambia constantemente

56

durante la ejecucin del programa sin estar definido de ninguna forma en el cdigo del programa. Por eso se dice que son estructuras de almacenamiento dinmicas. Como QBasic (ni Visual Basic) no trabaja directamente con punteros no podemos llegar a utilizar estas estructuras. En algn caso puede suceder que necesitemos almacenar elementos en un array (Vector, matriz, etc...), pero no podamos saber de antemano cuanto grande va a tener que ser y tampoco nos merezca la pena poner un lmite arbitrario que se alcanzar en seguida o bien no llegar a ser usado nunca desperdiciando mucha memoria. Para solucionar en parte este problema QBasic nos da la posibilidad de declarar los arrays usando la instruccin REDIM en lugar de DIM y despus en cualquier parte del programa poder redimensionarlos tantas veces como queramos usando valores que pueden ser variables. Veamos un ejemplo: REDIM mat(1 TO 1) INPUT "Cuantos nmeros vamos a almacenar?", maxi REDIM mat(1 TO maxi) FOR n = 1 TO maxi INPUT mat(n) NEXT INPUT "Cuantos nmeros vamos a almacenar ahora?", maxi2 REDIM mat(1 TO maxi2) FOR n = 1 TO maxi2 INPUT mat(n) NEXT Se puede ver que declaramos un vector de enteros de slo un elemento usando REDIM, a continuacin pedimos al usuario que escriba un nmero y despus volvemos a redimensionar el vector con este nmero de elementos para ya recorrerla como hemos hecho siempre. A continuacin repetimos el proceso volviendo a redimensionar el vector, perdendose la primera serie de nmeros que almacen en ella el usuario. Aqu hay que aclarar algunas cosas: y La primera orden REDIM mat(1 to 1) nos la podamos haber ahorrado, pero siempre es costumbre declarar las variables al principio del programa para tener una visin global de las variables que hay sin tener que recorrer el listado completo. y Si al declarar un array con la orden DIM usamos variables en vez de valores constantes lo declaramos como dinmico y despus podr ser redimensionado. y Para redimensionar un array dinmico ya existente habr que hacerlo siempre con la orden REDIM y en QBasic tendremos que usar las dimensiones que ya tiene, es decir, si antes era una matriz con dos dimensiones al redimensionarlo tenemos que especificar dos intervalos, ni ms ni menos, y por supuesto el mismo tipo de datos.

57

Al redimensionar un array se borran todos los valores que tuviera, quedando entero inicializado a valores cero o cadenas vacas. Visto esto slo queda por decir que siempre que sea posible se evite el uso de arrays dinmicos ya que el manejo interno de la memoria es mucho menos eficiente y podra dar lugar a errores como los tpicos de "Memoria agotada". Para evitar que los arrays sean dinmicos usar siempre valores constantes (nmeros) para definir sus dimensiones al declararlos con la orden DIM al principio del programa.
y

TEMA 1.13 PROGRAMACIN MODULAR


y y y y y y y y y y y

1.13.1 - Introduccin a la programacin modular 1.13.2 - La programacin modular en QBasic 1.13.3 - Llamada a procedimientos y funciones 1.13.4 - Definicin de procedimientos 1.13.5 - Variables locales 1.13.6 - Variables globales 1.13.7 - Paso de parmetros 1.13.8 - Definicin de funciones 1.13.9 - Recursividad 1.13.10 - Diferencia entre procedimientos y funciones 1.13.11 - Prototipos

1.13.1 - INTRODUCCIN A LA PROGRAMACIN MODULAR Hasta ahora hemos visto programas muy sencillos que simplemente eran una serie de instrucciones colocadas una detrs de otra en un listado. Esto va muy bien para nuestros programas extremadamente simples, pero conforme los proyectos se hacen algo ms grandes van apareciendo una serie de problemas. y Si hay partes que se repiten en distintos sitios la nica solucin es repetir el trozo de cdigo correspondiente dnde haga falta. y Si damos con un error en una de estas partes repetidas habr que corregirlo en todas, sin olvidar ninguna. y Si el programa es desarrollado por varias personas ser muy difcil que puedan trabajar a la vez. y El programa queda muy mal estructurado ya que est "todo seguido" y no se diferencian unas partes de otras. y Etc. Para ir solucionando estos problemas se ha desarrollado lo que se conoce como programacin modular y que consiste en dividir un programa en mdulos o subprogramas ms pequeos que realizan una accin determinada y son llamados por los otros mdulos cada vez que haga falta. Esta forma de trabajar nos proporciona las siguientes ventajas, que solucionan los problemas anteriores. y No habr partes repetidas en los programas.

58

Al solucionar un posible error en el mdulo correspondiente este queda solucionado de forma definitiva en todos los otros mdulos que usaran este mdulo. y Un programa puede ser desarrollado por varias personas o en tiempos diferentes ya que cada uno puede desarrollar su mdulo y al final solo habr que ponerlos en comn para que todo funcione. y Los programas quedan muy bien estructurados y el cdigo fuente se hace ms legible ya que est formado por partes pequeas unidas. Imaginemos por ejemplo en Windows los cuadros de dilogo que aparecen para abrir y guardar los archivos en los programas. Has observado que casi siempre son iguales?. Esto es porque son mdulos ya programado que se encuentran en una biblioteca de enlace dinmico (DLL) y son llamados por los programas. De esta forma el programador no se tiene que preocupar de programar las listas, los iconos, los botones, etc... sino que estos ya han sido programados anteriormente por otras personas y slo hay que llamarlos desde nuestro programa de Windows. Adems si se encuentra algn error basta con corregirlo y sustituir la DLL correspondiente (Esto es lo que hace constantemente Microsoft con sus "Service Packs" y sus "Segundas ediciones") para que todos los programas que la utilicen queden inmediatamente arreglados.
y

1.13.2 - LA PROGRAMACIN MODULAR EN QBASIC La programacin en Windows se basa en las DLL y por lo tanto en la programacin modular. En los lenguajes ms avanzados como C++ o Pascal se pueden usar bibliotecas de funciones que son llamadas desde nuestro programa y son enlazadas en el momento de compilar nuestro ejecutable EXE. En QBasic, al ser un lenguaje interpretado nos tenemos que limitar a usar mdulos o ms concretamente procedimientos que estn dentro del nico fichero BAS que compone nuestro programa. Las posibilidades son muy limitadas pero nos van a servir para aprender los conceptos ms importantes de esta tcnica. Hasta ahora hemos escrito las instrucciones en el editor de cdigo de Qbasic. Esta parte del programa es el "Mdulo Principal" que debe existir siempre y desde donde se llamar a los distintos mdulos del programa, si existen. En QBasic existen dos tipos de mdulos: procedimientos SUB y funciones. y Los procedimientos SUB son partes del programa que simplemente "hacen algo". y Las funciones son partes del programa que al llamarlas hacen un clculo y nos devuelven un valor. 1.13.3 - LLAMADA A PROCEDIMIENTOS Y FUNCIONES Ya hemos usado sin saberlo procedimientos y funciones ya incluidas en el lenguaje de QBasic. Por ejemplo al usar la instruccin PRINT lo

59

que hacemos es llamar a un procedimiento ya programado dentro de QBasic que se encargar de dibujar caracteres en la pantalla, o al usar la funcin SQR dentro de una expresin conseguimos calcular la raz cuadrada de un nmero sin tener que programar en ningn sitio las operaciones necesarias para hacer esto. La diferencia importante entre la llamada a los procedimientos y a las funciones es que para llamar a un procedimiento se escribe su nombre al principio de la lnea, mientras que las llamadas a funciones van dentro de expresiones o bien formando parte de condiciones. Si estn en una asignacin, las llamadas a funciones siempre van a la derecha del signo "igual". Tambin es muy importante darse cuenta de que los parmetros, si los hay, que se pasan a las funciones van entre parntesis, mientras que los de los procedimientos no es obligatorio que vayan entre parntesis, ya veremos por qu. Veamos algunos ejemplos, donde PRINT es un procedimiento y SQR es una funcin. PRINT "Hola" Llamamos a PRINT y le pasamos como parmetro "Hola". PRINT escribir "Hola" en la pantalla, pero no devuelve ningn valor. raiz = SQR(n) Llamamos a la funcin SQR y le pasamos como parmetro la variable n. El resultado devuelto lo asignamos a la variable raiz. Observa que SQR est a la derecha del signo igual y que el parmetro n va entre parntesis. PRINT SQR(4) Aqu llamamos a SQR para que nos calcule la raz de 4, y el valor devuelto por la funcin se lo pasamos a PRINT para que lo escriba en la pantalla. DO ... LOOP WHILE SQR(n)>10 ... IF SQR(n)>50 THEN ... END IF En estos dos ejemplos usamos los resultados de la funcin como parte de la expresin que se usar como condicin en instrucciones repetitivas y condicionales. A = PRINT(b) Esto se ve muy raro a primera vista Cmo le asignamos a una variable lo que escriba PRINT en la pantalla?. Esto no se puede hacer, est mal escrito. Las llamadas a procedimiento siempre van al principio de la lnea. SQR(4) Aqu se produce un error al pasar de lnea porque llamamos a la funcin, pero cuando nos devuelve el resultado no hacemos nada con l porque la funcin ni est en una asignacin ni en una condicin.

60

1.13.4 - DEFINICIN DE PROCEDIMIENTOS Hasta ahora hemos llamado a procedimientos que ya estn inludos en el lenguaje de QBasic, pero para poder utilizar los nuestros propios adems de llamarlos hay que programarlos. Todo el cdigo que hemos escrito hasta ahora en nuestros programas ha estado en el "Mdulo Principal" del programa. Para definir un procedimiento basta con seleccionar "Nueva sub" en el men "Edicin" de QBasic. Entonces aparecer un pequeo cuadro de dilogo donde tendremos que escribir el nombre del procedimiento y pulsar Enter. Despus de hacer esto desaparecer de la pantalla nuestro mdulo principal y en su lugar aparecer algo as como esto. SUB Prueba END SUB Esto es el principio y el final del procedimiento. Entre estas dos lneas ser donde tendremos que escribir todo el cdigo. Fuera lo nico que est permitido son comentarios. Siempre es muy recomendable escribir una pequea descripcin de lo que hace el procedimiento. Tambin es normal escribir los nombres de los procedimientos con la primera letra en maysculas. Para elegir los nombres deberemos elegir un nombre descriptivo y tendremos las mismas restricciones que con los nombres de variables (Sin acentos ni , mximo 40 caracteres, etc...). Para volver al mdulo principal o para pasar de un procedimiento a otro tendremos que pulsar la tecla F2 y en la lista que aparece elegir el procedimiento al que queremos ir. Podemos entrar y salir de los procedimiento todas las veces que sea necesario mientras estamos escribiendo el cdigo fuente del programa. Vemos con un ejemplo paso a paso. Vamos a hacer un programa que contenga un pequeo procedimiento para escribir un rtulo en la pantalla. Al usar programacin modular podemos llamar al procedimiento cada vez que queramos escribir el rtulo en lugar de tener que repetir todo el cdigo. All vamos. Abrimos un nuevo programa y empezaramos a escribir lo que va en su mdulo principal. En este caso no vamos a escribir todava nada. Ahora vamos a definir el procedimiento que se va a llamar EscribeRotulo. Para hacerlo seleccionamos "Nueva sub" en el men "Edicin". Tras escribir el nombre y pulsar Enter aparecer en la pantalla esto. SUB EscribeRotulo END SUB Ahora vamos a escribir las instrucciones que necsitamos para escribir el rtulo dentro de las dos lneas que ha escrito QBasic. SUB EscribeRotulo PRINT "******************" PRINT "* HECHO EN RONDA *" PRINT "******************" END SUB

61

Tambin podemos aadir en el encabezado una descripcin como comentario. 'Este procedimiento escribe "Hecho en Ronda" en 'la pantalla rodeado de asteriscos. ' SUB EscribeRotulo PRINT "******************" PRINT "* HECHO EN RONDA *" PRINT "******************" END SUB Ahora, para volver al mdulo principal pulsamos F2 y seleccionamos el primer nombre de la lista que ser "Sin_nombre" si todava no hemos guardado el programa, o el nombre del fichero si ya lo hemos guardado. En el mdulo principal bastar con escribir EscribeRotulo que es la llamada al procedimiento para que este se ejecute. En un programa con ms instrucciones podemos llamar a los procedimientos todas las veces que haga falta escribiendo su nombre, y por su puesto podemos llamar a un mdulo desde dentro de otro. Si abres el fichero del programa (*.BAS) con el editor de MS-DOS o con el Bloc de notas de Windows vers que los mdulos estn realmente a continuacin del mdulo principal. Al abrir los programas en QBasic se van separando para no tener listados tan largos y poder pasar fcilmente de uno a otro pulsando F2. 1.13.5 - VARIABLES LOCALES En nuestro pequeo procedimiento no hemos utilizado ninguna variable, simplemente nos limitamos a escribir un rtulo en la pantalla. Ahora vamos a ver un pequeo programa con un mdulo en el que se sumarn los valores de dos variables. Recuerda que aunque aqu aparece todo seguido en QBasic slo vers el programa principal y tendrs que pulsar F2 para acceder al procedimiento. CLS a = 2 b = 4 PRINT a + b MiProcedimiento 'Aqu llamamos al procedimiento PRINT a + b ' 'Este procedimiento asigna valores a las variables a y b 'y muestra en pantalla la suma. SUB MiProcedimiento a = 8 b = 3 PRINT a + b END SUB Veamos ahora el resultado que se producira en la pantalla al ejecutar el programa: 6

62

11 6 Extrao No?. Al principio la suma vale 6, despus dentro del procedimiento vale 11 con los nuevos valores y despus fuera por arte de magia vuelve a valer 6!!! Veamos lo que hace el programa paso a paso: y Borra la pantalla, como siempre. y Asigna a la variable a el valor de 4 y Asigna a la variable b el valor de 2 y Muestra en la pantalla la suma de a ms b que es 6 y Entra en el procedimiento, donde: o Asigna a la variable a del procedimiento el valor de 8 o Asigna a la variable b del procedimiento el valor de 3 o Muestra en la pantalla la suma estas dos variables que es 11 o Sale del procedimiento y sigue con el programa principal y Muestra en pantalla la suma de las variables a y b del programa principal que sigue siendo 6 porque sus valores no han podido ser modificados desde el procedimiento Aqu se puede ver fcilmente que las variables son "locales" al mdulo donde son declaradas (Recuerda que QBasic las declara automticamente al usarlas la primera vez), ya sea este el mdulo principal, un procedimiento SUB o una funcin, por lo tanto son variables totalmente distintas aunque tengan el mismo nombre. Para evitar confusiones se recomienda no repetir los nombres de las variables en los distintos mdulos. En los temas anteriores decamos que el tiempo de vida de una variable comienza cuando es declarada (En Qbasic, cuando se usa por primera vez), y termina cuando el programa termina, liberndose la posicin de memoria que utilizaba y perdindose su valor, por lo que no se conserva para la prxima vez que ejecutemos el programa. Con las variables locales de los procedimientos ocurre lo mismo, desaparecen al terminar la ejecucin del procedimiento y por lo tanto normalmente no se puede esperar que conserven su valor la prxima vez que llamemos al procedimiento, aunque sea durante la actual ejecucin del programa. En QBasic para evitar esto y que las variables conserven su valor entre las distintas veces que llamemos a los procedimientos se dispone de la instruccin STATIC que nos permite declarar variables dentro de mdulos de forma que conserven su valor en sucesivas llamadas. Para ms detalles sobre esta instruccin consulta la ayuda de QBasic. Si planificamos bien la estructura de nuestro programa normalmente no ser necesario utilizar esta instruccin. 1.13.6 - VARIABLES GLOBALES En algunas situaciones nos puede interesar que una variable "pueda ser vista" por todos los procedimiento que integran nuestro programa. Esta variable la declararemos de una forma especial al

63

propicio del mdulo principal del programa y ya podr ser usada (leda o modificada) por el propio mdulo principal o por cualquier otro procedimiento. Veamos el mismo programa del tema anterior, pero esta vez usando variables globales: DIM SHARED a,b CLS a = 2 b = 4 PRINT a + b MiProcedimiento 'Aqu llamamos al procedimiento PRINT a + b ' 'Este procedimiento asigna valores a las variables a y b 'y muestra en pantalla la suma. SUB MiProcedimiento a = 8 b = 3 PRINT a + b END SUB Ahora el resultado sera: 6 11 11 Se puede ver que el cdigo es muy parecido, salvo la (gran) diferencia de que declaramos al principio del mdulo principal las variables a y b usando la instruccin DIM SHARED. DIM es para declarar variables y SHARED para que sean globales (compartidas). En la primera parte del programa asignamos los valores a las variables y mostramos la suma. Despus llamamos al procedimiento que modifica los valores de las variables a y b, que en este caso son las mismas que las del mdulo principal, y muestra la suma, que es 11. Al salir del procedimiento, ya en el mdulo principal se muestra la suma que sigue siendo 11 porque el procedimiento ha modificado las variables globales. Las constantes definidas en el mdulo principal siempre sern globales, mientras que las que definimos dentro de un procedimiento SUB o funcin sern locales, solo visibles dentro del mismo igual que las variables locales. 1.13.7 - PASO DE PARMETROS En los procedimientos que hemos programado hasta ahora no existan parmetros. Lo ms normal es que el procedimiento tome alguna informacin como entrada, igual que hacen procedimientos ya definidos como PRINT que usa como parmetro la cadena de texto o expresin que queremos que se escriba en la pantalla. Al conjunto de parmetros de un mdulo se le conoce como INTERFAZ porque es la principal forma que tiene de compartir e intercambiar informacin con el resto del programa.

64

Veamos como ejemplo un procedimiento que acepta como parmetros dos enteros y escribe en la pantalla el mayor de ellos. Le vamos a llamar EscribeElMayor. Tambin est aqu la llamada que se hace desde el mdulo principal. 'Principal CLS EscribeElMayor 4, 7 'Escribe en pantalla slo el nmero mayor de los dos parmetros 'Datos de entrada: N1, N2: Reales SUB Escribe el Mayor(N1, N2) IF N1 > N2 THEN PRINT N1 ELSE PRINT N2 END IF END SUB Vamos a ver con detalle la primera lnea de la definicin del procedimiento. Usamos la palabra SUB seguida del nombre del procedimiento y a continuacin viene lo nuevo. Como antes no usbamos parmetros no ponamos nada ms, pero como ahora tenemos dos parmetros, los DECLARAMOS entre parntesis. Los parmetros son variables que sern manejadas desde dentro del mdulo, y por lo tanto igual que las otras variables normales tienen su tipo de datos y un nombre que debe de seguir las mismas normas de construccin. En este caso como no hemos usado sufijos se entiende que son reales de precisin sencilla, si hubiramos puesto al final del nombre un % seran enteros, o de cadenas si hubiramos puesto un $. Un mdulo (sea un procedimiento o una funcin que ya veremos ms adelante) puede llevar tantos parmetros como haga falta, o ninguno, y estos no tienen por que ser del mismo tipo. Lo importante es que a la hora de llamar al mdulo tenemos que escribir a continuacin del nombre constantes o expresiones que encajen en nmero y tipo con los parmetros que acepta el mdulo. En este ejemplo se esperan dos nmeros, por lo tanto habr que poner exactamente dos expresiones numricas, ni ms ni menos, y tampoco podemos usar cadenas en este caso. En el ejemplo pasamos como parmetros en la llamada al procedimiento dos expresiones constantes y por lo tanto no debe de haber ninguna duda, pero cuando pasamos como expresin una variable pueden pasar algunas cosas algo inesperadas. Veamos otro ejemplo. 'Principal CLS n = 4 PRINT n MultiplicaPorDos n

65

PRINT n 'Mdulo Multiplica por dos 'Multiplica por dos el argumento 'Datos de entrada: numero: Real que se multiplicar por dos SUB MultiplicaPorDos (numero) numero = numero * 2 END SUB El resultado sera: 4 8 Es fcil de entender: En el programa principal se asigna a n un valor, a continuacin se llama a un procedimiento que toma n como entrada, aunque dentro se llame numero, y al terminar se observa que el valor de n ha sido cambiado por el procedimiento. Esto ha ocurrido porque en Qbasic, si no se especifica lo contrario, se pasan los parmetros de los mdulos POR REFERENCIA, es decir, si un parmetro es una variable esta podr ser modificada por el mdulo. Para evitar esto, y que los mdulos no puedan modificar los valores de las variables que se les pasan como parmetros lo que hay que hacer es escribir en la llamada al mdulo la variable entre parntesis. A esto se llama PASO DE PARMETROS POR VALOR y en este caso lo que se pasa es slo un nmero, y no la direccin de la variable original que queda "a salvo" del mdulo. Veamos un ejemplo: 'Principal a = 8 b = 5 c = 7 PonACero (a), (b), (c) PRINT a, b, c PonACero (a), b, (c) PRINT a, b, c PonACero a, b, c PRINT a, b, c 'Este procedimiento asigna 0 a los tres parmetros 'Entreada: una, otra, yOtra: Reales SUB PonACero(una, otra, yOtra) una=0 otra=0 yOtra=0 END SUB El resultado sera: 8 5 7 8 0 7 0 0 0 Como se puede ver, la primera vez que llamamos al procedimiento pasamos los tres parmetros por valor y no se produce cambio alguno en las variables del programa principal. En la segunda llamada pasamos la variable b por referencia (No lleva

66

los parntesis) y su valor es modificado. La ltima vez pasamos todo por referencia y las tres variables son modificadas y acaban con valor 0. En los ejemplos que hemos puesto los parmetros tienen nombres distintos de las variables del programa principal. Es lo que se suele hacer, ya que deben llevar nombres representativos. Si hubiera coincidido algn nombre no pasa absolutamente nada porque se trata de variables distintas. Lo que no podemos hacer es usar nombres de variables globales ni de otros mdulos. En un apartado posterior se hace una comparacin con los procedimientos y se dan sugerencias de cuando usar paso de parmetros por referencia y cuando por valor. 1.13.8 - DEFINICN DE FUNCIONES Este apartado se ha quedado para casi el final del tema de mdulos porque la nica diferencia que hay entre un procedimiento y una funcin es que un procedimiento no devuelve directamente ningn valor (Aunque puede modificar variables del interfaz), mientras que una funcin devuelve un valor que es usado por la expresin en la que est insertada la llamada a la funcin. Para definir funciones usaremos la orden "Nueva Funcin" del men "Edicin" y escribiremos nuestro cdigo entre las lneas FUNCTION nombreFuncion END FUNCTION Como vimos en el apartado de llamadas a procedimientos y funciones para llamar a un procedimiento escribimos su nombre y sus parmetros al principio de una lnea y este hace "lo que sea", mientras que para llamar a una funcin habr que poner su nombre en una expresin seguida de los argumentos entre parntesis, si existen. Para determinar el tipo de dato que devuelve una habr que ver su nombre. Si no lleva sufijo se entiende que devuelve un valor real de precisin sencilla, si lleva un % devuelve un entero, si lleva un $ devuelve una cadena, etc. Y dentro de la funcin para indicar el valor a devolver usaremos el nombre de la funcin seguido del signo Igual y la expresin cuyo valor se devolver. Veamos unos cuantos ejemplos de funciones con un programa principal que las llama a todas: 'Principal CLS PRINT "La suma de dos y dos es " ; Suma(2, 2) media = Media3(4, 7, 12) PRINT "La media de 4, 7 y 12 es ", media PRINT "El mayor de 15 o 8 es "; ElMayor(15, 8) IF LaMasLarga("Ronda","Otorrinolaringlogo") = "Ronda" THEN PRINT "'Ronda' es ms larga que la otra palabra" ELSE PRINT "'Ronda' es ms corta que la otra palabra"

67

ENDIF PRNT NoHaceNada (2, "Palabra") 'Funcin Suma: Suma dos valores 'Entrada: n1 y n2: Reales, valores a sumar 'Salida: devuelve la suma como real FUNCTION Suma(n1, n2) Suma = n1 + n2 END FUNCTION 'Funcin Media3: Calcula la media de 3 valores enteros 'Entrada: n1, n2 y n3: Enteros 'Salida: devuelve la media como real FUNCTION Media3(n1, n2, n3) Media3 = (n1 + n2 + n3) / 3 END FUNCTION 'Funcin Mayor: Devuelve el mayor de dos nmeros 'Entrada: n1 y n2: Reales, valores a comparar 'Salida: devuelve el mayor de los dos como real FUNCTION Mayor(n1, n2) IF n1 > n2 THEN Mayor = n1 ELSE Mayor = N2 END IF END FUNCTION 'Funcin LaMasLarga$: Devuelve la cadena ms larga de las dos 'Entrada: cad1$ y cad2$: Cadenas de longitud variable 'Salida: devuelve la cadena ms larga FUNCTION LaMasLarga$(cad1$, cad2$) IF LEN(cad1$) > LEN(cad2$) THEN LaMasLarga$ = cad1$ ELSE LaMasLarga$ = cad2$ END IF END FUNCTION 'Funcin NoHaceNada$: Devuelve 0 siempre porque no le asignamos nada. ' (Los clculos que hace no tienen ningun efecto) 'Entrada: n% y cad$: Un entero y una cadena de longitud variable 'Salida: Siempre 0 FUNCTION NoHaceNada(n%, cad$) x = 123456 + 78 cadena$ = cad$ + "Hecho en Ronda" x = x / n% END FUNCTION

68

Por si hay alguna duda el resultado sera este: La suma de dos y dos es 4 La media de 4, 7 y 12 es 7.666667 El mayor de 15 o 8 es 15 'Ronda' es ms corta que la otra palabra 0 Ahora veamos algunas cosas importantes que hay en los ejemplos. La llamada a las funciones se puede hacer desde la intruccin PRINT o cualquier otra instruccin, en una expresin que se asignar a una variable, siempre a la derecha del signo igual, o en una expresin que se usa como condicin de un IF, WHILE, etc... Para devolver el resultado se asigna una expresin al nombre de la funcin, en su interior, normalmente al final. Si esto no se llega a hacer nunca, como pasa en la funcin NoHaceNada, se devuelve cero o una cadena de longitud cero si era una funcin de tipo cadena. Si se llegan a ejecutar ms de una de estas asignaciones el valor que se devuelve es el ltimo. Por supuesto el valor que se asigna tiene que ser numrico o de cadena dependiendo del tipo de datos de la funcin. En el mismo programa no puede haber dos mdulos con el mismo nombre. 1.13.9 - RECURSIVIDAD Como hemos visto la programacin modular consiste en que unos mdulos se llamen a otros para estructurar y dividir el programa, evitar partes repetidas, reutilizar cdigo, etc. El programa principal llama a unos mdulos (Ya sean procedimientos o funciones) y estos a su vez pueden llamar a otros mdulos. Conforme los mdulos terminan de ser ejecutados devuelven el control al que lo llam que sigue ejecutndose y cuando termina, si era el mdulo principal, el programa termina. Llamamos recursividad a la posibilidad de que un mdulo se pueda llamar a si mismo. Esto normalmente se hace con funciones que devuelven valores que vamos acumulando, comparando, etc. El principal problema de esta forma de programar es que si una funcin se llama indefinidamente a si misma las direcciones de retorno de las instrucciones se van acumulando en la memoria del ordenador hasta que no hay espacio suficiente y se produce un error de tipo "Espacio en pila agotado". Una "Pila" es una forma de almacenar datos en la memoria que usa internamente QBasic (Y cualquier otro lenguaje) de forma que se va amontonando informacin igual que si se tratara de una pila de libros o de platos para poder sacarla en el orden inverso (cogiendo siempre el de arriba), y si esta se hace muy grande ocupar todo el espacio que tiene disponible y provocar este error que no tiene nada que ver con ninguna batera de electricidad ni nada de eso. Para evitar el error lo que se hace siempre es poner alguna condicin que si ya no se cumple no seguimos llamando a la funcin y ya lo que

69

se hace es ir volviendo hacia atrs y terminando todas las copias de la funcin que tenemos "empezadas" hasta que acaba la ltima y se vuelve al mdulo principal y el programa termina. Veamos un ejemplo muy tpico de un programa que calcula el factorial de un nmero usando recursividad. 'Principal INPUT "Escribe el nmero: "; n# PRINT "El factorial de "; n#; " es "; Factorial#(n#) 'FACTORIAL: Calcula el factorial de forma recursiva 'Entrada: Real de doble precisin: Nmero a calcular 'Salida: El factorial como real de doble precisin FUNCTION Factorial#(n#) IF n# = 1 THEN Factorial# = 1 ELSE Factorial# = n# * Factorial#(n# - 1) END FUNCTION Ahora veamos el planteamiento del problema. El factorial de un nmero es el producto de todos los nmeros desde 1 hasta el nmero, por ejemplo el factorial de 4 es 4 * 3 * 2 * 1, es decir, 24. Para calcularlo de forma recursiva hemos definido el factorial de un nmero como el producto de dicho nmero por el factorial del "nmero anterior". Para salir de la recursividad ponemos como condicin que si el nmero es 1 el factorial ya es 1 y no tenemos que seguir llamando a la funcin. Hemos definido tanto la funcin como las variables de tipo real de doble precisin (ponindole el & como sufijo) porque es el tipo de datos de QBasic que admite valores ms altos ya que el factorial puede alcanzar valores muy elevados. Veamos lo que ocurrira paso a paso suponiendo que el usuario quiere calcular el factorial de 4. y El programa principal llama a factorial y le pasa 4. y Dentro de factorial, 4 es distinto de 1 y se calcula como 4 por factorial de 3. y Dentro de la segunda copia de factorial, 3 es distinto de 1 y se calcula como 3 por factorial de 2. y Dentro de la tercera copia de factorial, 2 es distinto de 1 y se calcula como 2 por factorial de 1. y Dentro de la cuarta copia de factorial, 1 es igual a 1 y se calcula factorial como 1 directamente. Esta copia de la funcin termina su ejecucin y devuelve 1. y La tercera copia de factorial toma el valor 1 devuelto por la cuarta, calcula el factorial 2 * 1 = 2 y termina devolviendo este valor. y La segunda copia de factorial toma el valor 2 devuelto por la tercera, calcula el factorial 3 * 2 = 6 y termina devolviendo este valor.

70

La primera copia de factorial toma el valor 6 devuelto por la segunda, calcula el factorial 4 * 6 = 24 y termina devolviendo este valor. y El programa principal toma este valor 24 que ya es el definitivo, lo escribe en pantalla y termina. Como se ha podido ver conforme vamos entrando en las funciones se van llamando a si mismas sucesivamente hasta el punto en que una de ellas ya no lo hace ms y termina devolviendo un valor a la anterior que lo va acumulando y termina. As todas se van cerrando hasta que llegamos al programa principal que se queda con el ltimo valor y termina el programa. Si hubiera habido alguna variable local es muy importante comprender que aunque haya muchas copias de la funcin y todas tienen el mismo nombre se trata de variables totalmente distintas, cada una de su copia de la funcin, que no compartirn sus valores en ningn caso y en todo caso estaran ocupando memoria por separado. Podemos comprobar que si intentamos calcular con este programa el factorial de un nmero superior a aproximadamente 30 se produce el error de "Espacio en pila agotado" ya que tenemos en memoria a la vez 30 copias de la funcin con sus variables de entorno y sus punteros a las funciones que los han llamado. En un programa no recursivo casi nunca llegaremos a tener funciones "anidadas tan profundamente" y no se producir este error. Como se ha podido ver la programacin recursiva es algo difcil de entender y que puede llegar a producir errores (En otros lenguajes se bloqueara el ordenador al desbordarse la pila). Este problema se poda haber resuelto con un bloque FOR de una forma tan sencilla como esta: factorial& = 1 FOR i = 1 TO n factorial& = factorial& * n NEXT Todos los problemas se pueden resolver siempre de la forma normal con condiciones y bucles (Esquema iterativo) o usando mdulos que se llaman a si mismo (Esquema recursivo). Solo deberemos utilizar la recursividad cuando demostremos en la fase de anlisis del programa que el algoritmo recursivo es ms rpido o ms corto que el iterativo. En la mayora de problemas sencillos normales y con las distintas posibilidades de usar bucles REPETIR y MIENTRAS que ofrece QBasic y los otros lenguajes estructurados es aconsejable evitar la recursividad siempre que se pueda ya que puede ser difcil demostrar matemticamente que un procedimiento complicado usado de forma recursiva no va a llegar a ejecutarse demasiadas veces desbordando la pila y llegando a detener la ejecucin del programa.
y

71

1.13.10 - DIFERENCIA ENTRE PROCEDIMIENTOS Y FUNCIONES En la programacin modular hemos diferenciado dos tipo de mdulos: Las funciones, que devuelven un valor tras su ejecucin y los procedimientos que no devuelven ningn valor. En el lenguaje de QBasic la forma de llamar a uno y a otro es distinta pero en otros lenguajes como C todos los mdulos son iguales y es el usuario el que tiene que determinar si usa al valor devuelto por los mdulos insertando su llamada en una expresin, o no. Queda claro que tendremos que usar una funcin cuando queramos obtener un valor y tendremos que usar un procedimiento cuando solo queramos que se haga algo o bien queramos estructurar el programa dividindolo en varias partes que sern llamadas cuando haga falta por un men situado en el mdulo principal. Nos puede ocurrir que necesitemos que un mdulo nos devuelva dos o ms valores del mismo o de distinto tipo. En este caso no podemos usar una funcin porque esta slo devuelve un valor. Habr que usar un procedimiento al que le pasaremos parmetros por referencia y nos los devolver modificados cuando termine. Veamos un ejemplo muy sencillo de un procedimiento que intercambia el valor de dos variables: 'Principal a = 92 b = 7 PRINT "a vale "; a; " y b vale "; b Intercambia a, b PRINT "a vale "; a; " y b vale "; b 'Procedimiento Intercambia 'Intercambia los valores de dos variables numricas 'Entrada: a y b: Reales cuyos valores sern intercambiados SUB Intercambia(a, b) aux = a a = b b = aux END SUB Donde el resultado sera: a vale 92 y b vale 7 a vale 7 y b vale 92 El programa es muy fcil de entender. El procedimiento modifica las dos variables que se le han pasado por referencia. En este caso las dos variables son del mismo tipo, pero en otro problema podan haber sido de cualquiera y se modificaran igual. Por ultimo aclarar dos conceptos importantes en la teora de la programacin modular: Llamamos "Funciones Puras" a las que tienen estas caractersticas: y Solo comparte datos con el resto del programa a travs de sus variables de interfaz, es decir, sus parmetros. y Todos los parmetros se pasan por valor.

72

No pide o muestra informacin al usuario ni a otros dispositivos del sistema (Discos, impresoras, etc...). y No usa tipos de datos definidos por el usuario en el programa principal. (Esto veremos lo que es en prximos temas) y No llama a funciones ni procedimientos que no sean "puros". De esta forma conseguimos que al llamar a una funcin con unos determinados parmetros siempre nos devuelva el mismo valor, y tambin conseguimos poderla usar en otro programa de QBasic solo con copiarla y pegarla sin tener que modificar ni aadir nada estando completamente seguros de que su funcionamiento va a ser siempre idntico. Llamamos "procedimientos puros" a los que tienen estas caractersticas: y Solo comparte datos con el resto del programa a travs de sus variables de interfaz, es decir, sus parmetros. y Todos los parmetros se pasan por referencia. y Puede compartir informacin con el usuario y con dispositivos externos. En algunos casos, especialmente en las funciones tendremos que intentar que sean puras, y en todos los programas ser conveniente no abusar de las variables globales ya que pueden hacer que los errores lgicos sean difciles de encontrar, adems de que si usamos un mdulo en otro programa ser necesario que existan las mismas variables globales para que todo funcione bien, cosa que puede ser un problema.
y

1.13.11 - PROTOTIPOS Si has guardado algn programa de los que incluyen procedimientos o funciones habrs observado que al hacerlo aparecen automticamente al principio del listado, en el mdulo principal, unas lneas que empiezan con DECLARE SUB o DECLARE FUNCTION seguida de los nombres y los parmetros de los mdulos. Estas lneas son lo que se conoce en programacin como prototipos de procedimientos y funciones. No tienen nada que ver con los prototipos de un nuevo invento que est en desarrollo ni nada de eso, sirven para indicarle a QBasic de que tipo son los parmetros y los datos que devuelven los mdulos para que los "conozca" antes de ser llamados y no tenga que ir a buscarlos por todo el cdigo fuente del programa cada vez que hagan falta. Estas lneas las escribe automticamente QBasic al guardar los programas y nosotros no tenemos que molestarnos en escribirlas ni conocer su sintaxis correcta. Si nuestro programa no tiene los prototipos, ya sea porque los hemos borrado o porque todava no hemos guardado el programa y no se han creado, todo funciona perfectamente pero se supone que las llamadas a mdulos van algo ms lento (no lo he cronometrado). Si borramos los prototipos volvern a aparecer la prxima vez que guardemos el programa.

73

Los prototipos, si existen, siempre van a ser la primera lnea del programa, antes no puede haber ninguna instruccin ejecutable. Si movemos los prototipos hacia abajo o escribimos alguna instruccin antes se generar un error y tendremos que volver a dejarlos al principio. Lo que s podemos escribir antes de los prototipos al principio del programa son lneas de comentario (Que empiezan con un apstrofo). Es comn en QBasic, cuando tenemos un programa terminado ms o menos largo, escribir al principio veintitantas lneas de comentario que incluyen un ttulo bonito y alguna descripcin para que ocupen la primera pantalla del listado y sea lo primero que se ve en el editor al abrir el programa. Si ya tenemos nuestros mdulos, hemos guardado el programa y se han generado los prototipos, y despus hacemos algn cambio en los parmetros de alguno de ellos es posible que la cabecera del mdulo no se corresponda con su prototipo y al ejecutar el programa se produzca algn error. En este caso la forma ms cmoda y ms rpida de solucionar el problema es borrando todos los prototipos y guardando el programa para que se generen de nuevo correctamente, mejor que intentar arreglarlos nosotros mismos. Tambin puede ser importante saber que un prototipo no se generar si todava no llamamos al mdulo desde ninguna otra parte del programa, aunque el cdigo ya est escrito.

TEMA 1.14 TIPOS DE DATOS DEFINIDOS POR EL USUARIO


1.14.1 - Definicin de nuevos tipos de datos 1.14.2 - Una aproximacin a la programacin orientada a objetos
y y

1.14.1 - DEFINICIN DE NUEVOS TIPOS DE DATOS Hasta ahora en una variable podamos almacenar solo un dato (Un nmero entero o real, una cadena, etc...) o podamos usar arrays para manejar gran cantidad de datos, pero siempre se tratara de una coleccin de datos del mismo tipo. En este tema vamos a ver cmo definir tipos de datos que contengan ms de un valor que puede ser de tipos distintos a la vez. Imaginemos una agenda donde necesitamos manejar nombre direccin, telfono, y edad de las personas. Vemos que los tres primeros datos son cadenas, aunque el telfono ser siempre ms corto que los otros dos, y la edad es un entero. Con lo que hemos visto hasta ahora tendramos que usar para cada individuo cuatro variables distintas que manejaremos por separado, ya sean variables simples o vectores con subndices para muchas personas a la vez. Veamos un programa muy sencillo que pide los datos de dos personas, los almacena en estas nuevas variables que vamos a ver y finalmente presenta en el nombre de la que sea ms joven. Que nadie se asuste, despus explicaremos lo que es cada cosa. TYPE TIPOPERSONA

74

nombre AS STRING * 30 direccion AS STRING * 40 telefono AS STRING * 9 edad AS INTEGER END TYPE DIM persona1 AS TIPOPERSONA DIM persona2 AS TIPOPERSONA CLS INPUT "Nombre de la primera persona: ", persona1.nombre INPUT "Direccin de la primera persona: ", persona1.direccion INPUT "Telfono de la primera persona: ", persona1.telefono INPUT "Edad de la primera persona: ", persona1.edad PRINT INPUT "Nombre de la segunda persona: ", persona2.nombre INPUT "Direccin de la segunda persona: ", persona2.direccion INPUT "Telfono de la segunda persona: ", persona2.telefono INPUT "Edad de la segunda persona: ", persona2.edad PRINT IF persona1.edad < persona2.edad THEN PRINT "El ms joven es "; persona1.nombre) ELSE PRINT "El ms joven es "; persona2.nombre) END IF El resultado sera: Nombre de la primera persona: Carlos Rodrguez Direccin de la primera persona: Carrera Espinel n 172 6A Telfono de la primera persona: 952870000 Edad de la primera persona: 23 Nombre de la segunda persona: Fernando Garcia Direccin de la segunda persona: Plaza del Ahorro n 21 4C Telfono de la segunda persona: 952879999 Edad de la segunda persona: 19 El ms joven es Fernando Garca Si hubiramos usado variables simples no tendra que haber ningn problema No?, pero como hemos usado un tipo definido por el usuario vamos a verlo todo paso a paso. Lo primero que hacemos de todo es declarar que vamos a usar un nuevo tipo de datos que se va a llamar TIPOPERSONA y va a estar formado por cuatro miembros que ahora declaramos de forma explcita: y Nombre va a ser de tipo cadena con un mximo de 30 caracteres.

75

Direccin va a ser de tipo cadena con un mximo de 30 caracteres. y Telfono va a ser de tipo cadena con un mximo de 9 caracteres. y Edad va a ser un entero. Y ya hemos terminado de declarar TIPOPERSONA A continuacin declaramos dos variables, persona1 y persona2 como de tipo TIPOPERSONA en vez de real o entero o algo as. Despus pedimos al usuario los datos de la primera persona y los vamos guardando cada uno en el lugar correspondiente de la variable persona1 usando el nombre de la variable seguido de un punto y del nombre del miembro al que nos estamos refiriendo en cada caso. El tipo de datos que podemos guardar en cada sitio depende del tipo de dato del miembro, por ejemplo en .edad no podemos guardar una palabra. Repetimos lo mismo, pero esta vez almacenando la informacin en persona2. Finalmente hacemos una comparacin entre las dos edades y escribimos el nombre de la persona ms joven. Como se puede ver no es tan difcil. Lo que hemos hecho es agrupar cuatro variables independientes bajo un nombre comn, pero de todas formas tendremos que manejar cada una por separado usando el nombre de la variable que hayamos declarado de este tipo seguido del punto y del nombre del miembro. Es muy importante entender que TIPOPERSONA no es una variable, sino un tipo de datos que hemos definido en nuestro programa y se une a los tipos STRING, INTEGER, LONG, SINGLE y DOUBLE de que ya dispone QBasic y que para poderlo utilizar hay que declarar explcitamente que una variable sea de este tipo usando la instruccin DIM y que ese nombre que le demos a la variable ser el que hay que utilizar en el cdigo del programa. Si usamos el nombre de la variable sin el punto ni el nombre de uno de sus componentes miembro se producir un error de "Tipos no coinciden" porque QBasic no sabe a cul de los miembros nos estamos refiriendo. Si escribimos mal el nombre del componente se producir el error "Elemento no definido". Si escribimos mal el nombre de la variable ocurrir que QBasic se creer que estamos usando una nueva variable y aparentemente no ocurrir nada, pero se pueden producir errores de lgica muy difciles de detectar, problema comn en QBasic por no exigir la declaracin de variables. Los componentes miembro pueden ser de cualquiera de los cinco tipos de que dispone QBasic, solo que en el caso de que se declaren como cadenas tendrn que ser de longitud fija. Habr que poner la palabra STRING seguida de un asterisco (*) y del nmero de caracteres que va a tener la cadena. Como vimos en el tema de tipos de datos si intentamos asignar una cadena de tamao superior al mximo no ocurre ningn error, pero todo lo que sobre se pierde.
y

76

Tambin podemos declarar algn miembro como de un tipo de datos ya definido por el usuario anteriormente en el mismo programa. Veamos un ejemplo: TYPE TIPONOMBRE nombre AS STRING * 15 apellido1 AS STRING * 15 apellido2 AS STRING * 15 END TYPE TYPE TIPODIRECCION calle AS STRING * 30 numero AS INTEGER piso AS INTEGER puerta AS STRING * 1 cp AS STRING * 5 ciudad AS STRING * 20 provincia AS STRING * 2 END TYPE TYPE TIPOPERSONA nombre AS TIPONOMBRE direccion AS TIPODIRECCION telefono AS STRING * 9 edad AS INTEGER END TYPE Si declaramos una variable de TIPOPERSONA haciendo: DIM persona AS TIPOPERSONA Para acceder a todos sus componentes escribiramos cosas como: persona.nombre.nombre persona.nombre.apellido1 persona.direccion.calle persona.direccion.ciudad persona.edad De esta forma podemos estructurar muy bien datos que tengan una estructura compleja utilizando una jerarqua de variables que puede resultar muy descriptiva, pero tampoco conviene pasarse. Normalmente no ser necesario usar ms de dos o tres niveles de datos definidos por el usuario porque si lo hacemos los identificadores de las variables pueden llegar a ser muy largos. Como se puede ver los miembros de distintos tipos de datos pueden llevar nombres iguales. Como siempre vamos a usar estos nombres junto con el nombre de la variable declarada que s va a ser distinto en cada caso no habr ninguna confusin. Tambin es muy til poder declarar arrays de un tipo definido por el usuario. Por ejemplo si usamos el TIPOPERSONA del primer ejemplo podemos declarar un vector de 50 personas haciendo: DIM personas (1 TO 50) AS TIPOPERSONA Y para acceder a los elementos del vector haramos cosas como... personas(3).nombre personas(17).edad personas(40).telefono

77

...igual que si se tratara de vectores de un tipo normal, solo que habr que especificar cual miembro queremos usar. Es posible declarar variables y mdulos con el mismo nombre que un tipo de datos definido por el usuario, pero para evitar confusiones es recomendable evitar esto a toda costa. Podemos definir tipos de datos dentro de procedimientos o funciones, pero si lo hacemos slo estarn disponibles para las variables que declaremos dentro del mismo mdulo. Habr que definirlos despus de la lnea SUB nombre o FUNCTION nombre. Los tipos definidos en el mdulo principal estarn disponibles para todos los mdulos del programa, pero si despus queremos exportar mdulos que los usen a otros programas tambin habr que copiar la definicin del tipo de dato para que todo funcione correctamente. Para la construccin de los nombres de los tipos de datos se siguen las mismas normas que para los nombres de variables y mdulos. Normalmente se escribe todo en maysculas para que resalte ms y se empieza siempre por TIPO para darse cuenta fcilmente de que no se trata de una variable o un mdulo. El uso intensivo de tipos de datos definidos por el usuario junto con las constantes ayudan a que el cdigo del programa quede muy detallado y sea ms fcil de leer. 1.14.2 - UNA APROXIMACIN A LA PROGRAMACIN ORIENTADA A OBJETOS Antes de nada aclarar que QBasic no es un lenguaje de programacin orientado a objetos. En el apartado anterior hemos visto como ejemplo una estructura a la que llambamos TIPOPERSONA que tena unos datos miembros como son nombre, edad, etc... y que nos serva como "molde" para crear una o muchas variables que tienen todas estas caractersticas. Es fcil pensar en los tipos de datos como la definicin de una CLASE de objetos que agrupa a una serie de DATOS MIEMBROS que van a tener todas las INSTANCIAS de las variables que se declaren de esa clase. Esto puede ser la base de la Programacin Orientada a objetos. Una nueva metodologa de programacin que crea lo que se llama "Tipos de datos abstractos" que adems de datos miembros incluyen procedimientos miembros (Mtodos) y se utiliza de forma que puede reutilizar datos de otras clases ya existentes creando lo que se conoce como "Jerarqua de clases". Estas clases sern usadas en los programas para definir Objetos de esa clase que tendrn todas estas caractersticas y podrn ser utilizados por los programas llamando a sus mtodos. Estas tcnicas se utilizan ampliamente en lenguajes de programacin mucho ms avanzados como C++, Object Pascal o Java donde los programadores pueden usar objetos ya existentes o adems crear los suyos propios definiendo estas clases. Si ha quedado claro el tema de tipos de datos definidos por el usuario en QBasic y los manejamos

78

con soltura puede ser que nos resulte un poco ms sencillo (o menos difcil) aprender a programar objetos en otros lenguajes ms modernos de los que se utilizan en la actualidad, aunque de todas formas habr que aprender toda una multitud de conceptos nuevos y tcnicas de programacin que no existen en QBasic.

TEMA 1.15 FICHEROS SECUENCIALES


y1.15.1 y1.15.2 y1.15.3

- Introduccin al uso de ficheros - Introduccin a los ficheros secuenciales - Escribir informacin en ficheros secuenciales y1.15.4 - Leer informacin de ficheros secuenciales y1.15.5 - Anexar informacin a ficheros secuenciales

1.15.1 - INTRODUCCIN AL USO DE FICHEROS Hasta ahora en nuestros programas para almacenar informacin hemos utilizado variables que son la forma que tienen los lenguajes de programacin de almacenar datos en la memoria RAM del ordenador. Es una forma muy cmoda y extremadamente rpida de manejar datos, pero tiene el grave inconveniente de que toda la informacin se pierde al finalizar la ejecucin del programa. En el tema de los arrays vimos varios ejemplos de programas donde se pedan los nombres de las personas que viven en un bloque de pisos. Todo funcionaba muy bien pero haba que hartarse de escribir todos estos nombres cada vez que entrbamos al programa porque al terminar se pierden. Una solucin para evitar esto podra ser incluir los nombres de las personas en el cdigo fuente del programa, pero estos nombres pueden cambiar alguna vez y el usuario del programa no tiene por qu saber cmo modificar esto. La solucin definitiva a este problema consiste en guardar la informacin en el sistema de archivos de las unidades de disco del ordenador donde permanecer por tiempo indefinido y podremos recuperarla o modificarla todas las veces que queramos. La informacin se almacena en los discos del ordenador en unas estructuras lgicas llamadas ficheros o archivos. Mientras que en una cinta de vdeo si grabamos una pelcula nosotros nos tenemos que preocupar de ver dnde est el final para grabar lo siguiente a continuacin y no borrar un trozo de lo que ya tenemos, en el ordenador es el sistema operativo (MS-DOS, Linux, Etc.) el que se encarga de controlar en que parte del disco se graban los datos y de ponerle un nombre y situarlo en el sistema de archivos (La FAT y los directorios) para que nosotros podamos acceder a esa informacin solo con dar el nombre del archivo sin tener que preocuparnos de en que parte del disco est o de si nos vamos a cargar otra informacin al grabar nuevos datos. Principalmente hay dos tipos de ficheros: Secuenciales y de acceso aleatorio. Aunque estn grabados en los discos de idntica forma se

79

diferencian de cmo los manejamos nosotros desde nuestro programa. En los siguientes apartados veremos las diferencias y la forma de usar cada uno de ellos. Ficheros secuenciales son aquellos en que los datos se graban o se leen lnea a lnea siempre empezando por el principio. Normalmente al abrirlo se copiar su contenido en alguna estructura de variables de nuestro programa para su utilizacin y posible modificacin. Despus si hay que guardarlo se har completamente sustituyendo a toda la informacin que hubiera anteriormente en el fichero, aunque alguna parte de la informacin vuelva a ser la misma. El concepto es similar a lo que hacemos con los ficheros de las aplicaciones normales como procesadores de texto o los propios ficheros *.BAS de los programas de QBasic. Ficheros de acceso directo o acceso aleatorio (No tiene nada que ver con los iconos de acceso directo de Windows) son los que nos permiten leer o modificar un determinado dato sabiendo su posicin sin tener que recorrer todos los datos anteriores. El ejemplo ms tpico de estos ficheros son las tablas de las bases de datos donde podemos aadir, modificar, consultar o borrar la informacin de una persona (un registro) sin preocuparnos de los dems. La extensin que elijamos para nuestros ficheros puede ser cualquiera, pero deberemos evitar usar extensiones ya usadas por otros programas que podran provocar conflictos en Windows. Lo ms normal es usar extensiones como .TXT para los ficheros secuenciales que solo contienen texto y .DAT en general para los que pueden contener texto o cualquier otro tipo de caracteres. 1.15.2 - INTRODUCCIN A LOS FICHEROS SECUENCIALES La forma ms elemental de trabajar con ficheros es leerlos lnea por lnea, una tras otra, para irlos analizando o irlos guardando en una estructura de datos de nuestro programa. Si queremos guardarlo se har de la misma forma, escribiendo lneas una tras otra desde la primera hasta la ltima. Los ficheros secuenciales normalmente se leen siempre enteros y para guardarlos se puede elegir entre reemplazar completamente su contenido o bien guardar las nuevas lneas a continuacin de las existentes, a lo que llamaremos "Anexar". Entendemos por "Lnea" a una secuencia de caracteres alfanumricos (Letras, nmeros y signos de puntuacin) terminados por el carcter especial de retorno de carro que es el que se genera cuando pulsamos la tecla Enter. Este carcter no lo vemos en la pantalla pero se genera automticamente al final de lo que escribimos con la orden PRINT, si no ponemos un punto y coma al final, como vimos en el tema de Entrada y Salida. Para trabajar con ficheros hay que "Abrirlos" especificando su nombre y si queremos leer o escribir en l. A continuacin leeremos o escribiremos los datos, segn corresponda por el modo en que est abierto, y finalmente lo cerraremos.

80

1.15.3 - ESCRIBIR INFORMACIN EN FICHEROS SECUENCIALES Vamos con un ejemplo de un programa que abre un fichero para escritura (lo crea), escribe en l los nombres de los das de la semana y finalmente lo cierra. OPEN "diasem.txt" FOR OUTPUT AS #1 PRINT #1, "Lunes" PRINT #1, "Martes" PRINT #1, "Mircoles" PRINT #1, "Jueves" PRINT #1, "Viernes" PRINT #1, "Sbado" PRINT #1, "Domingo" CLOSE #1 Este programa no produce ningn resultado en la pantalla, slo el rtulo "Presione cualquier tecla y contine" para indicar que ha terminado. Si buscamos el fichero diasem.txt y lo abrimos con el bloc de notas de Windows comprobaremos que contiene lo siguiente: Lunes Martes Miercoles Jueves Viernes Sabado Domingo Salvo los tpicos problemas con los acentos, es lo que esperbamos. Si lo abrimos con el EDIT de MS-DOS veremos los acentos correctamente. Ahora vamos a comentar detalladamente cmo funciona el programa. La primera lnea es la ms importante cuando trabajamos con ficheros. Si la traducimos al castellano dice algo as como: Abre "diasem.txt" para salida de datos y llmale #1 Visto esto ya est claro su significado pero de todas formas hay que decir un montn de cosas: y Al abrir un fichero para escritura si no existe se crea y si existe se borra su contenido para empezar desde cero. y Si no especificamos una ruta de directorios, como hemos hecho en este caso, el fichero se crea en el "directorio activo". El directorio activo es en el que nos encontrbamos al entrar a QBasic desde MS DOS. En el case de que hayamos entrado desde Windows ser el que aparece en la casilla "Iniciar en:" de las propiedades del acceso directo a QBasic. Para cambiar el directorio activo desde QBasic podemos usar la orden CHDIR seguida de la nueva ruta de directorios. Esta orden la veremos con ms detalle en el tema dedicado a comunicacin con aplicaciones externas.

81

Si especificamos una ruta tendr que existir o se producir un error. Al crear un nuevo archivo no se crean directorios que no existan. y El nombre y ruta del archivo a abrir se pueden especificar de forma literal, como hemos hecho aqu, o bien usar una expresin de cadenas cuyo valor sea la ruta y el nombre del archivo con la sintaxis correcta (Barras inversas, puntos y dems). La nica cosa que nos puede resultar extraa de la instruccin OPEN es ese #1 que aparece al final. A este nmero le llamamos "descriptor de archivo" y nos va a servir para identificar unvocamente al archivo que acabamos de abrir en las rdenes de entrada y salida de nuestro programa hasta que cerremos el archivo. Por lo tanto si abrimos otro archivo antes de cerrar este tendremos que usar el descriptor #2 y as sucesivamente. QBasic nos permite tener abiertos a la vez hasta 255 archivos, pero la memoria disponible y la clusula FILES del archivo CONFIG.SYS nos limitarn el nmero. Normalmente no necesitaremos ms de uno o dos archivos abiertos a la vez en nuestros programas sencillos. Siguiendo con el cdigo de nuestro programa a continuacin tenemos una serie de instrucciones PRINT que como llevan el descriptor de un archivo escriben en el archivo en lugar de en la pantalla. Podemos usarlas exactamente de la misma forma que cuando las usamos normalmente para escribir en pantalla. No tienen por qu estar todas juntas. Finalmente lo que hacemos es cerrar el archivo con la orden CLOSE. Conforme vamos escribiendo en el archivo con la orden PRINT la informacin no se graba en el disco inmediatamente, sino que se va almacenando de forma transparente para el usuario en un rea especial de la memoria (El "Buffer" de escritura en disco) o bien en registros de memoria especial dentro de la placa de circuitos del propio disco duro y cuando hay informacin suficiente es cuando el cabezal del disco se mueve y la informacin se graba fsicamente en el disco. Al cerrar el archivo nos aseguramos de que la informacin que quedara por escribir se grabe realmente en el disco, y que adems todos los recursos utilizados, as como el nmero del descriptor se liberen y queden disponibles para ser usados por otro archivo. Si programamos para un sistema multitarea (Windows 9x lo es, ms o menos) es muy importante ocupar los recursos del sistema el menor tiempo posible y por lo tanto deberemos cerrar los archivos que ya no nos hagan falta abiertos aunque todava vayamos a seguir utilizando otras partes del programa durante ms tiempo. Cuando abrimos un fichero este queda disponible para todos los mdulos (Programa principal, procedimientos y funciones) de nuestro programa, con lo que podemos abrirlo desde un mdulo, leer o escribir desde otro, y cerrarlo desde otro, pero siempre teniendo en cuenta que solo lo podemos usar despus de haberlo abierto y antes de cerrarlo.

82

1.15.4 - LEER INFORMACIN DE FICHEROS SECUENCIALES En el apartado anterior hemos abierto un archivo para escritura. Ahora vamos a abrir uno existente para leer su contenido. Veamos un ejemplo de un programa que abre el fichero ya existente "diasem.txt" que hemos creado con el programa anterior para mostrarlo en pantalla. CLS OPEN "diasem.txt" FOR INPUT AS #1 WHILE NOT EOF(1) LINE INPUT #1, linea$ PRINT linea$ WEND CLOSE #1 Este programa no produce ningn cambio en el archivo y su resultado por pantalla es: Lunes Martes Mircoles Jueves Viernes Sbado Domingo Ahora veamos cmo funciona. y Borramos la pantalla. y Abrimos el archivo "diasem.txt" para lectora (INPUT) y le llamamos #1. y Mientras no se acabe el archivo 1, leemos lnea por lnea y las vamos escribiendo en pantalla usando la variable linea$. y Cerramos el archivo. Como se puede ver, la instruccin OPEN es igual a la anterior, slo vara el modo de apertura que en este caso es INPUT, es decir, entrada de datos, lectura. Si el fichero especificado no existe se producir un error de tiempo de ejecucin. En el apartado de control de errores veremos cmo detectar si un fichero existe antes de inventar abrirlo. A continuacin vamos leyendo las lneas del archivo. Como no tenemos por qu saber cuantas lneas tiene el archivo, usaremos un bucle MIENTRAS que usa como condicin la funcin EOF (End Of File) que devuelve Verdadero si se ha alcanzado el final del archivo y falso en caso contrario. Observa que a EOF le pasamos como parmetro el descriptor del archivo, pero sin el carcter #. Esto es para poder usar alguna expresin matemtica que devuelva el nmero del archivo si es que tenemos varios abiertos y no queremos usar el nmero de forma literal. Dentro del bucle lo que hacemos es leer una lnea usando la instruccin LINE INPUT seguida del descriptor del archivo y del nombre de la variable donde la queremos guardar. Esta instruccin permite leer lneas de hasta 255 caracteres que acaben con un carcter especial de retorno de carro (Las que en el ejemplo

83

anterior escribimos con la orden PRINT). Seguidamente escribimos en la pantalla el valor de la variable line$ usando la orden PRINT como hemos hecho siempre. Finalmente, terminado el bucle, cerramos el archivo para dejar el recurso disponible. Como estaba abierto para lectura no se producir ningn cambio. Es muy importante entender que para leer dados de un fichero secuencial debemos usar siempre un bucle MIENTRAS, y no un REPETIR, ya que en este caso intentaramos leer al menos una lnea del fichero antes de comprobar si se ha llegado al final, y se puede dar el caso de que un fichero exista, pero est completamente vaco con lo que intentaramos leer ms all del final del final del archivo y generaramos un error de tiempo de ejecucin. En la ayuda de QBasic viene un ejemplo hecho con un bloque DO...LOOP y se puede comprobar que puede que no funcione correctamente en el caso de que el fichero est vaco. Tambin podemos decir aqu que si somos nosotros quienes escribimos el archivo a leer usando un editor de texto es conveniente pulsar al final la tecla ENTER una vez para que el cursor pase a la lnea de abajo y se genere el carcter de retorno de carro. Si no lo hacemos puede ocurrir que cuando leamos el archivo con nuestro programa de QBasic en algunos casos no se llegue a leer la ltima lnea. Esta indicacin tambin la hace MS-DOS para los ficheros CONFIG.SYS, AUTOEXEC.BAT y los ficheros de proceso pro lotes que nosotros podamos escribir. Para terminar con la teora de los ficheros secuenciales vamos a ver un ejemplo de un programa que copia un fichero de texto en otro ponindole todo el texto en maysculas con ayuda de la funcin UCASE$. El usuario tendr que escribir los nombres de los dos ficheros, el existente y el nuevo, y el programa los abrir los dos a la vez, uno para lectura y el otro para escritura. CLS INPUT "Nombre del fichero de origen ya existente: ", fich1$ INPUT "Nombre del nuevo fichero: ", fich2$ OPEN fich1$ FOR INPUT AS #1 OPEN fich2$ FOR OUTPUT AS #2 PRINT "Leyendo y grabando, por favor espera..." WHILE NOT EOF (1) LINE INPUT #1, linea$ PRINT #2, UCASE$(linea$) WEND CLOSE PRINT "Proceso terminado" El resultado por pantalla podra ser: Nombre del fichero de origen ya existente: diasem.txt Nombre del nuevo fichero: diasem2.txt Leyendo y grabando, por favor espera...

84

Proceso terminado Y en los ficheros el que pusiramos primero no se modificar, mientras que el segundo si no existe se crea y si existe se sobrescribe con el contenido del primero en maysculas. Ahora veamos algunas cosas del programa: y Como se puede ver, tenemos dos ficheros abiertos a la vez y los identificamos con un descriptor diferente. y Leemos datos mientras no se acabe el fichero 1 y los vamos escribiendo al fichero 2 convertidos a maysculas usando la funcin UCASE$. y Sacamos un mensaje por pantalla antes de empezar y despus de terminar para que el usuario vea "que ha pasado algo". Si prevemos que el proceso va a ser muy largo tendramos que sacar algn mensaje de progreso cada vez que se haga un nmero de pasadas del bucle. Para hacer esto usaramos algn contador. y Finalmente cerramos todos los ficheros abiertos usando la orden CLOSE sin pasarle ningn descriptor. Si escribes el programa en QBasic y lo ejecutas podrs comprobar que puede ser difcil acertar a la primera con el nombre del archivo, y ms si ponemos una ruta de directorios, unidades de disco, etc... con lo que se producir un error de ejecucin. Tambin si se te ocurre poner el mismo nombre las dos veces nos dar un error de tiempo de ejecucin diciendo que el fichero ya est abierto. En el tema de manejo de errores veremos cmo se pueden solucionar estos problemas. 1.15.5 - ANEXAR INFORMACIN A FICHEROS SECUENCIALES Cuando abramos un fichero para escritura usando el modo OUTPUT decamos que si no existe se crea y si existe se borra su contenido, pero en algunos caso nos puede interesar que el contenido del fichero no se borre y las nuevas lneas de informacin se aadan al final del fichero ya existente, incrementando su tamao. Es el caso de los ficheros .LOG que utilizan muchos programas para llevar un registro de acciones o de problemas que puedan surgir y en los que los distintos procesos o programas van aadiendo informacin conforme va haciendo falta. La forma de abrirlo es usando el modo APPEND en la instruccin OPEN, por ejemplo: OPEN "eventos.txt" FOR APPEND as #1 En este caso si no existe el fichero eventos.txt se crea, y si existe se conserva, aadindose la nueva informacin al final. La forma de trabajar con un fichero abierto en modo APPEND es exactamente la misma que en uno abierto en modo OUTPUT. Hay que recordar que no podemos asegurar que la informacin se graba fsicamente en el fichero hasta que no lo cerremos con la orden CLOSE. El tamao de los ficheros usados slo de esta forma siempre va a crecer y seremos nosotros los encargados de observar que no se hace

85

demasiado grande y de borrarlo cuando ya no nos haga falta la informacin que contiene.

TEMA 1.16 FICHEROS DE ACCESO DIRECTO O ALEATORIO


y y y

y y

1.16.1 - Introduccin a los ficheros directos 1.16.2 - Lectura y escritura en ficheros directos 1.16.3 - Mantenimiento de ficheros directos y 1.16.3.1 - Introduccin al mantenimiento de ficheros y 1.16.3.2 - Altas. Aadir registros y 1.16.3.3 - Consultas. Bsqueda secuencial de registros y 1.16.3.4 - Modificaciones de registros y 1.16.3.5 - Bajas. Borrar registros y 1.16.3.6 - Compactacin de ficheros directos y 1.16.3.7 - Elaboracin de listados de datos 1.16.4 - Breve idea sobre bases de datos 1.16.5 - Otras instrucciones relacionadas con ficheros

1.16.1 - INTRODUCCIN A LOS FICHEROS DIRECTOS Los ficheros secuenciales los usbamos de forma que era necesario leerlos lnea por lnea siempre desde el principio y sin saltar ninguna, y para guardarlos ocurra lo mismo. Ahora vamos a trabajar con ficheros de acceso directo o aleatorio, que estarn formados por "trozos" de igual tamao, y por lo tanto el ordenador ser capaz de saltar al registro que nosotros queramos para leerlo o escribirlo. Los registros se corresponden con la estructura de un tipo de datos definido por el usuario y para acceder a ellos nicamente se calcula su posicin inicial a partir del nmero de registro y de su tamao, que nos calcula la funcin LEN. No estn separados de ninguna forma, ya no hablamos de lneas ni de caracteres de retorno de carro. Otro concepto importante que se introduce con los ficheros directos es el de "Campo clave". El campo clave puede ser la posicin dentro del archivo donde se guarda ese registro en concreto. Esto implica que el campo clave es nico ya que no puede haber dos registros en la misma posicin. Un ejemplo de campo clave podra ser el DNI en una lista de personas o el nmero de habitacin en la lista de habitaciones de un hotel. Este dato no est escrito en la estructura de datos del archivo, sino que es determinado por la posicin del registro dentro del archivo. Si usamos como campo clave un nmero muy grande como el DNI nos encontraremos con el inconveniente de que el archivo llegar a tener un tamao muy grande ya que como veremos ms adelante los registros intermedios aunque estn vacos tambin existirn en el archivo. De la misma forma el campo clave es obligatorio de especificar y no lo podemos dejar en blanco, ya que no se sabra en qu posicin se guarda el registro. Lo ms inmediato es usar como campo clave un valor numrico bajo correlativo (1, 2, 3...) que el usuario vaya escribiendo, o bien que el

86

programa lo calcule automticamente imitando a los tipos de datos auto numricos de las bases de datos. 1.16.2 - LECTURA Y ESCRITURA EN FICHEROS DIRECTOS Vamos con un ejemplo que define un tipo de datos compuesto de nombre y edad, y abre un fichero en modo RANDOM para almacenar en l los datos de personas que va escribiendo el usuario, al estilo de como lo hara una agenda. TYPE TIPOPERSONA nombre AS STRING * 20 edad AS INTEGER END TYPE DIM persona AS TIPOPERSONA CLS OPEN "agenda.dat" FOR RANDOM AS #1 LEN = LEN(persona) n=0 DO n=n+1 INPUT "Nombre: ", persona.nombre INPUT "Edad: ", persona.edad PUT #1, n, persona INPUT "Meter ms datos (S/N)? ", respuesta$ LOOP UNTIL respuesta$="N" CLOSE #1 PRINT n; "registros grabados en la agenda." El resultado por pantalla podra ser este: Nombre: Pepe Edad: 22 Meter ms datos (S/N)? S Nombre: Paco Edad: 25 Meter ms datos (S/N)? S Nombre: Ana Edad: 28 Meter ms datos (S/N)? N 3 registros grabados en la agenda. Y el resultado en el sistema de archivos ser que se ha creado un nuevo archivo llamado agenda.dat, si no exista de antes, que contiene los datos de tres personas. De la forma que est hecho el programa nuestros tres registros iran a parar siempre al principio del archivo. En el siguiente ejemplo veremos cmo leer los datos del archivo, ahora vamos a ver cmo funciona este programa. y Lo primero que hacemos es definir un tipo de datos personalizado que tiene dos miembros, nombre de 20 caracteres y edad que es un entero. y A continuacin declaramos una variable como del tipo de datos que acabamos de definir y que nos va a servir como "Plantilla" para trabajar con nuestro archivo de acceso aleatorio. y Despus borramos la pantalla como siempre.

87

Ahora abrimos el archivo "agenda.dat" en modo de acceso aleatorio (RANDOM), le asignamos el descriptor #1 y con la clusula LEN le decimos cuanto largo van a ser los registros. Podamos haberlo calculado nosotros viendo cuando ocupan todos los miembros de nuestro tipo de datos y poner simplemente el nmero de bytes, pero es ms cmodo y ms seguro que lo haga la funcin LEN (Se llama igual que la clusula, pero cada cosa es diferente). y Seguidamente ponemos un contador (n) a cero. En este caso en QBasic no hubiera sido necesario, pero es mejor acostumbrarse a hacerlo siempre. y A continuacin entramos en un bloque REPETIR, incrementamos en 1 el contador y le pedimos al usuario que escriba el nombre de la persona y despus la edad y lo guardamos cada cosa en su sitio en la variable persona. y Ahora vamos a escribir en el fichero de acceso aleatorio el registro completo, donde ya tenemos almacenado el nombre y la edad que acaba de escribir el usuario. Para hacer esto usamos la instruccin PUT a la que le tenemos que pasar como parmetro el descriptor del archivo (el carcter # es opcional), el nmero de registro al que nos estamos refiriendo, para lo que usaremos el contador n, y los datos propiamente dichos que estn almacenados en la variable persona. y Preguntamos al usuario si quiere meter ms datos y si pulsa la N mayscula salimos del bucle, en caso contrario volvemos al principio donde incrementamos el contador, volvemos a llenar los miembros de la variable persona y finalmente los grabamos en la siguiente posicin del archivo. y Cuando salimos del bucle cerramos el archivo (hay que hacerlo siempre) y escribimos un mensaje en pantalla con el nmero de registros grabados aprovechando el valor de la variable n que usamos como contador. Cuando abrimos un archivo en modo RANDOM si no existe se crea, y si existe no ocurre nada, ya que como veremos a continuacin en l vamos a poder tanto leer como escribir registros. Si al grabar un registro nos referimos a uno que ya existe, este se sobre escribir entero, y si no existe el fichero aumentar de tamao hasta permitir usar ese nmero de registro, aadiendo si es necesario ms registros entre medio. La informacin que se almacenar en estos registros intermedios es indeterminada. Por ejemplo si tenemos un fichero con tres registros (1, 2 y 3) y escribimos en el registro 10 se almacenar nuestra informacin en el registro 10 y a la vez se crearn 6 registros ms (4, 5, 6, 7, 8 y 9) llenos de informacin indeterminada. El primer registro es siempre el 1 y el ltimo posible depender del espacio disponible en disco. Los ficheros que creemos de esta forma ya no los podremos editar con un editor de texto como el EDIT o el bloc de notas, ya que contienen una mezcla de letras y de valores numricos que se pueden
y

88

representar por caracteres especiales no imprimibles. Tambin es necesario abrirlos siempre usando el mismo tipo de datos como "molde", ya que de no hacerlo as todos los registros se mezclaran y no nos servira de nada. Ahora vamos a ver otro ejemplo de programa que lea el fichero que acabamos de crear con el programa anterior. TYPE TIPOPERSONA nombre AS STRING * 20 edad AS INTEGER END TYPE DIM registro AS TIPOPERSONA CLS OPEN "agenda.dat" FOR RANDOM AS #1 LEN = LEN(registro) numeroRegistros = LOF(#1) / LEN(registro) FOR n = 1 TO numeroRegistros GET #1, n, registro PRINT persona.nombre, persona.edad NEXT CLOSE #1 El resultado por pantalla podra ser este: Pepe 22 Paco 25 Ana 28 Y en el fichero no se producira ningn cambio porque no hay ninguna instruccin de escritura. Como se puede ver el programa es parecido al anterior. Hemos definido un tipo de datos que en esta ocasin se llama registro en vez de persona pero tiene exactamente la misma estructura y a continuacin abrimos el fichero de la misma forma. Podemos calcular el nmero de registros que tiene un fichero usando la funcin LEN que, como ya sabemos, nos da el tamao del registro, y la funcin LOF (Length Of File) que nos devuelve el tamao en bytes del fichero abierto cuyo descriptor le pasemos como parmetro (con o sin #) usando la frmula "Tamao del fichero dividido por tamao del registro". De esta forma si el fichero tiene por ejemplo 200 bytes y cada registro mide 40 bytes obtenemos como resultado que hay 5 registros. Como no hay caracteres retornos de carro ni nada de eso las cuentas siempre salen exactas. Una vez que sabemos cuntos registros hay podemos escribirlos en pantalla sencillamente usando un bucle FOR. Para leer informacin del fichero usamos la instruccin GET cuya forma de uso es similar a la de la instruccin PUT que ya hemos visto. Tras ejecutar la instruccin GET el contenido del registro queda almacenado en la estructura de la variable. Si leemos ms all del final del fichero no ocurre nada, a diferencia de lo que pasaba con los ficheros secuenciales, pero la informacin que se nos da es indeterminada, en el mejor de los casos ceros y espacios en blanco. De todas formas lo correcto es calcular cual es el ltimo registro como hemos hecho antes y no leer nada ms all para

89

evitar errores lgicos y posibles confusiones. Tambin nos har falta conocer siempre el nmero de registros para insertar los nuevos a continuacin y no sobre escribiendo los ya existentes como hace nuestro primer ejemplo que no es nada prctico. Esto lo veremos en los siguientes apartados dedicados a lo que se conoce como "Mantenimiento de ficheros" 1.16.3 MANTENIMIENTO DE FICHEROS DIRECTOS 1.16.3.1 INTRODUCCIN AL MANTENIMIENTO DE FICHEROS En el apartado anterior tenamos un programa para meter datos en un fichero, y otro para listar todos los datos que acabbamos de meter, y para complicarlo todo si ejecutbamos otra vez el primer programa como siempre empezbamos por el registro 1 seguro que sobre escribamos datos. Tambin para borrar informacin slo podamos borrar todos los datos a la vez borrando el fichero desde el sistema operativo y empezando con uno nuevo vaco. Esta sera una forma muy mala de trabajar con una base de datos de una agenda. Para manejar datos se han definido cuatro operaciones bsicas: Altas, bajas, modificaciones y consultas, adems de la elaboracin de listados y la compactacin del fichero. En estos apartados veremos unas breves (muy breves) indicaciones de como conseguir que nuestros programas de QBasic puedan hacer esto con un fichero de acceso directo. 1.16.3.2 - ALTAS. AADIR REGISTROS En nuestro ejemplo de la seccin anterior veamos como aadir registros a un archivo directo con la instruccin PUT. Lo hacamos muy bien, solo que siempre empezbamos por la primera posicin y de esta forma cada vez que ejecutamos el programa sobre escribamos datos y perdamos informacin. La idea de todo esto es conseguir poder aadir informacin al fichero cada vez que ejecutemos el programa, en este caso pediremos al usuario que escriba la posicin (campo clave principal) donde quiere guardar el nuevo registro. Veamos como ejemplo un procedimiento SUB que pide al usuario el nmero de registro, el nombre y la edad de la persona y los aade al final del fichero. Suponemos que la estructura de TIPOPERSONA est definida en el programa principal y que el fichero tambin est ya abierto y tiene el descriptor #1. 'Procedimiento ALTA 'Aade un nuevo registro a agenda.dat SUB Alta() DIM nuevaPersona AS TIPOPERSONA DIM registro AS INTEGER INPUT "Nmero de registro: ", registro INPUT "Nombre: ", nuevaPersona.nombre INPUT "Edad: ", nuevaPersona.edad

90

PUT #1, registro, nuevaPersona END SUB Si es la primera vez que usamos el programa y el fichero no existe, se crear uno nuevo. Los datos que hemos introducido se grabarn en la posicin que indiquemos como nmero de registro o campo clave. Si esta posicin de registro ya estaba ocupada por otros datos, la informacin se sobre escribir y se perder la anterior. Si es una posicin vaca ms all del final del archivo, el tamao crecer crendose registros hasta alcanzar la posicin especificada donde se grabar nuestra informacin. El contenido de estos registros intermedios es indeterminado, normalmente restos de datos previamente descartados existentes en esa posicin del disco o en memoria, y no lo podemos usar para nada. Este comportamiento de que el fichero vaya creciendo hace que si usamos como campo clave un nmero muy grande como un DNI, el fichero aumente de tamao considerablemente. Otros algoritmos ms elaborados nos avisarn si el campo clave ya est ocupado con informacin, nos mostrarn la informacin que contiene para darnos la posibilidad de sobreescribirla o de elegir otro campo clave, etc... 1.16.3.3 - CONSULTAS. BSQUEDA SECUENCIAL DE REGISTROS Una vez que tenemos informacin en el registro nos puede interesar buscar un nombre, por ejemplo, y que nos salga en la pantalla los otros datos de la persona, en nuestro ejemplo slo la edad. Para hacer esto hay complicados mecanismos de bsqueda muy rpida suponiendo que nuestro fichero est ordenado. Como no es el caso, la nica forma de encontrar algo es ir mirando registro por registro hasta que encontremos lo que buscamos o se acabe el fichero. Vamos con un ejemplo que pregunta al usuario el nombre de la persona que quiere buscar y recorre el fichero hasta encontrarla o hasta que se acaba. Suponemos que ya est abierto el archivo y definido el tipo de datos registro, igual que en los ejemplos anteriores. numregs = (LOF(1) / LEN(persona)) INPUT "Nombre a buscar: ", nombreBuscado$ n = 1 DO GET #1, n, persona n = n + 1 LOOP UNTIL RTRIM$(persona.nombre)=nombreBuscado$ OR n>numregs IF n <= numregS THEN PRINT "Registro...:"; n-1

91

PRINT "Nombre.....: "; persona.nombre PRINT "Edad.......:"; persona.edad ELSE PRINT "No se ha encontrado" END IF Lo primero que hacemos es calcular cuntos registros tiene el fichero para ver hasta dnde podemos llegar. Despus pedimos al usuario que escriba el nombre que quiere encontrar y lo guardamos en una variable del tipo adecuado, en este caso cadena. En el siguiente bloque vamos recorriendo el fichero desde el primer registro usando un contador hasta que encontremos el nombre buscado o bien el contador llegue a superar el nmero de registros del fichero. Para hacer la comparacin usamos la funcin RTRIM$ que lo que hace es quitar los espacios en blanco que hay a la derecha del nombre grabado en el registro porque como al definir el registro tenemos que usar cadenas de longitud fija los valores devueltos siempre contienen espacios al final para rellenar la cadena. Cuando salimos del bucle lo que hacemos es comprobar el contador con un IF. Si se ha superado el lmite es que no se ha encontrado, y en caso contrario es que se ha encontrado y podemos mostrar en pantalla todos los datos de la persona que siguen estando almacenados en la variable de registro, ya que despus no se ha ledo nada ms. Tambin incluimos el nmero del registro usando el contador menos uno, ya que antes de salir del bucle se sum uno. Si hay dos personas con el mismo nombre este cdigo slo encontrar la primera. Podemos hacer consultas por otros campos, o bien combinar varios campos con solo modificar la condicin del bloque repetitivo que usamos para recorrer el archivo. Si queremos poder mostrar ms de un registro encontrado habra que ir guardando los datos encontrados en un array del mismo tipo para despus mostrarlos en una lista. Habra que limitar el nmero de registros mostrados al tamao del array, igual que hace Windows 9x con las bsquedas de archivos y carpetas. 1.16.3.4 - MODIFICACIONES DE REGISTROS Al trabajar con ficheros no nos podemos limitar a permitirle al usuario aadir datos, tambin hay que dejarle hacer modificaciones, ya que la informacin puede cambiar o el usuario se puede equivocar al escribir los datos y tendr que corregirlos. La forma de hacer una modificacin consiste en sobre escribir un registro completo del fichero con la nueva informacin. Una forma muy fcil de hacer esto es preguntar al usuario qu registro quiere modificar, mostrarle los datos a ver si de verdad son estos y a continuacin pedirle la nueva informacin y grabarla en esa posicin del fichero sobre escribiendo a la anterior. Vamos a ver cmo sera (Archivos ya abiertos y tipos ya definidos): numregS = (LOF(1) / LEN(persona))

92

INPUT "Nmero de registro a modificar: ", nummodif% IF nummodif% < 1 OR nummodif% > numregS THEN PRINT "Nmero de registro incorrecto" ELSE GET #1, nummodif%, persona PRINT "Nombre.....: "; persona.nombre PRINT "Edad.......:"; persona.edad PRINT "Es este el registro que quieres modificar? (S/N)" WHILE INKEY$ <> "": WEND 'Para limpiar INKEY$ DO tecla$ = UCASE$(INKEY$) LOOP UNTIL tecla$ = "S" OR tecla$ = "N" IF tecla$ = "S" THEN PRINT "Escribe los nuevos datos..." INPUT "Nombre: ", persona.nombre INPUT "Edad: ", persona.edad PUT #1, nummodif%, persona PRINT "Los nuevos datos han sustituido a los anteriores" END IF END IF Lo primero es pedir al usuario que escriba el nmero del registro, este nmero lo habr averiguado en una consulta que haya hecho antes, por ejemplo. El nmero que escriba lo guardamos en una variable de tipo entero, ya que los registros nunca van a poder llevar decimales. A continuacin comprobamos que el registro existe en el fichero, su nmero es mayor que 0 y no mayor que el nmero mximo de registros que hemos calculado al principio. Slo si es as seguimos, si no damos un mensaje de error y terminamos. Si el registro existe lo mostramos en la pantalla y le preguntamos si es este el que quiere modificar. Si pulsa la S mayscula le pedimos los nuevos datos y los grabamos en el fichero en la misma posicin sobre escribiendo los datos antiguos. Al hacer modificaciones el tamao del fichero no cambia. El uso de la funcin INKEY$ para hacer preguntas tipo S o No al usuario lo veremos detalladamente en uno de los temas de ampliacin del curso. 1.16.3.5 - BAJAS. BORRAR REGISTROS En este modelo de programacin de ficheros de acceso aleatorio no existe una forma especfica de borrar informacin de un fichero, por esto lo que hacemos es simplemente sobre escribir el registro rellenndolo con espacios en blanco o valores cero, segn corresponda, para que la informacin no aparezca en las consultas ni en los listados. Vamos con un ejemplo de cmo hacerlo, casi igual al de las modificaciones.

93

numregS = (LOF(1) / LEN(persona)) INPUT "Nmero de registro a borrar: ", numborra% IF numborra% < 1 OR numborra% > numregS THEN PRINT "Nmero de registro incorrecto" ELSE GET #1, numborra%, persona PRINT "Nombre.....: "; persona.nombre PRINT "Edad.......:"; persona.edad PRINT "Es este el registro que quieres borrar? (S/N)" WHILE INKEY$ <> "": WEND 'Para limpiar INKEY$ DO tecla$ = UCASE$(INKEY$) LOOP UNTIL tecla$ = "S" OR tecla$ = "N" IF tecla$ = "S" THEN persona.nombre = SPACE$(LEN(persona.nombre)) persona.edad = 0 PUT #1, numborra%, persona PRINT "Registro borrado" END IF END IF Como se puede ver, lo nico que cambia es que en los mensajes en vez de decir "modificar" dice "borrar" y que sobre escribimos el registro con blancos y ceros en vez de con la informacin que escribe el usuario. Lo nico ms extrao es que para asignar espacios en blanco al nombre en vez de hacer persona.nombre = " " que sera lo ms fcil, usamos la funcin SPACE$ que nos devuelve una cadena de espacios en blanco, y para calcular cuantos espacios usamos la funcin LEN que nos devuelve el tamao del dato miembro. De esta forma no nos tenemos que acordar de cuanto largo era ese dato y si modificamos este tamao durante el desarrollo del programa no tenemos que modificar esta instruccin, evitando posibles errores. Visto esto se puede comprender que el tamao de los ficheros directos nunca se va a reducir, ni aunque borremos registros. Para solucionar este problema lo que podramos hacer en algunos casos ser compactar el fichero, en el apartado siguiente. 1.16.3.6 - COMPACTACIN DE FICHEROS DIRECTOS Como hemos visto, al borrar registros el tamao del fichero nunca se reduce, con lo que nuestro fichero va a crecer y crecer siempre pudiendo contener muchos registros vacos que lo nico que hacen es ocupar sitio y hacer que todo vaya un poco ms lento. Para solucionar este problema en las bases de datos se ha definido una tarea de mantenimiento que consiste en "compactar" los ficheros. No tiene nada que ver con lo que hacen programas como

94

WinZip, esto es "comprimir" ficheros y la forma de programar estas operaciones es terriblemente ms complicada. Este proceso de compactacin lo que va a hacer es agrupar los registros todos juntos al principios del archivo. Esto implica que se van a modificar los campos calve ya que cambiar la posicin donde est almacenado cada registro. Si el campo clave es algo significativo como por ejemplo el nmero de socio de un club, nmero de habitacin de un hotel, etc... no deberemos hacer este proceso de compactacin ya que cambiarn los campos claves y destruiremos la informacin. Solo lo deberemos hacer en casos donde el campo clave no sea significativo para nosotros y no importe que cambie. Imaginemos que en la vida real tenemos una agenda de papel llena de datos de personas de los cuales unos sirven y otros estn tachados porque son incorrectos o se han quedado obsoletos. Adems de llevar siempre un tocho de hojas muy gordo, a la hora de buscar algo tendremos que pasar siempre muchas hojas, perdiendo ms tiempo del necesario. Una forma muy cmoda de solucionar este problema sera conseguir una agenda nueva para quedarnos slo con los datos que queremos conservar. Para hacer esto recorreramos la agenda vieja y solo iramos copiando en la nueva los datos que no estn tachados. Al final tiramos la vieja y nos quedamos con la nueva para seguir usndola a partir de ahora. Este trozo de cdigo hace justamente eso mismo con nuestro fichero informtico de la base de datos. Suponemos que el fichero de nuestra agenda ya esta abierto y los tipos de datos definidos. numregs = (LOF(1) / LEN(registro)) OPEN "Agenda.tmp" FOR RANDOM AS #2 LEN = LEN(registro) n1 = 1 n2 = 1 WHILE n1 <= numregS GET #1, n1, registro IF registro.nombre <> SPACE$(LEN(registro.nombre)) OR registro.edad <> 0 THEN PUT #2, n2, registro n2 = n2 + 1 END IF n1 = n1 + 1 WEND CLOSE KILL "Agenda.dat" NAME "Agenda.tmp" AS "Agenda.dat" OPEN "Agenda.dat" FOR RANDOM AS #1 LEN = LEN(registro) 'Seguimos con el programa... Vamos a ver como funciona esto:

95

Lo primero que hacemos, como siempre, es calcular el nmero de registros que tiene el fichero para saber hasta donde podemos llegar. y A continuacin abrimos un fichero llamado "Agenda.tmp" (La extensin .tmp significa "temporal") y le asignamos el descriptor #2. Este fichero debe ser abierto con la misma estructura que el otro y suponemos que no existe y se crea uno nuevo y vaco al abrirlo. y Antes de entrar en el bucle inicializamos dos contadores al valor 1, uno nos va a servir para recorrer el fichero viejo y otro para recorrer el nuevo. Empezamos por 1 que es el primer registro. y Ahora usamos un bucle "Mientras" para recorrer el fichero viejo desde el principio hasta el final. Dentro de este bucle leemos cada registro y si no est completamente vaco lo copiamos al nuevo fichero e incrementamos ambos contadores. Si est vaco no hacemos nada y solo incrementamos el primer contador para poder seguir recorriendo el fichero viejo. y Para comprobar si el registro est completamente vaco habr que usar una expresin condicional que puede llegar a ser muy larga porque hay que comprobar cada dato miembro. En nuestro ejemplo esta expresin aparece ocupando dos lneas, pero si la escribes en QBasic tendr que ser toda seguida en la misma lnea, aunque sea muy larga. QBasic soporta lneas de hasta 256 caracteres de larga, si sigue sin caber tendrs que cambiar los nombres de las variables por otros ms cortos, aunque en esta ocasin sean menos descriptivos. y Una vez que salimos del bucle tendremos el fichero "Agenda.dat" tal como estaba y el nuevo "Agenda.tmp" compactado solo con los datos que nos interesan. y Para proceder al intercambio de los nombres de los ficheros y quedrnos solo con el nuevo podemos seguir estos pasos o Cerrar los dos ficheros, usando la orden CLOSE sin descriptor. o Borrar "Agenda.dat" usando la orden KILL, que veremos con detalle ms adelante. o Cambiarle el nombre a "Agenda.tmp" por "Agenda.dat" usando la orden NAME, que tambin veremos en prximos temas. o Volver a abrir "Agenda.dat" con el descriptor #1 para seguir trabajando con l normalmente desde el resto del programa. Esta operacin de mantenimiento la tendr que ejecutar el usuario de vez en cuando, especialmente despus de haber borrado muchos registros. En programas mucho ms sofisticados se podra hacer que se ejecute cada x das o tras haber borrado un determinado nmero de registros.
y

1.16.3.7 - ELABORACIN DE LISTADOS DE DATOS Los listados son una parte muy til de todo programa que trabaje con datos, para que el usuario pueda ver toda la informacin a la vez y no registro por registro. En los programas modernos se suele hablar de

96

"informes" para referirse a listados con un determinado formato que presentan los registros que cumplen cierta condicin. Aqu nos limitaremos a escribir en la pantalla un listado encolumnado de todos los registros que haya en nuestro fichero. La forma de dibujar un listado en la pantalla es bastante sencilla en principio. Bastara con recorrer el fichero y escribir cada registro en una lnea de la pantalla, pero surgen algunos problemas que vamos a ir solucionando ms adelante. Vamos con un ejemplo, de lo ms cutre, que recorre el fichero y escribe su contenido lnea por lnea todo seguido. Suponemos que ya tenemos abierto nuestro fichero y definido el tipo registro, igual que en los ejemplos anteriores. CLS numregs=LOF(1) - LEN(persona) c=1 WHILE c <= numregs GET #1, c, persona PRINT c; persona.nombre; persona.edad c=c+1 WEND El resultado por pantalla podra ser: 1Pepe 25 2 0 3 0 4Manolo Garca Garca32 5Paco 19 6 0 7 0 8 0 9Ana 40 10Juan Carlos 28 11Fernando 21 Lo primero, como siempre, calcular cuantos registros tiene el fichero y despus irlos leyendo y sacando por pantalla desde dentro de un bloque MIENTRAS. Se poda haber hecho con un PARA, pero como despus vamos a tener que hacerlo con un MIENTRAS de todas formas, mejor empezar as. El listado aparece ms o menos encolumnado porque las variables de cadena (nombre) se devuelven con espacios en blanco detrs hasta completar su longitud total, pero los enteros no ocuparan siempre lo mismo y nos estropearan el encolumnado, cosa que pasara a partir del registro 10 donde el nombre sera empujado un lugar hacia la derecha. Ms adelante iremos solucionando esto. Como se puede ver en el resultado del ejemplo anterior, han aparecido tambin los registros borrados (Sin nombre y con edad=0). Para evitar esto lo que hacemos comprobar con una condicin justo antes de escribir cada lnea que el registro no ha sido borrado. Sera as: CLS

97

numregs=LOF(1) - LEN(persona) c=1 WHILE c <= numregs GET #1, c, persona IF persona.nombre<>SPACE$(LEN(persona.nombre)) OR persona.edad>0 THEN PRINT c; persona.nombre; persona.edad END IF c=c+1 WEND Y el resultado podra ser: 1Pepe 25 4Manolo Garca Garca32 5Paco 19 9Ana 40 10Juan Carlos 28 11Fernando 21 Vemos que ya no aparecen los registros que fueron borrados. La condicin que hacemos es que el nombre (texto) no sea todo espacios o que la edad sea mayor que cero. Ahora slo aparecen los registros que nos interesan, pero todava se ve muy feo vamos a dibujarle un encabezado a la lista y dejar un poco de espacio entre las columnas para que no quede tan junto. CLS numregs=LOF(1) - LEN(persona) c=1 PRINT "Registro Nombre Edad" PRINT "============ ==================== ====" WHILE c <= numregs GET #1, c, persona IF persona.nombre<>SPACE$(LEN(persona.nombre)) OR persona.edad>0 THEN PRINT c, persona.nombre; " "; persona.edad END IF c=c+1 WEND Y el resultado, ms bonito sera: Registro Nombre Edad ============ ==================== ==== 1 Pepe 25 4 Manolo Garca Garca 32 5 Paco 19 9 Ana 40 10 Juan Carlos 28 11 Fernando 21 Dibujamos el encabezado una vez al principio. La lnea con guiones o signos igual nos puede servir para orientarnos con los tamaos de los campos. Dentro del bucle en la instruccin PRINT si separamos las variables por comas se pasa a la siguiente posicin de tabulacin y nos evitamos el problema del diferente nmero de cifras, aunque se

98

desperdicia mucho espacio porque las posiciones de tabulacin an QBasic miden 13 caracteres. Podemos meter espacios entre comillas para separar los campos. En este caso como hay muy pocos campos y hemos podido dejar los nmeros enteros para el final ha salido medio bien, pero en casos ms complicados sera necesario usar la instruccin TAB para definir las columnas. Veamos como se hara. CLS numregs=LOF(1) - LEN(persona) c=1 PRINT "Reg"; TAB(5); "Nombre"; TAB(26); "Edad" PRINT "==="; TAB(5); "===================="; TAB(26); "====" WHILE c <= numregs GET #1, c, persona IF persona.nombre<>SPACE$(LEN(persona.nombre)) OR persona.edad>0 THEN PRINT c; TAB(5); persona.nombre; TAB(26); persona.edad END IF c=c+1 WEND Y el resultado, todava mejor. Reg Nombre Edad === ==================== ==== 1 Pepe 25 4 Manolo Garca Garca 32 5 Paco 19 9 Ana 40 10 Juan Carlos 28 11 Fernando 21 El parmetro que le pasamos a la orden TAB debe ser un entero que indica la posicin a la que saltar el cursor dentro de la lnea actual. Recordemos que cada lnea tiene 80 caracteres de ancho y que la primera posicin por la izquierda es la 1. La orden TAB se utiliza como argumento de la orden PRINT, igual que si fuera una funcin, solo que en vez de devolver un valor lo que hace es llevar al cursor hasta la posicin especificada. En el encabezado nos podamos ahorrar las ordenes TAB y poner directamente los espacios, porque como todo es constante no va a variar nada. Para saber las posiciones lo ms cmodo es ir probando hasta que cada cosa caiga en su sitio, debajo de su ttulo. Ya se empieza a parecer a un listado. Siempre habr que ajustar las columnas segn nuestras necesidades y si no caben de ancho la nica solucin posible con lo que sabemos hasta ahora es no escribir todos los campos. Lo ltimo que vamos a solucionar es el terrible problema de que si tenemos muchos registros el listado avanzar rpidamente y solo veremos los ltimos veintitantos registros que caben en la pantalla. Pasa lo mismo que con la orden DIR de MS-DOS si no ponemos el

99

modificador /P. Para conseguir esto lo que hacemos es usar un segundo contador que vamos incrementando en cada lnea y cuanto veamos que llega a 20 lo ponemos a cero, y hacemos una pausa, cuando el usuario pulse una tecla borramos la pantalla, dibujamos el encabezado de nuevo y seguimos. Vamos con el ejemplo: CLS numregs=LOF(1) - LEN(persona) c=1 c2=1 PRINT "Reg Nombre Edad" PRINT "=== ==================== ====" WHILE c <= numregs GET #1, c, persona IF persona.nombre<>SPACE$(LEN(persona.nombre)) OR persona.edad>0 THEN PRINT c; TAB(5); persona.nombre; TAB(26); persona.edad END IF c=c+1 c2=c2+1 IF c2>20 THEN c2=1 PRINT "Pulsa cualquier tecla para seguir..." WHILE INKEY$<>"":WEND DO:LOOP WHILE INKEY$="" CLS PRINT "Reg Nombre Edad" PRINT "=== ==================== ====" END IF WEND El resultado, si hay menos de 20 registros sera igual que antes, y si hay ms sera que se van viendo de 20 en 20. Este ejemplo se puede mejorar para que las instrucciones donde se dibuja el encabezado slo aparezcan una vez, pero aqu no nos vamos a complicar tanto. De todas formas en el tema de manejo de la pantalla de texto que hay en la seccin de temas de ampliacin de este curso aparen las instrucciones a usar para hacer listados desplazables parecidos a los de Windows que son mucho ms "bonitos" y no tienen estos problemas. 1.16.4 - BREVE IDEA SOBRE BASES DE DATOS En informtica se conoce como base de datos a un mecanismo capaz de almacenar informacin para despus poder recuperarla y manejarla con facilidad y precisin. Visto as, los archivos de ejemplo de agendas que hemos creado en los apartados anteriores son bases de datos, pero cuando hablamos de bases de datos pensamos en programas que manejan gran cantidad de informacin de forma muy estructurada y que permiten

100

realizar consultas y operaciones de mantenimiento muy complejas y fiables. Al hablar de bases de datos hay que distinguir dos cosas muy importantes. Por un lado tenemos la base de datos en s, que es la informacin grabada en los discos, y por otro lado tenemos el Sistema de Gestin de Bases de Datos (SGBD o DBMS en ingls) que es el programa encargado de manejar toda la informacin. Es lo que sera un programa como MS Access o MySQL. Las bases de datos que manejan estos programas casi nunca contienen una sola tabla, es decir, un solo archivo de datos, sino que contienen muchas tablas relacionadas entre si. Por eso se habla de bases de datos relacionales. El hecho de que las tablas estn relacionadas unas con otras implica que al hacer un cambio en una de ellas, posiblemente se vean afectadas otras para que la informacin siga teniendo consistencia. Tambin estos sistemas de bases de datos usan muchos mecanismos como ndices o algoritmos de ordenacin para conseguir que el acceso a la informacin durante las consultas sea un proceso mucho ms rpido y completamente fiable. Nosotros, en nuestros programas estamos integrando una pequesima parte de los mecanismos que usan los SGBD para poder manejar la informacin de nuestros archivos, pero conforme nuestras necesidades aumenten y ya usemos otros lenguajes de programacin, ser mucho ms sencillo usar los servicios de un autntico SGBD para manejar nuestra informacin, en vez de programarlo nosotros. Esto quiere decir que el uso de lo que hemos visto en este tema de archivos de acceso directo se limitar a el mantenimiento de pequeos y sencillos conjuntos de datos cuando sea ms cmodo manejarlos de forma directa que de forma secuencial, pero desde el momento en que intervienen varios archivos de datos necesitaremos usar las funciones de uno de estos sofisticados SGBDs integrados en los modernos lenguajes de programacin. 1.16.5 - OTRAS INSTRUCCIONES RELACIONADAS CON FICHEROS Ya hemos visto como crear ficheros y leer o modificar la informacin almacenada en los mismos usando diferentes mtodos de programacin. Para terminar el tema de ficheros es necesario conocer algunas instrucciones que tiene QBasic para trabajar con ficheros enteros que haya en el sistema de archivos. Estas instrucciones nos ayudarn a trabajar con los ficheros sin tener que recurrir a ejecutar ordenes del sistema operativo. Vamos con ellas. CHDIR ruta$ Cambia el directorio activo. El directorio activo es el que usar QBasic para buscar los archivos que tiene que abrir o para crearlos si no se especifica una ruta en la instruccin OPEN. Puede ser difcil

101

determinar el directorio activo, por lo que conviene especificar uno con esta orden antes de abrir o crear ficheros. Si ejecutamos QBasic desde un icono de acceso directo de Windows el directorio activo ser el que aparece en el cuadro "Carpeta de trabajo" de la seccin "Programa" de las propiedades del acceso directo. Si ejecutamos QBasic desde MS-DOS el directorio activo ser donde nos encontrbamos cuando entramos en QBasic. La ruta que hay que especificar en esta instruccin puede ser un literal entre comillas o una variable de cadena que contenga la ruta adecuada. Si el directorio no existe se producir un error de tiempo de ejecucin. Hay que recordar que estamos en MS-DOS y por lo tanto solo podemos trabajar con nombres de directorios y archivos de no ms de 8 caracteres y sin espacios, por lo tanto los directorios "Archivos de Programas" y "Mis documentos" tendrn nombres parecidos a "ARCHIV~1" y "MISDOC~1". Para asegurarse de cules son estos nombres hay que mirar la casilla "Nombre MS-DOS" de la ventana de propiedades del directorio correspondiente en Windows. Para escribir el carcter "Gurruo" (~) hay que pulsar la tecla "Alternativa" y simultneamente escribir con el teclado numrico la cifra 126, correspondiente a su cdigo ASCII. La siguiente orden nos permite crear un directorio: MKDIR ruta$ El directorio que creemos ya estar disponible en el sistema de archivos para usarlo desde nuestro programa o desde cualquier otro. Si la ruta que especificamos ya existe se producir un error de tiempo de ejecucin. Es importante comprender que al crear un directorio este no pasa a ser el directorio activo. Para hacerlo habr que usar una instruccin CHDIR. Ahora vamos a ver como borrar un directorio existente: RMDIR ruta$ Esta instruccin borra el directorio que le pasemos como parmetro. Si no existe se producir un error. Tambin se producir el mismo error si el directorio no est vaco, es decir, contiene algn archivo o ms subdirectorios. Habr que borrarlos antes como veremos ms adelante. Normalmente debemos tener mucho cuidado con lo que borramos desde nuestros programas para no hacer ningn estropicio con la informacin que tenemos guardada en los discos. Podemos ver los ficheros y subdirectorios de un directorio con la siguiente instruccin: FILES Esta instruccin trabaja de forma parecida a la orden "DIR /W" de MS-DOS. Nos dibujar un listado a cuatro columnas con el contenido de un directorio, encabezada por el nombre del propio directorio y terminada por el espacio en bytes disponible en el disco. Los

102

subdirectorios se identifican porque llevan a la derecha el rtulo "< DIR >". Esta instruccin nos puede resultar algo til en los programas que hemos hecho hasta ahora ya que la informacin se escribe en pantalla una lnea debajo de otra, pero cuando hagamos diseos de pantallas ms elaborados ya no ser recomendable usar esta instruccin porque el listado de directorio se saltar todas nuestras especificaciones de pantalla, incluso ahora si el listado es muy largo slo se ven las ltimas lneas. Si usamos la orden FILES sin parmetros se mostrarn todos los ficheros y subdirectorios del directorio activo, pero podemos especificar rutas, nombres de archivos y caracteres comodn (? y *) para adecuar el listado a nuestras necesidades. Veamos unos cuantos ejemplos: FILES "*.txt" Lista todos los ficheros con extensin TXT que haya en el directorio activo. FILES "JUEGO.*" Lista todos los ficheros que se llamen JUEGO, sea cual sea su extensin, del directorio activo. FILES "C:\BASIC\PROGS\*.*" Lista todos lo que haya en el directorio BASIC\PROGS de la unidad C:. FILES "Imagen?.BMP" Lista todos los archivos cuyo nombre empiece por "Imagen" y vaya seguido por exactamente un carcter, su extensin sea BMP y stn en el directorio activo. Apareceran por ejemplo los archivos Imagen2.BMP o Imagen7.BMP, pero nunca el archivo Imagen14.BMP ya que contiene dos nmeros. Si la orden FILES no encuentra nada que listar se produce un error de tiempo de ejecucin. Si listamos un directorio vaco no se producir ningn error. Ya hemos visto como borrar directorios, ahora vamos a ver como borrar archivos: KILL Archivo$ Esta instruccin borra archivos, podemos usar una ruta y/o los comodines * y ?, cosa que puede ser peligrosa porque se borraran varios ficheros a la vez, una vez ms recomendar prudencia al usar esta orden para evitar estropicios irreparables, ya que en MS-DOS normalmente no tenemos papelera de reciclaje. Si no especificamos una ruta se entender que estamos trabajando sobre el directorio activo. Si el archivo que queremos borrar no existe se producir un error de tiempo de ejecucin. No confundir esta instruccin con la orden KILL de Linux que detiene procesos, es decir, obliga a terminar la ejecucin de un programa pero no borra nada del disco. Por ltimo vamos a ver como cambiar el nombre un fichero: NAME viejo$ AS nuevo$

103

viejo$ es el nombre actual del archivo que queremos renombrar, que debe existir para que no se produzcan errores. nuevo$ tiene que ser el nuevo nombre que le vamos a dar al archivo, y que no debe existir todava. Por ejemplo, para cambiar el nombre al archivo base.dat del directorio actual por base.bak tendramos que usar la instruccin NAME base.dat AS base.bak En el nombre del archivo ya existente (la primera parte antes de AS) podemos especificar una ruta de directorios. Para copiar o mover archivos no tenemos una instruccin especfica en QBasic. Tendremos que crearnos un modulo que lo abra en modo binario y lo copie carcter por carcter en un archivo nuevo en la otra ubicacin, si lo que queramos era moverlo despus habr que borrar el original. Para hacer esto y otras cosas ms complicadas puede ser ms cmodo llamar a la orden correspondiente de MS-DOS como se ver en temas posteriores.

TEMA 1.17 CONTROL DE ERRORES


y 1.17.1 - Instruccin GOTO. La ms conocida y odiada de BASIC y 1.17.2 - Introduccin a los errores y 1.17.3 - Control de errores 1.17.1 - INSTRUCCIN GOTO. LA MS CONOCIDA Y ODIADA DE BASIC Empecemos con un ejemplo "muy malo": CLS Pricipio: PRINT "Esto es el principio" GOTO Final Enmedio: PRINT "Esto es lo de en medio, pero no lo vers" Final: PRINT "Esto es el Final" Que dara como resultado en pantalla: Esto es el principio Esto es el final Ahora veamos como funciona: y Borra la pantalla. y Pasa por una etiqueta definida como Principio. y Escribe "Esto es el principio" y Busca una etiqueta que se llame "Final" y cuando la encuentra salta y sigue a partir de all. y Escribe "Esto es el final". y Como no hay nada ms, el programa termina. En este programa hemos definido tres etiquetas. Para definir etiquetas usamos las mismas normas que para los nombres de variables y al final le colocamos el carcter dos puntos (:). Las etiquetas siempre van al principio de la lnea y si usamos etiquetas es

104

til encolumnar todas las dems instrucciones ms a la derecha, para verlas al vuelo. Podemos definir etiquetas en cualquier mdulo de nuestro programa, pero QBasic solo las "encontrar" si estn definidas en el mdulo principal. Por esto no tiene sentido definir etiquetas dentro de procedimientos y funciones. Las etiquetas no hacen nada, simplemente estn ah. nicamente nos sirven para poder "Saltar" a ellas usando instrucciones especiales como la famosa GOTO (Go to..., ir a...) que salta incondicional e inmediatamente a la etiqueta que le indiquemos para seguir desde all con la ejecucin del programa. Sencillo No?. En verdad es muy sencillo pero puede dar lugar a toda una variedad de problemas lgicos que pueden ser casi imposibles de detectar, aislar y solucionar. Imagina un programa donde en vez de tres etiquetas haya trescientas y entre ellas haya centenares de instrucciones GOTO que dirigen la ejecucin del programa a un sitio lejano de nuestro listado. Una mala planificacin del programa seguramente har que bloques enteros no se lleguen a ejecutar nunca o que se produzcan bucles infinitos que lleguen a bloquear el programa. Por eso a los programas que usan estas instrucciones se les conoce como "Cdigo Espagueti". En los lenguajes de bajo nivel como es el caso de los ensambladores y en lenguajes de guiones muy sencillos como el que se utiliza para construir los ficheros .BAT de MS-DOS se usan intensivamente las etiquetas y las instrucciones de bifurcacin o salto incondicional, pero los modernos lenguajes de programacin estructurada (QBasic lo es) nos dan la posibilidad de usar estructuras de control iterativas (Bucles PARA, MIENTRAS y REPETIR), as como los procedimientos y las funciones que nos permiten en todo caso saber como funciona nuestro programa para poder solucionar posibles errores. Por esto es muy recomendable EVITAR A TODA COSTA EL USO DE ESTA METODOLOGA DE PROGRAMACIN CON INSTRUCCIONES GOTO Y SIMILARES en los lenguajes de alto nivel, salvo en el caso de las instrucciones de manejo de errores que vamos a ver en los siguientes apartados y que son el motivo de esta explicacin. 1.17.2 - INTRODUCCIN A LOS ERRORES Hemos ido viendo a lo largo de los temas anteriores que hay tres tipos de errores: y Errores de compilacin y Errores de tiempo de ejecucin y Errores lgicos Los primeros suelen ser cosas mal escritas o que no cumplen con las normas de sintaxis del lenguaje de programacin. La mayora de ellos en QBasic sern mostrados conforme vamos escribiendo nuestro cdigo al pasar de lnea, o en todo caso al intentar ejecutar el programa. Tendremos que corregirlos modificando lo que este mal para poder iniciar el programa.

105

Los errores de tiempo de ejecucin se producen durante la ejecucin del programa y son provocados por intentar hacer algo que no est permitido, como dividir entre cero, o bien porque alguno de los dispositivos del ordenador falle (Memoria agotada, no se encuentra un fichero, la impresora no tiene papel, etc...). Estos errores hacen que el programa se detenga. En QBasic se volver al editor y se mostrar un recuadro con el mensaje apropiado. En otros lenguajes o en programas ya compilados puede salir desde un simple mensaje hasta una pantalla azul tpica de Windows o lo ms normal es que el ordenador deje de responder y haya que reiniciarlo. Los errores lgicos se deben a una mala planificacin de los algoritmos, lo que da lugar, en el mejor de los casos, a que el programa funcione correctamente pero no haga lo que queremos y no resuelva el problema para el que ha sido diseado, o lo ms normal es que llegue a provocar un error de tiempo de ejecucin porque se llene el espacio disponible en memoria o se entre en un bucle infinito. En este tema vamos a ver cmo conseguir que cuando se produzca un error de tiempo de ejecucin el programa no quede detenido inmediatamente, sino que se salga de l de una forma un poco ms "elegante" o incluso se pueda solucionar el problema y seguir normalmente con la ejecucin del programa. Usando las tcnicas de programacin estructurada que hemos visto en este curso hasta antes del tema de ficheros si hacemos los programas bien NO TIENEN PORQU PRODUCIRSE ERRORES DE TIEMPO DE EJECUCIN. Nosotros somos los responsables de evitar que los bucles sean infinitos, de no hacer referencias a ndices de arrays que no existen, de que nada se llegue a dividir entre cero, etc... La nica fuente potencial de errores son los datos que pedimos al usuario, pero si depuramos los datos de entrada de forma que no se acepten valores no permitidos como vimos en el tema de bucles podemos asegurar en la amplia mayora de los casos que nuestro programa va a funcionar bien siempre, ya que solo acepta datos vlidos. El problema llega cuando empezamos a trabajar con dispositivos externos como son las unidades de disco donde se guardan los ficheros. Por muy bien hecho que est nuestro programa no podemos evitar que el usuario quiera abrir un fichero que no existe, o de un disquete que todava no ha introducido en la unidad correspondiente, o que se ha estropeado, que quiera escribir en un CD-ROM, en un disco que no tiene espacio suficiente o que est protegido contra escritura. Slo en este caso de los ficheros y en otros tambin especiales como cuando veamos cmo usar las impresoras ser necesario y recomendable programar rutinas de manejo de errores de las que vamos a ver en el siguiente apartado. En otros casos no nos merece la pena usar esto ya que las instrucciones GOTO ofrecen una forma especialmente mala de estructurar los programas y podemos despreciar la posibilidad de que se produzca algn error raro como

106

que se agote la memoria por culpa de otro programa y el nuestro no pueda seguir funcionando. 1.17.3 - CONTROL DE ERRORES Para activar el control de errores en QBasic usaremos la siguiente instruccin: ON ERROR GOTO linea Dnde linea es el nombre de una etiqueta que se haya definido en el programa principal. Esto quiere decir "Cuando se produzca un error ve inmediatamente a la etiqueta que se llama linea y sigue desde all". De esta forma conseguimos que el programa no se detenga inmediatamente al producirse un error. Como hemos dicho antes, solo debemos controlar los errores en instrucciones peligrosas como es el caso de las que abren los ficheros (OPEN). Una ver pasadas estas instrucciones debemos desactivar el manejo de errores usando la instruccin: ON ERROR GOTO 0 Lo ltimo de esta instruccin es un cero. A partir de ahora cuando se produzca un error ya no habr manejo de errores y el programa se detendr, pero si lo hemos hecho bien no tienen porqu producirse errores. Lo normal es activar el manejo de errores al principio de un procedimiento que, por ejemplo, abra un fichero y desactivarlo al final, antes de salir. De esta forma tenemos claramente delimitado donde usamos el manejo de errores, que estar activo solo mientras se ejecute ese mdulo, y no en el resto del programa que no usa instrucciones "conflictivas". Vamos con un ejemplo completo: 'Programa principal CLS INPUT "Nombre del fichero: ", fich$ MuestraTamanno fich$ END manejaerrores: SELECT CASE ERR CASE 53 PRINT "No CASE 68 PRINT "No CASE 71 PRINT "No CASE 76 PRINT "No CASE ELSE PRINT "Se END SELECT

se ha encontrado el archivo" se ha encontrado la unidad de disco" se ha encontrado el disco en la unidad" se ha encontrado el directorio" ha producido el error"; ERR

107

END 'Este procedimiento abre el archivo, muestra el tamao y cierra 'Entrada: f$: El nombre del archivo SUB MuestraTamanno (f$) ON ERROR GOTO manejaerrores OPEN f$ FOR INPUT AS #1 PRINT "El fichero ocupa"; LOF(1); "bytes." CLOSE ON ERROR GOTO 0 END SUB Este programa pide al usuario que escriba el nombre de un fichero para abrirlo y mostrar su tamao usando la funcin LOF que ya conocemos. Si escribimos un nombre de archivo vlido no se activa para nada el manejo de errores y el programa funciona normalmente, nos calcula el tamao en bytes del archivo y termina al llegar a la instruccin END que hay justo despus de la llamada al procedimiento. El resultado podra ser: Nombre del fichero: c:\autoexec.bat El fichero ocupa 219 bytes. Veamos como funciona el programa paso a paso en caso de que el fichero exista y no se produzca el error: y Entramos al mdulo principal. y Borramos la pantalla con CLS. y Pedimos al usuario que escriba el nombre de un fichero y lo guardamos en la variable fich$: y Entramos al procedimiento Muestratamanno y le pasamos la variable fich$ que contiene el nombre que ha escrito el usuario. y Ya dentro del procedimiento, activamos el control de errores. y Abrimos el fichero, que existe, para lectura y le asignamos el descriptor #1 y Calculamos el tamao del fichero abierto #1 con la funcin LOF y lo escribimos en pantalla junto con un mensaje. y Cerramos todos los ficheros abiertos (El nico que hay). y Desactivamos el control de errores y salimos del mdulo porque no hay ms instrucciones. y De vuelta al programa principal la siguiente instruccin que encontramos es END que hace que el programa termine, la usamos para que no se ejecuten las siguientes instrucciones que son las del control de errores. Aqu encontramos como novedad la instruccin END que hace que el programa termine de forma normal aunque no hayamos llegado al final del listado. La tendremos que poner siempre en el mdulo principal antes de la etiqueta del manejador de errores para evitar que estas instrucciones se ejecuten en caso normal de que no haya un error. En otros casos no es recomendable usar esta instruccin,

108

los programas y mdulos deben empezar por el principio y terminar por el final del listado. Ahora vamos a ver como funcionara el programa paso a paso en el caso de que quisiramos abrir un fichero que no existe y se produjera el error. y Entramos al mdulo principal. y Borramos la pantalla con CLS. y Pedimos al usuario que escriba el nombre de un fichero y lo guardamos en la variable fich$: y Entramos al procedimiento Muestra tamao y le pasamos la variable fich$ que contiene el nombre que ha escrito el usuario. y Ya dentro del procedimiento, activamos el control de errores. y Intentamos abrir el fichero como no existe se producira un error de tiempo de ejecucin que es detectado por el control de errores que contina inmediatamente con la ejecucin del programa a partir de la lnea definida como maneja errores en el programa principal. y Despus de esta lnea encontramos un SELECT CASE que usa la funcin ERR. Esta funcin devuelve el nmero del error que se ha producido. Cada error tiene un nmero nico que nos sirve para identificarlo y hacer una cosa u otra segn lo que haya ocurrido. En este caso lo usaremos para sacar un mensaje adecuado diciendo al usuario lo que ha pasado. y Despus del SELECT CASE, el programa termina, pero lo hace normalmente porque se ha acabado el listado, y no porque se ha producido un error, cosa que hay que evitar a toda costa. Hay que tener claro que al producirse el error el fichero no se ha abierto, y por lo tanto el descriptor #1 no contiene referencia a ningn fichero, por lo que no podemos intentar hacer nada con l, ni siquiera cerrar el fichero. Hay un gran nmero de cdigos de error, puedes ver una lista completa de ellos en la ayuda en pantalla de Qbasic. Nunca es necesario controlarlos todos, por ejemplo si estamos trabajando con ficheros solo pondremos algunos de los relacionados con ficheros. Siempre es conveniente usar algo como CASE ELSE para que quede controlado alguno que se nos haya podido escapar, en este caso bastar con mostrar el nmero por pantalla para poder identificar el error de alguna forma, pero siempre haciendo que el programa termine bien. Se pueden hacer manejos de errores mucho ms elaborados que lleguen a solucionar el problema y el programa pueda seguir funcionando como si nada. Imagina que queremos abrir un fichero .INI dnde est la configuracin del programa, y este se ha borrado. En este caso de forma totalmente transparente al usuario podramos crear uno nuevo vaco o con valores por defecto y dejarlo abierto para que el programa siga trabajando con l normalmente. En este caso usaramos la instruccin RESUME para continuar con el programa por la lnea siguiente a la que produjo el error, una vez que

109

ya hayamos abierto el nuevo fichero para escritura, hayamos escrito en l lo que sea, lo hayamos cerrado y lo hayamos vuelto a abrir para lectura, todo esto en el manejador de errores.

TEMA 1.18 LLAMADA A PROGRAMAS EXTERNOS


Lo ltimo que nos queda por ver en la seccin de temas bsicos del curso de introduccin a la programacin es cmo conseguir que nuestro programa sea capaz de iniciar otros programas externos, ya sean ejecutables .EXE u ordenes de MS-DOS internas o externas o bien otros programas de QBasic. Para conseguir lo primero usaremos la instruccin SHELL seguida de la orden de MS-DOS o del programa ejecutable o programa BAT que queremos ejecutar. Vamos con unos cuantos ejemplos: SHELL "dir" Hace que en la pantalla de nuestro programa aparezca el listado del contenido del directorio activo. SHELL "dir c:\basic\juegos" Hace que en la pantalla de nuestro programa aparezca el listado del contenido del directorio c:\basic\juegos. Si este directorio no existe no se producir ningn error en nuestro programa, simplemente la orden DIR nos sacar un mensaje diciendo que no se ha encontrado el achivo. SHELL "lote.bat" Ejecuta el archivo de proceso por lotes llamado lote.bat. Lo buscar en el directorio activo y si no lo encuentra lo buscar en los directorios que aparecen en la variable de entorno PATH del MS-DOS. Si no lo encuentra sacar el tpico mensaje "Comando o nombre de archivo incorrecto", pero en nuestro programa no ocurrir ningn error. SHELL "winmine" En la mayora de los casos, iniciar el buscaminas de Windows, un programa ejecutable. Si no lo encuentra pasar lo mismo que en el ejemplo anterior, pero tampoco generar ningn error. SHELL Si no escribimos ningn argumento, se abrir una instancia de MSDOS dentro de nuestro programa de QBasic. Aqu el usuario podr ejecutar rdenes como se hace normalmente. Para volver al programa tendr que escribir EXIT y pulsar Enter. Habr que advertir esto al usuario antes de entrar para que no se quede "encerrado" si no sabe como salir. Tambin es peligroso ejecutar determinados programas ya que en estos entornos los recursos de memoria son muy limitados. Esta instruccin SHELL nos puede servir para ejecutar ordenes de MS-DOS o pequeos programas que nos pueden hacer falta usar. Si ejecutamos algo de MS-DOS, que no es un sistema multitarea, nuestro programa quedar detenido hasta que este programa termine. En el caso que llamemos a algn programa Windows, como

110

el caso del buscaminas, nuestro programa lo iniciar y seguir funcionando. Normalmente no ser necesario llamar a programas externos, salvo en situaciones muy especficas, y siempre debern ser programas sencillos que consuman muy pocos recursos, si llamamos a algn programa muy grande como Windows desde QBasic y fallan los sistemas de seguridad puede que el ordenador se bloquee. Tambin nos puede interesar que nuestro programa termine y pase el control a otro programa de QBasic, por ejemplo en el caso de que sea un men. Para hacerlo haramos esto. RUN "Programa.bas" Al ejecutar esta orden nuestro programa termina inmediatamente y QBasic carga e inicia el otro programa Basic. Esta orden equivale a que nosotros detengamos nuestro programa inmediatamente, en el editor abramos el otro programa y lo ejecutemos pulsando F5. Si al usar la orden RUN todava no habamos guardado los cambios en el cdigo fuente aparecer un mensaje del editor pidindonos que lo hagamos, esto no es un error, cuando lo hagamos se continuar con el otro programa. Si el programa no existe se producir un error de tiempo de ejecucin. Si el programa no es un programa Basic se producir un error de compilacin al intentar iniciarlo, nuestro programa anterior para entonces ya habr terminado. Al pasar de un programa a otro se cierran los ficheros que hubiera abiertos y desaparecen todas las variables. En algunos casos, todava ms rebuscados, nos puede interesar seguir trabajando con los ficheros abiertos y con las variables globales que fueron declaradas con la instruccin COMMON. Para conseguir esto en vez de usar la instruccin RUN usaremos la instruccin CHAIN, que trabaja de forma idntica, excepto en que conserva los ficheros abiertos y reconoce las variables declaradas con COMMON en programas anteriores. Normalmente una buena jerarqua de procedimientos SUB ser ms recomendable y ms estructurada que usar la orden RUN o CHAIN. Estas rdenes solo las deberemos usar en casos muy sencillos como un pequeo men que nos abra varios programas. La orden CHAIN no la deberemos usar casi nunca y sobre todo no encadenar varios programas ya que perderemos completamente la pista de dnde declaramos las variables y abrimos los archivos, cosa que puede dar lugar a errores muy difciles de solucionar, como ocurre siempre que no estructuramos bien los programas.

TEMA 2.1 DETECCIN DE TECLAS PULSADAS. LA FUNCIN INKEY$


y y y

2.1.1 - Uso de la funcin INKEY$ 2.1.2 - Reconocimiento de teclas especiales 2.1.3 - Hacer pausas en los programas

111

2.1.1 - USO DE LA FUNCIN INKEY$ La funcin INKEY$ nos devuelve el contenido del buffer del teclado, es decir, cual es la ltima tecla o combinacin de teclas que se ha pulsado. Esta funcin es ms primitiva que INPUT, pero no la pudimos ver en el tema de entrada y salida bsica porque todava no sabamos cmo usar los bucles, algo imprescindible para usar esta funcin ya que el programa no se detiene como ocurre con la instruccin INPUT. Tambin hay que tener en cuenta que no es algo bsico de la teora de programacin, ya que en otros lenguajes puede que funcione de formas muy distintas. Para usar la funcin lo que haremos es almacenar su valor en una variable, ya que al leer el valor de INKEY$ el carcter que contenga se borra del buffer de teclado y ya no lo veremos ms. Vamos con el primer ejemplo: CLS tecla$ = INKEY$ PRINT tecla$ Este ejemplo funciona, aunque al ejecutarlo no lo parezca. Lo que hace es borrar la pantalla, almacenar la ltima tecla pulsada (durante el tiempo de vida del programa) en la variable tecla$ y a continuacin escribirla en pantalla. Lo que pasa es que como el programa no se detiene no nos da tiempo a pulsar ninguna tecla y no aparece nada en pantalla, salvo el conocido rtulo "Presione cualquier tecla y contine". Para solucionar esto tenemos que recurrir a los bucles y programar lo que se conoce como "mecanismo de espera activa". All vamos, el siguiente fragmento de cdigo ser de los ms usados en juegos y programas interactivos hechos con QBasic: DO tecla$=INKEY$ LOOP WHILE tecla$="" Al hacer esto se entra en un bucle que lee continuamente la entrada de teclado y la almacena en la variable tecla$. No salimos del bucle mientras no pulsemos nada y la variable tecla$ siga estando vaca. Una vez que pulsemos una tecla esta quedar almacenada en la variable para usarla ms adenante donde haga falta. Esto ya funciona bien, pero todava no es infalible. Puede ocurrir que en el buffer del teclado quede alguna pulsacin de tecla residual de alguna instruccin de entrada anterior y que al llegar aqu se salte la espera. Para que esto no ocurra tendremos que "limpiar el buffer del teclado" antes de entrar al bucle. Para hacerlo usaremos otro bucle que lee las posibles teclas (y las va borrando) del buffer hasta que se quede vaco. Vamos a ver un ejemplo completo que espera a que pulsemos una tecla y la saca por pantalla. El primer bucle, de tipo WHILE, corresponde a la limpieza del buffer y el segundo, de tipo DO..LOOP, a la espera activa. CLS

112

WHILE INKEY$<>"" WEND DO tecla$=INKEY$ LOOP WHILE INKEY$="" PRINT tecla$ Como se puede ver, este bucle de limpieza es un bucle WHILE, es decir, un bucle rechazable. Si el buffer ya est vaco ni siquiera entramos, en caso contrario entramos y leemos y borramos la tecla que pudiera tener antes de ir al otro bucle de reconocimiento de teclas. 2.1.2 - RECONOCIMIENTO DE TECLAS ESPECIALES Como hemos dado por supuesto en los ejemplos anteriores las teclas se almacenan en el buffer del teclado usando el cdigo ASCII o la representacin del carcter que llevan. Por ejemplo la "a minscula" se almacena como "a" o su cdigo ASCII que es el 97, la "a mayscula" lo hace como "A" o 65, el nmero cinco como "5" o 53, el punto como "." o 46 , el espacio en blanco como " " o 32 y as con todos los caracteres que tenemos en la seccin alfanumrica del teclado. Para trabajar con los cdigos ASCII usaremos la conocida funcin CHR$(), de esta forma es lo mismo decir... IF tecla$ = "A" THEN ...que ... IF tecla$ = CHR$(65) THEN De esta forma tambin podemos identificar otras teclas solo con conocer su cdigo ASCII. Estos son algunos de los ms usados: Escape.....: 27 Enter......: 13 Tabulador..: 9 Retroceso..: 8 Tambin es posible identificar las teclas especiales de flechas (Imprescindible para los juegos), las teclas de funcin y las otras como Inicio, Fin, Insertar, etc. Al pulsar estas teclas se almacenan en el buffer del teclado dos caracteres, siendo el primero el CHR$(0). Para identificar estas teclas no hay ms que usar en las comprobaciones CHR$(0)+El carcter que tengan asignado, aqu va una lista de los ms usados: Flecha arriba......: Flecha abajo.......: Flecha izquierda...: Flecha derecha.....: F1.................: F2.................: F3.................: F4.................: F5.................: CHR$(0)+"H" CHR$(0)+"P" CHR$(0)+"K" CHR$(0)+"M" CHR$(0)+";" CHR$(0)+"<" CHR$(0)+"=" CHR$(0)+">" CHR$(0)+"?"

113

F6.................: F7.................: F8.................: F9.................: F10................: F11................: F12................: Insertar...........: suprimir...........: Inicio.............: Fin................: Retroceder pgina..: Avanzar pgina.....:

CHR$(0)+"@" CHR$(0)+"A" CHR$(0)+"B" CHR$(0)+"C" CHR$(0)+"D" CHR$(0)+CHR$(133) CHR$(0)+CHR$(134) CHR$(0)+"R" CHR$(0)+"S" CHR$(0)+"G" CHR$(0)+"O" (Letra o mayscula) CHR$(0)+"I" (Letra i mayscula) CHR$(0)+"Q"

Podemos ver una lista completa en la ayuda en pantalla de QBasic en el apartado llamado "Cdigos de exploracin del teclado". Aqu tambin aparecen los cdigos de teclas combinadas como por ejemplo CONTROL+Z. Vamos a ver un ejemplo final de un programa que reconozca todas las teclas alfanumricas normales adems de Enter, Escape y las flechas que son especiales. En temas siguientes cuando hagamos programas mejores usaremos ampliamente el reconocimiento de teclas. CLS PRINT "Pulsa las teclas que quieras, escape para salir" PRINT DO WHILE INKEY$ <> "": WEND 'Limpia buffer de entrada DO tecla$ = INKEY$ LOOP WHILE tecla$ = "" SELECT CASE tecla$ CASE CHR$(0) + "H": PRINT "Has pulsado la flecha arriba" CASE CHR$(0) + "P": PRINT "Has pulsado la flecha abajo" CASE CHR$(0) + "K": PRINT "Has pulsado la flecha izquierda" CASE CHR$(0) + "M": PRINT "Has pulsado la flecha derecha" CASE CHR$(13): PRINT "Has pulsado Enter" CASE CHR$(27): PRINT "Has pulsado Escape, adios" CASE " ": PRINT "Has pulsado la barra de espacio" CASE ELSE: PRINT "Has pulsado la tecla " + tecla$ END SELECT LOOP UNTIL tecla$ = CHR$(27) Observa que este programa no reconoce las teclas de funcin, entre otras, para hacerlo no hay ms que aadir las instrucciones CASE con los cdigos correspondientes.

114

2.1.3 - HACER PAUSAS EN LOS PROGRAMAS Una forma muy sencilla de detener la ejecucin de un programa, por ejemplo para que el usuario pueda leer algo es usar la instruccin SLEEP. Al usar esta instruccin el programa se detiene hasta que el usuario pulse una tecla. Si aadimos un nmero a continuacin, por ejemplo: SLEEP 10 Ocurrir que el programa se detendr por un mximo de 10 segundos, continuando pasado este tiempo automticamente aunque el usuario no pulse ninguna tecla. Esta instruccin funcionar en la mayora de los casos, pero puede ocurrir que alguna vez no llegue a detener el programa porque hay datos residuales en el buffer de entrada, en estos casos podremos usar lo que hemos visto en el apartado anterior: Limpieza del buffer+Espera activa, es decir esto: WHILE INKEY$<>"":WEND DO:LOOP WHILE INKEY$="" Esta estructura la podemos ampliar para hacer una pregunta al usuario y dejarle responder S o No pulsando S o N, por ejemplo. WHILE INKEY$<>"":WEND DO tecla$=INKEY$ LOOP UNTIL tecla$="S" OR tecla$="N" Aqu entramos en el bucle de espera activa y no salimos hasta que el usuario pulse la S o la N, y despus ms adelante en el programa viendo el valor de tecla$ ya decidiramos entre hacer algo o no. Pero todava hay un problema, este ejemplo tal como est slo reconoce la S y la N maysculas, por lo que si el usuario tiene el "Bloqueo de maysculas" desactivado puede que no se le ocurra como seguir. Para arreglar el problema usaremos la funcin UCASE$ que convierte todas las letras (Menos la y las vocales acentuadas) a maysculas. Vamos con el ejemplo completo: CLS PRINT "Quieres hacer no se qu? S/N: " WHILE INKEY$<>"":WEND DO tecla$=UCASE$(INKEY$) LOOP UNTIL tecla$="S" OR tecla$="N" IF tecla$="S" THEN PRINT "Has dicho que s" ELSE PRINT "Has dicho que no" END IF Ampliando ms este ejemplo podramos construir un men "cutre". Si dejamos al usuario la posibilidad de pulsar ms teclas, normalmente nmeros, y despus los comprobamos con un SELECT CASE

115

podramos decidir entre hacer varias cosas. Es una estructura similar a la usada en el ejemplo de reconocer teclas del apartado anterior.

TEMA 2.2 NMEROS ALEATORIOS Y CONTROL DE TIEMPO


y y

2.2.1 - Nmeros aleatorios. Funcin RND 2.2.2 - Control de tiempo

2.2.1 - MMEROS ALEATORIOS. FUNCIN RND Cuando diseamos un programa informtico lo que intentamos es resolver un problema obteniendo un resultado lo ms exacto y fiable posible, usando la gran precisin de los clculos que hacen los ordenadores. De hecho, una de las normas bsicas para asegurar que un algoritmo funciona correctamente es poder asegurar y demostrar que para unos mismos datos de entrada los resultados o datos de salida van a ser siempre los mismos. En algunos casos como son los videojuegos nos puede interesar que suceda justamente lo contrario de lo que se ha dicho en el prrafo anterior, es decir, que cada vez que ejecutemos el programa sucedan cosas distintas y aparentemente impredecibles. Otras aplicaciones donde tendra que suceder esto seran programas simuladores de clculos y fenmenos cientficos y algunas aplicaciones matemticas o estadsticas especiales. Para poder llegar a estas situaciones nos encontramos con el terrible problema de conseguir que el ordenador (tan perfecto, exacto, preciso, etc...) sea capaz de generar nmeros de cualquier manera sin responder a ningn orden aparente. Esto tcnicamente es imposible y por lo tanto en vez de hablar de nmeros aleatorios sera ms correcto llamarlos nmeros pseudo-aleatorios porque no son completamente impredecibles, pero lo son casi, lo suficiente como para resolver nuestras necesidades. Para generar estos nmeros (pseudo-) aleatorios el ordenador puede usar mecanismos tales como medir el tiempo entre dos pulsaciones de teclas, o el necesario para enviar o recibir un dato a travs de una red. Como esto puede ser difcil de conseguir en algunos casos, es mucho ms cmodo usar lo que se conoce como "generador de nmeros aleatorios" que es una formula matemtica que al aplicarla repetidas veces pasndole los valores anteriores nos va devolviendo una serie de nmeros sin orden aparente. Una de estas frmulas se llama "Generador de Marsaglia" y viene a ser algo as como: zN = (2111111111 * zN-4 + 1492 * zn-3 + 1776 * zn-2 + 5115 * zn-1 + C) MOD 232 C = FLOOR((2111111111 * zN-4 + 1492 * zn-3 + 1776 * zn-2 + 5115 * zn-1 + C) / 232) Esta frmula nos devolvera una serie de nmeros que no empezara a repetirse hasta que lo hayamos calculado 3 * 10 elevado a 47 veces, un 3 con 47 ceros detrs, ms que suficiente. Pero que nadie se asuste, de hecho no se ni si estn bien copiados los nmeros. En

116

QBasic (y en otros lenguajes de programacin) no nos hace falta programar tantos relos, basta con usar el valor que nos devuelva la funcin RND cada vez que queramos obtener un nmero aleatorio. Vamos con un ejemplo de los ms sencillo del curso de programacin: CLS PRINT RND Al ejecutarlo se escribira en pantalla el nmero aleatorio que devuelva la funcin RND, un valor decimal (casi) impredecible, por ejemplo: .7055475 Ya lo tenemos ah, un nmero generado por el ordenador sin responder a ningn criterio aparente (Lleva un cero delante, pero QBasic se lo quita al escribirlo en la pantalla y solo se ve el punto decimal), pero todava hay un problema, ejecuta el programa varias veces y mira el resultado. Puedes comprobar que siempre sale el mismo nmero, ya no es tan impredecible. Esto se debe a que no hemos inicializado el generador de nmeros aleatorios de QBasic. Si tratas de descifrar algo de la frmula de Marsaglia que hay ms arriba observars que para calcular un nmero pseudo-aleatorio se necesitan algunos de los calculados anteriormente. Esto va muy bien si ya los hemos calculado, pero si es la primera vez no los tenemos. En este caso tendremos que usar lo que se conoce como "Semilla", que es un nmero arbitrario usado para que empiece a funcionar el generador. Si este nmero es el mismo siempre los nmeros aleatorios devueltos sern siempre una misma serie, por lo tanto hay que usar como semilla un nmero lo ms "aleatorio" posible como por ejemplo el nmero de segundos que han pasado a partir de las doce de la noche, cosa que nos devuelve la funcin TIMER. Para inicializar el generador de nmeros aleatorios usaremos la instruccin RANDOMIZE seguida de la semilla, normalmente TIMER. Vamos con el ejemplo completo: RANDOMIZE TIMER CLS PRINT RND Ahora s que cada vez que ejecutemos el programa tendremos un nmero distinto. El valor solo se repetira si ejecutamos el programa otro da a la misma hora, mismo minuto, mismo segundo. Algo difcil de conseguir y un riesgo aceptable en programas que no necesiten elevadas medidas de seguridad como los nuestros, pero no en otras aplicaciones como pudieran ser mquinas recreativas. De hecho, en sistemas ms serios como Lunux se puede observar como al apagar el ordenador se guarda en algn sitio la semilla aleatoria para poderla seguir usando la prxima vez. La instruccin RANDOMIZE TIMER es algo as como remover las bolas en un sorteo de lotera, debemos usarla una vez al principio de los programas que usen la funcin RND, pero no es necesario usarla

117

ms, de hecho repetirla en sitios como dentro de un bucle podra resultar contraproducente. El valor devuelto por RND va a ser un nmero decimal de precisin sencilla mayor o igual que cero y menor que uno, es decir, alguna vez podra salir el cero, pero nunca saldr el uno. Este valor lo podemos multiplicar o redondear para adaptarlo a nuestras necesidades. Vamos con un programa que contiene dos ejemplos: CLS RANDOMIZE TIMER valorDado = INT(RND*6)+1 PRINT "Hemos lanzado un dado y ha salido"; valorDado IF RND > 0.5 THEN PRINT "Hemos lanzado una moneda y ha salido 'cara'" ELSE PRINT "Hemos lanzado una moneda y ha salido 'cruz'" END IF Que dara este resultado (Con estos u otros valores): Hemos lanzado un dado y ha salido 4 Hemos lanzado una moneda y ha salido 'cruz' En el primer caso multiplicamos el valor de RND por seis, ya tenemos un nmero decimal que puede ir desde 0 a 5.999 (Nunca 6). A continuacin lo redondeamos hacia abajo usando la funcin INT, ya tenemos un entero entre 0 y 5. Como queremos un valor posible entre 1 y 6, que son las caras que tienen los dados, no hay ms que sumarle 1. En el segundo caso lo que hacemos es comprobar el valor de RND para hacer una cosa u otra. Como en este caso queremos que las dos partes del IF tengan las mismas posibilidades ponemos el punto de comprobacin en la mitad, en 0.5. Si quisiramos variar las posibilidades no tendramos ms que cambiar ese nmero. Si queremos ms de dos posibilidades podemos usar un SELECT CASE con varios intervalos. Por ltimo vamos a ver como conseguir que RND nos repita el ltimo nmero que gener sin tener que grabarlo en ninguna variable. Para conseguirlo bastara con escribir RND(0) en vez de RND. Vamos con un ejemplo: CLS RANDOMIZE TIMER PRINT "Un nmero aleatorio.............:"; RND PRINT "Repitamos el mismo nmero.......:"; RND(0) PRINT "Otra vez ms....................:"; RND(0) PRINT "Ahora un nmero aleatorio nuevo.:"; RND PRINT "Vamos a sacarlo otra vez........:"; RND(0) El resultado podra ser (con otros nmeros): Un nmero aleatorio.............: .2051972 Repitamos el mismo nmero.......: .2051972 Otra vez ms....................: .2051972 Ahora un nmero aleatorio nuevo.: .1969156 Vamos a sacarlo otra vez........: .1969156

118

Como se puede ver, si llamamos a RND sin argumentos, como hemos hecho hasta ahora, nos da un nmero aleatorio, pero si lo hacemos pasndole como argumento el cero entre parntesis (Tiene que ser obligatoriamente el cero) lo que haces es repetir el mismo nmero en vez de calcular uno nuevo, que sera el siguiente de la serie. Si usamos RND(0) por primera vez en el programa, sin haber usado antes RND, no pasa nada, tendremos un nmero aleatorio. Esto no se utiliza demasiado, pero en algn caso podemos ahorrarnos una variable y una instruccin de asignacin si queremos usar el mismo nmero en varios sitios. 2.2.2 - CONTROL DE TIEMPO El ordenador lleva instalado un reloj digital que usa para muchas cosas, como por ejemplo para almacenar en los directorios la fecha y hora en que se guard cada archivo. Por supuesto nosotros tambin podemos utilizar este valor en nuestros programas. La forma ms sencilla de obtener la hora ya la hemos visto en el apartado anterior. Es usar el valor devuelto por la funcin TIMER, un entero largo con los segundos que han pasado desde las doce de la noche. Con esta funcin ya podemos hacer un programa que nos salude de distinta forma dependiendo del momento del da que sea. All vamos... CLS SELECT CASE TIMER CASE IS < 28800: PRINT "Buenas madrugadas" CASE 28801 TO 43200: PRINT "Buenos das" CASE 43201 TO 72000: PRINT "Buenas Tardes" CASE ELSE: PRINT "Buenas noches" END SELECT El resultado, si ejecutamos el programa a las diez de la maana sera: Buenos das Es una forma de que nuestro programa sea algo "inteligente", aunque todava se podra mejorar (mucho). Lo que hemos hecho es ver cuantos segundos han pasado a las ocho de la maana, a medioda y a las ocho de la tarde para definir los intervalos, nada ms. Tambin podemos usar la funcin TIMER para cronometrar el tiempo que tarda en pasar algo. Bastara con almacenar la hora en una variable antes de empezar, hacer lo que sea, y al terminar almacenar la hora en otra variable y restarlas. Obtendramos el nmero de segundos. Vamos a cronometrar lo que tarda el ordenador en escribir en pantalla los nmeros del 1 al 10.000. All vamos... CLS inicio=TIMER FOR n = 1 TO 10000 PRINT n NEXT final = TIMER tiempo = final - inicio PRINT "Ha tardado"; tiempo; "segundos"

119

Este programa, al final de la tira de 10.000 nmeros nos dara un mensaje con el nmero de segundos que ha tardado. Todo funcionara bien excepto si durante la ejecucin del programa han dado las doce de la noche, obtenindose en este caso un nmero negativo. Para solucionar el problema habra que colocar la siguiente lnea justo antes de sacar el mensaje. Lo que hacemos es sumarle todos los segundos de un da si ha dado negativo. IF tiempo < 0 THEN tiempo = tiempo + 86400 Ahora vamos a recordar dos funciones que nos devuelven la fecha y la hora en modo texto, son DATE$ y TIME$. CLS PRINT "Hoy es "; DATE$ PRINT "Son las "; TIME$ Esta dara como resultado algo como: Hoy es 01-20-2011 Son las 10:42:35 Con la hora no hay nada que aclarar, pero la fecha est en formato americano, primero el mes, despus el da y al final el ao. El mes y el da siempre con dos cifras y el ao con cuatro. Estas funciones pueden ser ms intuitivas pero para manejar las cifras las tenemos que separar usando la funcin MID$. Vamos con el ejemplo: CLS PRINT "El da es "; MID$(DATE$, 4, 2) PRINT "El mes es "; MID$(DATE$, 1, 2) PRINT "El ao es "; MID$(DATE$, 7, 4) PRINT PRINT "La hora es "; MID$(TIME$, 1, 2) PRINT "Los minutos son "; MID$(TIME$, 4, 2) PRINT "Los minutos son "; MID$(TIME$, 7, 2) Y el resultado podra ser: El da es 20 El mes es 01 El ao es 2011 La hora es 10 Los minutos son 42 Los minutos son 35 Muy sencillo. Lo nico que hemos hecho es "recortar" con la funcin MID$ el trozo de cadena donde est cada cosa, que siempre va a estar en el mismo sitio, y separarlo para hacer con l lo que queramos. Y si nos hiciera falta sumarlos o compararlos podramos convertirlos a enteros usando la funcin VAL. Aplicando esto mismo, vamos con un ejemplo de una funcin a la que vamos a llamar FECHA$ y nos va a devolver la fecha actual como la usamos normalmente en castellano: Da, mes y ao separados por barras. FUNCTION FECHA$ aux$ = MID$(DATE$, 4, 2) aux$ = aux$ + "/" + MID$(DATE$, 1, 2) aux$ = aux$ + "/" +MID$(DATE$, 7, 4)

120

FECHA$ = aux$ END FUNCTION Al llamarla desde cualquier parte de nuestro programa nos devolvera, en formato cadena algo como 20/01/ 2011, que queda ms bonito que 01-20- 2011. Para terminar vamos a ver como cambiar la fecha y hora del sistema desde nuestro programa. Algo que no es habitual ni recomendable, pero que en algn caso nos puede hacer falta. Sera usando las instrucciones, no funciones sino instrucciones, DATE$ y TIME$ y asignndoles cadenas que contengan la fecha o la hora en el formato adecuado. Vamos con unos ejemplos: DATE$ = "04-20-2011" DATE$ = "04-20-92" DATE$ = "04-06-1994" TIME$ = "10:24:35" TIME$ = "10:24" TIME$ = "10" La fecha podemos establecerla poniendo siempre el mes primero y despus el da y el ao, este ltimo dato puede tener dos o cuatro cifras. En el ltimo ejemplo podramos dudar entre si la fecha es el 4 de junio o el 6 de abril, se almacenara este ltimo, 6 de abril. Cuidado con esto, siempre mes, da, ao. En el caso de la hora si no ponemos los minutos o los segundos se entendern que son 00. Si intentamos asignar una fecha o una hora no vlida se producir un error de tiempo de ejecucin. Normalmente no es recomendable modificar estas cosas desde los programas, si lo hacemos mal y no nos damos cuenta estaremos un tiempo usando el ordenador con el reloj mal, provocando problemas en los directorios, los antivirus, el planificador de tareas programadas, etc.

TEMA 2.3 MS SOBRE LAS CADENAS DE CARACTERES


y y y

2.3.1 - Acerca de las cadenas de caracteres 2.3.2 - La funcin e instruccin MID$ 2.3.3 - Otras funciones de manejo de cadenas

2.3.1 - ACERCA DE LAS CADENAS DE CARACTERES En QBasic el manejo de cadenas de caracteres es extremadamente sencillo. En otros lenguajes de programacin como es el caso de C las cadenas se tratan como un array de datos de tipo carcter (un tipo que no tenemos en QBasic) de longitud aproximadamente igual al nmero de caracteres de la cadena, y por lo tanto su longitud mxima est siempre limitada. Tambin esta forma de trabajar implica que para hacer algo tan simple como una asignacin de cadenas en esos lenguajes haya que recurrir a funciones especiales del lenguaje y no podamos usar directamente el operador de asignacin, el signo igual, como si se tratara de nmeros.

121

En este tema vamos a ver con detalle algunas funciones de QBasic relacionadas con cadenas que nos van a servir para manejar los datos de este tipo que nos hagan falta en nuestro programa. Vamos a recordar un poco lo que ya sabemos de las cadenas: Al igual que ocurre con las otras variables, no es necesario declararlas. Basta con usar un nombre de variable terminado por el carcter dlar ($). En este caso tendremos una cadena de texto de longitud variable. En algunos casos nos puede interesar que las cadenas sean de longitud fija para ahorrar memoria (Unos 10 bytes por cadena). En este caso bastara con declararlas usando: DIM nombreVariable AS STRING * 25 Dnde el nmero que va a continuacin del asterisco es la longitud mxima de la cadena. Es especialmente recomendable declarar las cadenas como de longitud fija en el caso de los arrays. Por ejemplo si hacemos: DIM tabla (1 TO 40, 1 TO 3) AS STRING * 5 Nos estamos ahorrando unos 120 bytes en todo el array, adems de que el manejo de cadenas de tamao fijo por parte del ordenador es algo ms rpido por tratarse de estructuras estticas. En el caso de las estructuras de datos definidas por el usuario (tipos registro) que incluyan cadenas, estas siempre tendrn que declararse con una longitud determinada. 2.3.2 - LA FUNCIN E INSTRUCCIN MID$ Una de las funciones ms tiles que incluye QBasic para el manejo de cadenas de caracteres es MID$. Ya la hemos visto anteriormente en varios ejemplos, pero vamos a hacerlo ahora con ms detalle. Esta es su sintaxis. MID$("cadena", inicio, longitud) Esta funcin lo que hace es extraer una porcin de una cadena. Le debemos pasar tres argumentos: y "cadena" es una cadena de caracteres entre comillas o bien el nombre de una variable de cadena cuyo valor va a ser utilizado por la funcin, o una expresin que devuelva como resultado una cadena. y inicio es la posicin del primer carcter que queremos sacar de la cadena, el carcter de ms a la izquierda es el 1. Si este valor es superior al tamao de la cadena, la funcin MID$ devolver como resultado una cadena vaca. y longitud es el tamao del trozo de cadena que queremos extraer. Si se sobrepasa el final de la cadena no ocurre nada, slo se devolver lo que se pueda de antes del final. Vamos con unos ejemplos: CLS miCadena = "Hecho en Ronda, Ciudad Soada" PRINT MID$(miCadena, 1, 5) PRINT MID$(miCadena, 5, 3) PRINT MID$(miCadena, 15, 1)

122

PRINT MID$(miCadena, 26, 2) PRINT MID$(miCadena, 300, 1) PRINT MID$(miCadena, 10, 3000) El resultado sera: Hecho o e , a Ronda, Ciudad Soada Esta funcin nos puede servir para extraer un determinado carcter de una cadena. Algo que puede parecer trivial, pero que nos va a simplificar mucho determinados problemas. Vamos con un ejemplo tonto: CLS miCadena = "LMXJVSD" INPUT "Escribe un nmero del 1 al 7: ", num PRINT "El"; num; " carcter de "; miCadena; " es "; MID$(miCadena, num, 1) Un resultado posible sera: Escribe un nmero del 1 al 7: 4 El 4 carcter de LMXJVSD es J Muy sencillo. Slo decir que no hemos depurado el dato de entrada y que si escribimos un cero o negativo habr un error. Vamos con otro ejemplo ms til que utiliza esta misma tcnica para determinar la letra del DNI. Para hacer esto lo que hay que hacer es comparar el resto del nmero dividido entre 23 con una serie de letras ordenadas de una forma caracterstica y dar la que corresponda. CLS INPUT "Escribe el nmero del DNI: ", dni& PRINT "La letra es: ": MID$("TRWAGMYFPDXBNJZSQVHLCKE", (dni& MOD 23) + 1, 1) El resultado podra ser: Escribe el nmero del DNI: 74926208 La letra es: M Podemos ver que el clculo se hace en una sola linea de cdigo usando la instruccin MID$. y El primer argumento que le pasamos a MID$, la cadena de caracteres, es la tira de letras ordenadas de forma caracterstica. y La posicin de inicio es el clculo propiamente dicho. Le sumamos uno porque la primera letra es la 1 y no la 0. y El tamao es 1 porque siempre queremos una y slo una letra. Es importante ver que la variable dnde guardaremos el nmero debe ser de tipo entero largo. Si la usamos como de tipo real (sin poner el &) se redondear y no saldr bien el clculo. Con esta tcnica el algoritmo ha resultado sumamente corto. A lo mejor sera ms sencillo o intuitivo haberlo resuelto usando un vector de caracteres para poder acceder a sus posiciones individuales, o bien

123

un SELECT CASE. Con cualquiera de estas soluciones el listado del programa hubiera sido mucho ms largo. Tendramos 23 posibilidades en el SELECT CASE o bien 23 asignaciones al vector. Este problema del DNI es muy frecuente. Le falta depurar los datos de entrada y convertirlo en forma de funcin, para que sea mucho ms portable. Para terminar con MID$ hay que decir que adems de ser una funcin, se puede usar tambin como instruccin para modificar la propia cadena. La sintaxis sera MID$( VariableCadena$, inicio, longitud) = Cadena$ El funcionamiento es parecido, salvo unas cuantas diferencias: y El primer argumento tiene que ser una variable de cadena y no un literal entre comillas ni una expresin. y El valor de inicio no puede ser mayor que la longitud de la cadena, en este caso se producira un error de tiempo de ejecucin. Vamos con un ejemplo: miCadena$ = "Hecho en Ronda" PRINT miCadena$ MID$(miCadena$, 10, 5) = "Soria" PRINT miCadena$ MID$(miCadena$, 10, 5) = "Sevilla" PRINT miCadena$ MID$(miCadena$, 7, 8) = "aqu" PRINT miCadena$ El resultado sera... Hecho en Ronda Hecho en Soria Hecho en Sevil Hecho aquevil Hay que tener en cuenta que la longitud del tramo de cadena a reemplazar queda definido por el valor del tercer parmetro, y no por el tamao de la expresin de cadena que asignamos. Si este es mayor se cortar, y si no llega slo se usar lo que haya, quedando lo dems como estaba. De esta misma forma la cadena nunca aumentar o disminuir su longitud total usando esta funcin. Como mucho podremos disminuir su longitud aparente usando espacios en blanco, pero realmente seguirn estando ah formando parte de la cadena. MID$ se usa mucho ms como funcin que como instruccin. Hay que tener claro para que sirve cada cosa y cuando se est usando una u otra. Como funcin va siempre a la derecha del operador de asignacin o dentro de una expresin, y como instruccin va siempre al principio de la lnea de cdigo.

124

2.3.3 - OTRAS FUNCIONES DE MANEJO DE CADENAS QBasic nos ofrece varias funciones tiles para hacer operaciones con cadenas de caracteres. Una de las ms tiles es MID$ a la que hemos dedicado el apartado anterior completo. Aqu van otras: Empecemos con dos funciones algo parecidas a MID$, pero menos avanzadas. LEFT$("cadena", numCaracteres) RIGTH$("cadena", numCaracteres) La primera de ellas devuelve un determinado nmero de caracteres del principio de la cadena, de la izquierda, y la otra los devuelve del final, de la derecha. Siempre en el mismo orden en que estn en la cadena. Se puede hacer referencia tanto a una cadena literal entre comillas o a una variable o expresin de tipo cadena. Si el nmero de caracteres especificado es mayor que la longitud total de la cadena se devolver la cadena entera. Si es cero se devolver una cadena vaca, y si es negativo habr un error. cadena$ = "Hecho en Ronda" PRINT LEFT$(cadena$, 5) PRINT RIGHT$(cadena$, 5) Dara como resultado: Hecho Ronda Ahora vamos con otras que convierten a maysculas y minsculas... UCASE$("Cadena") LCASE$("Cadena") UCASE$ convierte todas las letras que haya en la cadena, variable de cadena o expresin, a maysculas (Upper Case), y LCASE$ a minsculas (Lower Case). Es importante tener en cuenta que no se reconocen como letras ni los acentos ni la ee ni la u con diresis () y por lo tanto no se convierten correctamente. Vamos con un ejemplo. cadena$ = "Una cigea en un balcn de frica" PRINT UCASE$(cadena$) PRINT LCASE$(cadena$) Dara: UNA CIGEA EN UN BALCN DE FRICA una cigea en un balcn de frica Ahora vamos con otras dos funciones que eliminarn los espacios en blanco que pueda haber en los extremos de una cadena. LTRIM$("Cadena") RTRIM$("Cadena") LTRIM$ elimina los espacios que puede haber delante (A la izquierda, left) y RTRIM$ los que pueda haber por el final (A la derecha, right). Esta ltima funcin es muy til para trabajar con datos almacenados en cadenas de longitud fija (Como las que se usan en los tipos de datos definidos por el usuario para los registros). Estas cadenas siempre van rellenas con espacios hasta ocupar su longitud total, y si las manejamos con todos estos espacios pueden pasar cosas como

125

que fallen las comparaciones o que pasen cosas imprevistas en los diseos de pantallas. Ambas funciones pueden ser tiles para depurar datos introducidos por teclado en los que el usuario haya podido escribir espacios intiles antes o despus. Vamos con un ejemplo: cadena$ = " Hola! " PRINT "*"; LTRIM$(cadena$); "*" PRINT "*"; RTRIM$(cadena$); "*" Y el resultado: *Hola! * * Hola!* En Visual Basic se dispone de una funcin TRIM$ que elimina los espacios tanto delante como detrs de la cadena. Aqu no la tenemos, pero su programacin sera muy sencilla construyendo una nueva funcin a partir de estas dos. Ahora vamos con una funcin que nos devuelve una cadena llena con el nmero de espacios que le digamos SPACE$(num) Puede parecer intil, pero nos ayudar bastante en el diseo de pantallas. Por ejemplo: PRINT "Hola"; SPACE$(40); "Que hay" Dara: Hola Que hay O el siguiente ejemplo ms elaborado: CLS FOR n = 0 TO 5 PRINT SPACE$(5 - n); "/"; SPACE$(n + n); "\" NEXT Dibujara esta figura: /\ / \ / \ / \ / \ / \ Vamos con otra funcin parecida, pero que en vez de devolver una cadena de espacios la devuelve del carcter que nosotros le digamos: STRING(Longitud, "carcter") o bien STRING(Longitud, Cdigo-ASCII) Como se puede ver podemos especificar el carcter que queremos usando una cadena o bien usando el nmero de su cdigo ASCII. Esto es especialmente til cuando queremos dibujar un carcter que no aparece en el teclado. Vamos con unos ejemplos: CLS PRINT STRING$(10, "*") PRINT STRING$(5, 60) PRINT STRING$(15, "RONDA")

126

PRINT STRING$(8, 126) Que dara ********** <<<<< RRRRRRRRRRRRRRR ~~~~~~~~ Al igual que la anterior, esta funcin nos ser muy til para construir diseos de pantallas, pero hay que tener cuidado de no confundir su nombre STRING$ con el de tipo de datos cadena que es STRING sin el dlar detrs. Vamos ahora con otra funcin que en vez de devolvernos la cadena con cierta modificacin nos va a devolver un nmero que corresponde a la longitud (Nmero de caracteres) de la cadena. LEN("cadena") Vamos con un ejemplo que en combinacin con la funcin STRING$ que acabamos de ver nos subraye una palabra usando guiones. CLS INPUT "Escribe algo: "; cad$ PRINT " "; STRING$(LEN(cad$), "-") Dara algo como: Escribe algo: YA ESCRIBO ALGO --------------Esta funcin LEN tambin nos devuelve el espacio en bytes que ocupa en memoria cualquier variable. Basta con pasarle como parmetro el nombre de una variable que no sea de cadenas. A esta categora de funciones de manejo de cadenas habra que aadir otras que ya hemos visto como son DATE$ y TIME$, as como algunas menos utilizadas como HEX$ y OCT$ que convierten un nmero a sistemas de numeracin en base 16 y en base 8 respectivamente, pero ya que el resultado es en forma de cadena no nos servir para hacer ningn clculo. Combinando estas funciones podemos construir otras ms potentes que nos hagan cosas como por ejemplo dibujar recuadros o centrar textos. Esto es lo que se ver en el tema siguiente de manejo de pantalla del texto.

TEMA 2.4 MANEJO DE LA PANTALLA EN MODO TEXTO


y y y y y y y y

2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8

Introduccin a los interfaces de texto Posicionar el cursor en la pantalla Texto en colores Redefinir colores Caracteres semi grficos Caracteres especiales de relleno Caracteres no imprimibles Esmilis y ASCII-ART

127

2.4.1 - INTRODUCCIN A LOS INTERFACES DE TEXTO A estas alturas del siglo XXI casi todos los ordenadores personales utilizan modernos entornos grficos como son Windows o los tambin conocidos KDE y GNOME en Linux. Estos sistemas pueden representar cualquier imagen a partir de pequeos puntos que pueden ser de uno de los millones de colores soportados. Para aplicaciones ms sencillas, como es el caso del entorno de QBasic, se utiliza lo que se denomina Interfaz de texto, que es mucho ms fcil de controlar por el ordenador y ms que suficiente para determinadas aplicaciones. En todos los ejemplos que hemos hecho hasta ahora hemos podido comprobar como los resultados se iban presentando en la pantalla de forma secuencial, es decir, unos debajo de otro conforme se iban generando. Cuando se alcanzaba la parte baja de la pantalla todo el contenido suba automticamente para dejar una nueva lnea vaca abajo. Algunas operaciones ms "sofisticadas" que hemos llegado a hacer han sido borrar todo usando la instruccin CLS o encolumnar la presentacin de algunos listados ajustando los argumentos que le pasamos a la instruccin PRINT. De todas formas los resultados siempre se han visto de color gris sobre fondo negro. Muy triste. En este tema vamos a ver cmo se puede conseguir escribir exactamente en la posicin de la pantalla que queramos, as como en distintos colores de letra y de fondo. Tambin usaremos unos caracteres especiales conocidos como "Semigrficos" para dibujar lneas y recuadros que den ms consistencia a nuestras presentaciones. 2.4.2 - POSICIONAR EL CURSOR EN LA PANTALLA El cursor es un objeto que se puede mover por la pantalla y su posicin indica el sitio exacto dnde se escribir lo siguiente. En los procesadores de textos o en el propio editor de QBasic, es la "barrita" intermitente por donde van apareciendo las letras que vamos escribiendo en el teclado. En cualquier programa en modo texto el cursor siempre va a existir y va a indicar el lugar dnde se va a escribir lo siguiente que haya que escribir, ya sea porque lo haga el usuario con el teclado o bien el propio programa atendiendo a sus instrucciones de salida. En los programas de QBasic slo es visible cuando se pide al usuario que escriba algo usando la instruccin INPUT. El resto del tiempo est ah en su sitio, pero no lo vemos. Cuando usamos cualquier instruccin PRINT sin punto y coma al final se escribe lo que sea y el cursor invisible pasa al principio de la siguiente lnea a la espera de la siguiente instruccin de escritura. De esta forma todos los resultados se van mostrando uno debajo de otro lnea por lnea. La pantalla normal en modo texto que usamos aqu est compuesta por 25 lneas de 80 caracteres de ancho.

128

La primera lnea, la de ms arriba es la nmero 1 y la de abajo es la 25. La columna de ms a la izquierda es la 1 y la de ms a la derecha, la ltima, es la 80. Para llevar el cursor a cualquiera de estas posiciones y escribir en la parte de la pantalla que queramos no hay ms que usar la siguiente instruccin: LOCATE linea, columna De esta forma tan sencilla, usando una instruccin LOCATE delante de cada PRINT o INPUT ya podemos controlar exactamente donde va a salir cada cosa. Vamos con un ejemplo: CLS LOCATE 22, 30 PRINT "Esto va a salir abajo" LOCATE 3, 35 PRINT "Y esto arriba" LOCATE 12, 1 PRINT "A la izquierda" LOCATE 14, 67 PRINT "A la derecha" LOCATE 13, 37 PRINT "Centro" SLEEP Y este sera el resultado. El recuadro representa al borde de la pantalla.

129

Cmo se puede ver, las frases ya no aparecen escritas de arriba a abajo en el mismo orden en que estn escritas en el listado del programa. Cada una aparece en el sitio donde la anterior instruccin LOCATE ha llevado el cursor de texto. Para ajustar estas posiciones no hay ms que variar los nmeros de fila y columna de las instrucciones LOCATE. La instruccin SLEEP que aparece al final es para que el rtulo "Presione cualquier tecla y contine." no aparezca hasta que no pulsemos una tecla. Esto ser muy comn para que no se nos estropeen los diseos de pantalla. Otra cosa que nos va a estropear los diseos de pantalla va a ser el desplazamiento vertical que hace automticamente el interfaz en modo texto para que conforme vamos escribiendo lo anterior se desplaza hacia arriba, como ha venido pasando en los programas que hemos hecho hasta ahora. Esto hace que si escribimos algo en la lnea 24, las lneas 1 a 24 suban una posicin, desapareciendo lo que haya en la primera lnea. Y si escribimos algo en la lnea 25, las lneas 1 a 24 subirn 2 posiciones, desapareciendo lo que hubiera escrito en las dos primeras lneas. La solucin ms cutre para salvar este problema sera no escribir nunca en las lneas 24 y 25, pero hay otra mejor. Poner un punto y coma al final de todas las instrucciones PRINT o a continuacin de la palabra INPUT en las instrucciones de entrada, siempre que estn situadas en una de estas dos ltimas lneas. De esta forma evitaremos que al terminar la instruccin el cursor baje y se produzca el desplazamiento vertical automtico. Ahora ya podemos realmente escribir en cualquier parte de la pantalla usando la instruccin LOCATE. Vamos con un ejemplo que aclare esto del desplazamiento automtico. CLS LOCATE 1, 20: PRINT "Principio" LOCATE 25, 20: PRINT "Final" SLEEP Si ejecutas este fragmento de cdigo observars que la palabra PRINCIPIO no aparece en la pantalla, ya que tras escribir en la lnea 25 se produce este desplazamiento automtico. Para solucionar el problema aadimos un punto y coma al final de la instruccin PRINT conflictiva: CLS LOCATE 1, 20: PRINT "Principio" LOCATE 25, 20: PRINT "Final"; SLEEP Y podrs observar como aparecen ambas palabras escritas en la pantalla, una arriba y otra abajo, justamente en las posiciones que hemos especificado. Problema solucionado. Un ltimo comentario. En los dos fragmentos anteriores has podido observar que las instrucciones LOCATE y PRINT van en la misma lnea

130

separadas por el carcter "Dos puntos" (:). Esto es comn hacerlo para acortar el listado, ya que estas dos instrucciones casi siempre van a ir por parejas. 2.4.3 - TEXTO EN COLORES Hasta ahora todos nuestros programas han dado los resultados en la pantalla usando letras de color gris claro sobre fondo negro. Para llorar, pero esto va a cambiar ahora mismo. El interfaz en modo texto nos ofrece la posibilidad de usar diecisis colores de letra y ocho de fondo. No es que sea una maravilla tecnolgica comparada con los millones de colores que usan los entornos grficos actuales, pero para cumplir nuestros objetivos son ms que suficientes. Vamos a ver la paleta de colores predeterminada del modo de pantalla VGA: Como se puede ver, cada color lleva un asociado un nmerito. Este nmero se conoce como "Atributo de color" y nos va a servir para especificar los colores mediante la siguiente instruccin: COLOR primerPlano, fondo Por ejemplo, si queremos escribir con letras amarillas sobre fondo azul (muy tpico), no tenemos ms que usar una instruccin COLOR 14, 1 antes de las correspondientes instrucciones PRINT o INPUT. Al usar una instruccin COLOR, se cambian los colores de la pantalla para sucesivas instrucciones de escritura en pantalla hasta que se vuelva a cambiar usando otra instruccin COLOR. No hace falta poner una instruccin COLOR delante de cada PRINT o INPUT si no vamos a cambiar el color. Como color de primer plano podemos usar cualquiera de los 16 atributos de color disponibles. Como color de fondo solo los 8 primeros. Si no especificamos color de fondo, se conservar el que hubiera. El color de fondo en principio slo afecta al trozo de pantalla que hay por detrs de los caracteres que escribimos. Si queremos colorear toda la pantalla de un determinado color, una forma muy rpida de hacerlo es especificar una instruccin COLOR justo antes de una CLS, por ejemplo: COLOR 15, 4 CLS Har que toda la pantalla se coloree de rojo oscuro. En sucesivas instrucciones PRINT se escribir lo que sea en color blanco fuerte sobre este rojo mientras no usemos otra instruccin COLOR. Hay que tener cuidado de no usar el mismo color para primer plano y para fondo. Si lo hacemos no pasa nada, nuestro programa va a seguir funcionando perfectamente, pero sera como escribir con lpiz blanco en un papel, no se vera nada de nada.

131

Tambin podemos conseguir que los caracteres aparezcan en la pantalla parpadeando. Para hacerlo no hay ms que sumar 16 al color de primer plano que queramos, por ejemplo: COLOR 30, 2 Har que el texto escrito posteriormente aparezca de amarillo parpadeando sobre fondo verde. (14 del amarillo ms 16 son 30). No se puede hacer que el fondo parpadee. Una cosa importante a tener en cuenta con el parpadeo es que NO VA A FUNCIONAR si estamos ejecutando QBasic desde Windows en una ventana. Para que funcione habr que pasar a pantalla completa pulsando ALT+ENTER o el botn correspondiente de la barra de botones de la ventana, y an as a la primera tampoco funciona algunas veces, lo que se hace es afectar al color de fondo. No es conveniente abusar del parpadeo, puede resultar molesto para la vista. Se debe de utilizar slo para resaltar pequeos mensajes o simbolitos en la pantalla. Como ejemplo veamos como podra quedar una portada sencilla en colores para un programa de agenda que integre todo lo que vimos en el tema de ficheros.

Para conseguir esto no habra mas que insertar las instrucciones COLOR correspondientes en los lugares adecuados, como se ve aqu: CLS PRINT COLOR 4 PRINT , , "* * * * * * * * * * * *" PRINT , , "* "; COLOR 10: PRINT "AGENDA SUPER BARATA "; COLOR 4: PRINT "*" PRINT , , "* *" PRINT , , "*"; COLOR 2: PRINT " J.M.G.B. Ronda 2003 "; COLOR 4: PRINT "*" PRINT , , "* * * * * * * * * * * *" PRINT PRINT

132

COLOR 13 PRINT , "A ... Aadir nueva persona" PRINT , "B ... Borrar persona" PRINT , "M ... Modificar persona" PRINT COLOR 5 PRINT , "N ... Buscar persona por nombre" PRINT , "D ... Buscar persona por direccin" PRINT , "T ... Buscar persona por telfono" PRINT , "E ... Buscar persona por edad" PRINT COLOR 1 PRINT , "L ... Ver listado de personas" PRINT COLOR 9 PRINT , "C ... Compactar base de datos" PRINT COLOR 11 PRINT , "S ... Salir" Estas son las instrucciones slo para dibujar la pantalla. El resto para que funcione el men habra que progrmarlo por ejemplo con la funcin INKEY$ y un SELECT CASE que segn la tecla pulsada llame a cada uno de los mdulos del programa. 2.4.4 - REDEFINIR COLORES En el apartado anterior hemos visto cmo conseguir que nuestros programas escriban en la pantalla usando distintos colores. Esto, junto con los semigrficos que veremos en el apartado siguiente, es ms que suficiente para hacer que nuestros programas tengan una interfaz en modo texto bastante conseguida. Observando la paleta VGA de 16 colores se puede comprobar que los colores estn un poco "descoloridos". No es que al monitor le pase nada cuando entramos a MS-DOS, es que son los que hay y estn definidos as. Tambin nos puede pasar que queramos usar determinados colores, por ejemplo para construir un logotipo, y no nos venga bien ninguno de los que tenemos. Esto se puede arreglar un poco. El modo de pantalla de texto nos permite usar 16 colores SIMULTNEAMENTE, pero estos no tienen porqu ser siempre los mismos. Los podemos cambiar fcilmente por cualquier otro de esta paleta ms amplia de 64 colores.

133

Podemos intercambiar cualquiera de los 16 atributos de color que tenemos disponibles por uno de estos 64 colores usando la siguiente instruccin: PALETTE original, nuevo Vamos a ver lo que significa esto. Puede dar lugar a confusiones: y Original es el ATRIBUTO de color que queremos cambiar (Un nmero entre 0 y 15) y Nuevo es el color de esta paleta que queremos usar (Un nmero entre 0 y 63) Supongamos que queremos cambiar el atributo 3 (Que originalmente es un azul grisaceo muy feo) por un celeste ms bonito que aparece en nuestra paleta con el ndice 43. No habra ms que hacer: PALETTE 3, 43 Y a partir de ahora cada vez que usemos la instruccin COLOR 3 se escribir con nuestro celeste. El 3 ya no es el azul grisaceo feo. Podemos intercambiar los colores todas las veces que queramos e incluso asignar valores repetidos a distintos atributos. Lo que hay que tener en cuenta es que slo vamos a ver en la pantalla 16 colores a la vez, pero estos podrn ser cualquiera de los 64. Otra cosa que hay que tener en cuenta es que los cambios hechos con la instruccin PALETTE tambin afectan a todo lo que ya haya escrito en la pantalla del color que hemos cambiado. Imagina que hay escrito algo en color 10 original (Verde fuerte), y ahora asignamos al atributo 10 el color de paleta 51 para seguir escribiendo en verde ms suave. Automticamente todo lo escrito en verde fuerte pasa a ser verde suave. Para poder ver los dos verdes a la vez habra que usar otro atributo y dejar el 10 como estaba. Con un poco de prctica haciendo esto se puede conseguir que determinados textos se enciendan o se apaguen poco a poco (Cambiando los colores por otros cada vez ms oscuros o ms claros dentro de un bucle), algunos efectos interesantes como ocultar el rtulo "Presione cualquier tecla y contine". La instruccin PALETTE no tiene ningn inters para aprender a programar, pero nos puede servir para dar a nuestros programas un toque personal o extrao al utilizar combinaciones de colores que se salen de los 16 tan tpicos de la amplia mayora de programas de MSDOS. Hay que recordar que como fondo slo podemos usar los ocho atributos primeros (0 a 7), pero ya podemos conseguir cosas como escribir sobre fondo amarillo o blanco. Pero con cuidado!, que no haya que usar gafas de sol delante del ordenador. 2.4.5 - CARACTERES SEMIGRFICOS Ya sabemos escribir en colores. Con un poco de idea ya podemos dibujar recuadros aplicando distintos colores de fondo a distintas zonas de la pantalla, pero todava podemos llegar un poco ms all. Podemos dibujar recuadros con bordes y lneas horizontales, verticales y esquinas usando unos caracteres especiales que nos

134

permiten "ensamblar" las lneas en la pantalla como si se tratara de un puzzle: Los caracteres SEMIGRFICOS, casi grficos. Estos caracteres no los tenemos en el teclado, y por lo tanto para conseguirlos habr que recurrir a sus cdigos ASCII, que son los siguientes:

Recordemos que para introducir un cdigo ASCII hay que pulsar simultneamente la tecla alternativa (ALT) y el cdigo correspondiente en el bloque numrico situado a la derecha del teclado. Para demostrar la utilidad de los caracteres semigrficos vamos a crear una nueva portada para nuestro programa de agenda. Va a ser esta, que usa tambin colores personalizados.

Como se puede ver, bastante conseguida. Ya no tiene casi nada que envidiarle a la interfaz de un programa comercial de MS-DOS. Lo que hace falta es que todo lo dems tambin funcione perfectamente y sea til. Ahora vamos con el listado del programa necesario para dibujar esto: PALETTE 0, 32 PALETTE 3, 11 PALETTE 4, 8 PALETTE 5, 17 PALETTE 7, 63 PALETTE 10, 4 PALETTE 11, 52 PALETTE 12, 36 PALETTE 13, 38 PALETTE 14, 54

135

COLOR 15, 3 CLS COLOR 15, 4 LOCATE 2, 11: PRINT ""; STRING$(57, 205); "" LOCATE 3, 11: PRINT "|"; SPACE$(57); "|" LOCATE 4, 11: PRINT ""; STRING$(57, 205); "" COLOR 14, 4: LOCATE 3, 14 PRINT "A G E N D A S U P E R B A R A T A" COLOR 1, 7 LOCATE 7, 20: PRINT "--------------------------------------" LOCATE 8, 20: PRINT "| A ... |" LOCATE 9, 20: PRINT "| B ... |" LOCATE 10, 20: PRINT "| M ... |" LOCATE 11, 20: PRINT "--------------------------------------" LOCATE 12, 20: PRINT "| N ... |" LOCATE 13, 20: PRINT "| D ... |" LOCATE 14, 20: PRINT "| T ... |" LOCATE 15, 20: PRINT "| E ... |" LOCATE 16, 20: PRINT "--------------------------------------" LOCATE 17, 20: PRINT "| L ... |" LOCATE 18, 20: PRINT "--------------------------------------" LOCATE 19, 20: PRINT "| C ... |" LOCATE 20, 20: PRINT "--------------------------------------" LOCATE 21, 20: PRINT "| S ... |" LOCATE 22, 20: PRINT "--------------------------------------" COLOR 0, 7 LOCATE 8, 29: PRINT "Aadir nueva persona" LOCATE 9, 29: PRINT "Borrar persona" LOCATE 10, 29: PRINT "Modificar persona" COLOR 10 LOCATE 12, 29: PRINT "Buscar persona por nombre" LOCATE 13, 29: PRINT "Buscar persona por direccin" LOCATE 14, 29: PRINT "Buscar persona por telfono" LOCATE 15, 29: PRINT "Buscar persona por edad"

136

COLOR 12: LOCATE 17, 29: PRINT "Ver listado de personas" COLOR 11: LOCATE 19, 29: PRINT "Compactar base de datos" COLOR 13: LOCATE 21, 29: PRINT "Salir" COLOR 14, 5: LOCATE 25, 1: PRINT SPACE$(80); LOCATE 25, 10: PRINT "Curso de Programacin"; COLOR 13: LOCATE 25, 46: PRINT "J.M.G.B. HECHO EN RONDA"; SLEEP A que asusta? Como se puede ver, ya es un listado bastante largo. Antes de seguir hay que aclarar un pequeo problema que tiene: En Windows no podemos representar los caracteres semigrficos y los he sustituido por guiones, tuberas y escuadras para tener una idea de dnde va cada cosa. Al copiar este listado a QBasic tendrs que sustituirlos por los correspondientes semigrficos usando la tabla de cdigos ASCII. Los del recuadro superior son de lnea doble y los de abajo sencillos. Para empezar a desliar este listado de cdigo tan largo hay que tener en cuenta que es un esquema secuencial. No hay bucles ni operaciones complejas. As ya se ve bastante ms sencillo. Si te las con los colores redefinidos quita las instrucciones PALETTE y usa solo los colores normales. As de paso te vas aprendiendo sus 16 cdigos, tambin te servirn para algunos otros lenguajes de programacin. Cuando est terminado redefine los colores que quieras. Adems de los caracteres semigrficos sencillos y dobles que hemos visto aqu, existen otros conocidos como "mixtos" que se pueden usar en las esquinas y en las uniones de lneas horizontales y verticales para ensamblar lneas sencillas y dobles, es lo que se ve en las esquinas y uniones de las lneas que forman este dibujo.

Esto podra resultar interesante, lo que ocurre es que estos caracteres semigrficos mixtos no son muy comunes y no aparecen en todas las tablas de caracteres ASCII. En el caso de que el usuario de nuestro programa est utilizando una de estas tablas de caracteres extraa, no podr ver los caracteres estos y nuestro diseo de pantalla quedar roto. Normalmente las esquinas de los recuadros se cambiarn por vocales acentuadas y otros smbolos extraos, algo as como esto:

Como no nos merece la pena correr el riesgo de que nuestros diseos de pantalla queden as de estropeados, lo mejor es evitar el uso de los semigrficos mixtos. Aunque los veamos en nuestra tabla de caracteres ASCII, puede que el usuario del programa no los tenga y

137

no pueda verlo correctamente. Lo mismo ocurre al imprimir si la impresora utiliza una tabla de caracteres ASCII distinta. Recordar que estamos en MS-DOS y no hay fuentes True Type ni nada parecido. Con un poco de imaginacin se pueden construir diseos de pantallas ms que aceptables usando slo los semigrficos simples y dobles que ya hemos visto junto con los caracteres de relleno que veremos en el apartado siguiente. Para terminar con los semigrficos vamos a ver un pequeo algoritmo que usado como un procedimiento nos sirva para dibujar un recuadro en las posiciones de pantalla que nos interesen, algo muy til.

Lo que hace es sencillo. Dibuja un cuadro con el borde de lnea doble con la esquina superior derecha en la posicin v,h y la anchura y la altura la obtiene de las variables largo y alto. La primera lnea dibuja la parte superior, el bucle FOR dibuja la zona intermedia rellenando lo de dentro con espacios, y la ltima lnea dibuja el borde inferior. Ponemos un punto y coma al final de cada lnea para que si dibujamos el recuadro en la parte baja de la pantalla no se produzca el desplazamiento automtico y se nos estropee todo el diseo de pantalla. Este ejemplo se podra ampliar especificando los colores en la llamada a la funcin o escribiendo algn tipo de barra de ttulo como si fuera una ventana de las del editor de QBasic. 2.4.6 - CARACTERES ESPECIALES DE RELLENO Adems de los semi grficos tambin existen otros caracteres que nos van a ser muy tiles para de disear las pantallas de nuestros programas en modo texto. Son los caracteres de relleno, aqu estn sus cdigos ASCII, porque tampoco los tenemos en el teclado:

Estos caracteres nos van a permitir rellenar zonas enteras de la pantalla para evitar que se vean todo del mismo color. Los ms utilizados son los tres primeros que nos ofrecen distinta densidad de puntos. El cuarto rellena todo con el color de primer plano, y los dos ltimos se usan slo para cosas como dar efectos de sombras a recuadros y poco ms. Para usar estos caracteres se suele recurrir a un bucle FOR que recorre la pantalla por filas y dentro dibuja series del carcter correspondiente usando la funcin STRING$. Vamos con un ejemplo que va a rellenar con el carcter 177 la mitad izquierda de la pantalla. FOR v = 1 TO 25

138

LOCATE v,1: PRINT STRING(40,177); NEXT Una cosa que aparece al usar grandes cantidades de estos caracteres es el llamado "efecto Moir" (Muar), consistente en una serie de lineas curvas que aparecen dibujadas en la pantalla. Se produce por una interferencia entre los puntos que forman estos caracteres de relleno y los pxeles fsicos del monitor. Si nuestro monitor nos da ese problema, no se puede evitar, pero s atenuar usando colores de primer plano y de fondo que no sean demasiado distintos. Ahora vamos con otro diseo ms elaborado que nos ofrece un diseo de fondo de este tipo:

FOR v = 1 TO 25 FOR h = 1 TO 79 STEP 2 LOCATE v, h: PRINT STRING$(2, INT(RND * 3) + 176); NEXT NEXT Aqu se elige aleatoriamente uno de los tres caracteres disponibles en el momento de dibujarlo. Se han agrupado los caracteres de dos en dos para que salgan cuadros ms o menos cuadrados. El siguiente ejemplo es parecido, pero esta vez los cuadros aparecern ordenados ya que se usa un contador en vez de los nmeros aleatorios. c = 0 FOR v = 1 TO 25 FOR h = 1 TO 79 STEP 2 c = c + 1 IF c = 3 THEN c = 0 LOCATE v, h: PRINT STRING$(2, 176 + c); NEXT NEXT El uso de estos caracteres de relleno junto con los recuadros de colores hechos a base de semi grficos es una buena forma de dar a nuestros programas en modo texto una apariencia bastante aceptable.

139

2.4.7 - CARACTERES NO IMPRIMIBLES Si se observa la tabla de cdigos ASCII se puede ver que los caracteres que van desde el 0 hasta el 31(El 32 es el espacio en blanco) son smbolos extraos y adems no se pueden escribir usando la tecla ALT. Estos caracteres estn asociados a teclas del teclado como Enter, escape, tabulador o retroceso entre otras y a ordenes especiales para el ordenador y la impresora como son saltos de pgina y de lnea, fin de fichero, toque de timbre, etc. Estos caracteres no estn pensados para ser dibujados en la pantalla ni para ser impresos. Tienen asociados esos smbolos porque alguno tenan que tener, pero ya que los tenemos ah puede ser que en algunos casos queramos escribirlos en la pantalla. Vamos con sus cdigos ASCII...

Y ahora vamos a ver como escribirlos. (Solo en QBasic y en los programas de MS-DOS. En Windows no se puede) Hay dos formas. La primera sera usar la funcin CHR$, por ejemplo para dibujar un corazn bastara con poner PRINT CHR$(4). La segunda, ms cmoda es pulsar la combinacin de teclas CONTROL + P y despus, a continuacin ALTERNATIVA + el cdigo en el teclado numrico, por ejemplo para dibujar el corazn habra que pulsar CTRL+P y ALT+4 con lo que este smbolo quedara directamente dibujado en la pantalla. Esto de CTRL+P nos vale para dibujar estos caracteres especiales en la mayora de programas de MS-DOS as como en el propio intrprete de comandos (Smbolo C:\>). Hasta aqu muy bien. Ya podemos dibujar caras, flechas y otros smbolos en nuestras pantallas, pero no podemos olvidar que estos caracteres son especiales y hay que tener unas ciertas precauciones. La primera es que si un smbolo nos da algun problema como que se nos descoloquen los dems, pues directamente evitar usarlo y nada ms. Otras precaucione son no usar los smbolos que no llevan asociado ningn carcter como son el de cdigo ASCII 0 y tener en cuenta que cada vez que nuestro programa dibuje un ASCII 7 se oir un pitido en el altavoz interno o bien sonar el sonido predeterminado de Windows.

140

Estos caracteres los usaremos exclusivamente en instrucciones PRINT y no deberemos almacenarlos en ficheros, especialmente en ficheros secuenciales. Si los intentaremos imprimir van a provocar fallos en la impresora. De hecho si intentas imprimir tal cual est la tabla de caracteres ASCII que viene en la ayuda de QBasic y que incluyes estos smbolos observars cosas como alguna lnea se rompe o incluso que se hace un salto de pgina y un trozo de la tabla se imprime en la siguiente hoja. Esto se debe a que se han encontrado caracteres de saltos de lnea y de pgina que la impresora ha interpretado como lo que son. Tambin hay que tener precaucin si estos caracteres aparecen de forma literal en las instrucciones del cdigo fuente del programa. Si imprimes dicho cdigo puedes encontrarte con problemas similares cada vez que salga uno de estos caracteres. Por lo tanto si piensas imprimir el cdigo fuente del programa utiliza funciones CHR$ en lugar de los caracteres literales. Tambin hay que saber que estos caracteres no se pueden ver en Windows, por lo que si abres el fichero BAS del cdigo de tu programa en un editor de Windows como por ejemplo el bloc de notas, directamente no los vers o los vers marcados como un cuadro negro. 2.4.8 - ESMILIS y ASCII-ART Para enriquecer un poco ms las pantallas de nuestros programas en modo texto podemos usar la forma que tienen los propios caracteres para hacer pequeos dibujos. Lo ms sencillo de esto es lo que se conoce como esmilis (Correctamente se escribe smiley) y que estamos acostumbrados a utilizar en los foros y chats de Internet. Algunos de los ms comunes y de significado conocido son los siguientes: :-) :-( :-D :-o X-D Se pueden encontrar listados muy extensos llenos de estos smbolitos junto con su significado, pero de todas formas puede que estos esmilis sean muy poca cosa para decorar nuestros programas. Por eso podemos recurrir a diseos ms complicados conocidos como ASCII-ART. Vamos con unos ejemplos... __ / /\ / / \ / / /\ \ / / /\ \ \ / /_/__\ \ \ /________\ \ \ \___________\/ 1111111111111111111111111111 1110000001111111111000000111 1100000000011111100000000011 1100000000001111000000000011

141

1100000000000110000000000011 1100000000000000000000000011 1110000000000000000000000111 1111000000000000000000001111 1111100000000000000000011111 1111111000000000000001111111 1111111110000000000111111111 1111111111000000001111111111 1111111111100000011111111111 1111111111110000111111111111 1111111111111001111111111111 1111111111111111111111111111

./ | _________________ / / / __________ //\_ /' / | (__________) ||.' `.________________________ / / | __________ ||`._.'~~~~~~~~~~~~~~~~~~~~~~~~` / \ \__(__________)__\\/ | `\ | | ___________________ | |_________________...-------'''- - - =- - = - = `. /| | \- = = - -= - = - == - =| ( | | |= -= - = - = - = - =--= = - = =| \| |_________________/- = - -= =_- =_-=_- -=_==_=_= -| | | ```------...___________________.' |________| \ / _ | | ,,,,,,, /=\ ,-' `-, /\___________ (\\\\\\\||=| | | \/~~~~~~~~~~~` ^^^^^^^ \=/ `--------' ` Podemos encontrar montones de ellos o atrevernos nosotros mismos a dibujar los nuestros (Por ejemplo un logotipo). Desde el punto de vista de la programacin lo nico que hay que tener en cuenta es que hay que dibujarlos lnea por lnea usando una instruccin PRINT para cada una, y que en muchos casos no podemos olvidar incluir espacios a la izquierda de determinadas lneas para que el dibujo no se estropee. Por ejemplo, para dibujar la primera de las anteriores figuras habra que hacer...

142

PRINT " __" PRINT " / /\" PRINT " / / \" PRINT " / / /\ \" PRINT " / / /\ \ \" PRINT " / /_/__\ \ \" PRINT "/________\ \ \" PRINT "\___________\/" Estos dibujos fueron concebidos inicialmente para ser impresos en impresoras antiguas que no soportaban la impresin de grficos, por lo que en principio es suficiente con que sean en blanco y negro, pero tambin nos podemos molestar en ponerlos de varios colores dibujndolos con distintas instrucciones PRINT. Tambin podemos construir grandes letras para dibujar los ttulos y rtulos de nuestros programas, por ejemplo... _ ___ ___ ___ ___ ___ ___ _ _ ___ _ _ ___ __ __ _ _ /\/| ___ /_\ | _ )/ __| \| __| __/ __| || |_ _| | | |/ / | | \/ | \| ||/\/ / _ \ / _ \| _ \ (__| |) | _|| _| (_ | __ || | || | ' <| |__| |\/| | .` | \| | (_) | /_/ \_\___/\___|___/|___|_| \___|_||_|___\__/|_|\_\____|_| |_|_|\_|_|\_|\___/ ___ ___ ___ ___ _____ _ ___ ____ ____ ____ ______ | _ \/ _ \| _ \/ __|_ _| | | \ \ / /\ \ / /\ \/ /\ \ / /_ / | _/ (_) | /\__ \ | | | |_| |\ V / \ \/\/ / > < \ V / / / |_| \__\_\_|_\|___/ |_| \___/ \_/ \_/\_/ /_/\_\ |_| /___| Estas letras estn construidas usando principalmente barras y smbolos de subrayado. Tambin es comn construirlas usando los caracteres semigrficos o el smbolo # repetido (Esto ltimo es lo que hace la instruccin banner de Linux). Para dibujar un rtulo con las letras anteriores podemos hacerlo de forma literal con cuatro instrucciones PRINT (Una por cada lnea) y los trozos de letras entre comillas... PRINT " _ _ ___ ___ _ _ ___ ___ _ _ ___ ___ _ _ ___ _" PRINT "| || || __|/ __|| || |/ _ \ | __|| \| | | _ \/ _ \| \| || \ /_\" PRINT "| __ || _|| (__|| __ | (_) | | _||| .` | | / (_) | .` || |) |/ _ \" PRINT "|_||_||___|\___||_||_|\___/ |___||_|\_| \_|_\\___/|_|\_||___//_/ \_\"

143

Pero ya que estamos programando, lo ms cmodo sera tener un procedimiento que nos dibujara el rtulo que queramos en la posicin que le digamos solo con hacer una llamada del tipo... DibujaRotulo "Hecho en Ronda", 5, 10 Habra que construir un programa con muchas instrucciones Mid$ que vaya construyendo las letras lnea por lnea. Se puede encontrar en la seccin de programas terminados de esta web.

TEMA 2.5 GRFICOS


y y y

2.5.1 - Introduccin a los interfaces grficos 2.5.2 - Los modos de pantalla en QBasic 2.5.3 - Rendimiento en modo grfico

2.5.1 - INTRODUCCIN A LOS INTERFACES GRFICOS Todos los resultados de los programas que hemos hecho hasta ahora en este tutorial de programacin los hemos podido ver en la pantalla a travs de lo que denominamos "Interfaz de texto", primero slo en blanco y negro, y ms adelante en colores. A partir de aqu vamos a tener la posibilidad de usar la pantalla como una matriz de puntos de colores (Pxeles) donde tendremos la podremos dibujar lo que queramos usando las instrucciones adecuadas. Dos conceptos muy importantes que hay que tener en cuenta a la hora de trabajar con interfaces grficos son la profundidad de color y la resolucin. La profundidad de color se refiere al nmero mximo de colores que vamos a poder presentar en la pantalla simultneamente. La resolucin es el nmero de pxeles que forman la pantalla, a mayor resolucin mayor calidad de imagen. En los apartados siguientes veremos primero la forma de iniciar el modo de pantalla grfica de QBasic, algo muy sencillo, y despus cmo dibujar figuras en la pantalla para que hagan nuestros programas y juegos ms vistosos. 2.5.2 - LOS MODOS DE PANTALLA EN QBASIC Olvidmonos un momento del tutorial de programacin. Imaginemos que estamos trabajando con Windows, un interfaz grfico muy moderno. Nuestra pantalla estar trabajando seguramente con una resolucin de 800 por 600 pixeles y una profundidad de color de 16.7 millones de colores. De esta forma, adems de las letras que aparecen por ejemplo en las barras de ttulo de las ventanas, podemos ver dibujados en la pantalla los bordes de las propias ventanas, los iconos, los botones, y hasta una imagen de fondo en el escritorio que posiblemente sea una fotografa digitalizada con muchos colores. En medio de todo este despliegue de color pulsamos sobre un icono que nos va a lanzar un juego que tenemos instalado en nuestro ordenador. Este juego al empezar a funcionar nos cambia la resolucin a 320 por 200 puntos y la profundidad de color a 256

144

colores. Podremos ver que como la resolucin ha descendido, los bordes de ciertas figuras redondeadas aparecen "pixelados", es decir, formando escalones. Podemos ver mejor los pixeles porque ahora son ms grandes. Tambin al reducir el nmero de colores las imgenes ya tienen un aspecto "como de dibujos animados". Esto es suficiente para un juego sencillo en la mayora de los casos, pero para ver una fotografa digital como la que tenamos en el fondo del escritorio ya nos vendran cortos. En medio de nuestra interesante partida con el juego, el ordenador se bloquea (algo muy comn en Windows) y nos vemos obligados a reiniciarlo. Tras el arranque aparece un mensaje que nos indica que Windows va a funcionar en "modo a prueba de fallos". Una de las cosas que habrn cambiado ser la resolucin, que se ha reducido a 640 por 480 pixels y el nmero de colores que se ha quedado en 16. Podremos ver como la imagen del fondo del escritorio se ve muy fea y los iconos se ven algo ms gordos que antes y con unos colores un poco extraos. Al abrir alguna ventana veremos que todo aparece un poco ms grande porque hay menos resolucin. Ya volviendo al curso de programacin, vamos a ver ahora cmo conseguir que nuestros programas de QBasic trabajen con ms o menos resolucin y ms o menos profundidad de color segn nos convenga en cada caso. Ambos valores no los podemos cambiar de forma independiente, sino que van relacionados en lo que se conoce como "Modos de pantalla". Estos son los ms usados:
MODO PRESENTACIN TEXTO RESOLUCIN PROFUNDIDAD DE COLOR

SCREEN 0 SCREEN 12 SCREEN 13

Interfaz de texto Interfaz grfico

80 x 25 caracteres 80 x 30 caracteres 40 x 25 caracteres

640 x 480 pixels 320 x 200 pixels

16 colores

16 colores

Interfaz grfico

256 colores

Adems de estos tres, hay otros ms, pero han quedado en desuso ya que son monocromticos (Blanco y negro) o bien de cuatro colores. Se usaban en los ordenadores ms antiguos y en los actuales es posible que ya no lleguen ni a funcionar (Producirn un error de tiempo de ejecucin al intentar iniciarlos). Tambin existen otros modos con ms resolucin y con ms colores, pero QBasic no los soporta. Para aprender a programar, que es lo que estamos haciendo aqu, estos tres son suficientes.

145

En cada caso seremos nosotros los responsables de seleccionar cual es el mejor para nuestro programa, conociendo sus ventajas e inconvenientes. El modo 0 es el interfaz de texto que hemos venido usando hasta ahora, es el que QBasic usa por defecto si no le decimos que use otro. Si no tenemos necesidad de dibujar figuras grficas, es el ms indicado en la mayora de los casos ya que es muy rpido. El modo 12 es el de ms alta resolucin que tenemos en QBasic, nos servir para los casos en que tengamos que dibujar diagramas o figuras ms complejas con muchas lneas. Su principal inconveniente es que es bastante ms lento que los otros porque el ordenador tiene que manejar muchos puntitos para dibujar la pantalla. Los colores para el modo 12 son los mismos que la paleta bsica normal del interfaz de texto, es decir, estos.

Hay que tener en cuenta que en el modo 12 no disponemos de los colores intermitentes como tenamos en la pantalla de texto, y que para escribir letras tenemos ciertas limitaciones que veremos en apartados posteriores. El modo 13 es de muy baja resolucin, por lo tanto las imgenes se vern muy "pixeladas" y su tamao en puntos no podr ser muy grande ya que "no cabrn en la pantalla". Como ventaja principal tiene la posibilidad de usar una paleta de 256 colores, algo muy valioso para los juegos, y adems es bastante rpido, ya que al haber 256 colores cada pixel de la pantalla se corresponde exactamente con un byte en la memoria grfica, y las operaciones que tiene que hacer el ordenador para dibujarla son ms sencillas. Este modo de pantalla se ha usado durante aos para la programacin de montones de juegos para MS-DOS que todava hoy nos ofrecen una calidad grfica ms que aceptable. Para el modo 13 los colores son los de la siguiente paleta:

146

Como se puede ver, los primeros 16 son iguales a los VGA, a continuacin una escala de grises, y los siguientes van agrupados de 24 en 24, cosa que se puede utilizar para hacer efectos como degradados o sombras. Igualmente en el modo 13 tampoco hay colores intermitentes. El cambio de modo de pantalla en QBasic es extremadamente sencillo, basta con usar la siguiente instruccin: SCREEN modo Dnde modo es uno de los numeritos que hemos visto antes: 0, 12 o 13. El efecto que tiene esta instruccin es el borrado completo de la pantalla y que a partir de ahora podremos escribir y dibujar adaptndonos a la nueva resolucin y a los colores que tenemos. Normalmente esto lo hacemos una vez nada ms al principio del programa (si es para modo texto no hace falta) y ya no lo cambiamos ms, aunque si hace falta se puede hacer todas las veces que sea necesario. En los siguientes apartados vamos a ver cmo se pueden dibujar figuras en la pantalla grfica una vez que la hayamos activado usando la instruccin SCREEN. Un efecto que notaremos si tenemos un monitor moderno digital ser que se apague y no volvamos a ver imagen alguna hasta pasado un instante. Lo mismo ocurrir cuando termine un programa en modo grfico y volvamos al editor de QBasic que es en modo texto. Si estamos trabajando con QBAsic en una ventana de Windows, para entrar a modos grficos se pasa obligatoriamente a modo de pantalla completa, y cuando termine el programa y volvamos al editor de QBasic posiblemente lo encontremos tambin a pantalla completa. Para solucionar este problema consulta el anexo 3.3 de este totorial.

147

2.5.3 - RENDIMIENTO EN MODO GRFICO Como podremos comprobar a lo largo de estos temas dedicados a los grficos, el rendimiento de estas operaciones es bastante bajo independientemente de que tengamos un ordenador sper moderno o la mejor tarjeta grfica del mercado con una gran cantidad de memoria. Este bajo rendimiento se debe principalmente a que QBasic es un lenguaje interpretado y a que las instrucciones no estn optimizadas para alcanzar gran velocidad. Todas las instrucciones se reducen a dibujar puntos en la pantalla y QBasic no es capaz de dibujarlos lo suficientemente rpido. Los modernos videojuegos en tres dimensiones y con alta resolucin, millones de colores, efectos de iluminacin transparencias, reflejos, sombras... se reducen a eso, a dibujar en la pantalla puntos de colores. Los programas calculan cual es el color que tiene que tener cada punto y estos se colocan a gran velocidad como valores numricos en la memoria grfica del ordenador. El adaptador grfico va leyendo esa informacin para dibujar la pantalla entera muchas veces por segundo, mientras ms mejor. Los programas y las bibliotecas grficas que permiten hacer eso (openGL, directX...) estn escritas directamente en lenguaje ensamblador y depuradas para que todo se haga de la forma ms rpida posible. Tambin los controladores de nuestro sistema grfico estn escritos de forma que trabajan de forma sincronizada con estos programas para obtener el mayor rendimiento, mientras que QBasic utiliza el modo de pantalla VGA que se usa desde hace aos y no aprovecha para nada estas tecnologas. No sabe ni que existen. Podemos depurar un poco nuestros programas y controlar cosas como que dentro de los bucles que se repiten mucho se ejecuten el menor nmero de instrucciones y clculos matemticos posibles, pero por muchas vueltas que le demos a nuestro cdigo, en QBasic no podemos esperar obtener buenos resultados a una velocidad aceptable. Nos tendremos que conformar con sencillos dibujos que nos van a servir para aprender a programar y poder dar el salto a un lenguaje ms avanzado que nos permita usar todas estas nuevas tecnologas.

TEMA 2.6 TRAZADO DE FIGURAS GRFICAS


y y y y y y

2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.6.6

El cursor grfico Puntos. Instruccin PSET Lneas y rectngulos. Instruccin LINE Crculos y valos. Instruccin CIRCLE Rellenar reas. Instruccin PAINT Dibujar figuras complejas. Instruccin DRAW

148

2.6.1 - EL CURSOR GRFICO Hay varias instrucciones especficas que nos permiten dibujar en la pantalla en modo grfico. Antes de empezar a estudiar su utilizacin es importante darse cuenta de que en la pantalla en modo grfico existe, adems del cursor de texto, un cursor grfico. Este sera comparable a "la punta de un lpiz que va dibujando cosas". Al iniciar uno de los modos grficos con la instruccin SCREEN el cursor grfico, siempre invisible, se sita en el centro de la pantalla, y posteriormente, conforme vayamos dibujando cosas con las instrucciones que vamos a ver a continuacin, se ir moviendo para situarse siempre al final de la ltima figura que hayamos dibujado. Algo parecido a lo que ocurra con el texto, pero que es conveniente aclarar. 2.6.2 - PUNTOS. INSTRUCCIN PSET La primera instruccin que vamos a ver en modo grfico nos va a permitir dibujar un punto en la pantalla en la posicin que nosotros indiquemos, y del color que nosotros queramos. Su sintaxis es: PSET (h,v),color Los valores de h y v, siempre entre parntesis y separados por una coma son las coordenadas horizontales y verticales del lugar en en el que vamos a dibujar el punto. Recordemos que la esquina superior izquierda de la pantalla tiene las coordenadas 0,0 y las de la esquina inferior derecha dependen del modo de pantalla elegido, en el modo 12 sern (639,479) y en el modo 13 sern (319,199). Observa que no son (640,480) o (320,200) porque, a diferencia de las coordenadas de texto, empezamos por cero y no por uno. Si nos salimos de la pantalla especificando coordenadas mayores o menores de los lmites permitidos, no ocurre nada, simplemente que el punto se dibujar fuera de la pantalla y no lo veremos. El valor que elijamos para el color depende de la paleta asociada al modo de pantalla que tengamos activo: Si es el modo 12 podremos usar un valor entre 0 y 15 o si es el modo 13 tendremos disponibles los valores desde 0 a 255. Evidentemente, si dibujamos un punto negro sobre fondo negro no se ver. Si no especificamos un color se usar el de la anterior orden de dibujo, si era la primera orden se usar el color blanco. En la anterior sintaxis ponamos unas coordenadas absolutas, es decir, el cursor grfico se mova hasta la posicin indicada y se dibujaba el punto. Tambin es posible indicar una posicin relativa a la posicin actual del cursor grfico usando la palabra STEP antes del parntesis de las coordenadas: PSET STEP(0,0), 14 Esta instruccin dibujar un punto amarillo en la posicin actual del cursor grfico, es decir, al final de la ltima figura dibujada o en el centro de la pantalla si es la primera instruccin de dibujo. Vamos con unos cuantos ejemplos ms de esto de las posiciones relativas: SCREEN 13

149

PSET (200,100), 10 PSET STEP(0,40), 14 PSET STEP(60,0), 12 PSET STEP(-20,-20), 9 Este trozo de cdigo activar el modo 13 y dibujar un punto verde exactamente en la posicin (200,100), es decir, 200 pixels a la derecha y 100 pixels ms abajo de la esquina superior izquierda de la pantalla. Seguidamente se dibujar un punto amarillo 40 pixels ms abajo del anterior, uno rojo 60 pixels a la derecha del amarillo, y finalmente uno azul 20 pixels ms arriba y 20 pixels a la izquierda del rojo. Como se puede ver, al usar coordenadas relativas, ya es til usar valores negativos. Por supuesto, los valores de las coordenadas y de los colores no tienen porqu ser expresiones constantes, pueden ser el resultado de una expresin matemtica con o sin variables, por ejemplo... PSET (inicio+largo, inicio+alto), colorActual O algo muy til y muy normal es que las instrucciones de dibujo estn dentro de bucles que las repitan. Normalmente no nos sirve de nada dibujar un punto solo, siempre dibujaremos muchos desde dentro de bucles, por ejemplo: SCREEN 12 FOR n=100 to 500 STEP 5 PSET (n,50),14 NEXT Esta instruccin dibuja en la pantalla una sucesin de puntos amarillos alineados. Si nos salimos no pasa nada, simplemente que se dibujarn fuera y no los veremos, pero si indicamos un color de fuera de la paleta del modo de pantalla actual se producir un error de tiempo de ejecucin y el programa se parar. 2.6.3 - LNEAS Y RECTNGULOS. INSTRUCCIN LINE La siguiente instruccin que vamos a ver nos va a permitir dibujar una lnea recta en la pantalla. Su sintaxis es... LINE (h1,v1)-(h2,v2),color Aqu debemos especificar dos coordenadas: la del principio de la lnea recta y la de su final. Tras dibujar la lnea el cursor grfico permanecer en el final, es decir, en la posicin de pantalla (h2,v2). Igual que en el caso anterior, podemos especificar coordenadas relativas usando la palabra STEP antes del parntesis para una o las dos posiciones. Esto es til para dibujar lneas poligonales, en este caso cada segmento se dibujara con una instruccin LINE y todas menos la primera empezaran con LINE STEP(0,0) para que empiecen junto al final de la anterior, por ejemplo este cdigo dibuja un tringulo: LINE (150,100)-(100,200),10 LINE STEP(0,0)-(200,200) LINE STEP(0,0)-(150,100)

150

Si la ltima coordenada de la ltima instruccin es igual a la primera coordenada de la primera instruccin, tendremos una lnea poligonal cerrada, en caso contrario quedar abierta. Si la lnea que estamos dibujando es completamente horizontal o completamente vertical ser dibujada toda continua, si es diagonal aparecer formando como "Escalones". Este efecto es lo que se conoce como "pixelado" y ser ms visible en el modo 13 ya que al ser de menos resolucin, los pixels son "ms gordos". Esta instruccin LINE tambin nos permite dibujar un rectngulo (o un cuadrado) de forma muy sencilla, simplemente aadiendo una coma y una letra B a continuacin del color, por ejemplo... LINE (20, 20)-(70, 30), 12 Dibujara una lnea recta diagonal que va desde (20,20) hasta (70,70). Y... LINE (20,20)-(70,30), 12, B dibujara un rectngulo con una de sus esquinas en la posicin (20,20) y la opuesta en la posicin (70,70). El borde del rectngulo tendr una anchura de un pixel y en este caso ser de color amarillo. Si queremos que el rectngulo se dibuje relleno basta con poner las letras BF (Borde y fondo) en vez de la B sola, por ejemplo... LINE (20,20)-(70,30), 12, BF Si no queremos especificar el color, para que se coja el usado en la instruccin anterior, basta con no ponerlo, quedarn dos comas juntas. LINE (20,20)-(70,30), , BF Un poco extrao, pero cuidado con equivocarse, porque si ponemos... LINE (20,20)-(70,30), BF Se dibujar una lnea recta entre las dos posiciones, y ser del color que se almacene en una nueva variable llamada BF que seguramente ser el cero que es negro y puede que no veamos. Como norma general es mejor poner el color siempre. Al usar la segunda coordenada relativa en los recuadros, sean rellenos o no, podemos calcular fcilmente su tamao exacto sin tener que hacer clculos, por ejemplo: LINE (20,20)-STEP(99,99), 14, BF Nos dibujara un cuadrado amarillo relleno de 100 por 100 pxeles (99 + 1) con la esquina superior izquierda en la posicin que indicamos en la primera coordenada sin tener que calcular nosotros donde ira la otra esquina. Hay que tener en cuenta que en el modo de pantalla 13 los cuadrados siempre los vamos a ver un poco ms altos que anchos aunque hayamos puesto las longitudes de sus lados iguales, ya que la resolucin de este modo de pantalla (320x200 pxels) no es proporcional a las dimensiones fsicas del monitor que siempre guardan una proporcin de 3:4. Para dibujar un cuadrado que se vea cuadrado de verdad habra que dibujarlo con una altura un poco menor que su anchura para que se disimule el error, pero tampoco no merece mucho la pena complicarse tanto en la mayora de los casos.

151

Por ltimo, podemos conseguir que las lneas y los cuadros sin relleno (B) se dibujen de forma discontinua, es decir, unos puntos s y otros no. Para hacer esto se pone al final del todo un numerito que nos indica el patrn de puntos a utilizar. Este valor es un nmero de 16 bits (entre 0 y 65.535) que lo podemos poner en hexadecimal para no liarnos comenzndolo con los caracteres &H. (entre &H0000 y &HFFFF) Vamos con unos ejemplos: LINE (20,20)-(70,30), 14,B ,&H5555 Se dibujara un rectngulo amarillo con el borde punteado, si no queremos poner el color haramos: LINE (20,20)-(70,30), ,B ,&H5555 Y si queremos que sea una lnea y no un rectngulo no tendramos que poner la letra B, pero s su correspondiente coma, sera as: LINE (20,20)-(70,30), , ,&H5555 Esto no se utiliza mucho. Si queremos otro patrn de puntos distinto basta con ir probando nmeros. Los ms tiles pueden ser &H5555 que nos van dibujando "uno s y otro no" y &HFF00 que nos haran un efecto parecido a la lnea discontinua de una carretera. Si queremos una lnea a dos colores se puede hacer dibujando una normal de un color y justo encima una discontinua del otro color, por ejemplo... LINE (100,100)-(300,200), 15 LINE (100,100)-(300,200), 12, ,&HFF00 Esto nos dibujara una lnea a trozos blancos y rojos. 2.6.4 - CRCULOS Y VALOS. INSTRUCCIN CIRCLE La siguiente instruccin nos va a servir para dibujar una circunferencia (slo la lnea del contorno) en nuestra pantalla grfica. CIRCLE (h, v), radio, color En este caso se dibujar una circunferencia con el centro situado en la posicin (h,v) y con el radio (medido en pxels) que indiquemos. Tras el trazado de la misma el cursor de grficos permanecer en el centro. Por ejemplo: CIRCLE (100, 200), 50, 12 Dibujar una circunferencia de color rojo con el centro en la posicin (100,200) y con un radio de 50 pixels. Si no especificamos el color se tomar el de la anterior instruccin de dibujo, como en los casos anteriores. Tambin tenemos la posibilidad de usar una coordenada relativa anteponiendo la palabra STEP al parntesis, por ejemplo... LINE (40, 10)-(100,200),14 CIRCLE STEP(0,0),10 ...dibujara una lnea recta inclinada de color amarillo y en su extremo final una circunferencia del mismo color con un radio de 10 pxels. La instruccin CIRCLE tambin nos permite dibujar arcos de circunferencia, aunque esto no se usa mucho, no hay porqu dibujarla siempre entera. Para hacerlo hay que poner a continuacin del color el ngulo inicial y final del arco que queremos dibujar. Los ngulos van en radianes, la circunferencia completa tiene unos 6.28 radianes (2 veces pi) y el ngulo 0 est la la derecha, el pi/2 radianes

152

(90) est arriba, el pi radianes (180) est a la izquierda y el 3/2 pi radianes (270) est abajo. De esta forma, por ejemplo, para dibujar la mitad superior de una circunferencia (desde 0 hasta pi radianes) habra que poner: CIRCLE (100, 100), 40, 14, 0, 3.14 Y si nos queremos ahorrar el color y que lo coja de la instruccin anterior, pues no lo ponemos. CIRCLE (100, 100), 40, , 0, 3.14 Por ltimo, tambin podemos dibujar valos en vez de circunferencias. Para hacerlo aadimos otro parmetro ms a continuacin. Este parmetro ser la proporcin entre el radio horizontal y el vertical. Por ejemplo, si es dos, el radio vertical ser del doble que el horizontal, y si es 0.5 el radio vertical ser la mitad del horizontal y la circunferencia aparecer aplastada. El radio que nosotros hayamos puesto en la instruccin ser el mayor de los dos, es decir, el horizontal si este valor es menor que 1 o el vertical si nuestro valor de aspecto es mayor que uno. En este ejemplo dibujamos slo la mitad izquierda de una circunferencia verde aplastada que sera cuatro veces ms ancha que alta si estuviera entera:

CONST PI = 3.14; CIRCLE (200, 200), 160, 9, PI/2, 3*(PI/2),0.25 Si queremos dibujar el mismo valo entero (Algo menos rebuscado), no ponemos los valores de principio y fin del arco, pero s tenemos que respetar sus posiciones poniendo las comas: CIRCLE (200, 200), 160, 9, , ,0.25 El dibujo de circunferencias no nos permite rellenarlas (Eso lo haremos en el apartado siguients), as como tampoco el trazado de una lnea punteada. Las ltimas cosas que hemos visto de los arcos y de los valos ya no sern aplicables en Visual Basic. En todo caso las circunferencias se dibujan muy pixeladas, especialmente si son de pequeo tamao. Hay que tener en cuenta que en el modo de pantalla 13 las circunferencias no saldrn redondas, siempre un poco ms altas que anchas, ya que su resolucin (320x200 pxels) no es proporcional a las dimensiones fsicas del monitor que siempre guardan una proporcin de 3:4. 2.6.5 - RELLENAR REAS. INSTRUCCIN PAINT Tambin es posible conseguir que una parte de nuestra pantalla se rellene de un color o de un patrn. Sera algo parecido a la

153

herramienta "Cubo de pintura" que tenemos en la mayora de programas de dibujo como es el caso del Paint de Windows, pero con alguna diferencia. El funcionamiento de la herramienta "Cubo de pintura" del Paint consiste en que al pulsar sobre una parte de la pantalla con esta herramienta, todos los pxels adyacentes del mismo color cambiarn al nuevo color. Se produce un efecto como que la "pintura" se va extendiendo, slo que es muy rpido y no lo notamos. En QBasic la instruccin que tenemos para hacer esto es la siguiente: PAINT (h, v), colorrelleno, colorlmite Al ejecutar esta instruccin se producira un efecto parecido a "Echar pintura del color colorrelleno en la posicin (h, v) y esta se ira extendiendo por todos los sitios hasta encontrar pxeles del color colorlmite". Una gran diferencia con los programas de dibujo es que el color se va extendiendo hasta quedar encerrado por pxeles del colorlmite, independientemente de los otros colores que se encuentre por el camino, a los que cubrir. Para especificar una posicin relativa tambin podemos usar coordenadas relativas anteponiendo la palabra STEP como en casos anteriores. Vamos con un ejemplo que rellenara un crculo, algo muy comn: SCREEN 12 CIRCLE (100, 100), 40, 12 PAINT (100,100), 14, 12 Lo primero que hacemos es dibujar un crculo de color rojo (12) sobre el fondo de la pantalla que es negro porque no hay nada ms dibujado. A continuacin echamos pintura amarilla (14) dentro del crculo y le decimos que pare cuando encuentre pxeles rojos (12), con lo que la pintura no llegar a salirse del crculo. El color puede ser el mismo que el color lmite, pero el color lmite no puede ser el mismo que el color de fondo que tiene el sitio donde empezamos a pintar, ya que entonces solamente se dibujara un punto. Esta instruccin puede dar lugar a que la "pintura" se salga y llene toda la pantalla, vamos con un ejemplo: SCREEN 12 CIRCLE (100, 100), 40, 12 LINE (120, 120)-STEP(50, 50), 9, B PAINT (100, 100), 10, 12 Primero dibujamos una circunferencia roja, despus un cuadrado azul encima de la circunferencia, y al rellenar desde dentro de la circunferencia observaremos como el color verde "se sale" de la circunferencia y llena toda la pantalla, borrando tambin el recuadro azul. Algo as sera:

154

Puedes observar fcilmente que el color verde se ha salido del crculo por los dos agujeros que quedan a la derecha y abajo. Estos se produjeron al dibujar el cuadrado azul, borrando algnos pxeles rojos. Para evitar este problema no hay ms que planificar cuidadosamente el orden en que se van dibujando los distintos objetos grficos y las posiciones exactas donde usamos la instruccin PAINT. Por ltimo decir que en vez de un color slido podemos rellenar usando un patrn de relleno, en este caso habra que sustituir el color por una cadena de ocho caracteres que codificara el estilo del patrn. Para ver ms detalles consultar la ayuda en pantalla de QBasic. No profundizaremos ms en el tema porque de todas formas la instruccin PAINT ya no podr ser utilizada en Visual Basic. 2.6.6 - DIBUJAR FIGURAS COMPLEJAS. INSTRUCCIN DRAW En los apartados anteriores hemos visto como podemos dibujar figuras elementales como puntos, lneas, recuadros y circunferencias. Para dibujar figuras ms complejas podemos optar entre usar una serie de instrucciones de estas o bien usar una sola de la que vamos a ver a continuacin. DRAW CadenaComando$ Esta instruccin es completamente distinta a las anteriores. En principio no aparecen coordenadas y Qu es eso de CadenaComando$...? Ahora lo vamos a ver. La instruccin DRAW lo que hace es mover el cursor de grficos a partir de la posicin actual donde se encuentre para ir dibujando lo que nosotros queramos. Y para saber lo que nosotros queremos que dibuje se utiliza esta CadenaComando$ que no es ms que una expresin de cadena o una cadena de caracteres entre comillas que contiene algunos de los siguientes caracteres para que QBasic los interprete y sepa lo que tiene que dibujar. Estos son los caracteres que hay que poner para que el cursor grfico se mueva en las distintas direcciones y vaya dibujando como si fuera la punta de un lpiz (Up, Down, Left, Right, E,F,G,H):

Con un ejemplo se ve ms claro. Las letras pueden ser tanto maysculas como minsculas. Pero se prefieren en minsculas para dar ms legibilidad.

155

Imaginemos que queremos dibujar una escalera con cinco escalones que sube hacia la derecha. Basta con poner... DRAW "ururururur" Ya est, mira que sencillo. Si hubiramos hecho esto con instrucciones LINE habra que haber puesto 10 instrucciones! Y haberse hartado de calcular coordenadas. Ahora vamos a ir aadiendo cosas... Si ejecutamos esto veremos que efectivamente se dibuja la escalera, pero muy pequeita, ya que cada escaln solo mide de alto o de ancho un pixel. Para conseguir que los trazos sean ms largos en la direccin que indiquemos no hay ms que poner el nmero de pxeles a continuacin de la letra. Haciendo lo siguiente conseguiremos que cada escaln mida tres pxeles de alto y cinco de ancho:

DRAW "u3r5u3r5u3r5u3r5u3r5" Vamos con otro ejemplo ms elaborado que dibuja una flecha apuntando hacia la izquierda (Empezando por la punta):

DRAW "e6d3r6d6l6d3h6" Adems de esto podemos conseguir cosas como que el cursor grfico vuelva atrs tras haber hecho un trazo, por ejemplo para dibujar esta especie de estrella haramos:

DRAW "nu6nd6nl6nr6ne6nf6ng6nh6" Hemos puesto una N delante de cada letra, de esta forma tras dibujar cada punta de la estrella el cursor vuelve al centro antes de dibujar la siguiente. Tambin podemos hacer que el cursor se mueva sin dibujar, sera algo parecido a levantar el lpiz del papel y moverlo, por eso para dibujar una lnea discontinua haramos: DRAW "r6br6r6br6r6br6r6br6r6" Hemos puesto una letra B delante de los trazos que no queremos que se dibujen. Con esta forma de dibujar consegumos movel el cursor a saltitos a partir de la posicin de la pantalla donde termin el trazo anterior.

156

Tambin podemos hacer que salte directamente a una posicin de la pantalla usando la letra M seguida de las coordenadas horizontal y vertical separadas por una coma. Por ejemplo para dibujar un tringulo usando esta tcnica haramos... DRAW "bm100,100m140,100m120,70m100,100" Al principio llevamos el cursor hasta la posicin (100,100) sin dibujar (Poniendo la B delante) para que no se dibuje una lnea desde la posicin actual del cursor hasta el tringulo, cosa que no queremos. Despus vamos saltando a los otros dos vrtices del tringulo y finalmente volvemos hasta la posicin inicial para que la figura quede cerrada. Observa que usando esta tcnica ya podemos dibujar lneas diagonales que no estn necesariamente a 45 grados. Al usar la instruccin DRAW se utiliza el color de la anterior instruccin de dibujo, o el blanco si era la primera. Tambin podemos especificarlo nosotros usando la letra C seguida del nmero del color que queramos igual que en las dems instrucciones que ya hemos visto. Tambin como novedad podemos cambiar de color todas las veces que queramos dentro de la misma secuencia de comandos, de esta forma podremos dibujar un trozo de la figura de un color y otro de otro distinto. Como ejemplo de esto vamos a dibujar un recuadro con las lneas izquierda e inferior de verde (Color 10) y las lneas derecha y superior de azul (Color 9).

DRAW "c10d40r40 c9u40l40" Tambin puedes observar en este ejemplo que se ha utilizado un espacio para separar un poco las "dos partes" del recuadro. Puedes utilizar todos los espacios que quieras para separar las distintas partes de un dibujo, especialmente si hay una tira de letras muy larga. Por ltimo vamos a ver como hacer que las figuras dibujadas con la orden DRAW puedan estar rellenas de color. Sera algo parecido a la instruccin PAINT, pero integrada en la orden DRAW. Basta con llevar el cursor grfico a una posicin dentro de la figura (Con la letra B para que no se dibuje) y all dentro usar la letra P seguida del color que queremos usar de relleno y del color de bordes, que tendr que ser el que hemos utilizado para dibujar el contorno para que "la pintura no se salga". Como ejemplo vamos a dibujar un tringulo con el borde rojo y lo de dentro blanco...

157

DRAW "c12 bm100,100m140,100m120,70m100,100 bm110,95 p15,12" Empezamos por el vrtice inferior izquierdo en la posicin (100,100) y cuando volvemos a l para cerrar la figura hacemos saltar el cursor hasta dentro sin dibujar y rellenamos. Es importante llevar el cursor hasta el sitio interior sin dibujar ya que si lo hacemos dibujando se intentar rellenar a partir de un punto ya dibujado seguramente con el color de borde y no funcionar. La instruccin DRAW tiene todava ms posibilidades, pero como el uso de estas otras opciones es muy raro, todava ms que el de la propia DRAW, no las vamos a ver aqu. Para saber cuales son mira la ayuda en pantalla de QBasic. Esta instruccin es til para dibujar figuras complejas, pero lo que aprendas de ella slo te va a servir para QBasic. En Visual Basic ya desaparece y en otros lenguajes si existe algo parecido funciona de forma totalmente distinta.

TEMA 2.7 OTRAS OPERACIONES CON GRFICOS


y y y y y

2.7.1 2.7.2 2.7.2 2.7.2 2.7.2

Definir marcos de visualizacin. Instruccin VIEW Definir un sistema de coordenadas personalizado Copiar y pegar. Instrucciones GET y PUT Averiguar el color de un pixel. Funcin POINT Escribir texto en modos grficos

2.7.1 - DEFINIR MARCOS DE VISUALIZACIN. INSTRUCCIN VIEW Un marco de visualizacin es un mecanismo que tiene QBasic para poder dibujar solamente en una parte de la pantalla sin miedo a meternos en otras partes ya dibujadas y estropear el trabajo ya hecho con instrucciones anteriores. Al definir un marco de visualizacin se define un rectngulo de manera que QBasic solo podr dibujar dentro de l. El resto de la pantalla se queda como est y no podr ser alterado hasta que se defina otro marco de visualizacin. Esta tcnica no es muy utilizada y solo nos va a ser til en casos muy determinados, pero ya que en la ayuda de QBasic se hace referencia constantemente a esto, vamos a verlo. Para definir el marco de visualizacin utilizaremos la siguiente instruccin, similar a LINE. VIEW (h1, v1)-(h2,v2) As definimos un recuadro dentro del cual podremos dibujar sin miedo a salirnos e invadir otras partes de la pantalla. Vamos con un ejemplo:

158

SCREEN 12 VIEW (100, 100)-(400, 300) LINE(0, 0)-(500, 400), 14 Haciendo esto definimos un recuadro imaginario que abarca parte del centro de la pantalla. Despus dibujamos una lnea que va desde la esquina superior izquierda de nuestro recuadro (Las coordenadas ahora empiezan desde ah) hasta donde llegue segn las dimensiones que les hemos dado, pero en todo caso slo se dibujar el trozo de lnea que cae dentro de nuestro marco de visualizacin. El resto de la pantalla queda tal cual est. Si nos liamos con el nuevo origen de coordenadas y queremos seguir usando el normal con el origen en la esquina superior izquierda de la pantalla no hay ms que poner la palabra clave SCREEN delante de las coordenadas del marco de visualizacin, por ejemplo: VIEW SCREEN(100, 100)-(400, 300) As dejamos como estaba el origen de coordenadas, pero siempre se dibujarn solamente los trozos de figuras que entren en el marco de visualizacin. Al definir un marco de visualizacin, las figuras que hubiera ya dibujadas en esa zona se respetan, es como si el marco de visualizacin fuera transparente. En algunos casos nos puede interesar rellenarlo directamente de un color. Para hacerlo solo hay que poner el valor de color a continuacin de las coordenadas, por ejemplo para que se rellene de azul sera: VIEW (100, 100)-(400, 300), 1 Tambin podemos aprovechar para dibujar un borde alrededor del marco de visualizacin. Habra que poner a continuacin el color que queremos para el borde, por ejemplo: VIEW (100, 100)-(400, 300), 1, 14 As dibujaramos el interior azul oscuro y un borde amarillo alrededor. Es importante entender que este borde va fuera del marco de visualizacin, y por lo tanto tampoco podr ser alterado con las instrucciones de dibujo. Si no queremos relleno, para conservar lo que ya haya dibujado, pero s queremos el borde, tambin se puede hacer. Habr que respetar la posicin del color de relleno, pero sin poner ningn nmero, aunque parezca extrao se pueden poner dos comas juntas: VIEW (100, 100)-(400, 300), , 14 Para usar la pantalla completa como marco de visualizacin es muy sencillo, simplemente: VIEW En esta instruccin no podemos usar la palabra clave STEP para indicar coordenadas relativas. 2.7.2 - SISTEMA DE COORDENADAS PERSONALIZADO Hasta ahora, para definir las posiciones donde si dibujaban las figuras usando las instrucciones de dibujo hemos usado un sistema de coordenadas en el que la coordenada superior derecha de la pantalla

159

(o del marco de visualizacin actual) era la (0, 0) y la de la esquina inferior izquierda dependa del modo de pantalla (poda ser (639, 479) para SCREEN 12 o (319, 199) para SCREEN 13). Este sistema es extremadamente cmodo porque cada unidad representa a un pixel de la pantalla, pero en algunos casos nos puede interesar definirnos otro sistema de coordenadas donde cada unidad represente a ms o manos de un pixel. Supongamos que queremos que cada unidad represente a 64 pixeles en horizontal y a 48 en vertical. Sera algo como dividir la pantalla en una cuadrcula de 10 por 10 cuadros. Si estamos en SCREEN 12 habra que hacer... WINDOW (0, 0)-(10, 10) De esta forma la coordenada INFERIOR IZQUIERDA de nuestra pantalla pasa a llamarse (0,0) y la SUPERIOR DERECHA pasa a ser la (10,10) Es importarse darse cuenta que se ha cambiado superior por inferior, pero no izquierda por derecha. Hecho esto, si ejecutamos la instruccin... PSET (5, 5) ...se dibujar un punto nada ms y nada menos que en el centro de la pantalla, que tiene las coordenadas (5,5) con nuestro nuevo sistema de coordenadas. Si nos liamos con el cambio de arriba por abajo es mejor usar la palabra clave SCREEN a continuacin de WINDOW. De esta forma la primera coordenada corresponde a la esquina superior derecha y la segunda a la inferior izquierda, como hemos hecho hasta ahora. Sera, por ejemplo... WINDOW SCREEN (0, 0)-(10, 10) Para volver a dejar el sistema de coordenadas original de nuestro modo de pantalla bastara con usar la instruccin WINDOW sin nada ms. WINDOW Esto de los sistemas de coordenadas personalizados casi nunca nos ser de utilidad, ya que es ms cmodo trabajar directamente con el original de cada modo de pantalla que va relacionado directamente con el nmero de pxeles. De todas formas nos va a servir para entender mejor el concepto de TWIP que es la unidad que utiliza Visual Basic por defecto para trabajar con las coordenadas de los grficos. De todas formas all tambin es sencillo pasar a trabajar con pxeles directamente y olvidarse tambin de los TWIPS que no son ms que un sistema de coordenadas personalizado definido por el propio Visual Basic. 2.7.3 - COPIAR Y PEGAR. INSTRUCCIONES GET Y PUT En todos los programas de dibujo hay procedimientos para copiar un fragmento de imagen y pegarlo por la pantalla todas las veces que queramos. Nuestro lenguaje de programacin no va a ser menos y nos permite, con algunas limitaciones, capturar con una instruccin

160

un rea rectangular de nuestra pantalla grfica y despus pegarla una o ms veces por la pantalla usando otra instruccin. En Windows, por ejemplo, para todas las operaciones de copiar y pegar se utiliza una zona especial de la memoria llamada portapapeles que es controlada por el sistema operativo y nosotros nicamente nos tenemos que limitar a meter y sacar de all todo lo que queramos. En nuestros programas de QBasic no hay portapapeles ni nada que se le parezca, y por lo tanto somos nosotros los encargados de "construir uno". Para hacerlo tendremos que declarar un vector de tipo entero de tamao suficiente para almacenar temporalmente los fragmentos de imgenes que queremos copiar y pegar. La instruccin que usaremos para "copiar" un trozo de pantalla es esta... GET (h1, v1)-(h2, v2), nombreVector Esta instruccin tiene el mismo nombre que la utilizada para sacar informacin de los ficheros de registros de acceso directo o aleatorio, pero al usarla con esta sintaxis, ya QBasic se da cuenta que es para trabajar con grficos. Como se puede ver, tiene una sintaxis muy parecida a la instruccin LINE. Lo que hace es coger el trozo de pantalla delimitado por el recuadro que va desde la coordenada (h1, v1) a (h2, v2) y almacenarlo en el vector que le pasemos, que deberemos haber declarado previamente y disponer de tamao suficiente. El contenido de la pantalla no sufrir ningn cambio. Esto parece muy sencillo, pero hay varias cosas que hay que tener en cuenta. Lo ms importante es que el tamao del vector debe ser suficiente. Para calcularlo exactamente se puede usar la frmula que viene en la ayuda en pantalla de QBasic en el tema "Matrices para imgenes de pantalla y compatibilidad". Recordemos que en la ayuda de QBasic llaman matrices a todos los arrays, ya sean de una, dos o ms dimensiones. En nuestro caso lo que nos interesa es un vector de enteros con una sola dimensin. Como esta frmula est muy liada podemos hacernos una idea aproximada del tamao del vector sabiendo que si en el modo de pantalla 13 hay 256 colores, cada pixel ocupa un byte. Bastar con calcular el rea del cuadro que vamos a copiar (altura por anchura) y aadirle siempre unos 256 bytes ms. En el modo 12, como slo hay 16 colores, cada pixel ocupa en memoria 4 bits, que es un nibble o medio byte y por lo tanto el tamao requerido para el vector ser la mitad que en el caso anterior, siempre aadiremos algo ms. Con un ejemplo que haremos ms adelante esto quedar ms claro. Ya hemos visto como "copiar" la imagen a "nuestro portapapeles". Ahora vamos a ver como "pegar" en la pantalla lo que acabamos de copiar. Habr que utilizar otra instruccin distinta, que es esta... PUT (h, v), nombreVector, operacionPegado Esta instruccin , que tambin se llama como la de escribir en ficheros pero que nunca se va a confundir, lo que hace es dibujar en

161

la pantalla, a partir de la posicin (h, v) hacia la derecha y hacia abajo, el fragmento de imagen que hayamos almacenado previamente en el vector con una instruccin GET. Lo de operacinPegado se refiere a una palabra clave que habr que poner al final para decirle a QBasic como tiene que dibujar los puntos que componen nuestra imagen. Si esta palabra es AND, OR o XOR, los puntos de la imagen que estamos pegandose combinarn con lo que ya hubiera dibujado en esa parte de la pantalla segn estas operaciones lgicas. Si la operacinPegado es PSET los puntos se dibujarn de forma normal borrando en todo caso lo que hubiera detrs. Si usamos PRESET los puntos se dibujarn borrando lo que hubiera detrs, pero en este caso en vdeo inverso. Lo normal es utilizar siempre PSET. Si no lo ponemos, QBasic creer que estamos queriendo utilizar XOR, cosa que nos puede confundir. Vamos con un ejemplo de como conseguir que un trozo de imagen que dibujemos en la pantalla en modo grfico, usando por ejemplo la orden DRAW, se pueda copiar por toda la pantalla sin tener que repetir la correspondiente orden DRAW en cada caso.

'declaramos un vector DIM portapapeles(1 TO 400) AS INTEGER ' activamos la pantalla grfica y dibujamos algo SCREEN 12 CIRCLE (10, 10), 8, 14 DRAW "c9bm4,10e6d3r6d6l6d3h6" 'capturamos lo que haya dentro de un recuadro de 20x20 pixels GET (0, 0)-STEP(19, 19), portapapeles 'pegamos lo capturado tantas veces como queramos con PUT FOR h = 0 TO 620 STEP 20 FOR v = 0 TO 460 STEP 20 PUT (h, v), portapapeles, PSET NEXT NEXT 'y ya est Como se puede ver, el ejemplo se explica por s solo, pero de todas formas vamos a verlo con un poco ms de detalle y vamos a comentar algunas cosillas. El resultado de ejecutar este programa sera que dibujamos en la esquina superior izquierda de la pantalla una circunferencia amarilla y dentro una flecha azul. Esto lo copiamos con la orden GET y lo

162

almacenamos en un vector llamado portapapeles que hemos declarado previamente con un tamao de 400, suficiente, ya que la imagen que vamos a almacenar tiene un tamao de 20x20puntos y cada punto ocupa en este modo de pantalla 4 bits. Despus con PUT pegamos la imagen desde dentro de un bucle para que rellene toda la pantalla. Usamos la operacin PSET para que no se combine con el dibujo original que sigue estando en la esquina superior izquierda. En este caso justo despus de GET hemos usado PUT. Pero esto no tiene porqu ser as. Entre medio podemos borrar la pantalla, dibujar otras cosas o hacer lo que queramos, siempre que no modifiquemos los valores almacenados en nuestro vector. Incluso se podra cambiar de modo de pantalla y despus volver al original. Hay que tener en cuenta que no podemos copiar algo en un modo de pantalla y despus pegarlo en otro distinto que tenga una resolucin horizontal distinta. Tambin es importante darse cuenta que al dibujar con las instrucciones de dibujo que hemos visto anteriormente nos podamos "salir" de la pantalla sin mayores consecuencias, pero al usar las instrucciones GET y PUT nunca nos podemos salir fuera, ya que se producira un error de tiempo de ejecucin y el programa se detendra. Para finalizar con estas dos instrucciones, decir que su principal limitacin es que el mximo tamao de los vectores que podemos declarar es de 64 KB (usando subndices desde -32768 hasta 32767) y por lo tanto queda limitado el tamao mximo de las imgenes que queremos copiar. De todas formas este tamao mximo seguramente no lo podamos tampoco alcanzar porque las dems variables y el propio QBasic van gastando ms memoria y nos dar un error de memoria agotada si intentamos declarar vectores muy grandes. Estas instrucciones ya dejan de existir en Visual Basic. All, en un entorno totalmente grfico hay una instruccin que nos permitir hacer eso con mucha mayor flexibilidad e incluso leyendo la informacin directamente desde archivos de imgenes normales. En QBasic estas instrucciones tienen utilidad en casos muy sencillos de repetir patrones parecidos al que hemos visto en el ejemplo. 2.7.4 - VER EL COLOR DE UN PIXEL. INSTRUCCIN POINT Para terminar con los grficos vamos a ver una cosa muy sencilla, pero extremadamente til. Se trata de una funcin que nos va a devolver el color que tiene un punto de la pantalla grfica. Esto puede parecer algo que no sirve para mucho, pero como ahora veremos nos va a ser de mucha utilidad para resolver una gran cantidad de problemas relacionados con los grficos de una forma bastante sencilla, aunque poco eficiente. Vamos a lo que vamos, y vamos a ver la sintaxis de esta funcin: POINT (h,v) As de sencillo. Usamos la funcin dentro de una expresin o a la derecha del operador de asignacin y le pasamos como parmetros

163

las coordenadas del punto y nos devuelve el valor de color correspondiente. Una cosa importante es que no podemos usar la palabra clave STEP para indicar coordenadas relativas, por lo tanto algo como... punto = POINT STEP (10, 10) ...estara mal y dara un error detectado incluso por el editor al intentar dar un salto de lnea. Si especificamos una coordenada que est fuera de la pantalla, no pasa nada, simplemente se devuelve el valor -1. La instruccin POINT nos devuelve el valor del atributo de color del punto que especifiquemos, independientemente de que el color que se representa en la pantalla haya sido alterado por alguna instruccin PALETTE. Esto nos permite por ejemplo convertir en negro varios colores para que coincidan con el fondo, dibujar figuras con estos colores "invisibles" y despus detectar su presencia con la instruccin POINT en alguno de los mtodos que se describen a continuacin, especialmente el de mapas de durezas utilizado ampliamente para la programacin de videojuegos. Esta funcin es bastante til en la pantalla de grficos. Una aplicacin interesante puede ser la implementacin de una especie de "pantgrafo virtual" con el que iremos copiando punto por punto un rea de la pantalla a otra posicin con la posibilidad de cambiar el tamao o los colores de la muestras obtenidas. Esto se hace metiendo dentro de dos bucles FOR anidados la instruccin POINT y una instruccin PSET para dibujar los puntos del mismo color en otra parte de la pantalla. Con este ejemplo vamos a copiar un trozo de la pantalla a otra posicin 200 pixeles ms a la derecha y con el doble de tamao. FOR h = 0 TO 49 FOR v = 0 TO 49 c=POINT(h, v) PSET(h * 2) + 200, v * 2), c NEXT NEXT Este mtodo es muy lento, pero para reas pequeas nos da grandes posibilidades cambiando todos o alguno de los los colores, no dibujando alguno o haciendo otras operaciones matemticas. Tambin en este ejemplo apareceran puntos separados en la nueva ubicacin. Podemos arreglar esto usando instrucciones LINE, circunferencias, rectas que vengan de un punto, botones dibujados con varias LINE, etc. Los resultados pueden ser bastante vistosos. Estos son algunos ejemplos de los resultados que se pueden obtener usando esta tcnica.

164

Otra aplicacin ampliamente utilizada de la instruccin POINT en los videojuegos es lo que se conoce como deteccin anticipada de color o mapas de durezas. Esto sirve para que por ejemplo en un juego de laberintos no podamos meter a nuestro personaje a travs de las paredes. Si no lo hiciramos as tendramos que desarrollar una sofisticada estructura de datos con matrices o algn otro algoritmo que puede que en proyectos pequeos no merezca la pena. De esta forma slo hay que saber que si el fondo por el que se va a mover nuestro personaje es siempre negro, por ejemplo, a todas las partes de la pantalla que sean de otro color no va a poder entrar. Esto se evita comprobando el color de la nueva posicin antes de desplazarnos a ella. Los mapas de durezas son una representacin de la pantalla a menor resolucin que el original y de un color que ha sido convertido por la instruccin PALETTE en invisible al coincidir con el fondo. Se usan normalmente en videojuegos sencillos de plataformas para saber exactamente por que parte de los escenarios se puede mover el personaje, donde estn las plataformas, etc... trasladando las coordenadas reales de la pantalla a las del mapa de dureza para comprobar los colores sin interferir con los detalles del escenario original.

165

Se pueden ver ejemplos de utilizacin de estas tcnicas de programacin y de algunas otras parecidas en la seccin de videojuegos terminados de esta pgina web. Otra aplicacin menos til de la funcin POINT es que nos devuelva la posicin actual del cursor de grficos. Como una funcin slo nos puede devolver un valor, le tenemos que decir lo que queremos que nos devuelva, con estos nmeros. POINT(0) Devuelve el valor X (horizontal) de la coordenada actual en la pantalla o marco de visualizacin activo (Medida en pxeles). POINT(1) Devuelve el valor Y (vertical) de la coordenada actual en la pantalla o marco de visualizacin activo (Medida en pxeles). POINT(2) Devuelve el valor X (horizontal) de la coordenada lgica actual de nuestro sistema de coordenadas personalizado si lo tenemos definido. POINT(3) Devuelve el valor Y (vertical) de la coordenada lgica actual de nuestro sistema de coordenadas personalizado si lo tenemos definido. Si no tenemos definido un sistema de coordenadas personalizado (lo ms normal), POINT(0) devolver lo mismo que POINT(2) y POINT(1) lo mismo que POINT(3) ya que siempre estamos contando en pixeles.

166

2.7.5 - ESCRIBIR TEXTO EN MODO GRFICO En la pantalla grfica tambin tenemos la posibilidad de escribir textos usando la instruccin PRINT de la misma forma que hacamos en la pantalla normal en modo texto, pero nos vamos a encontrar con una serie de inconvenientes que seguramente nos van a hacer de desistir en la mayora de los casos. Vamos a ver cuales son estos inconvenientes y la forma de evitarlos en la medida de lo posible. Las pantalla de texto a la que estamos acostumbrada es extremadamente rpida, pero en la pantalla de grficos todo es ms lento. Puede ocurrir incluso que veamos aparecer nuestro texto lnea por lnea como en las pelculas si nuestro ordenador tiene en este momento una carga de trabajo ms alta de lo normal por culpa de otros programas que est ejecutando Windows en ese momento. En las instrucciones de dibujo ya nos hemos acostumbrado a dibujar nuestras figuras exactamente en la posicin de pixel que queramos, pero con la instruccin PRINT nos tenemos que acomodar a la lnea y columna ms prxima al sitio que queramos usando instrucciones LOCATE y puede que esto no siempre nos venga bien para encajar las letras en el sitio justo. Tendremos que adecuar nuestros grficos al texto y no al contrario. En la pantalla en modo SCREEN 12 tenemos 80 columnas y 30 lneas para escribir. Los caracteres se dibujan con un tamao algo ms pequeo al del modo texto y con una definicin aceptable. Een modo SCREEN 13 tenemos 40 columnas y 25 lneas. Los textos escritos aqu tendrn un aspecto parecido al del teletexto, con unos caracteres de doble ancho. Como este modo de pantalla es de baja resolucin los caracteres aparecern muy pixelados. Si llegamos a escribir en las lneas ms bajas de la pantalla sin usar un punto y coma al final de las instrucciones PRINT, se producir el desplazamiento automtico arrastrando hacia arriba el texto y tambin nuestros grficos. Como color para las letras podemos usar cualquiera de los que nos permite nuestro modo de pantalla (16 o 256), pero el fondo va a ser siempre y obligatoriamente del color 0 (negro). No se permite el uso de colores intermitentes. A diferencia de lo que pudiramos pensar, las letras no se dibujan transparentes sobre lo que ya haya dibujado en la pantalla, sino que siempre van a ir sobre un recuadro negro. Este negro lo podemos cambiar redefiniendo el color 0 con una instruccin PALETTE, pero de todas formas esto es un grave inconveniente si queremos escribir sobre un fondo ya dibujado. Estos inconvenientes hacen que normalmente los rtulos que aparecen en nuestras pantallas en modo grfico no se escriban utilizando instrucciones PRINT. Los lenguajes de programacin ms avanzados incluyen bibliotecas de fuentes y las correspondientes instrucciones de dibujo para escribir utilizndolas, as como funciones que nos permiten averiguar el ancho de los textos y otras cosas. Por

167

norma general estas tipografas no incluyen las letras acentuadas ni la ee ni el smbolo del Euro. En QBasic vamos a tener que ser nosotros mismos los que nos construyamos nuestras instrucciones personalizadas para escribir en modo grfico. En todo caso nos tendremos que molestar en dibujar todos y cada uno de los smbolos que vamos a querer escribir (Letras maysculas y minsculas, nmeros, signos de puntuacin y matemticos, caracteres especiales, etc...) Hay dos posibilidades: y Caracteres de mapa de bits. Consiste en ir dibujando cada carcter punto por punto usando instrucciones PSET. Esto nos da la posibilidad de dibujar los caracteres usando varios colores para formar por ejemplo un degradado. El inconveniente es que los caracteres podrn cambiar a tamao solamente si el nuevo es un mltiplo del anterior. Esto se consigue en vez de dibujar puntos, dibujando cuadraditos de dos por dos pxeles o tres por tres... A medida que aumentemos de tamao los caracteres perdern resolucin. Tambin hay que tener en cuenta que dibujar las letras punto por punto puede ser un proceso bastante lento. y Caracteres vectoriales escalables. Cada carcter es dibujado por una instruccin DRAW, lo que permite cambiar fcilmente su tamao usando el parmetro "s" dentro de la orden DRAW. Los caracteres as dibujado siempre mantendrn su resolucin aunque normalmente presentarn un aspecto de lneas muy finas porque no es recomendable intentar rellenar reas si las figuras se van a escalar. La definicin de los puntos o trazos que componen cada carcter quedar almacenada en un vector para ser leda cada vez que sea necesario. Para llenar el vector se harn asignaciones directas de literales o bien se obtendr la informacin de un fichero secuencial que leeremos al principio del programa. En la seccin de esta web dedicada a programas grficos y videojuegos se pueden ver ejemplos de utilizacin de tipografas de ambos tipos.

TEMA 2.8 IMPRESIN


Todo programa de gestin que se precie debe ser capaz de generar listados o informes y poderlos sacar por la impresora. Esta operacin en QBasic es sencilla. Basta con enviar lo que queremos imprimir lnea por lnea a la impresora con la instruccin LPRINT de la misma forma que hemos venido usando la instruccin PRINT. Vamos con un ejemplo. LPRINT "PRUEBA DE IMPRESIN DESDE QBASIC" LPRINT "====================================" LPRINT "Hoy es "; DATE$; " y son las "; TIME$ LPRINT "La raz de 256 es"; SQR(256) El resultado sera que obtendramos por impresora un papel con algo parecido a lo siguiente:

168

PRUEBA DE IMPRESIN DESDE QBASIC ==================================== Hoy es 08/02/ 2011 y son las 16:42:05 La raz de 256 es 16 Si queremos imprimir en posiciones especficas, como por ejemplo para rellenar los casilleros de un recibo, tendremos que ajustar las posiciones imprimiendo lneas en blanco (Usando LPRINT sin argumentos) y metiendo espacios entre las palabras. Lo importante es recordar que siempre se empieza en la parte superior izquierda del papel y se avanza lnea por lnea hacia abajo. No podemos saltar hacia atrs ya que el papel nunca corre para atrs dentro de la impresora. Hasta aqu todo muy bonito, pero ahora vienen los problemas. El primer problema es que en este proceso se pueden dar una gran variedad de errores que hagan que nuestro programa quede detenido, por lo que habr que hacer un control de errores similar al que hacemos en los procesos de manejo de ficheros. Los errores que pueden ocurrir pueden ir desde que ni siquiera el usuario tenga impresora, que esta est apagada o desconectada del ordenador, que no tenga papel o que haya algn fallo durante la transmisin de los datos. Estos errores vienen identificados cada uno con su cdigo y se manejan con una estructura similar a la que vimos para el caso de los ficheros. Algunos de los cdigos de error ms comunes relacionados con la impresin son los siguientes:
CDIGO DE ERROR 24 25 57 68 69 70 DESCRIPCIN Lmite de tiempo en dispositivo Fallo en dispositivo Error en dispositivo de entrada/salida Dispositivo no disponible Desbordamiento del bfer de comunicaciones Permiso denegado

El trabajo con las impresoras es extremadamente complicado debido a que existen gran cantidad de controladores de impresin distintos, por lo que puede que aunque nuestro programa funcione bien y no se produzca ningn error, el trabajo no se llegue a imprimir por culpa de conflictos con el controlador o con el sistema operativo. QBasic en particular y los programas para MS-DOS en general tienen muchas limitaciones para imprimir. En nuestro caso solo

169

imprimiremos a travs de la impresora que est conectada al primer puerto paralelo de nuestro ordenador (LPT1), por lo tanto si tenemos una impresora USB o bien una conectada a otro equipo de la red local, en principio ya no podemos imprimir desde QBasic. Se podra conseguir instalando una serie de controladores y programas especiales que hagan operaciones como redireccionar los puertos, cosa que puede que no merezca la pena si solo usamos QBasic para aprender a programar. Otro problema es la tipografa y el tamao de letra a utilizar. En todo caso se usar la que tenga por defecto la impresora que ser normalmente algn tipo Courier de ancho fijo y con un tamao como para que quepan 80 caracteres por lnea. En las impresoras antiguas se puede cambiar este tipo de letra tocando determinados botones o moviendo algunos conmutadores internos. Nuestro programa podra llegar a cambiar esto, as como usar negritas, cursivas y pocos efectos mas insertando determinados cdigos de caracteres especiales entre las palabras de nuestras instrucciones LPRINT, pero esto sera especfico para cada modelo de impresora. Estos cdigos de control o secuencias de escape vendrn detallados en el manual de la impresora (o no), pero no conviene usarlos a no ser que estemos haciendo un programa especfico para ella, ya que con toda seguridad no van a funcionar en otra impresora de otro tipo. Si la impresin de textos es complicada, la impresin de grficos ya es poco menos que imposible. Siguiendo detalladamente las instrucciones que vengan en el manual de la impresora, si es que vienen, el proceso consistira bsicamente en tratar a la impresora como si fuera un fichero de acceso directo, abrindola con una orden de tipo... OPEN LPT1 FOR OUTPUT AS #1 Y escribiendo en dicho fichero los cdigos de control adecuados junto con la informacin de los pixels que forman la imagen en formato binario, vamos, un lo muy grande. Y encima todo en blanco y negro. Visto esto se puede comprobar que imprimir desde QBasic es bastante entretenido, y todo para conseguir unos resultados muy pobres y poco innovadores a la hora de aprender a programar (Es igual que usar instrucciones PRINT). Tambin hay que tener en cuenta que cada prueba que hagamos conlleva el gasto de una hoja de papel y de tinta, mientras que en la pantalla podemos escribir todas las veces que queramos de forma totalmente gratuita. Si a esto aadimos que al usar QBasic en Windows con las actuales impresoras USB se ha perdido completamente la compatibilidad y que en Visual Basic y otros lenguajes modernos el proceso de impresin es bastante distinto comprendemos fcilmente que no nos merece la pena preocuparnos demasiado de aprender a imprimir desde los programas de QBasic.

170

TEMA 2.9 EFECTOS DE SONIDO Y MSICA


En la poca que apareci QBasic no era muy comn que los ordenadores tuvieran un sistema de sonido como los que hoy conocemos, pero s podemos emitir algunos sonidos a travs del altavoz interno que tienen todos los PCs. La instruccin ms sencilla que tenemos para emitir sonidos es esta... BEEP omo su propio nombre nos sugiere, lo que hace es hacer sonar por el altavoz interno un pitido similar al que se oye cuando encendemos el ordenador. Si estamos ejecutando QBasic desde Windows y tenemos una tarjeta de sonido, entonces la instruccin BEEP lo que hace es generar el sonido predeterminado de Windows a travs de los altavoces multimedia de nuestro equipo. Es importante entender que durante el tiempo que dura el sonido, sea el pitido o el wav de Windows, la ejecucin del programa queda detenida. Otra instruccin un poco ms avanzada para reproducir sonidos es la siguiente... SOUND frecuencia, duracin La frecuencia la tenemos que expresar en hertzios. Se admiten valores entre 37 y 32767. Un valor bajo indica un sonido grave, y uno alto un sonido agudo. Valores aceptables estn entre 300 y 3000 hertzios. Por debajo se oyen mal, y por encima pueden resultar molestos. La duracin viene indicada en 1/18 segundos. Esto quiere decir que si ponemos como duracin 18 el sonido durar un segundo, si ponemos 36 durar 2 segundos, si ponemos 9 durar medio segundo y as con todos los valores. Es importante no poner valores muy altos (No mas de dos o tres segundos) ya que el sonido no parar hasta que no acabe el tiempo indicado, incluso aunque el programa termine o lo detengamos pulsando CONTROL+PAUSA. La instruccin SOUND nos va a servir para dar pitidos. En algunos casos nos puede interesar dar pitidos de mas de un tono con varias instrucciones SOUND o incluso usar bucles para conseguir cosas como esta... 'JMGB - Hecho en Ronda - 1996 FOR v = 1 TO 5 FOR h = 1 TO 5 SOUND (h * 100) + (v * 200) + 100, 2 SOUND (v * 200) + 600, 2 SOUND (h * 200) + 400, 2 NEXT NEXT Si nos atrevemos con la msica, por ejemplo para los videojuegos, QBasic nos provee de un mecanismo para componer melodas, parecido al de los timbres de los telfonos mviles...

171

PLAY "cadenacomandos" Su funcionamiento es similar al de la orden DRAW que vimos en el tema de grficos. Se le pasa una cadena de comandos entre comillas formada por una serie de letras y nmeros que indican a QBasic la msica que tiene que tocar. En la ayuda de QBasic se detallan los comandos que hay disponibles para esta instruccin. Si tienes unos conocimientos mnimos de msica sers capaz de componer algn tipo de sintonas para que suenen en tus programas. Estos son instrucciones PLAY (Cada una ocupa una lnea entera seguida, aunque aqu pueda aparecer cortada). PLAY "MBT180o2P2P8L8GGGL2E-P24P8L8FFFL2D" PLAY "MBo3L8ED+ED+Eo2Bo3DCL2o2A" PLAY "MNT250L8O2DL4F+L8F+F+EF+L4G.F+L8F+L4EL8EEDEL4F+. DL8DL4F+L8F+F+EF+L4G. BL8BL4AL8AL4GL8EL2Dmn" En nuestros programas no conviene abusar de la msica y de los sonidos. Hay que tener en cuenta que en los sistemas de sonido modernos el usuario tiene la posibilidad de ajustar el volumen de los altavoces o incluso de apagarlos, pero al usar el altavoz interno del PC el usuario no tiene ms remedio que escuchar todos los sonidos que produzca nuestro programa. Normalmente se usan pitidos cortos para reaccionar ante ciertas acciones. Si nuestro programa hace un uso mas intensivo de pitidos y msica sera conveniente dotarlo de un mecanismo de forma que el usuario pueda desactivarlos, por ejemplo usando variables globales y condiciones antes de intentar hacer algn sonido... INPUT "Quieres or los sonidos? - (S/N):"; sonido$ ' '...resto del programa... ' IF sonido$ = "S" THEN SOUND 100,5 En todo caso el uso de los sonidos tendra que ser solo en momentos puntuales. Al pasar a otros entornos de programacin ms avanzados ya tendremos la posibilidad de aprovechar todas las posibilidades de la tarjeta de sonido.

TEMA 3.1 REFERENCIA RPIDA DEL LENGUAJE DE QBASIC


Tipos de datos
NOMBRE SUFIJO DESCRIPCIN MNIMO Y MXIMO

STRING

Cadenas de longitud variable (Texto con cualquier carcter)

0 a 32767 caracteres

172

INTEGER

Enteros (Nmeros enteros, sin decimales) Enteros largos (Nmeros enteros ms grandes) Decimales de precisin sencilla (Nmeros con decimales) Decimales de doble precisin (Nmeros con ms decimales)

-32768 a 32767

LONG

&

2.147.483.648 a 2.147.483.647

SINGLE

-2,8x1045 a 2,8x1045

DOUBLE

-4,9x10324 a 4,9x10324

Operadores aritmticos
OPERADOR + * / NOMBRE Suma Resta Multiplicacin Divisin Resto de divisin (Mdulo) Potencias

MOD

Operadores lgicos
OPERADOR

NOMBRE Operador Y Operador O Operador monario de negacin

AND

OR

NOT

173

XOR

Operador O exclusivo Operador de equivalencia Operador de implicacin Operadores de comparacin


OPERADOR = > < >= <= <> NOMBRE Igual Mayor que Menor que Mayor o igual que Menor o igual que Distinto de

EQV

IMP

Modos de pantalla recomendados


MODO PRESENTACIN TEXTO RESOLUCIN PROFUNDIDAD DE COLOR

SCREEN 0 SCREEN 12 SCREEN 13

Interfaz de texto

80 x 25 caracteres 80 x 30 caracteres 40 x 25 caracteres

16 colores

Interfaz grfico

640 x 480 pixels 320 x 200 pixels

16 colores

Interfaz grfico

256 colores

Paleta 16 de colores para SCREEN 0 y SCREEN 12 (VGA)

Colores disponibles para alternar en SCREEN 0

174

Paleta de 256 colores para SCREEN 13

TEMA 3.2 CDIGOS DE CARACTERES ASCII


Mapa de caracteres bsicos

175

Mapa de caracteres extendidos

TEMA 3.3 USO DE QBASIC CON EL EMULADOR DOSBOX


y y y y y y

3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6

Introduccin a los emuladores y a DOSBox Instalacin del emulador DOSBox Instalacin del entorno grfico D-Fend Configuracin del perfil de QBasic Uso de QBasic en el emulador Aprovechando el emulador

3.3.1 - Introduccin a los emuladores y a DOSBox Un emulador es un programa informtico que nos permite usar aplicaciones de un determinado sistema en otro distinto. Por ejemplo abrir juegos de una videoconsola en un ordenador, usar programas y documentos de Macintosh en Windows, etc... La nica limitacin es que el sistema emulado debe ser de menores prestaciones que el sistema que tenemos fsicamente. Esto de los emuladores se usa principalmente en el mundo de los videojuegos.

176

QBasic se usaba en los aos 1990 en ordenadores Intel x86 con apenas 1MB de memoria RAM, tarjeta grfica y monitor en blanco y negro o con color VGA, sin tarjeta de sonido y bajo el sistema operativo MS-DOS con todas sus limitaciones. Ahora usamos ordenadores con cientos de megas de memoria RAM, tarjeta de sonido, sistema grfico muy avanzado optimizado para 3D y sistemas operativos muy avanzados como Windows XP o Linux. Es posible que QBasic no funcione de la misma forma que lo haca en los ordenadores para los que fue diseado. Normalmente el editor de cdigo y los programas en modo texto no suelen dar grandes problemas, pero cuando entramos en los modos de pantalla grfica ya empiezan los problemas con la pantalla y en programas complejos nos podemos encontrar que funcionan mucho ms rpido o mucho ms lento de como deberan. Si QBasic no funciona bien en los ordenadores modernos no creo que sea ningn grave problema para nadie. Pero en aquella poca haba una gran cantidad de videojuegos hechos para MS-DOS que por motivos comerciales eran de gran calidad. Estos videojuegos ahora no funcionan en los ordenadores actuales y no estamos dispuestos a dejarlos perder. Para eso ha aparecido DOSBox, un programa libre que emula a un ordenador antiguo con sistema operativo MS-DOS en un ordenador moderno. DOSBox est pensado para hacer funcionar videojuegos, pero tambin puede perfectamente con programas de otro tipo como QBasic. La principal ventaja que vamos a encontrar usando QBasic dentro de DOSBox va a ser en los programas grficos. Los podremos ver cmodamente en una ventana de Windows sin cambiar de resolucin, por lo que podremos abrirlos y cerrarlos todas las veces que queramos sin miedo a tener problemas con el monitor.

177

3.3.2 - Instalacin del emulador DOSBox DOSBox es un programa libre que se distribuye bajo la licencia GNU. Es completamente gratuito y para conseguirlo lo nico que hay que hacer es ir a la pgina web del proyecto y descargar la ltima versin disponible para nuestro sistema operativo. Existen versiones para Windows, para las distribuciones de Linux ms conocidas y para otros sistemas operativos como Mac, OS/2 o BeOS. http://DOSBox.sourceforge.net En el caso de Windows, el archivo a descargar ocupa poco ms de un megabyte. Una vez descargado lo abrimos y se iniciar el proceso automtico de instalacin. Aunque viene en ingls es muy sencillo, similar al de otros programas. Se pedir el directorio de instalacin, normalmente asegrate de que se cree dentro de C:\Archivos de programa\ para mayor comodidad.

Una vez instalado, aparecer en el escritorio o en el men de inicio un acceso directo para abrir el emulador. Si entras vers que DOSBox se abre en una ventana en modo texto parecida a la del smbolo del sistema de MS-DOS y que por defecto muestra la unidad Z:\>

178

El uso del emulador sera bsicamente parecido al del sistema operativo MS-DOS, escribir rdenes como CD, MD, DIR, etc... Pero adems hay que hacer otras cosas como montar las unidades de disco igual que si estuviramos en Linux y configurar determinados parmetros de cada juego o programa modificando un archivo de texto. Este proceso ya no es tan sencillo y por lo tanto lo haremos de otra forma, usando un front-end o interface grfico para Windows que har estas tareas mucho ms sencillas. Puedes cerrar la ventana de DOSBox escribiendo exit y pulsando la tecla Enter. A continuacin instalaremos el entorno grfico y nunca tendremos que usar el emulador directamente. 3.3.3 - Instalacin del entorno grfico D-Fend
INFO Desda agosto de 2007 el programa D-Fend ya no se encuentra disponible en su pgina oficial. A la espera de encontrar otro entorno grfico estable y avanzado para DosBox y actualizar este Tutorial, puedes localizarlo en webs de software gratuito e instalarlo como se explica ms abajo.

Para facilitar el uso del emulador DOSBox han aparecido muchos front-ends. Puedes instalar uno o varios simultneamente sin problemas. En este caso usaremos D-Fend que es de los ms conocidos. D-Fend tambin es software libre y se puede descargar gratuitamente desde su pgina de internet. http://members.home.nl/mabus

179

El proceso de instalacin es el normal de cualquier programa. Cuando te pida la carpeta de instalacin asegrate por comodidad que se crea dentro de C:\Archivos de programa\

Una vez instalado, la primera vez que abras D-Fend aparecer un cuadro de dilogo preguntando dnde est instalado DOSBox. Selecciona la carpeta donde lo instalaste anteriormente. 3.3.4 - Configuracin del perfil de QBasic El funcionamiento de D-Fend es muy simple. Lo que haremos es crear un perfil para cada juego o programa que queramos usar. Este perfil contiene informacin sobre la localizacin del programa ejecutable, el tipo de tarjeta de sonido, de monitor, cantidad de memoria que necesita, etc... Todos los perfiles se van acumulando en un ndice en la pantalla principal de D-Fend y de esta forma cada vez que queramos abrir un juego ser suficiente con seleccionarlo en este ndice y nada ms.

180

Lo que tenemos que hacer ahora mismo es crear un perfil para poder abrir QBasic. Existe un asistente paso a paso para ir introduciendo la informacin necesaria. Pulsa el botn que tiene un sombrero de mago, situado el primero a la izquierda de la barra de herramientas de D-Fend. El asistente consiste en una sucesin de cuadros de dilogo que van pidiendo informacin, cada vez que rellenes lo que pide pulsa el botn >> y pasamos al siguiente paso del asistente. y Enter profile information Aqu indicamos la localizacin de QBasic o Profile name: Aqu puedes escribir el nombre que quieras. Ser lo que aparecer en la lista de programas. Por ejemplo QBasic o Close DOSBox after game exit Marca esta casilla para que se cierre el emulador al terminar y que no se quede la ventana abierta en negro. o Game EXE Pulsa el botn Browse y selecciona el archivo QBasic.exe en la carpeta donde tengas el QBasic. o Setup EXE Deja este cuadro en blanco porque QBasic no tiene programa de instalacin. y Enter the amount of cycles and select the videocard Los valores de esta pantalla afectan a la velocidad de funcionamiento del emulador y cual es la tarjeta grfica. o En esta pantalla puedes dejar los valores por defecto. Si algo no va bien ya lo cambiaremos ms adelante.

181

Configure the mounts Aqu tenemos que montar una unidad de disco virtual para que funcione el emulador. Crearemos una unidad C:\ ficticia a partir de una carpeta de nuestro disco duro, donde est instalado QBasic. o Clear all Pulsa este botn para que se borren los puntos de montaje que haya podido crear automticamente el asistente. A continuacin crearemos uno nuevo para QBasic. o Add... Pulsa este botn para aadir un punto de montaje. Se abrir un nuevo cuadro de dilogo para crear la unidad virtual.  Mount type Drive  Select drive: Pulsa el botn browse y selecciona la carpeta donde tienes el QBasic. Debers tener tus archivos .BAS dentro de esta carpeta para poderlos abrir porque todo lo que haya fuera de la carpeta que especifiques aqu no ser visto por el emulador. Si quieres selecciona otra carpeta ms arriba para abarcar ms. Las normas de seguridad recomiendan no asignar la unidad c:\ autntica entera, aunque si quieres puedes hacerlo sin problemas y tendrs acceso a todo el disco duro desde dentro del emulador.  Drive label Etiqueta del volumen. Escribe aqu lo que quieras o djalo en blanco.  Enable input/output control Puedes marcar esta casilla  Mounted drive letter Normalmente la letra C  Pulsa OK para cerrar este cuadro de dilogo y seguir con el asistente y Configure SoundBlaster Aqu configuramos la tarjeta de sonido. Como QBasic no la utiliza da igual lo que hagamos. o En esta pantalla puedes dejar los valores por defecto. y Configure GUS Aqu configuramos ms propiedades del sonido. Como QBasic no la utiliza da igual lo que hagamos. o En esta pantalla puedes dejar los valores por defecto. y Configure Midi Aqu configuramos la msica MIDI. Como QBasic no la utiliza da igual lo que hagamos. o En esta pantalla puedes dejar los valores por defecto. y Configure PC Speaker Aqu configuramos el sonido a travs del altavoz interno del ordenador que s utiliza QBasic. o Enable PC Speaker Asegrate de que esta casilla est marcada o PC Speaker Rate Deja el valor que tiene, 22.050 o Tandy Speaker Rate Deja el valor que tiene, 22.050 o Enable disney sounds source Puedes desmarcarla y Memory Cantidad de memoria del ordenador emulado o Memory (MB) Puedes bajar este valor a 4 MB o menos. Ningn ordenador de la poca tena 16 megas de RAM o XMS y EMS Puedes desmarcar estas casillas, QBasic solo usa memoria convencional.
y

182

Pulsa el botn Finish y se habr creado el nuevo perfil para QBasic en el ndice. El asistente no ha pedido nada relacionado con el ratn porque la mayora de los juegos no lo utilizan. Vamos a activarlo para que funcione en QBasic. En el ndice de programas de D-Fend selecciona el perfil que acabamos de crear para QBasic y haz clic con el botn derecho del ratn. En el men contextual selecciona Edit Profile. Aparece un cuadro de dilogo con todas las propiedades del perfil. Estn en distinto orden a como fueron saliendo en el asistente y hay muchas nuevas. En las pestaas de la parte superior selecciona General, y en los controles que aparecen marca la casilla Auto lock mouse.

Hecho esto ya debera de funcionar todo perfectamente. Si algo falla o quieres afinar el rendimiento del emulador, entra en este cuadro de dilogo y cambia los valores de las distintas propiedades del perfil. 3.3.5 - Uso de QBasic en el emulador El funcionamiento de QBasic dentro del emulador es completamente igual a como funcionara en el sistema operativo real. Encontraremos que al iniciar un programa en modo grfico la ventana cambia de tamao pero sigue siendo una ventana en vez de pasar a pantalla completa, motivo principal para estar usando el emulador. Tambin el sonido seguramente se oir a travs de los altavoces del sistema de sonido del ordenador en vez de por el altavoz integrado en la torre. El ratn al principio nos puede despistar. El cursor ser un carcter en modo texto en vez de la flecha tpica de Windows. No se activar hasta que hagamos clic dentro de la ventana de QBasic. A partir de ahora desaparecer la flecha y moveremos el cursor de QBasic. Al haber seleccionado Auto Lock Mouse, el cursor no puede salir de la

183

ventana, por lo que no alcanzaremos a tocar cosas como el men de inicio o las otras ventanas aunque las estemos viendo alrededor en el escritorio. Para "liberar" el cursor puedes pulsar la tecla Windows en el teclado para que se abra el men de inicio, o bien la combinacin de Teclas Alt+Tab para cambiar a otra aplicacin. Para volver a usar el ratn en QBasic haz clic dentro de la ventana. Hacer esto muchas veces puede causar que en QBasic deje de responder el teclado como viene ocurriendo en el sistema operativo real, siempre podrs guardar tu trabajo y salir activando los mens con el ratn. Otro inconveniente del emulador para los juegos donde hay que escribir y por lo tanto para QBasic es que no se utiliza un mapa de caracteres internacional, por lo que algunas letras acentuadas y algunos smbolos pueden variar de la representacin de las teclas del teclado. Por este motivo es ms cmodo hacer las tareas de escritura de cdigo ms largas usando QBasic sin el emulador, y dejar el emulador solo para abrir programas grficos que ya estn terminados o no necesitan escribir mucho cdigo. Para escribir caracteres o smbolos que "no salen" al pulsar su tecla siempre puedes usar la tabla de cdigos ASCII o intentar alguna de estas teclas. SMBOLO \ = & ) TECLA < / = SMBOLO < ^ ( _ TECLA ; & ) ?

3.3.6 - Aprovechando el emulador Una vez que tenemos instalado el emulador DOSBox y el Front end podemos aprovechar para recuperar y usar con seguridad una gran cantidad de juegos antiguos de alta calidad y programas muy conocidos de MS-DOS que aunque ahora ya no sean tiles los podemos ver por curiosidad. Se pueden llegar a hacer cosas como instalar Windows 3.1 para usarlo desde dentro del emulador. Al mismo tiempo D-Fend nos permite mantener una lista muy organizada de todos los juegos que tengamos y de hacer cosas como capturas de pantallas y sonidos o crear accesos directos en el escritorio.

184

La mayora de juegos antiguos han sido cedidos gratis por sus desarrolladores al dominio pblico o bien fueron programados por empresas que ya no existen y tambin pueden ser usados libremente. Infrmate de las condiciones de cada juego o programa que quieras usar. Hay pginas que se dedican a recopilar este tipo de juegos, conocidos como abandonware. Puedes encontrar muchos juegos pero siempre hay que tener en cuenta que su descarga tiene que ser totalmente gratuita y sin obligacin de enviar mensajes SMS ni de descargar o instalar otro tipo de software ni nada.

Vous aimerez peut-être aussi