BASIC es un lenguaje de programación de propósito general que ofrece economía sintáctica
, control de flujo, estructuras sencillas y un buen conjunto de operadores. Es u
n lenguaje que no está especializado en ningún tipo de aplicación. Esto lo hace un len guaje versátil y potente, con un campo de aplicación ilimitado y, sobre todo, se pue de aprender rápidamente. En poco tiempo, un programador puede utilizar la totalida d del lenguaje. La palabra BASIC proviene de la expresión inglesa Beginner's All-p urpose Symbolic Instruction Code: código de instrucciones simbólicas de propósito gene ral para principiantes. El BASIC fue el primer lenguaje de programación desarrolla do. Lo fue a mediados de la década de los sesenta por los profesores John G. Kemen y y Thomas E. Kurtz en el Dartmouth College, en California. Su código se basa en e l vocabulario inglés y en las expresiones matemáticas. Consta de cientos de instrucc iones para utilización de gráficos, procesado de textos, uso de variables, archivos, etc. EL BASIC utiliza un alfabeto formado por los caracteres alfabéticos: A -Z, c ifras 0-9, caracteres especiales como operadores aritméticos: +, -, *, etc., y otr os: (,),$, etc. El lenguaje BASIC es, originalmente, un lenguaje interpretado. E xisten dos tipos de lenguaje: interpretados y compilados. Los interpretados son aquellos que necesitan del código fuente para funcionar (por ejemplo, GW -BASIC y QBasic). Los compilados, convierten el código fuente en un archivo objeto y éste en un archivo ejecutable. Este es el caso del lenguaje C y de las versiones más compl etas y recientes del BASIC. Existen muchas versiones (im plementaciones) del len guaje BASIC. En el esquema siguiente se puede observar la evolución del desarrollo de los diferentes lenguajes de programación y, entre ellos, la de BASIC. AÑO LENGUAJE 1900s BINARIO 1946 Plankalkul 1949 Short Code 1950 ASM (ensamblador) 1951 A-0 19 52 AUTOCODE 1956 FORTRAN 1956 COBOL 1958 ALGOL 58 1960 LISP 1961 FORTRAN IV INVENTOR Bool Konrad Zuse DESCRIPCION primer lenguaje creado para jugar al ajedrez lenguaje traducido a mano lenguaje ensamblador Grace Hopper Alick E. Glennie IBM fue el primer compilador compilador muy rudimentario sistema de TRAducción de FORm ulas matemáticas Compilador IBM Intérprete orientado a la Inteligencia Artificial sistema de TRAducción de FORmulas matemáticas 1961 COBOL 61 Extendido 1960 ALGOL 60 Revisado 1964 PASCAL 1964 BASIC 1965 SNOBO L 1965 APL 1965 COBOL 65 1966 PL/I 1966 FORTRAN 66 1967 SIMULA 67 1968 ALGOL 68 1968 SNOBOL4 1970s GW-BASIC 1970 APL/360 1972 SMALLTALK 1972 C 1974 COBOL 74 197 5 PL /I 1977 FORTRAN 77 1980s SMALLTALK/V 1980 C con clases 1981 PROLOG IBM Digi talk Laboratorios Bell Ministerio Japonés de Comercio Internacional e Industria (M ITI) Ministerio de Defensa de los EE.UU AT&T Bell Laboratories (Bjarne Stroustru p) Lenguaje sencillo sistema de TRAducción de FORmulas matemáticas pequeño y rapido le nguaje con clases Lenguaje estandar para la Inteligencia Artificial Centro de In vestigación de Xerox en Palo Alto Laboratorios Bell pequeño y rapido lenguaje con ti pos antiguo y clásico BASIC IBM sistema de TRAducción de FORmulas matemáticas sólo anota ción Niklaus Wirth Universidad de Dartmouth (california) programación estructurada B eginners All Purpose Symbolic Instruction Code 1982 ADA lenguaje muy seguro 1984 C++ 1985 CLIPPER 1985 QuickBASIC 1.0 1986 QuickBASIC 2.0 1987 QuickBASIC 3. 0 1987 QuickBASIC 4.0 1987 CLIPPER SUMMER '87 1988 QuickBASIC 4.5 1989 QuickBASI C 7.1 1989 ASIC v5.0 1990s VISUAL C++ 1990s VISUAL BASICScript 1990 HTML compilador compilador para bases de datos Microsoft® Microsoft® Microsoft® Microsoft® compilador de BASIC soporte de tarjeta gráfica EGA 43 lineas con la tarjeta EGA ta rjetas Hercules, VGA compilador para bases de datos Microsoft® Microsoft® tarjeta SVGA ultima version de QuickBASIC interprete tipo QBASIC shareware Microsoft® Tim Berners-Lee lenguaje de script para internet 1991 QBasic 1.0 1993 XML 1993 SGML 1993 QBasic 1.1 1990s WML 1990s ASP 1990s PHP 1995 JAVA 1995 CLIPPER 5.01 1995 GNAT ADA95 1995 FORTRAN 95 1991 VISUAL BASIC 1 .0 1992 VISUAL BASIC 2.0 1993 VISUAL BASIC 3.0 1994 VISUAL BASIC 4.0 1995 VISUAL BASIC 5.0 1998 VISUAL BASIC 6.0 1990s C# 2001 VISUAL BASIC .NET Microsoft® C. M. SperbergMcQueen Charles F. Goldfarb Microsoft® para MS-DOS 5.0. Compatible 99.9% con QuickBasic para internet para internet par a MS-DOS 6.0 para internet Microsoft® para internet para internet Sun Microsystems para internet y proposito general compilador para bases de datos Ministerio de Defensa de los EE.UU IBM Microsoft® Microsoft® Microsoft® Microsoft® Micro soft® Microsoft® lenguaje muy seguro sistema de TRAducción de FORmulas matemáticas Microsoft® La evolución de Visual Basic QuickBasic es un lenguaje de programación de alto nivel, que se puede utilizar de forma compilada. Tiene varios predecesores. El primero fue BasicA, desarrollado por Microsoft a principios de los 80. Sus características principales: tenía pocas i nstrucciones era un lenguaje interpretado se requería números identificativos de cad a línea de có digo, haciendo la programación tediosa y limitada los archivos fuente se guardaban en un formato propio El siguiente predecesor de QuickBasic se denomin aba GW -Basic, aparecido a finales de los 80 con algunas mejoras: los archivos f uente se guardaban en u n formato de texto regular, legibles por otros programas la interfaz de programación era ligeramente más amigable GW-Basic no era todavía dema siado adecuado para un uso masivo, de modo que Microsoft en 1987 crea una actual ización de Basic denominada Quic kBasic, con las siguientes ventajas: una interfaz de usuario bastante sencilla una extensa ayuda on-line que incluía índice, e ideas y ejemplos acerca de cómo usar cada instrucción utilidades básicas de edición, como búsque das y sustituciones, cortado y pegado, etc., que aumentaban la productividad no era preciso incluir números de línea Microsoft incluyó con el sistema operativo MS -DOS 5.0 una versión reducida de Quick Basic denominada QBasic 1.0. Esta versión y su actualización posterior, 1.1, se util izaban sólo de forma interpretada pero sus posibilidades eran todavía bastante ampli as. En lo que resta nos referiremos específicamente a la implementación de BASIC den ominada QBasic por su versatilidad y sencillez de uso. La mejor forma de aprender un lenguaje es programando con él. El programa más sencil lo que se puede escribir en BASIC es el siguie nte: Como podemos imaginar, este programa sin una sola instrucción no hace nada, pero s e interpreta correctamente y nos da una idea de que no se necesita mucho para em pezar a programar en BASIC. Esto no es cierto en otros lenguajes como C, en los que ha y que utilizar obligatoriamente encabezados de programa y limitadores de bloques de sentencias. Un programa algo más complicado, pero que hace algo, es el siguiente: ' Este es mi primer programa en BASIC CLS PRINT "Bienvenido a la programación en l enguaj e BASIC" END Con él visualizamos en la pantalla el mensaje: Bienvenido a la programación en lenguaje BASIC En el programa se incluye la línea: CLS Esta línea constituye una sentencia. La sentencia consta únicamente de la instrucción CLS , y ésta produce el resultado de borrar la pantalla. La siguiente sentencia in cluye la instrucción PRINT. Ésta toma como argumento una cadena de caracteres limita dos por dobles comillas " " y la imprime en la salida habitual, que generalmente es la pantalla del PC en el que trabajamos. La instrucción END termina el program a. Ni siquiera es obligatorio finalizar con END, aunque es conveniente por una c uestión de claridad. De cualquier manera, si extraemos esa sentencia, el programa funcionará exactamente igual (el programa finaliza automáticamente cuando no se encu entran más líneas de código). La inclusión de comentarios en un programa es una saludabl e práctica, como lo reconocerá cualquiera que haya tratado de leer un listado hecho por otro programador o por sí mismo, varios meses atrás. Para el intérprete o el compi lador, los comentarios son inexistentes, por lo que no generan líneas de código ejec utable, permitiendo abundar en ellos tanto como se desee. En el lenguaje BASIC se toma como comentario todo carácter que sigue a la c omilla simple: ' o a la palabra clave REM. Como se observa, un programa en QBasi c es simplemente un fichero de caracteres que contiene un conjunto de instruccio nes que un programa especial, el intérprete, se encarga de transformar en un código que la computadora puede ejecutar (aunque no se genera un archivo .EXE). Antes d e continuar hagamos un inciso sobre la nomenclatura utilizada en QBasic para des ignar a distintos elementos del lenguaje: Elemento Sentencia Instrucción (Comando) Palabra clave Keyword Cadena String Número Definición Es una instrucción que realiza una operación. Se utilizan estas palabras de forma intercambiable. Se usa mucho la forma comando debido a su utilización en inglés. Una palabra que forma parte del le nguaje QBasic. Cuando se utiliza una palabra clave en una sentencia, QBasic auto máticamente la convierte a mayúsculas. No se puede utilizar una palabra clave para d ar nombre a una constante o variable (palabra reservada). Una cadena de texto (c aracteres). En Qbasic se delimita con comillas, por ejemplo, "Bienvenido". La ca dena "" (vacía) es válida. Designa a números enteros, decimales de precisión simple y de cimales de precisión doble. Un valor (cadena o número) que no cambia a lo largo de l a ejecución del programa. Hay dos clases de constantes; las constantes literales e stán escritas directamente en el código: "Hola", 16; las constantes simbólicas o con n ombre son valores constantes a los que se les asigna un nombre. Por ejemplo, LON G_MAX en lugar de 16. Las constantes simbólicas se definen usando la palabra clave CONST. Un contenedor con nombre para un número o una cadena. Son el medio para qu e el programa "recuerde" datos. Se pueden crear variables de varias maneras: cal culando un valor, tomando una entrada de usuario, leyendo un archivo, etc. Desig na una operación matemática. Los operadores pueden ser aritméticos, relacionales o lógic os. Devuelve una cadena o un número. Puede tomar uno o más parámetros de entrada para calcular el result ado. No hace nada en el programa. Se utiliza como una nota ex plicativa que contribuye a clarificar el código y a recordar más tarde lo que se hiz o. Designa a un conjunto de líneas dentro de la estructura del programa, que guard an una relación de algún tipo entre sí. Designa a un grupo de líneas que se ejecutan una serie de veces. Constante Variable Operador Función Comentario Bloque Bucle La estructura general de un módulo de código en QBasic consta de un programa princip al y de varios subprogramas y/o funciones: ' Programa principal declaración de funciones y subprogramas declaración de variable s sentencias END SUB Subprograma1 ( ) declaración de variables sentencias END SUB FUNCTION Funcion1 ( ) declaración de var iables sentencias END FUNCTION ... ... ... SUB Subprograman( ) declaración de vari ables sentencias END SUB FUNCTION Funcionn ( ) declaración de variables sentencias END FUNCTION Cuando la envergadura del programa es grande se suele fragmentar el módulo en vari as partes, incluyendo cada parte en un fichero s eparado. Normalmente colocaremo s en cada fichero todas las subrutinas y funciones que se encarguen de una tarea del programa. Por tanto, un programa en QBasic puede estar formado por diferent es módulos o fuentes. Es conveniente mantener los fuentes de un t amaño no muy grand e -para encadenar la ejecución de los diferentes módulos se utiliza la instrucción CHA IN (CHAIN nombre.archivo$-. Introducción Si se deseara imprimir los resultados de multiplicar un número fijo por otro que a dopta valores entre 0 y 9, la forma normal de programar esto sería crear una const ante para el primer número y un par de variables para el segundo y para el resulta do del producto. Una variable no es más que un nombre para identificar una (o vari as) posiciones de memoria donde el programa guarda los distintos valores de una misma entidad . En un programa BASIC es conveniente d efinir todas las variables que se utilizarán antes de comenzar a usarlas, a fin de indicarle al intérprete o c ompilador de que tipo serán y, por tanto, cuánta memoria debe destinar para albergar a cada una de ellas, aunque en QBasic no es obligatorio. Veamos un ejemplo: ' Multiplica dos números enteros DIM multiplicador AS INTEGER un entero DIM result ado AS INTEGER entero multiplicador = 1000 ' defino <multiplicador> como ' defin o <resultado> como un ' asigno valores resultado = 2 * multiplicador PRINT "Resultado = ", resultado END ' muestro el r esultado Así, una variable es un lugar donde se puede almacenar temporalmente un dato. En B ASIC las variables tienen un nombre que las identifica, y sirve para hacer refer encia a ellas. También tienen un tipo, que es el tipo de datos que puede almacenar . El valor de las variables es, como su propio nombre indica, variable. Podemos alterar su valor en cualquier punto del programa. Identificadores, tipos y tamaños Para dar un nombre a una variable tenemos qu e usar un identificador. La longitu d de un identificador puede variar entre uno y varios caracteres, por lo general , 40. En la mayoría de los casos el primer carácter debe ser una letra. A continuación se muestran varios ejemplos de nombres de identificado res correctos e incorrec tos: Correcto cuenta prueba23 Incorrecto 1cuenta prueba* puerto.paralelo puerto_paralelo El lenguaje QBasic no es sensible a mayúsculas y minúsculas (no es case sensitive), de modo que para el intérprete es lo mismo el identific ador cuenta que otro denom inado Cuenta. Los intérpretes y compiladores reservan determinados términos ó palabras claves (keywo rds), para el uso sintáctico del lenguaje, tales como: CLS, PRINT, END, etc. y no se pueden utilizarla en nombres de variables. Para crear una variable en un luga r determinado del un programa escribiremos primero el tipo de variable y luego e l identificador con el que queremos nombrar la variable. A esto se le denomina d efinir una variable. La forma general de la definición es: DIM identificador [AS tipo] DIM identificador[sufijo] Por ejemplo: DIM numero AS INTEGER entero DIM numero% entero DIM frase AS STRING ' crea DIM f rase$ ' crea DIM a!, b# ' crea de precisión simple y doble, respect. DIM a AS SING LE, b AS DOUBLE la variable frase, de tipo cad ena la variable frase, de tipo ca dena dos variables a y b, de tipo número ' crea dos variables a y b, ... ' crea la variable numero, de tipo número ' crea la variable numero, de tipo número En QBasic, las variables no se pueden inic ializar (es decir, establecer un valo r inicial) en el momento de creación. Por ejemplo, es incorrecto: DIM numero% = 0 DIM frase$ = "Hola" ' incorrecto, falla el intérprete ' incorrecto, falla el intérprete La asignación de valores a las variables hay que realizarla posteriormente a la de finición de las mismas. En QBasic, se pueden definir variables en cualquier parte de un programa. Los tipos de datos atómicos definidos por QBasic son: cadenas de c aracteres números enteros números en coma flotante Sufijo Tamaño conjunto de STRING $ caracteres (de 8 bits) INTEGER Tipo Intervalo 0 a 32 767 caracteres Descripción Las variables string son las que almacenan texto. Ejemplos: "¿Qué estás haci endo?", "Alpha Sigma Pi", "323" (esto no es un número porque se delimita con comil las). % 16 bits Las variables enteras almacenan números enteros (sin -32 768 a 32 parte decimal). 767 Ejemplos: 4323, 1, -7543, 30000 (no se utilizan separadores de millares). -2 147 483 648 Almacenan también enteros. a 2 147 483 Ejemplos: 54500, 0, 123456789. 647 3.4 E-38 a 3.4E+38 (aprox. 6 dígitos de precisión) 1.7 E-308 a 1.7 E+308 (aprox . 12 dígitos de precisión) Las variables de precisión simple almacenan números de coma f lotante con 6 dígitos de precisión. Ejemplos: 123.456, 12300000000000, .000000000000 0008. Las variables de precisión doble almacenan números de coma flotante con 12 dígit os de precisión. Ejemplos: 10000000000000000000000000000000000000000000, .00000000 0000000400005. LONG & 32 bits SINGLE ! 32 bits DOUBLE # 64 bits Veamos el siguiente programa de ejemplo acerca del uso de variables: ' Convierte grados Centígrados a Fahrenheit DIM cels!, fahr! cels = 25 fahr = 32 + 9 * fahr / 5 ' Temperatura en º C ' Temperatura en º Fahrenheit PRINT ">>> ";cels; " ºC son ";fahr;" ºF END En él se definen dos variables single, se asigna un valor a la primera y se calcul a la segunda mediante una expresión aritmética. En la instrucción PRINT, el ; indica q ue se concatenen los valores a mostrar. En Qbasic no es obligatorio definir el tipo de datos de una variable antes de ut ilizarla, aunque si es conveniente. De hecho, el programa anterior también funcion a así: ' Convierte grados Centígrados a Fahrenheit cels = 25 ' Temperatura en º C fahr = 32 + 9 * fahr / 5 ' Temperatura en º Fahrenheit PRINT ">>> ";cels;" ºC son ";fahr;" ºF E ND En este caso es el intérprete el que asigna automáticamente el tipo de los datos a l as variables. Esto facilita bastante la tarea de programar, sobre todo a usuario s inexpertos, aunque no es lo más eficiente. La instrucción LET La sentencia LET asigna un valor a una variable. La sintaxis de uso es: [LET] variable = expresión expresión es una expresión del mismo tipo que la variable - número o string -. No se puede poner un valor string en una variable numérica y vic eversa. La palabra clave LET es opcional. En la mayor parte de los usos se presc inde de LET y de deja sólo variable = expresión (es más claro y sencillo). Si se inten ta poner un valor decimal en una variable entera el valor se redondea. Se pueden poner valores enteros en variables Single y Double. Ejemplos: LET micadena$ = "Esto es una prueba." LET resultado% = var1% + var2% - var3% mez cla# = i% + l& + s! + d# Ámbito o alcance de las variables Otra característica de las variables es su alcance. El alcance se refiere a los lu gares de un programa en los que podemos utilizar una determinada variable. Disti nguiremos así dos tipos principales de variables: globales locales Una variable es global cuando es accesible desde todo el programa, y es local cuando solo puede acceder a ella la función que la creó. Variable Global Variable Local ' Muestra un número entero DIM SHARED x1% x1% = 100 Muestra END SUB Muestra PRINT x1% END SUB ' Muestra un número entero DIM x2% x2% = 100 Muestra END SUB Muestra PRINT x2% END SUB En el primer caso el resultado que se obtiene al ejecutar el programa es: 100 mientras que en el segundo es: 0 La palabra clave SHARED consigue que la variable x1 sea global, es decir, a cces ible en todo el código (todos los subprogramas) del módulo. Por el contrario, x2 es local y sólo se conserva su valor en el programa principal, no siendo accesible de sde los subprogramas. Otro ejemplo: Variable Global ' Muestra un númer o entero Valor PRINT x1% END SUB Valor SHARED x1% ' Aquí sólo se es cribe SHARED, ' no DIM SHARED x1% = 100 END SUB Variable Local ' Muestra un número entero Valor PRINT x2% END SUB Valor DIM x2% PRINT x2% END SUB De nuevo, el resultado que se obtiene al ejec utar el programa es en el primer c aso: 100 mientras que en el segundo: 0 Introducción: funciones para E/S de datos Unas de las principales vías de comunicación de un programa con el usuario son la pa ntalla (terminal) y el teclado. La entrada y salida de datos por estos medios se pueden realizar mediante varias instrucciones de QBasic. Las fundamental es son : PRINT INPUT y algunas más avanzadas: PRINT USING LINE INPUT Otras que permiten el control de la salida por pantalla son: LOCATE CLS PRINT: Salida por pantalla Comenzaremos con la instrucción principales de salida de datos: PRINT. La función PR INT escribe texto y/o números en la pantalla (aunque también en un archivo). Su uso en sencillo, sólo hay que añadir detrás de la palabra clave la lista de datos que se d esea visualizar. Por ejemplo: ' Muestra un mensaje PRINT "Hola" PRINT "mundo" END El resultado es: Hola mundo Nótese que PRINT imprime un sal to de línea tras el texto. Se puede inhibir el salto automático de línea si se añade ; tras el texto: ' Muestra un mensaje PRINT "Hola"; PRINT "mundo" END cuyo resultado es: Hola mundo Un ejemplo de uso con valores numéricos y de texto es: ' Muestra valores numéricos a% = 50 b% = 100 PRINT "El valor de a es"; a%; " y el valor de b es"; b% END cuyo resultado es: El valor de a es 50 y el valor de b es 100 Nótese que al concatenar el texto con los números mediante ; se añade automáticamente un espacio en blanco antes de cada valor numérico. Si en lugar de utilizar ; se util iza , para la concatenación: ' Muestra valores numéricos a% = 50 b% = 100 PRINT "El valor de a es", a%; " y el valor de b es", b% END el resultado es: El valor de a es 50 y el valor de b es 100 es decir, produce una separación de 14 espacios. INPUT: lectura del teclado Algo muy usual en un programa es esperar que el usuario introduzca datos por el teclado. Para ello contamos con la i nstrucción INPUT. Un ejemplo: ' Lee un número entero desde el teclado DIM num% PRINT "Introduce un número"; INPUT num% PRINT "Has tecleado el número"; num% END Este programa muestra el mensaje Introduce un número? (nótese la interrogación añadida) y espera a que el usuario introduzca un entero. Si el usuario escribe 23 y pulsa INTRO lo que se observa es: Introduce un número? 23 Has tecleado el número 23 Podemos preguntar por más de una variable a la vez en un solo INPUT: ' Lee varios enteros desde el teclado DIM a%, b%, c% PRINT "Introduce tres números "; INPUT a%, b%, c% PRINT "Has tecleado los números"; a%; b%; c% END De esta forma, cuando el usuario ejecuta el programa debe introducir los tres da tos separados por una coma y finalizar con un retorno de carro. Introduce tres números? 1,2,3 Has tecleado los números 1 2 3 Es muy común escribir un mensaje con PRINT antes de realizar un INPUT, como en los dos ejemplos anteriores. Pero INPUT facilita la labor si se utiliza así: ' Lee un entero desde el teclado DIM num% INPUT "Introduce un número: ", num% PRIN T "Has tecleado el número"; num% END que produce la salida: Introduce un número: 23 Has tecleado el número 23 La utilización de INPUT para introducción de cadenas de texto es similar: ' Lee una cadena de caracteres desde el teclado DIM palabra AS STRING INPUT "Esc ribe una palabra: ", palabra PRINT "He guardado: "; palabra END cuyo resultado es, si se escribe el texto y se pulsa INTRO: Escribe una palabra: Tecnología He guardado: Tecnología Se puede limitar el tamaño de la cadena que se almacena si se especifica la longit ud máxima de dicha cadena en la instrucción DIM: DIM palabra AS STRING*5 cadena de texto caracteres ... ... ' Define palabra como una ' de una longitud máxima de 6 en este caso el resultado es: Escribe una palabra: Tecnología He guardado: Tecno PRINT USING Esta instrucción es similar a PRINT pero es capaz de dar un formato especificado d e salida a los datos. La sintaxis de uso es: PRINT USING formato; l ista_de_expresiones donde formato es una cadena de texto que contiene distintos especificadores que aplican formato a la lista_de_expresiones . Esta lista no es más que un conjunto d e expresiones numéricas o de texto concatenadas con ; o ,. Los especificadores que se pueden utilizar en formato son: Caracteres que dan formato a una expresión numér ica # Posición de dígito. Después del dígito, imprime . Posición de punto decimal. signo para los números , A la izquierda del punto decimal, negativos. imprime una coma cada 3 dígitos. Imprime $ adelante. + Posición del signo de número. espacios adelante con * ^^^^ Imprime con f ormato exponencial. Combina ** y $$. ¦¦ ¦ ¦ $$ ¦ ** ¦ **$ Llena el Caracteres que dan formato a una expresión de texto & Imprime la cadena completa. Imprime los primeros n ! Imprime sólo el primer caráct er caracteres, donde n es el de la cadena. de espacios entre ¦ \\ ¦ ¦ número Caracteres utilizados para imprimir caracteres literales _ Imprime el carácter de formato Cualquier carácter que no siguiente como li teral. en la tabla será impreso como literal. ¦ ¦ ¦ esté Veamos un ejemplo en el que se da formato a expresiones numéricas y de texto: ' Calcula el importe total de un conjunto de productos INPUT "Introduzca el nomb re del producto: ", nombreitem$ INPUT "¿Cuántos elementos?: ", numitems% INPUT "¿Cuál es el importe unitario?: ", importeitem! importetotal! = numitems% * importeitem! PRINT PRINT "Producto Cantidad Importe Importe Total" PRINT "-------- -------- - -------- -------------" PRINT USING "\ \ ##,### ###,###.## #,###,###.##"; nombre item$; numitems%; importeitem!; importetotal! END Este programa produce el resultado: Introduzca el nombre del producto: relé ¿Cuántos elementos?: 30 ¿Cuál es el importe unitar io?: 1.5 Producto Cantidad Importe Importe Total -------- -------- --------- --- ---------relé 30 1.50 45.00 LINE INPUT Esta instrucción es sumamente similar a INPUT, si bien almacena en una sola variab le toda la entrada proporcionada desde teclado hasta encontrar un retorno de car ro y no considera las comas (,) separadores de variables (como sucedía con INPUT). Por ejemplo: ' Lee una cadena de caracteres desde el teclado DIM frase AS STRING LINE INPUT " Escribe una frase: ", frase PRINT "He guardado: "; frase END Si al ejecutar este programa se introduce la primera oración del Quijote se obtien e: Introduce una frase: En un lugar de la Mancha, de cuyo nombre ... He guardado: E n un lugar de la Mancha, de cuyo nombre ... Posicionado del cursor y borrado de pantalla LOCATE: posicionado del cursor Hemos visto que cuando usamos PRINT se escribe en la posición actual del cursor y se mueve éste al final de la cadena que hemos escrito. Pero ¿qué sucede cuando queremo s escribir en una posición determinada de la pantalla?. La solución está en la función L OCATE. Supongamos que queremos escribir 'Hola' en la fila 20, columna 10 de la p antalla: ' Muestra Hola en la fila 20, columna 10 LOCATE 20, 10 PRINT "Hola" END Obsérvese que primero se indica la fila (y) y luego la columna (x). La esquina sup erior izquierda es la posición (1, 1). CLS Ahora ya sólo resta saber cómo se limpia la pantalla. Ello es tan fácil como usar: CLS (clear screen, borrar pantalla). Esta función no sólo borra la pantalla, sino que si túa el cursor en la posición (1, 1), en la esquina superior izquierda. ' Borra pantalla y muestra mensaje CLS PRINT "Hola" END Introducción Los subprogramas y funciones son rutinas, procedimientos o conjuntos de sentenci as que realizan una labor específica. Los subprogramas o subrut inas nacieron de l a necesidad de no repetir innecesariamente un trabajo ya hecho. Pueden invocarse desde el cuerpo del programa principal cuantas veces se desee. Están en el núcleo d e lo que se denomina programación estructurada. En QBasic existen dos clas es de s ubrutinas o procedimientos: los subprogramas propiamente dichos (procedimientos SUB), que realizan un conjunto de tareas y no devuelven ningún valor. las funcione s (procedimientos FUNCTION), que devuelven un valor de un tipo que se puede espe cificar. Los subprogramas y las funciones admiten parámetros (argumentos), que son datos que le pasan al procedimiento las sentencias que la llaman. La sintaxis h abitual en la definición de un subprograma es: SUB identificador [(lista_de_parámetros)] bloque_de_código END SUB donde: - identificador es el nombre del subprograma. Debe ser un identificador valido; - lista_de_parámetros es una lista de variables, separadas por comas, que conforma n los datos que le pasamos al subprograma. - bloque_de_código es un conjunto de sentencias. La lista de parámetros o argumentos es opcional. Podemos escribir como ejemplo: ' Ejemplo de uso de un subprograma hola END ' Subprograma que muestra un mensaje de bienvenida SUB hola PRINT "Hola" END SUB que simplemente es una función que cuando es llamada imprime en pantalla un mensaj e de saludo. Cuando el programa al ejecutarse alcanza el cierre del procedimient o ( END SUB), éste finaliza y devuelve el control al punto del programa que lo lla mó. La sintaxis habitual en la defi nición de una función es: FUNCTION identificador [tipo] [(lista_de_parámetros)] bloque_de_código nombre_de_la_ función = expresión bloque_de_código END FUNCTION donde: - identificador es el nombre de la función. Debe ser un identificador valido; - ti po es un sufijo [%, !, etc] o una expresión AS INTEGER, AS SINGLE, etc. que define el tipo de datos que devlelve la función. - lista_de_parámetros es una lista de vari ables, separadas por comas, que conforman los datos que le pasamos a la función. - nombre_de_la_función = expresión es una sentencia que produce que la función retorne el valor determinado por expresión. - bloque_de_código es un conjunto de sentencias. El tipo es opcional. La lista de argumentos es también opcional. Podemos escribir como ejemplo: ' Ejemplo de uso de una función INPUT "Introduce dos enteros: ", s1%, s2% PRINT "L a suma es: "; Suma%(s1%, s2%) END ' Función que suma dos enteros y devuelve la sum a como otro entero FUNCTION Suma% (s1%, s2%) Suma% = s1% + s2% END FUNCTION Cuando el programa al ejecutarse alcanza el cierre del procedimiento ( END FUNCT ION), éste finaliza y devuelve un valor de tipo entero al programa que lo llamó. Retorno de valores Cuando la función finaliza hemos dicho que se devuelve un valor. Para obligar a la función a retornar un determinado valor se utiliza la sentencia nombre_de_la_función = expresión donde el valor de expresión es el que se asigna a la función. Por ejemplo: FUNCTION lista% lista% = 1 END FUNCTION devuelve el entero 1 cada vez que es llamada. En QBasic podemos devolver cualqui er tipo de datos de los llamados escalares. Los tipos de datos escalares son los tipos numéricos y el tipo string. En QBASIC no se pueden devolver vectores ( arra y). Paso de parámetros a un subprograma Utilizando la lista de argumentos podemos pasar parámetros a una función. En esta li sta se suele colocar un conjunto de identificadores, separados por comas, que re presentan cada uno de ellos a uno de los parámetros de la función. Obsérvese que el or den de los parámetros es importante. Para llamar a la función habrá que colocar los pa rámetros en el orden en que la función los espera. Cada parámetro puede tener un tipo diferente. Para declarar el tipo de los parámetros añadiremos su tipo tras el identi ficador. Así: SUB imprime (numero%, letra$) PRINT numero%, letra$ END SUB es una función que admite dos variables, una entera y otra de tipo cadena. En los lenguajes de programación estructurada hay dos formas de pasar variables a una fun ción: por referencia, o por valor Cuando la variable se pasa por referencia, la fu nción puede acceder a la variable original. Este enfoque es habitual en lenguajes como el BASIC. (En C, sin embargo, todos los parámetros se pasan por valor. La fun ción recibe una copia de los pa rámetros y variables, y no puede acceder a las varia bles originales. Cualquier modificación que efectuemos sobre un parámetro no se refl eja en la variable original. Esto hace que no podamos alterar el valor de la var iable por equivocación.) Por ejemplo, el programa: ' Imprime valores n% = 1 l$ = "A" CLS PRINT n% imprime n%, l$ PRINT n% END SUB imprime (numero%, l etra$) numero% = 2 PRINT numero%, letra$ END SUB da como resultado: 1 2 2 A Es decir, la variable n, que originalmente valía 1, toma el valor 2 tras la llamad a al procedimiento imprime. Declaración y comprobación de tipos Al igual que para las variables, cuando un procedimiento (función o subprograma) s e va a usar en un programa, o cuando un procedimiento se define en otro fichero (procedimiento externo), se debe declarar antes del lugar donde se define. La de claración de un procedimiento consiste en especificar el tipo de datos que va a re tornar, cuando se trata de una función, y el número de argumentos y su tipo. Una dec laración típica de función es: DECLARE {FUNCTION | SUB} nombre_del_procedimiento[sufijo] [([lista_de_parámetros]) ] Esto avisa al intérprete/compilador de que el procedimiento lo vamos a definir des pués. La lista de parámetros con tipo difiere de la lista de argumen tos antes prese ntada en que el tipo de cada argumento se coloca dentro de la lista, tras de su correspondiente identificador, como hacíamos en la definición de variables. Por ejem plo: DECLARE FUNCTION imprime$(numero AS INTEGER, letra AS STRING) DECLARE FUNCTION i mprime$(numero%, letra%) declara una función que devuelve un carácter y tiene dos parámetros, un entero y un ca rácter. NOTA: DECLARE es requerido si se hace una llamada a un procedimiento FUNCTION o a uno SUB sin CALL (véanse los ejemplos). QBasic generara automáticamente instrucciones DE CLARE cuando se guarda un programa. La lista de argumentos permite al intérprete/compilador hacer comprobación de tipos, ya que el tipo y numero de argumentos debe coincidir en la declaración, definición y llamada a una función. Véase, por ejemplo, el programa: DECLARE FUNCTION imprime$ (numero%, char$) "imprime" ' Ejemplo DIM caracter AS S TRING CLS caracter = imprime(100, "a") PRINT "El carácter escrito fue ";caracter E ND ' ' Define una función que imprime un entero y un carácter. Devuelve el mismo carác ter de entrada. ' Declara la función FUNCTION imprime$(numero AS INTEGER, letra AS STRI NG) PRINT numero, letra impri me$ = letra END FUNCTION Ejemplos Subprograma sin argumentos (no devuelve nada) Este programa llama a la función Bor raPantalla que despeja la pantalla mediante la orden CLS (clear screen) y muestr a el mensaje "la pantalla está limpia". Por supuesto, es de nula utilidad pero sir ve para empezar. DECLARE SUB BorraPantalla () ' LLamada a un subprogra ma sin argumentos BorraPan talla ' Llamamos al subprograma ' También podríamos escribir: CALL BorraPantalla END SUB BorraPantalla CLS PRINT "La pantalla está limpia" END SUB Subprograma con argumentos (no devuelve nada) En este ejemplo la función compara t oma dos números, los compara y nos dice cuál es mayor. DECLARE SUB compara (a AS INTEGER, b AS INTEGER) ' Compara números DIM num1 AS INTEGER, num2 AS INTEGER INPUT "Introduzca dos números: ", num1, num2 compara num1, num2 argumentos compara(num1, num2) END SUB compara (a AS INTEGER, b AS I NTEGER) a y b a la función IF a > b THEN PRINT a; " es mayor que "; b ELSE PRINT b; " es mayor que "; a END IF END SUB ' Pasamos los parámetros ' Llamamos a l subprogramas con sus dos ' También podríamos escribir: CALL Función con argumentos (devuelve un valor) Este ejemplo es como el anterior pero d evuelve como resultado el mayor de los dos números. DECLARE FUNCTION compara% (a AS INTEGER, b AS INTEGER) ' Compara números DIM num1 AS INTEGER , num2 AS INTEGER DIM resultado AS INTEGER INPUT "Introduzca dos número s: ", num1, num2 resultado = compara(num1, num2) resultado el valor que devuelve la función ' Almacenamos en PRINT "El mayor de los dos es "; resultado END FUNCTION compara% (a AS INTEGER, b AS INTEGER) parámetros a y b a la función IF a > b THEN compara% = a ELSE compara% = b END IF END FUNCTION ' Pasamos los En este ejemplo podíamos haber hecho también: PRINT "El mayor de los dos es "; compara(num1, num2) De esta forma nos hubiésemos ahorrado tener que definir la variable resultado. Introducción En Qbasic las sentencias se ejecutan sucesivamente una tras otra (secuencialment e). Esto define un camino o dirección según la cual se va desarrollado el programa. Sin embargo, habrá momentos en que el programa deba ejecutar determinadas partes d ependiendo del estado en el que se halle el propio programa o las variables exte rnas. Esto permitir modificar el orden de la ejecución para adaptarse al estado de l programa y bifurcar hacia nuevas subrutinas cuando se cumplan ciertas condicio nes, que el programador fija de antemano. Los mecanismos en QBasic que permiten llevar esto a cabo son: La instrucción IF La primera sentencia de control es la sentencia if. Admite dos tipos de sintaxis : IF expresión THEN sentencia_1 [ELSE sentencia_2] o también: IF expresión THEN [bloque_sentencias_1] [ELSEIF expresion_2 THEN [bloque_sentencia s_2]]... ELSE [bloque_sentencias_n] END IF Esta sentencia es equivalente a la que poseen la mayoría de lenguajes de programac ión y sirve para bifurcar en un punto de programa. Permite al programa tomar dec i siones. En su primera forma, la sentencia_1 sólo se ejecuta si el resultado de eva luar la expresión es verdadero (no cero); en caso contrario, se ejecuta sentencia_ 2. En la segunda forma, tenemos varias posibilidades: si al evaluar la expresión e l resultado es verdadero se ejecutan las instrucciones bloque_ sentencias_1, per o si el resultado es falso se evalúa la expresión_2 y si ésta es verdadera se ejecuta bloque_sentencias_2, y así sucesivamente. En cualquier caso sólo una de los bloques de sentencias s e ejecuta. Veamos algunos ejemplos: IF num% = 1 THEN PRINT "la variable num vale 1" PRINT "la variable num no vale 1 " ELSE Tras evaluarse la expresión IF y ejecutarse la sentencia adecuada, el programa con tinúa con la línea siguiente a la de la última se ntencia del IF. Para la sentencia IF vale como expresión cualquiera válida en QBasic, incluso las llamadas a funciones: IF suma(num1, num2) >= 0 THEN PRINT "La suma de num1 y num2 es positiva" END IF Como sentencias valen cualquier tipo de sentencia válida en QBasic, entre ellas la propia sentencia IF. En este caso hablaremos de sentencias IF anidadas. Por eje mplo: IF num > 0 THEN IF num = 1 THEN PRINT "num es igual a 1" ELSEIF num > 1 THEN PRI NT "num es mayor que 1" ELSE PRINT "num es menor que 1" END IF END IF En este caso, si la variable num es menor o igual que cero el programa no mostra rá ningún mensaje, y en caso contrario se mostrará el mensaje correspondiente a la com paración del valor con la unidad. El bucle WHILE Un bucle es un conjunto de sentencias que se ejecutan repetidamente hasta que se alcanza una condición de fin de bucle, o condición de salida. El bucle while es el tipo de bucle más sencillo. En su modo más simple se escribe: WHILE expresión bloque_sentencias WEND El bucle while comienza por evaluar la expresión. Si es cierta, se ejecuta bloque_ sentencias. Entonces se vuelve a evaluar la expresión. De nuevo, si es verdadera, se vuelve a ejecutar bloque_sentencias. Este proceso continúa hasta que el resulta do de evaluar la expresión es falso. Por esto se le llama a esta expresión la condic ión de salida. Por ejemplo: variable% = 10 WHILE variable% >= 1 PRINT "la variable vale "; variable% variabl e% = variable% -1 WEND En este caso se imprime el valor de la variable hasta que se llega a 1. Normalme nte, en las sentencias del bucle WHILE se coloca alguna instrucción que modifique la expresión de control, como vemos en el ejemplo anterior. El bucle DO - WHILE La sintaxis de este bucle es: DO [{WHILE | UNTIL} expresión] bloque_sentencias LOOP o también: DO bloque_sentencias LOOP [{WHILE | UNTIL} expresión] Su funcionamiento es análogo el del bucle WHILE, salvo que la expresión de control s e puede evaluar al final del bucle (si se utiliza la segunda opc ión de sintaxis). Esto nos garantiza que el bucle DO_WHILE se ejecuta al menos una vez. Incluye t ambién la posibilidad de ejecutar el bucle mientras ( WHILE) expresión sea verdadera o hasta (UNTIL) que expresión sea verdadera. Por ejemplo: n% = 9 DO PRINT "número actual "; n% n% = n% -1 LOOP WHILE n% >= 0 este programa muestra el valor de n hasta que vale 0. Y el siguiente produce el mismo resultado. n% = 9 DO PRINT "número actual "; n% n% = n% -1 LOOP UNTIL n% = -1 El bucle FOR La sintaxis del bucle FOR es: FOR contador = inicio TO fin [STEP incremento] bloque_sentencias NEXT [contador] Este bucle se utiliza para realizar un conjunto de acciones un número determinado de veces.Su versión más sencilla es: FOR i% = 0 TO 10 PRINT "i vale "; i% NEXT i% Esta versión del bucle imprime el valor de i desde 0 con un incremento de una unid ad mientras que esta variable no alcance el valor 10. FOR i% = 1 TO 10 STEP 2 PRINT "i vale "; i% NEXT Y en este ejemplo se muestran los valores 1, 3, 5, 7 y 9 (desde 1 con un increme nto de 2). El bucle FOR es equivalente a un bucle WHILE escrito del siguiente mo do: contador = inicio WHILE contador <= fin bloque_sentencias contador = contador + incremento WEND Este bucle WHILE puede servirnos para salir fácilmente de dudas al escribir un buc le FOR, ya que se ve claramente el orden de ejecución de las expresiones y sentenc ias dentro del bucle FOR. Como se ve, en cada pasada del bucle FOR se sigue el o rden: evaluación del valor del contador, ejecución de bloque_sentencias y e incremen to del contador. Las sentencia EXIT Hay veces en que interesa romper un bucle en una determinada posición, para finali zar su ejecución. Esto suele ser habitual cuando el bucle tiene una gran complicac ión o cuando necesitamos salir "por las malas" de él. Esto último suele ser frecuente cuando en el bucle se producen "condiciones de error". Para este tipo de salto d isponemos de la sentencia EXIT. Su sintaxis es: EXIT {DO | FOR} La sentencia EXIT rompe la ejecución de un bucle o bloque de instrucciones y conti núa en la instrucción que sigue al bucle o bloque. Por ejemplo: a% = 10 DO WHILE 1 PRINT a% IF a% <= 1 THEN EXIT DO a% = a% -1 LOOP Aunque en apariencia este es un bucle sin fin, ya que la condición WHILE 1 es siem pre cierta, este bucle se acaba cuando la variable a valga 1. El bucle simplemen te imprime su valor y decrementa la variable. Otro ejemplo: FOR i% = 1 TO PRINT INPUT IF a$ NEXT 10 i% a$ = "S" THEN EXIT FOR el cual imprime enteros entre 1 y 10 h asta que se introduce por teclado la la p ulsación S. también se utiliza con procedimientos SUB o FUNCTION, para abandonar el procedimie nto antes de ejecutar todas las sentencias que preceden al correspondiente END EXIT {SUB | FUNCTION}. La sentencia de selección múltiple SELECT CASE Esta sentencia sirve para agrupar varias sentencias IF en una sola, en el caso p articular en el que una variable es comparada a diferentes valores, todos ellos constantes, y que realiza acciones si coincide con ellos. Su sint axis es: SELECT CASE expresión_control CASE lista_expresiones_1 bloque_sentencias_1 [CASE l ista_expresiones_2] [bloque_sentencias_2]]... [CASE ELSE [bloque_sentencias_n]] END SELECT Su sintaxis es más complicada que la de anteriores bucles, ya que agrupa un mayor número de acciones y posibilidades en una sola sentencia. El modo de funcionamient o es el siguiente: primero se evalúa la expresión de control. A continuación se compar a con lista_expresiones_1 de la primera etiqueta CASE. Si son iguales, se ejecut a bloque_sentencias_1. Si no, se compara con lista_expresiones_2 , y así sucesivam ente. Se repite el proceso hasta agotar todas las etiquetas case. Si al llegar a la etiqueta ELSE no se ha ejecutado ninguna sentencia ésta es la acción por defect o. La etiqueta ELSE es opcional. Si no la ponemos el programa simplemente salta fuera del SELCT CASE. Vamos a ver un ejemplo de múltiples casos con IF y luego con SELECT CASE: ' Ejemplo de selcción con IF DIM num AS INTEGER INPUT "Introduce un número " , num I F num = 1 THEN PRINT "Es un ELSEIF num = 2 THEN PRINT "Es un ELSEIF num = 3 THEN PRINT "Es un ELSE PRINT "No es END IF END 1" 2" 3" ni 1, ni 2, ni 3" Ahora con SELECT CASE: ' Ejemplo de selección con SELCT CASE DIM num AS INTEGER INPUT "Introduce un número ", num SELECT CASE num CASE 1 PRINT "Es un 1" CASE 2 PRINT "Es un 2" CASE 3 PRINT "Es u n 3" CASE ELSE PRINT "No es ni 1, ni 2, ni 3" END SELECT END Como vemos, el código con SELECT CASE es más cómodo de leer. Esta sentencia tiene además otras posibilidades. Veamos el siguiente ejemplo: ' Otro ejemplo INPUT "Escriba nivel de riesgo aceptable (1 -10): ", Total SELECT CASE Total CASE IS >= 8 PRINT PRINT de valores." CASE 3 TO 7 PRINT PRINT corpor aciones." CASE 1, 2 PRINT PRINT END SELECT END "Riesgo y ganancia máximos." "Seleccione plan de inversiones en bolsa "Riesgo y ganancia de moderados a altos." "Seleccione fondo mutuo o bonos de "Sin riesgo, pocas ganancias." "Seleccione plan de pensión individ ual." En él observamos que en lista_expresiones se pueden utilizar las siguientes formas : expresión[, expresión] ... expresión TO expresión IS operador_relacional expresión Introducción Una posible definición de array sería: "Un conjunto de datos del mismo tipo, identif icados por el mismo nombre, y que se pueden distinguir mediante un núm ero de índice ." Pero ¿qué quiere decir esto y para qué lo queremos?. Pues bien, supongamos que quer emos almacenar la temperatura media de cada hora del día y la temperatura promedio del día. Con lo que sabemos hasta ahora podríamos hacer algo así: ' Declaramos 24 variables, una para cada hora del día DIM temp1!, temp2!, temp3!, temp4!, temp5!, temp6!, temp7!, temp8! DIM temp9!, temp10!, temp11!, temp12!, temp13!, temp14!, temp15!, temp16! DIM te mp17!, temp18!, temp19!, temp20!, temp21!, temp22! , temp23!, temp0! DIM media! ' Asignamos el valor de cada una INPUT "Introduzca las temperaturas desde las 0 hasta las 23 separadas por comas: ", temp0!, temp1!, ... temp23! media = ( temp0 ! + temp1! + temp2! ... + temp23! ) / 24 PRINT "La temperatu ra media es "; medi a! Los puntos ... se utilizan por brevedad en el ejemplo; no constituyen una exp resión válida en QBasic . Observamos que hay que realizar un notable trabajo repetitivo de escritura de códi go. Precisamente es aquí donde son de utilidad lo s arrays. Vamos a repetir el pro grama anterior con un array: DIM temp!(24) DIM media! DIM hora% ' Con esto declaramos las 24 variables ' Ahora damos valor a cada una FOR hora% = 0 TO 23 PRINT "Introduzca temperatura de las"; hora%; "horas: "; INPUT "", temp!(hora%) media! = media! + temp!(hora% ) NEXT hora% media! = media! / 24 PRINT "La temperatura media es "; media! El programa resulta más rápido de escribir y más cómodo para el usuario que el anterior. Como ya hemos comentado, cuando decl aramos una variable lo que estamos haciend o es reservar una zona de la memoria para ella. Cuando declaramos el array de es te ejemplo reservamos espacio en memoria para 24 variables de tipo SINGLE. El ta maño del array (24) lo indicamos entre paréntesis al d efinirlo. Esta es la parte de la definición que dice: Un array es un conjunto de datos del mismo tipo identific ados por el mismo nombre . La parte final de la definición dice: y se distinguen m ediante el índice . En el ejemplo recorremos la matriz mediante u n bucle FOR y va mos dando valores a los distintos elementos de la matriz. Para indicar a qué eleme nto nos referimos usamos un número entre paréntesis (en este caso la variable hora), este número es lo que se llama índice del array. En el ejemplo anterior el primer e lemento de una matriz tiene el índice 0, el segundo tiene el 1, y así sucesivamente. De modo que si queremos dar un valor al elemento 4 (índice 3) haremos: temp!(3) = 20 No hay que confundirse. En la declaración del array el número entre corchetes es el número total de elementos; en cambio, cuando usamos la matriz, el número entre corch etes es el índice. Declaración de un array La forma general de declarar un array es la siguiente: DIM identificador_del_array[sufijo_tipo_dato](dimensión) [AS tipo_de_dato] donde: el sufijo_tipo_dato o tipo_de_dato indican uno de los tipos de datos cono cidos ( %, !, INTEGER, SINGLE , etc). En el ejemplo era SINGLE (!) El identifica dor_del_array es el nombre que le damos (en el ejemplo era temp). La dimensión es el número de elementos que tiene el array. Como se ha indicado antes, al declarar un array reservamos en memoria tantas variables del tipo_de_dato como las indica da en dimensión. también se puede especificar dando los límites superior e inferior de los subíndices del array: dimensión DIM identificador_del_array[sufijo_tipo_dato]( [inferior TO] superior ) En el ejemplo anterior podríamos haber puesto también: DIM temp(0 TO 23) AS SINGLE variables ' Con esto declaramos las 24 Inicialización de un array En QBasic se inicializan los arrays al asignar valores a cada elemento de los mi smos. Es decir, por ejemplo: temp!(9) = 20 temp!(12) = 25 temp!(20) = 23 Ahora el elemento 10, es decir, temp!(9), valdrá 20. El elemento 13 valdrá 25, y el elemento 21 valdrá 23. El resto, si no se asignan, tomarán valor 0 por defecto. Por ejemplo, el programa: DIM temp!(24), hora% temp!(9) = 20 temp!(12) = 25 temp!(20) = 23 FOR hora% = 0 T O 23 PRINT "La temperatura a las";hora%;" era de"; temp!(hora%);"grados." NEXT h ora% dará como resultado: La temperatura a las 0 era de 0 grados. La temperatura a las 1 era de 0 grados. La temperatura La temperatura ... La temperatura ... La temperatura ... La tempe ratura La temperatura La temperatura La temperatura a las 2 era de 0 grados. a las 3 era de 0 grados. a las 9 era de 20 grados. a la s 12 era de 25 grados. a a a a las las las las 20 21 22 23 era era era era de d e de de 23 grados. 0 grados. 0 grados. 0 grados. Vemos que los elementos a los que no se ha asignado valor inici al son nulos Arrays de más de una dimensión En las secciones anteriores veíamos ejemplos de matrices con una sola dimensión (un sólo índice o subíndice). Pero las matrices pueden contar con más dimensiones. La utiliz ación de matrices multidimensionales no es más que la extensión natural de la correspo ndiente a unidimensionales. Supongamos que deseamos controlar mensualmente las t emperaturas registradas durante todo un mes. Podemos definir una matriz que depe nda de dos índices: el día del mes y la hora del día. Por ejemplo: DIM temp!(31,24) DIM dia%, hora% ' Matriz de temperaturas "día,hora" ' Ahora damos valor a cada una FOR dia%=1 TO 31 FOR hora% = 0 TO 23 PRINT "Intro duzca la temperatura del día";dia%;"a las"; hora%; "horas: "; INPUT "", temp!(dia% ,hora%) PRINT "La temperatura introducida es: "; temp!(dia%,hora%) NEXT hora% NE XT dia% Vemos que para dimensionar la matriz no tenemos más que utilizar: DIM matriz(dimension1,dimension2) o también: DIM matriz(inferior_1 TO superior_1, infe rior_2 TO superior_2) Y para acceder al elemento caracterizado por los subíndices i,j de la matriz no ha y más que escribir la expresión matriz(i,j). Esto es generalizable a más dimensiones. Paso de un array a un procedimiento En QBasic podemos pasar un array entero a una función. Lo que tenemos que hacer es poner como argumento de la función el identificador del array seguido de () para que se reconozca como tal. Considérese este ejemplo: DECLARE FUNCTION sumar% (m() AS INTEGER) cuyo argumento es una matriz ' Ejemplo de paso de un array a una función DIM matriz(10) AS INTEGER FOR contador% = 1 TO 1 0 matriz(contador%) = 2*contador% valores a los elementos PRINT contador%, matri z(contador%) NEXT contador% PRINT "La suma es "; sumar(matriz()) END FUNCTION su mar%( m() AS INTEGER ) sin especificar la dimensión suma% = 0 FOR i% = 0 TO 10 sum a% = suma% + m(i%) NEXT i% sumar = suma% END ' Declara la función ' Asigna ' El argumento es el array m() Este programa muestra los 10 elementos de un array y halla su suma. Obsérvese que en el argumento de la función sumar se especifica el array pero sin dimensiones, p ara que tome la dimensión del array utilizado en la llamada al procedimiento en el programa principal. Los archivos o ficheros brindan una forma de guardar permanentemente los datos y resultados de nuestros programas. Es importante indicar que los ficheros no son únicamente los archivos que guardamos en el disco duro. En QBasic algunos disposi tivos del ordenador (como e l puerto serie de comunicaciones) se tratan como fic heros. Lectura de archivos. Operaciones y funciones básicas Para comenzar, vamos a analizar un ejemplo que lee un fichero de texto ( origen. txt) y muestra su contenido en la pantalla: ' Lee un archivo de texto línea a línea DIM linea AS STRING OPEN "origen.txt" FOR IN PUT AS #1 lectura ' Abre el archivo para PRINT "Contenido del f ichero:" DO WHILE NOT EOF(1) ' Comprueba si se ha alcanza do el fin del archivo LINE INPUT #1, linea ' Lee una línea del archivo (hasta RC/L F: salto de línea) PRINT linea LOOP CLOSE #1 END ' Cierra el archivo Analizaremos este ejemplo poco a poc o deteniéndonos en: Apertura del fichero para lectura: OPEN Ahora es preciso abrir el fichero. Para ello usamos la función OPEN. Esta sentenci a tiene el siguiente formato: OPEN nombre_archivo$ [FOR modo] AS [#]nº_archivo% En el ejemplo usábamos: OPEN "origen.txt" FOR INPUT AS #1 El nombre de fichero se puede indicar directamente (como en el ejemplo) o usando una variable y se puede abrir de diversas formas. Esto se especifica con el parám etro modo. Los modos posibles son: INPUT OUTPUT RANDOM APPEND BINARY especifica que el archivo va a ser abierto para leer información de forma secuenci al. especifica que el archivo va a ser abierto para escribir inform ación de forma secuencial. especifica que el archivo va a ser abierto en el modo de acceso ale atorio. RANDOM es el modo de archivo predeterminado. especifica que el archivo v a a ser abierto para añadir información de salida secuencial y coloca el puntero de archivo al final del archivo. Una instrucción PRINT # o WRITE # añade la información a l archivo. especifica el modo de archivo binario. En este modo, es posible leer o escribir información en cualquier posición de byte del archi vo usando instruccion es GET o PUT. es un identificador que permite distinguir los distintos ficheros que se utiliza n, cuando se usan varios simultáneamente. nº_archivo Lectura de caracteres del fichero: LINE INPUT # Ahora ya podemos empezar a leer el fichero. Para ello podemos utilizar la función LINE NPUT #, que lee las líneas de texto del archivo de una en una. Se puede usar tambié n la función INPUT # que lee texto del archivo hasta que encuentra una coma ( ,). El formato de la instrucción INPUT # es: [LINE] INPUT #nº_archivo, variable$ En este caso lo usamos como: LINE INPUT #1, linea donde linea es una variable string. Tomamos una línea de fichero, la almacenamos e n linea y el puntero del fichero se coloca al principio de la línea siguiente. Comprobación de fin de fichero: EOF() Cuando entramos en el bucle WHILE, la lectura se realiza hasta que se encuentre el final del fichero. La detección del final del fichero se puede llevar a cabo co n la función EOF(). Esta función es de la forma: EOF(nº_archivo) Esta función comprueba si se ha llegado al final de nº_archivo, en cuyo caso devuelv e un valor verdadero (no 0). Si no se ha llegado al final de fichero devue lve f also (0). Por eso lo usamos del siguiente modo: DO WHILE NOT EOF(1) o DO UNTIL EOF(1) Cierre del fichero: CLOSE Una vez realizadas todas las operaciones deseadas sobre el fichero hay que cerra rlo. Es importante no olvidar este paso pues el fichero podría corromperse. Al cer rarlo se vacían los buffers y se guarda el fichero en disco. Un fichero se cierra mediante la instrucció n CLOSE. Esta función tiene el siguiente formato: CLOSE [#]nº_archivo Aquí nº_archivo es el identificador del archivo indicado en OPEN cuando el archivo s e abrió: CLOSE #1 CLOSE sin argumentos cierra todos los archivos abiertos. Lectura de caracteres del fichero: INPUT$() Esta función es muy útil para leer caracteres un nº de caracteres determinado del arch ivo. Su formato es: INPUT(nº_caracteres, [#]nº_archivo) Esta función lee desde el fichero nº_archivo el número de bytes especificado por nº_caracteres. Por ejemplo, el siguiente programa muestra todo el contenido del fi chero "origen.txt" byte a byte. ' Lee un archivo de texto carác ter a carácter DIM l etra AS STRING*1 OPEN "origen.txt" FOR INPUT AS #1 lectura PRINT "Contenido del fichero:" DO WHILE NOT EOF(1) ' Comprueba si se ha alcanzado el fin del archivo letra = INPUT$(1,1) ' Lee un carácter del ar chivo PRINT letra; LOOP CLOSE #1 END ' Cierra el archivo ' Abre el archivo para Escritura de archivos. Operaciones y funciones básicas Vamos a completar ahora la parte que faltaba en la sección anterior: escribir en u n fichero. Vamos a hacerlo de nuevo mediante un eje mplo. En éste, abrimos un fich ero origen.txt y lo copiamos en otro destino.txt. Además, el fichero se muestra en pantalla (las partes nuevas se han destacado en negrita). ' Ejemplo de escritura de archivos DIM linea AS STRING OPEN "origen.txt" FOR INP U T AS #1 para lectura OPEN "destino.txt" FOR OUTPUT AS #2 destino para escritur a DO WHILE NOT EOF(1) LINE INPUT #1, linea origen PRINT #2, linea archivo destin o PRINT linea LOOP CLOSE #1, #2 END ' Muestra contenido en pantalla ' Escribe la línea en el ' Abre el archivo origen ' Abre el archivo ' Lee una línea del archivo Como en el caso de la lectura de archivos, analizaremos este ejemplo poco a poco deteniéndonos en: Apertura del fichero para escritura: OPEN El siguiente paso, como antes, es abrir el fichero usando OPEN. La diferencia es que ahora tenemos que abrirlo en modo escritura. Usamos el modo OUTPUT (crea el fichero, o lo vacía si existe) porque queremos crear un fichero: OPEN "destino.txt" FOR OUTPUT AS #2 Escritura de caracteres en el arc hivo: PRINT # Como se puede observar en el ejemplo, la lectura del fichero se hace igual que a ntes, con LINE INPUT #. Para la escritura usamos la función PRINT , cuya sintaxis es: PRINT #nº_archivo, lista_expresioens En este caso lo usamos como: PRINT #2, linea De esta forma vamos escribiendo en el fichero destino.txt el contenido del fiche ro origen.txt. Si hubiésemos escrito la anterior instrucción así: PRINT #2, linea; (nótese el ; final) en el archivo destino.txt las líneas del archivo origen apare ce rían concatenadas (es decir, no se producirían saltos de línea) Comprobación fin de fichero Como siempre que leemos datos de un f ichero debemos comprobar si hemos llegado al final. Sólo debemos comprobar si estamos al final del fichero que leemos. No te nemos que comprobar el final del fichero en el que escribimos puesto que lo esta mos creando y aún no tiene final. Cierre del fichero: CLOSE Y, por fin, lo que nunca debemos olvidar al trabajar con ficheros: cerrarlos. De bemos cerrar tanto los ficheros que leemos como aquellos sobre los que escribimo s. CLOSE #1, #2 Obsérvese que cerramos ambos archivos con una única instrucción. Otras funciones para el manejo de ficheros WRITE Esta sentencia es muy similar a PRINT #, de hecho, la sintaxis es idéntica: WRITE #nº_archivo, lista_expresioens Sin embargo, WRITE inserta comas (,) entre los datos y comillas (") alrededor de cadenas de texto. Para notar la diferencia considérese el sig uiente ejemplo: OPEN "destino.txt" FOR OUTPUT AS #1 PRINT #1, 1, 2, "Tecnología" WRITE #1, 1, 2, " Tecnología" CLOSE #1 Como resultado, en el archivo "destino.txt" se escribirá: 1 2 Tecnología 1,2,"Tecnol ogía" GET y PUT lee información de un archivo colocándola en un buffer de acceso aleatorio o en una variable. PUT escribe una variable o un buffer de acceso aleatorio en un archivo . GET La sintaxis es: GET [#]numarchivo%[,[numregistro&][,variable]] PUT [#]numarchivo%[,[numregistro& ][,variable]] numarchivo% es el número de un archivo abierto. numregistro&: para a rchivos de acceso aleatorio, es el número del regi stro que será leído o escrito. Para archivos binarios la posición de byt e donde se inicia el proceso de leer o escribir. variable Para GET, es una varia ble utilizada para recibir información del archivo. Para PUT, una variable que con tiene la información de salida que ser escrita en el archivo. La variable generalm ente tiene el tipo de datos definido por el usuario. Por ejemplo: TYPE RegistroPrueba ' Define un tipo de datos con un texto de 20 caracteres y un número de precisión simple Alumno AS STRING * 20 Nota AS SINGLE END TYPE DIM MiClase AS RegistroPrueba anterior ' Declara una variable edl tipo OPEN "FINAL.DAT" FOR RANDOM AS #1 LEN = LEN(MiClase) MiClase.Alumno = "Pérez Ruiz" MiClase.Nota = 9 PUT #1, 1, MiClase CLOSE #1 OPEN "FINAL.DAT" FOR RANDOM AS #1 LEN = LEN(MiClase) GET #1, 1, MiClase PRINT "Estudiante:"; MiClase.Alumno PRINT "Nota:"; MiClase.Nota CLOSE #1 Este programa produce la salida: Estudiante: Pérez Ruiz Nota: 9 Posicionamiento en el fichero: LOC y SEEK Supongamos que queremos posicionarnos en un byte concreto de un archivo para lee r o escribir en dicha posición. La función LOC devuelve la posición actual del puntero del archivo identificado por nº_archivo. Su sintaxis es: LOC(nº_archivo%) La instrucción SEEK establece la posición del archivo para la siguiente acción de lect ura o escritura: SEEK [#]nº_archivo%, posición& numarchivo% es el número de un archivo abierto. posición& es la posición donde ocurrirá la siguiente acción de leer o escribir. Para archivos de acceso aleatorio, un número de registro. Para otros arc hivos, la posición de byte con relación al principio del archivo. El primer byte ocu pa la posición 1. OPEN "prueba.txt" FOR RANDOM AS #1 FOR i% = 1 TO 10 PUT #1, , i% NEXT i% SEEK #1 , 2 ' Se posiciona en el segundo registro GET #1, , i% ' Lee el registro donde s e halla posicionado PRINT "Datos: "; i%; " Registro actual : "; LOC(1) Este programa escribe secuencialmente 10 registros con los enteros entre 1 y 10, se posiciona en el segundo registro y lo lee; proporciona la salida : Datos: 2 Registro actual: 2 Introducción Para intercambiar información con dispositivos exteriores, el computador utiliza l os puertos de entrada y salida (E/S). Estos p uertos se caracterizan por la dire cción que tienen asignada en el mapa de memoria, y es posible la comunicación con lo s mismos enviando o recogiendo bytes a/en dicha dirección. Así, nos centraremos ahor a en las funciones de acceso a los puertos hardware que proporciona Qbasic. Al i gual que en determinadas circunstancias es preciso comunicarse con dispositivos externos, también es necesario intercamb iar información con el propio PC. Ello se r ealiza con frecuencia leyendo y escribiendo valores en/a determinadas posiciones de la memoria del mismo. Por ejemplo, podemos conocer el estado en el que se en cuentra el teclado del PC sin más que acceder al valo r almacenado en el segmento 0x40, offset 0x17 de la memoria. Con él podemos discernir si la tecla "bloq mayús" e stá pulsada o no, si la tecla "Alt" está pulsada o no, etc. Asimismo, podemos recoge r información del hardware actualmente instalado en el PC y, entre otras cosas, de terminar en qué direcciones de E/S se hallan los puertos hardware mencionados en e l párrafo anterior. Así existen en QBasic algunas funciones que proporcionan acceso a la memoria. Acceso a los puertos hardware Lectura del puerto Para recoger información procedente de un puerto hardware (lectura del puerto), Qb asic proporciona la funciñon INP. Esta función devuelve un byte leído del puerto ( har dware) de E/S. Esta función se utiliza de la siguiente manera: valor% = INP(puerto_dir) donde: es la dirección (valor entero: 0-65.535) que identifica el puerto hardware de la cual se quiere leer y valor% y es una variable entera en la que se almacen a el byte leído (0 255). puerto_dir En el siguiente ejemplo vamos a obtener un byte del puerto paralelo (bidireccion al ) instalado en el PC (en la dirección de E/S 378h): % ' Lee un byte del puerto paralelo DIM valor AS INTEGER DIM puerto AS INTEGER: puerto = &H378 ' dirección del puerto paralelo valor = INP(puerto) PRINT "El byte leído del puerto "; puerto; " es "; va lor END Escritura en el puerto Para enviar información a un puerto hardware ( escritura en el puerto), Qbasic pro porciona la función OUT. Esta función envía un byte a un puerto (hardware) de E/S. La sintaxis de la función es: OUT puerto_dir%, valor% donde: es la dirección del puerto hardware en la cual se quiere escribir (un enter o: 0-65.535); valor es el byte (0-255) que se envía al puerto. puerto_dir En el siguiente ejemplo vamos a enviar la letra C al primer puerto paralelo bidi reccional instalado en el PC (en la dirección de E/S 378h): ' Envía un carácter al puerto paralelo DIM valor AS INTEGR DIM puerto AS INTEGER: pu erto = &H 378 ' dirección del puerto paralelo valor = ASC("C") letra C (el entero: 67) ' valor contiene el código ASCII de la OUT puerto, valor PRINT "Valor "; valor; " enviado al puerto "; puerto END Acceso a la memoria Lectura de la memoria Para leer la información almacenada en la memoria, QBasic proporciona la función PEE K. Ésta devuelve el valor de byte almacenado en la posición d e memoria designada po r segmento:offset (direccionamiento segmentado de la memoria). Esta función se dec laran de la siguiente manera: valor% = PEEK(dirección%) donde: es la posición de memoria relativa a la dirección del segmento actual, establ ecida por DEF SEG (dirección es un entero: 0-65.535) valor es el byte (0-255) que se lee de la memoria. dirección NOTA: DEF SEG establece el segmento de memoria sobre el que se realizará la operac ión de lectura (un valor entre 0 y 65.535). Por ejemplo DEF SEG = 0. Como ejemplo, veamos cómo se determina cuántos puertos paralelo se hallan instalados en un PC, y qué direcciones de E/S tienen asignadas. Para ello, es preciso leer en la posición d e memoria 408h una tabla de 3 palabras de 16 bits (2 bytes) que contienen las di recciones de los puertos paralelo presentes en el sistema: ' Determina los puertos paralelo instalados en el PC y sus direcciones DEF SEG = 0 FOR puerto% = 0 TO 2 1 (LPT2), 2 (LPT3) ' selecciona el segmento de memoria 0 ' número de puerto paralelo: 0 (LPT1), ' dirección del puerto dir% = PEEK(&H408 + puerto% * 2) + 256 * PEEK(&H408 + puert o% * 2 + 1) IF dir% = 0 THEN PRINT "No hay puerto asignado a LPT"; puerto% + 1 E LSE PRINT "La direcci¢n de LPT"; puerto% + 1; " es "; dir% END IF NEXT puerto% END Escritura en la memoria Para almacenar un dato en la posición de memoria designada por segmento:offset, QB asic proporciona la función POKE. Ésta almacena un carácter (1 byte). La sintaxis de l a función es: POKE dirección%, valor% donde: es la posición de memoria r elativa a la dirección del segmento actual, estab lecida por DEF SEG (dirección es un entero: 0-65.535) valor es el byte (0-255) que se desea escribir en la dirección de memoria especificada. dirección Como ejemplo, veamos cómo se fuerza por software la activación de "Bloq Mayús". Para e llo hay que cambiar el bit 6 de la posición 417h, lugar de las variables de la BIO S donde residen los banderines de estado del teclado: ' Cambia el estado de Bloq Mayús DEF SEG = 0 Estado% = PEEK(&H417) ' Lee el estado del tecl ado POKE &H417, (Esta do% XOR &H40) ' Cambia el estado del Bloq Mayús, bit 6 END Trataremos de dar unas breves indicaciones acerca de cuál es la forma más correcta d e programar. Qué aspecto dar al código fuente para que sea más legible y elegante, cómo distribuir el código fuente, etc Aspecto del código Obsérvese el siguiente código: DIM cadena AS STRING, i AS INTEGER PRINT "Introduce una cadena:"; : INPUT "", ca dena$: FOR i%=1 TO LEN(cadena) PRINT MID$(cadena, i%, 1) NEXT i% END Ahora compárese con este otro: ' Muestra uno a uno los caracteres de una cadena DIM cadena AS STRING, i AS INTE GERR PRINT "Introduce una cadena:"; INPUT "", cadena FOR i=1 TO LEN(cadena) PRIN T MID$(cadena, i, 1) NEXT i END Parece claro que la segunda es más conveniente. Desde luego no es la única forma, ni la más correcta; es, simplemente, un estilo. Cada uno tiene su esti lo personal, aunque conviene que tampoco sea 'muy personal', si es para compartirlo. El tabul ar la escritura del código lo hace más legible y fácil de mantener. Los comentarios El código fuente debe estar bien documentado, de tal forma que si se lo pa samos a otra persona pueda entenderlo sin grandes dificultades. Quizá se piense: "mi código es mío y nunca se lo voy a dejar a nadie, así que para que voy a comentarlo". Parec e lógico, pero imagínese que se abandona temporalmente y en el futuro se quiere r et ocar el programa. Es muy probable el olvido de por qué se hizo tal o cual cosa, (p asa muy a menudo) y es preciso gastar un montón de tiempo descifrando algo que en su día estaba entendido perfectamente. Aquí está un ejemplo de un programa mal comenta do: ' Empiezo el programa DIM cadena AS STRING, i AS INTEGER llamada cadena y otra l lamada i ' declaro una variable PRINT "Introduce una cadena: " ' Imprimo el mensaje 'Introduce una cadena' INPUT "", cadena ' Pregunto por el valor de cadena FOR i=1 TO LEN(cadena) ' Hago un b ucle repetitivo PRINT MID$(cadena, i, 1) NEXT i ' Cierro el bucle END ' Termino el programa Estos comentarios están bien si el código forma parte de algún curso, pero no de un pr ograma de uso habitual. Todo el mundo (q ue lleva un poco de tiempo programando en BASIC) sabe que con FOR se realiza un bucle repetitivo y que con END se final iza el programa. Los comentarios deben decirnos qué es lo que hace el programa, pa ra qué sirven las variables y explicar las partes críti cas o confusas del programas . Algo mas correcto sería: ' EJEMPLO.BAS ' Programa ejemplo creado para el curso de BASIC (QBasic 1.1) ' Víct or R. González, 2/02/2002 DIM cadena AS STRING, i as INTEGER almacenará el texto int roducido de caracteres de <cadena> PRINT "Introduce una cadena: " INPUT "", cade na FOR i=1 TO LEN(cadena) hasta la longitud de la cadena PRINT MID$(cadena, i, 1 ) NEXT i END ' Muestra cada carácter ' En <cadena> se ' <i> será el contador Este programa es tan sencillo que no tiene puntos oscuros para comentar. Es impo rtante indicar para qué se van a usar las variables. Cuando tengamos muchas y pase el tiempo es difícil descifrar para que era cada una. Los nombres de las variables Cuando hacemos un programa con prisas o por no pensar mucho damos a las variable s cualquier nombre. Supongamos un programa con una sección de declaración de variabl es como la que sigue: DIM i%, j%, k% DIM x1#, x2#, x3#, x4# DIM a$, b$ ' ... muchas más variables ¿Cómo podemos acordarnos al cabo de un tiempo de qué era cada variable?. Por eso es co nveniente que aparte de indicar para qué sirve cada variable les demos nombres des criptivos: DIM cuenta.x%, cuenta.y%, cuenta.z% z DIM x.minimo#, x.maximo# DIM y.minimo#, y. maximo# rectángulo DIM tecla.inicio$, tecla.fin$ comienzo y fin ' ... etc ' Esquin as del ' Pulsaciones de ' Nº de posiciones x, y, 2002 Víctor R. González