Documentos de Académico
Documentos de Profesional
Documentos de Cultura
ENTREGA NMERO 1
Vladimir Rodrguez
Vladimir Rodrguez
PRIMERA PARTE
Vladimir Rodrguez
Vladimir Rodrguez
Vladimir Rodrguez
Presionar en Next.
Seleccionar un directorio de instalacin. Es recomendable aceptar el directorio que aparece por defecto (C:\FPC\2.x.x). Apretar el botn Next.
En la siguiente ventana, se seleccionan los componentes del compilador que van a ser instalados. Se recomienda seleccionar Full Instalation. En caso de que dispongan de poco espacio en su disco duro, pueden seleccionar Minimal Instalation
Vladimir Rodrguez
Asociar todos los tipos de archivos que ofrece, sobre todo los que terminan con la extensin .pas y luego apretar el botn Next.
Presionar en Install y esperar a que termine de instalar todos los archivos. Esto a veces puede tardar bastante a pesar de que el copilador pesa muy poco.
Vladimir Rodrguez
Para terminar presione enter varias veces hasta que termine de desplegar.
Vladimir Rodrguez
1. 2. 3. 4.
o o
Accedan Mi Pc Panel de Control Sistema Seleccionen la pestaa Avanzado Accionen el botn Variables de entorno
En la lista superior pulsen nueva e ingresen estos valores: nombre: path valor: %path%;C:\FPC\2.2.2\BIN\I386_WIN32 5. Dar aceptar en todas las ventanas. Insisto que el paso anterior no debera ser necesario, ya que el instalador se encarga de realizarlo, sin embargo algunos usuarios han reportado ese problema. Por ms informacin ver: Installing under DOS or Windows en el sitio de Free Pascal.
Vladimir Rodrguez
OK
Acceder al men: OptionsSave Luego cerrar y volver a abrir el IDE. Con esto queda solucionado. En caso de presentar problemas con esto envenme un e-mail a mstrvladi@hotmail.com y les responder en breve para ayudarles a solucionarlo.
Vladimir Rodrguez
10
Vladimir Rodrguez
11
Al presionar enter luego de escribir esa instruccin quedaremos posicionados en el directorio donde alojamos nuestro programa. Obviamente si nosotros creamos otro directorio para trabajar con nuestras aplicaciones pascal especificaremos la ruta de dicho directorio. Una vez estamos en el directorio correcto escribimos el nombre de nuestro programa y damos enter. Enseguida se ejecutar y podremos ver sus resultados:
Del mismo modo podemos compilar nuestros programas desde la lnea de comandos. Nos posicionamos en el directorio donde radica nuestro cdigo fuente y escribimos la instruccin fpc HolaMundo.pas para este caso, de lo contrario la instruccin es fpc NombreDelArchivo.pas donde NombreDelArchivo ser el nombre con el cual guardamos nuestro cdigo fuente.
Vladimir Rodrguez
12
Vladimir Rodrguez
13
Hemos comenzado como siempre escribiendo PROGRAM seguido de un identificador para nuestro programa y un punto y coma. En la tercera lnea vemos la palabra reservada VAR, la cual indica que a continuacin se declararn todas las variables que usar nuestro programa en sus tareas. Una variable guarda informacin de algn tipo especfico y luego usa esta para realizar diferentes acciones, incluso modificar su valor. Nosotros podremos modificar el contenido de una variable siempre que queramos (detallaremos esto ms adelante) y en cualquier momento dentro de nuestro programa. Para declarar una variable, luego de la palabra VAR, le damos un nombre (identificador) a la variable y definimos de qu tipo ser. La sintaxis para hacerlo es la siguiente: NombreDeLaVariable: tipo; en nuestro ejemplo, en la lnea 4 hemos declarado una variable llamada nombre del tipo string, o sea, una variable que contendr cadenas de caracteres. String es una palabra reservada de pascal y corresponde a un tipo primitivo del mismo. Todo lo que sea del tipo String almacenar cadenas de caracteres. Nosotros no usaremos mucho este tipo en general ya que no resulta muy til a los efectos de este manual, pero an as sirve conocerlo. En la lnea 6 indicamos el inicio del programa. Como ven, la declaracin de variables se hace antes de iniciar el programa. Esto es porque el compilador primero asigna la memoria RAM que necesitar para almacenar cada variable y luego comienza con la ejecucin del programa. Las variables declaradas se guardan en celdas de memoria aunque no se usen, por lo cual no conviene declarar variables inservibles. En la lnea 7 usamos write para imprimir un mensaje al usuario y dejar el cursor a continuacin del mensaje, ya que all se escribir el nombre. En la octava lnea utilizamos el procedimiento readln para leer informacin de la entrada estndar e indicamos entre parntesis que dicha informacin debe ser almacenada en la variable nombre (la informacin se leer al presionar enter). Siempre luego del procedimiento read o readln se especifica entre parntesis la o las variables que guardarn la informacin leda, de lo contrario, el programa no guardar ningn registro de la informacin administrada por el usuario y continuar su curso. En la novena lnea utilizamos el procedimiento writeln al cual le pasamos dos campos de informacin esta vez, primero el texto que debe imprimir y luego, separado por una coma, el nombre de la variable cuya informacin queremos que aparezca en pantalla. Cuando uno necesita imprimir varias cosas con un solo procedimiento write o writeln debe separar los campos con una coma. En este caso, un campo contiene el texto y el otro la variable nombre. Cuando se especifica el nombre de una variable dentro de un campo de estos procedimientos, lo que aparecer en pantalla es el contenido de la variable. En este caso la variable nombre guardar la palabra que nosotros escribamos y luego ser lo que aparezca en pantalla al imprimir su contenido. Ejemplo de ejecucin: Ingresa tu nombre: Vladimir Hola Vladimir Al igual que con write y writeln, la diferencia entre read y readln es que la primera lee informacin de la entrada estndar y deja el cursor al final de la lnea en que la informacin fue ingresada. La segunda deja el cursor justo al inicio de la lnea siguiente. Pueden probar a escribir el programa anterior intercambiando estas funciones para ver el resultado y comprenderlo mejor. Notas: No declarar una variable luego de la palabra VAR es un error de sintaxis. No usar los dos puntos (:) para luego indicar el tipo de la variable es un error de sintaxis. Dos variables no pueden llamarse de la misma manera ni tampoco pueden llamarse igual que el programa, dicho de otra manera, no pueden repetirse dos identificadores, eso es un error de sintaxis.
Vladimir Rodrguez
14
Vladimir Rodrguez
15
Vladimir Rodrguez
16
Vladimir Rodrguez
17
Vladimir Rodrguez
18
AND ARRAY BEGIN CASE CONST DIV DO DOWNTO ELSE END FILE FOR FUNCTION GOTO IF IN LABEL MOD NIL NOT OF OR PACKED PROCEDURE PROGRAM
RECORD REPEAT SET THEN TO TYPE UNTIL VAR WHILE WITH ABS ARCTAN BOOLEAN CHAR CHR COS DISPOSE EOF EOLN EXP FALSE GET INPUT INTEGER LN
MAXINT NEW NIL ODD ORD OUTPUT PACK PAGE PRED PUT READ READLN RESET REWRITE ROUND SIN SQR SQRT SUCC TEXT TRUE TRUNC UNPACK WRITE WRITELN
Vladimir Rodrguez
19
Tipos de datos:
Ordinales: Son aquellos que, dado un elemento, podemos saber cual es el siguiente y/o el anterior adems de que poseen una cantidad finita de elementos. Por ejemplo dado un nmero entero sabemos que el siguiente es ese nmero ms uno y el anterior es el nmero menos uno. Dado el 5 su predecesor es el 4 y su sucesor es el 6. Integer Nmeros enteros, sin ninguna cifra decimal. Char Caracteres. Boolean Valores lgicos, pueden valer True o False. Enumerados Definido por el programador. Subrango Definido por el programador. Para estos tipos llamados Ordinales tenemos tres funciones predefinidas por Pascal que suelen ser tiles: Pred(valor_de_tipo_ordinal) Devuelve el predecesor para cualquier valor de tipo ordinal excepto si ste es el primero de todos. En este caso tenemos un error en tiempo de ejecucin. Succ(valor_de_tipo_ordinal) Devuelve el sucesor para cualquier valor de tipo ordinal excepto si ste es el ltimo de todos. En este caso tenemos un error en tiempo de ejecucin. Ord(valor_de_tipo_ordinal) Devuelve el ordinal para cualquier valor de tipo ordinal. Ya hablaremos de esto ms tarde, al igual que de las dos funciones anteriores. Reales: Solo tenemos el tipo real para esta categora. Las funciones anteriores no funcionan con valores de tipo real ya que es imposible saber, dado un real, cual es su sucesor o su predecesor. Estructurados: Forman estructuras de datos, en general, bastante complejas. String Array Record File Set Cadena de caracteres. Tablas de datos (String es un array de caracteres). Registros de datos. Secuencias de datos (No trabajaremos con este tipo). Conjuntos de datos.
Punteros: Hablaremos de ellos al final de este tutorial. Notas: El mayor entero posible es identificado por la palabra reservada MAXINT, la cual es una constante (hablaremos de estas ms adelante).
Operadores en pascal: Veamos los operadores aritmticos de Pascal, los cuales son usados para crear expresiones matemticas y realizar clculos:
Vladimir Rodrguez
20
Precedencia de operadores: Dada una expresin matemtica donde se combinen varios de estos operadores, al igual que en el lgebra comn, se realizarn primero los productos (*), las divisiones (div), (mod) y (/) y luego las sumas (+) y restas (-). Para quebrar esta precedencia de quin va primero que quin, se utilizan los parntesis. Ejemplo: A+B*C En este caso aparece primero la suma y luego un producto. Sin embargo el producto tiene precedencia ante la suma y se evaluar primero B*C para luego sumar ese resultado con A. Si se quisiera realizar primero la suma habra que utilizar parntesis y escribir aquella expresin como (A+B)*C. Ahora s se realizar primero la suma y luego el producto ya que los parntesis tienen la precedencia absoluta excepto si existen funciones que deben evaluarse dentro.
Funciones matemticas de pascal: Pascal nos provee de varias funciones matemticas predefinidas para realizar algunos clculos comunes. En los siguientes ejemplos la letra x simbolizar un nmero o expresin del tipo adecuado para la funcin en cuestin: ABS(x) SQR(x) SQRT(x) SIN(x) ARCTAN(x) EXP(x) LN(x) Calcula el valor absoluto de X. X debe ser integer o real. El resultado es del mismo tipo que X. Calcula el cuadrado de X. X debe ser integer o real. El resultado es del mismo tipo que X. Calcula la raz cuadrada de X siendo X un real o entero mayor o igual que 0. El resultado es un del tipo real. Calcula el seno de X expresado en radianes. X debe ser integer o real. El resultado es del tipo real. Calcula el arco tangente de X. X debe ser integer o real. El resultado es del tipo real. Calcula la exponencial de X (ex donde e= 2.7182818). X debe ser integer o real. El resultado es del tipo real. Calcula el logaritmo neperiano (base e) de X siendo X mayor que cero. X debe ser integer o real. El resultado es del tipo real. Suprime la parte decimal de X dejando solo el valor anterior a la coma. X debe ser real. El resultado es del tipo integer. Redondea el valor de X al entero ms prximo. X debe ser real. El resultado es del tipo integer. Obtiene el nmero de orden de X dentro de un conjunto de valores definidos por su tipo. X debe ser de un tipo ordinal. El resultado es de tipo integer.
Vladimir Rodrguez
21
Un ejemplo del uso de DIV y MOD. Introduccin a los comentarios: Se nos plantea el siguiente problema. Dado un nmero entero de cuatro cifras que ser ledo desde la entrada estndar se nos pide que hagamos la suma de sus cifras. Por ejemplo, si el usuario ingresa el nmero 5648 el programa debe realizar la suma de 5+6+4+8 y mostrar el resultado. Se asume que el usuario ingresar efectivamente un nmero de cuatro cifras. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 {El siguiente programa desglosar un nmero de cuatro cifras en sus cuatro nmeros distintos, uno por cada cifra y realizar la suma entre ellas} PROGRAM SumaCifras; Var numero: integer; //El valor que ser ledo. c1, c2, c3, c4: integer;//Una variable para cada cifra. BEGIN write('Ingresa un nmero de cuatro cifras: '); readln(numero); //Leemos el nmero de 4 cifras. (*Asignamos a cada variable el valor de la cifra que le corresponde*) c1:= numero mod 10; c2:= (numero mod 100) div 10; c3:= (numero mod 1000) div 100; c4:= numero div 1000;
//Mostramos el resultado al usuario. write('El resultado de sumar ',c4,'+',c3,'+',c2,'+',c1,' es: ' ,c1+c2+c3+c4); 22 END. Este es nuestro primer programa en el que vemos el uso de comentarios. Los comentarios son anotaciones hechas por el propio programador para ir explicando lo que se va realizando en su cdigo. Esto es debido a que normalmente el software se desarrolla en equipo y resulta imprescindible que otros lean el cdigo que uno escribe y sean capaces de entenderlo. Los comentarios son omitidos por el compilador por lo cual no afectan en absoluto al funcionamiento del programa, solo son una documentacin explicativa. Existen dos tipos de comentarios, los comentarios multilnea y los comentarios de fin de lnea. Los primeros pueden abrirse con { y cerrarse con } tal como se ve en las primeras tres lneas de nuestro programa, o abrirse con (* y cerrarse con *) tal como se ve en la lnea 14 de nuestro programa. Un comentario multilnea puede ocupar una o ms lneas de cdigo, tantas como sea necesario. Los comentarios de fin de lnea se abren con // y no requieren ningn smbolo de cierre ya que, como su nombre lo indica, finalizan al terminar la lnea en que aparecen. Podemos verlos en las lneas 7, 8, 12 y 20 de nuestro ejemplo. En el IDE se vern de color gris. Ahora miremos un poco la lgica de nuestro programa, como usando mod y div solamente, puede desglosarse un nmero en sus respectivas cifras. En la lnea 12 leemos de la entrada estndar un valor y lo guardamos dentro de numero. Luego, en la lnea 15, mediante el smbolo de asignacin := le damos a c1 el valor de la primera cifra de nuestro nmero. Fjense que si uno tiene un nmero de ms de una cifra y lo divide entre 10, el resto de esa divisin ser la primera cifra si leemos de derecha a izquierda. Teniendo por ejemplo el 1456, si dividimos ese nmero entre 10 (divisin entera, sin llevarlo a decimales) obtenemos como resto el 6 y como resultado el 145. En la lnea 13 obtenemos el resto de dividir nuestro nmero entre 100, eso da como resultado el entero formado por las dos primeras cifras (de derecha a izquierda) y luego (notar que esta operacin est entre parntesis para que se realice primero) mediante div obtenemos el cociente de dividir ese nmero entre 10. Por ejemplo, si tenemos 1456 y lo dividimos entre 100, el resultado es 14 y el resto 56. Teniendo el resto, al dividirlo entre 10 tenemos como resultado el 5 y como resto el 6. De este modo en las lneas 17 y 18 obtenemos las ltimas dos cifras de nuestro nmero. Ahora las variables c1, c2, c3 y c4 contienen cada una los valores de una cifra de nuestro valor inicial. En la lnea nmero 21 mostramos mediante el procedimiento write los resultados de nuestro programa.
Vladimir Rodrguez
22
Bien, en la lnea 3 vemos la palabra reservada const, que indica que desde all se declararn constantes. La sintaxis para declaracin de constantes es NombreDeLaConstante= valor; donde valor representa un valor de algn tipo primitivo de pascal (integer, real, char, string entre algn otro). El nombre de la constante es un identificador. En este caso, en la lnea 4 del programa hemos declarado la constante iva y le hemos dado el valor 23, que representa el 23% que vale el IVA en mi pas. Notar que las constantes se declaran antes que las variables.
Vladimir Rodrguez
23
Vladimir Rodrguez
24
Vladimir Rodrguez
25
Algunas notas: El punto y coma acta como separador. Nosotros acostumbramos a escribir una instruccin por lnea por una cuestin de legibilidad del cdigo, sin embargo al compilador eso no le importa al igual que tampoco la sangra (indentacin) que dejemos. De este modo resultar lo mismo escribir Writeln(Hola mundo); Readln(variable); que escribir writeln(Hola mundo); readln(variable); Ejercicios: Antes de continuar debern ser capaces de realizar la gran mayora de los ejercicios que aparecern en los links a continuacin. Estos los dirigirn a una pgina de Facultad de Ingeniera y los ejercicios estn pensados para reforzar todo lo que yo ya les he explicado hasta aqu. La mayora se resuelve de forma dinmica con la pgina WEB con lo cual ustedes podrn verificar sus resultados al instante. Reitero, traten de resolver la mayor parte de esos ejercicios antes de continuar o de lo contrario no podrn desempearse bien en los temas que trataremos en lo que sigue del tutorial. Prctico 1: http://www.fing.edu.uy/inco/cursos/prog1/pm/field.php/Practicos/Practico1 Prctico 2: http://www.fing.edu.uy/inco/cursos/prog1/pm/field.php/Practicos/Practico2 En caso de tener problemas con estos enlaces o con las pginas a las que estos los llevan envenme un e-mail a mstrvladi@hotmail.com y les ayudar a la brevedad.
Vladimir Rodrguez
26
SEGUNDA PARTE
Seleccin y Repeticin
Vladimir Rodrguez
27
Vladimir Rodrguez
28
1 PROGRAM viejo; 2 3 Var 4 edad: integer; //Variable para guardar la edad. 5 6 BEGIN 7 //Mostramos mensaje y leemos la edad. 8 write('Ingrese su edad: '); 9 readln(edad); 10 11 //Mostramos lo mismo que ya ingres. 12 writeln('Usted tiene ',edad,' aos.'); 13 14 //Si tiene ms de 50 aos le decimos viejo. 15 If edad>50 then 16 writeln('Usted es un viejo') 17 else 18 Writeln(Usted es joven); 19 20 END.
Vladimir Rodrguez
29
Como vemos, nuestro programa nos pide el ingreso de nmeros enteros que corresponden a nuestra edad. En la lnea 12 decimos si el valor en edad1 es mayor a 50 entonces si el valor en edad2 es menor a 50 escribe Eres un viejo joven. Anidacin de un ifthen con un ifthenelse: Es posible hacer todas las anidaciones que se quieran, sin embargo como ya dije antes, debemos tener cuidado. Aqu dar un ejemplo de una anidacin que nosotros podramos interpretar de forma errnea. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 PROGRAM anidacion2; Var edad1, edad2: integer; BEGIN Write(Ingresa tu edad: ); Readln(edad1); Write(Ingresa nuevamente tu edad: ); Readln(edad2); If edad1>50 then If edad1=edad2 then Writeln(Eres un viejo)
Vladimir Rodrguez
30
En este ejemplo uno podra interpretar que si tenemos ms de 50 aos y adems ingresamos las dos veces la misma edad aparecer el mensaje Eres un viejo, y si edad1 era menor a 50 nos aparecer el mensaje Eres joven. Sin embargo no es as, esa instruccin se fijar si nuestra edad es mayor a 50 y solo si eso es cierto luego ver si ambas edades eran iguales, si es as aparecer el mensaje Eres un viejo y si no aparecer el mensaje Eres joven. La correcta indentacin de esa instruccin es la siguiente: If edad1>50 then If edad1=edad2 then Wirteln(Eres un viejo) else Writeln(Eres un joven); Como ven, solo se ejecutar esto si edad1>50 resulta ser true. La forma correcta de escribir esta instruccin para obtener el resultado que interpretbamos anteriormente sera: If edad1>50 then begin If edad1=edad2 then Wirteln(Eres un viejo); end else Writeln(Eres un joven); Como ven, hemos encerrado entre begin y end la segunda instruccin if. Esto hace que todo lo que est dentro del rea abarcada por esas dos palabras sea como una sola instruccin para el programa, a pesar de que all dentro podemos poner todas las que queramos. De no hacer esto, el else formara parte del segundo if y no del primero. Debemos recordar que un else cierra siempre al if que se encuentre ms prximo a l. Para quebrar esto debemos usar begin y end. Los delimitantes begin y end se usan para indicar que al tomar cierto flujo de ejecucin dada una condicin el programa debe efectuar muchas instrucciones. No servira de mucho que por cada if que usemos solo podamos dictar una instruccin. La sintaxis genrica sera la siguiente: If condicion then begin Instruccion1; Instruccion2; . . . InstruccionN; end else begin Instruccion1; Instruccion2; . . . InstruccionN; end; Vean que tambin es posible usar los delimitantes begin y end para la instruccin else del if. Estos begin y end forman lo que llamamos bloques de instrucciones y difieren del begin y end principales ya que estos forman un nico bloque que es el programa completo. Luego de un end va enseguida un punto y coma que indica el final del bloque y no un punto como en el caso del end principal. Notar que el end anterior al else no finaliza con punto y coma. Esto se debe a lo que ya expliqu antes. Recordar que nunca, antes de una instruccin else que forma parte de un if debe ir colocado un punto y coma.
Vladimir Rodrguez
31
Aqu vemos que en la instruccin IF hay dos condiciones entre parntesis unidas por la palabra reservada AND. Esto significa que se ejecutar la instruccin del IF si ambas condiciones son verdaderas, o sea, si se cumple a la vez que edad1>50 y que edad1=edad2. Basta con que una de ellas sea falsa para que no se ejecute la instruccin del IF y se pase a la del ELSE. En ese caso no sabremos si sucedi porque el usuario era joven o si era un mentiroso que ingres dos edades diferentes. Estas composiciones son evaluadas por Free Pascal con el mtodo conocido como circuito corto. Esto es que, si la primera condicin del AND es falsa, ya no se evaluar la segunda porque aunque esta fuera verdadera la condicin total resulta falsa. As nacen las tablas de verdad. AND en ingls significa Y. Si nosotros quisiramos haber dicho que se ejecute el IF si la primera condicin la otra eran verdaderas deberamos haber usado el operador OR. Este es verdadero si al menos una de las condiciones es verdadera. Tambin se evala por circuito corto, o sea, si la primera condicin es verdadera ya no se evaluar la segunda porque aunque resulte falsa la condicin total OR ser verdadera. Tablas de verdad: AND Expresin1 TRUE TRUE FALSE FALSE Expresin2 TRUE FALSE TRUE FALSE OR Expresin1 TRUE TRUE FALSE FALSE Expresin2 TRUE FALSE TRUE FALSE Expresin1 OR Expresin2 TRUE TRUE TRUE FALSE Expresin1 AND Expresin2 TRUE FALSE FALSE FALSE
Existe tambin el operador booleano NOT que niega una condicin, o sea, da el valor opuesto a esta. En principio uno se pregunta para que servira algo como eso. Ms adelante cuando comencemos a trabajar con la variables boolean veremos el uso de este operador. Veamos su tabla: NOT Expresin NOT Expresin TURE FALSE FALSE TRUE
Vladimir Rodrguez
32
Un ejemplo matemtico: Bien. Haremos un programa en el cual aplicaremos casi todo lo dado hasta ahora. Se nos pide realizar un programa que calcule las races de polinomios de segundo grado cuyos coeficientes sern ingresados por el usuario, y muestre en pantalla los mensajes correspondientes. Por si alguno de ustedes no est bien familiarizado con polinomios explicar brevemente lo que es uno de segundo grado. Un polinomio es una funcin matemtica con una nica incgnita X la cual est elevada a distintos exponentes en los diferentes trminos de la ecuacin. Para que se vea mejor, un polinomio de segundo grado tiene la forma aX2 + bX + c donde a, b y c son nmeros reales. Por ejemplo, un polinomio podra ser 2X2 + 4X + 2. Los nmeros que acompaan a las X (a, b y c) son llamados coeficientes. Las races de un polinomio son aquellos valores de X que hacen que la ecuacin sea nula, o sea, que valga 0. En el ejemplo que tenemos all, una raz de ese polinomio es -1. Si cambiamos la incgnita X por -1 efectivamente vemos que todo se anula: 2*(-1)2 + 4*(-1) + 2 = 2*1 4 + 2 = 2 4 + 2= -2 + 2 = 0. Dado un polinomio de segundo grado puede suceder que tenga dos races diferentes, una nica raz (llamada raz doble) o que no tenga raz. Para hallar las races de un polinomio aX2 + bX + c se usa la conocida frmula de Bascaras:
-b(b2-4*a*c) 2*a
El trmino bajo la raz cuadrada (b2-4*a*c) es llamado discriminante ya que de l depende si el polinomio tiene dos races, una sola o no tiene. Si el discriminante es mayor que 0 efectivamente tendremos dos races distintas ya que por la forma de la frmula tenemos un que divide la frmula en dos partes, por un lado resolvemos la ecuacin con el signo de + luego de b, y luego resolvemos la frmula con el signo de luego del b:
-b+(b2-4*a*c) 2*a
-b-(b2-4*a*c) 2*a
Esto dar dos resultados diferentes, cada uno correspondiendo a una raz. Si el discriminante es nulo, o sea igual a 0, la raz cuadrada de 0 es igual a 0, por tanto sera estpido separar en dos la ecuacin ya que hacer b+0 o b-0 da exactamente igual, por lo cual el resultado ser nico. En este caso tenemos una nica raz doble. Si el discriminante es menor que 0 no podremos hallar su raz cuadrada ya que no existe raz de un nmero negativo. En este caso la frmula no puede resolverse y decimos que no existen races reales. En este caso pueden hallarse races complejas, pero eso no lo haremos ahora ya que tiene sentido solo para aquellos que se lleven bien con las matemticas.
Muy bien, teniendo cierto conocimiento sobre el problema comencemos a plantearnos como lo resolveremos para programarlo. El usuario ingresar en la entrada estndar los coeficientes a, b y c de nuestro polinomio. Teniendo esos valores primero debemos verificar que el valor a sea distinto de 0 ya que de ser as no estaramos enfrentndonos a un polinomio de segundo grado sino a uno de grado uno o cero. Luego deberemos evaluar el discriminante y segn lo que este de cmo resultado tomaremos tres caminos distintos para mostrar los resultados al usuario.
Vladimir Rodrguez
33
Vladimir Rodrguez
34
Vladimir Rodrguez
35
write('Ingrese una opcion y presione ENTER: '); readln(opcion); writeln; If opcion='1' then writeln('MENSAJE DESPLEGADO') else if opcion='2' then begin write('Ingrese un nmero entero: '); readln(numero); writeln('Ha ingresado ',numero,'.'); end else if opcion='3' then begin write('Ingrese un caracter: '); readln(opcion); writeln('Ha ingresado ',opcion); end else writeln('ERROR. No existe la opcin ',opcion); END.
En este ejemplo trabajaremos por primera vez con una variable del tipo char, o sea que contendr caracteres, no una cadena como el tipo string sino uno solo. Esto es por un simple motivo. Al ser del tipo char cualquier smbolo que ingresemos ser ledo sin problemas. Nosotros mostramos las opciones del men con nmeros, entonces uno podra decir que para leer la opcin que el usuario ingrese podramos haber declarado una variable integer o real, sin embargo de hacerlo as, si el usuario en vez de ingresar un nmero ingresa una letra el programa dar un error en tiempo de ejecucin al intentar almacenar una letra en una variable numrica y se cerrar. Sin embargo, al ser del tipo char, si el usuario ingresa una letra esta se leer sin problemas y podremos desplegar al usuario un mensaje de error que indique que ha seleccionado una opcin incorrecta, y de ser posible pedirle que lo intente de nuevo. Notar como en las condiciones de los IF comparamos el carcter ledo en la variable opcion con un carcter escribiendo este entre comillas simples. Es importante saber que si el usuario ingresa un 1 en su entrada, nosotros leeremos el carcter 1 y NO el nmero 1. El carcter 1 es como una letra o un smbolo, no es un nmero que se puede sumar o restar, es un smbolo. Del mismo modo que uno no puede sumar letras, a+b por ejemplo, no se pueden sumar caracteres numricos. No es lo mismo 1 que 1. El primero es un nmero y el otro un carcter. 1+2 equivale a la cadena 12, y 1+2 es igual a 3. Esta diferencia resulta esencial, es importante que lo comprendan. No he puesto comentarios en este programa apropsito para que ustedes lo lean y entiendan por s solos. Bsicamente decimos, si el usuario ingresa el 1 muestra un mensaje, sino, si el usuario ingresa el 2 pide que ingrese un numero y mustraselo, sino, si el usuario ingresa el 3 pdele que ingrese un carcter y mustraselo, sino dile que lo que ha ingresado no es una opcin correcta.
Vladimir Rodrguez
36
write('Ingrese una opcion y presione ENTER: '); readln(opcion); writeln; Case opcion of '1': writeln('MENSAJE DESPLEGADO'); '2': begin write('Ingrese un nmero entero: '); readln(numero); writeln('Ha ingresado ',numero,'.'); end; '3': begin write('Ingrese un caracter: '); readln(opcion); writeln('Ha ingresado ',opcion); end; else writeln('ERROR. No existe la opcin ',opcion); end; //De la instruccin CASE. END.
Hasta la lnea 16 nuestros ejemplos son exactamente iguales. Ahora, en la lnea 17 en vez de tener un IF tenemos la declaracin de la instruccin CASE. Su sintaxis es la siguiente: Case variable of Etiqueta1: instruccion1; Etiqueta2: instruccion2; ... EtiquetaN: instruccinN; Else Intruccion_por_defecto; End; Esta instruccin se fijar en el valor de la variable colocada entre la palabra CASE y la palabra OF, en nuestro ejemplo se trata de la variable opcion. Luego tomar el camino en el cual el valor de la variable sea igual al de la etiqueta. El ELSE al final indica lo que se debe hacer si la variable no corresponde con ninguna de las etiquetas anteriores. Este ELSE puede ir o no. Si no est no se ejecutar nada y se continuar con lo que haya debajo en el cdigo, pero no habr error. En este caso hemos usado el ELSE par decir al usuario que no ingres nada de lo esperado. Notar que antes de este ELSE s debe ir un punto y coma que cierre la instruccin anterior, no como en el caso de los ELSE que se usan en una instruccin IF. Siempre al final de la instruccin CASE debe ir un END que finalice en punto y coma para indicar el final de esta instruccin. No colocarlo es un error de sintaxis. Notar que las instrucciones para cada etiqueta pueden ser bloques de instrucciones delimitados por sus respectivos BEGIN y END. Es importante saber que las etiquetas representan los valores que debe tomar la variable del encabezado del CASE para que el programa tome tal o cual camino. Es posible indicar ms de un valor a la vez en una misma etiqueta, simplemente separando cada valor por una coma: Case variable of Valor1, valor2: instruccion1; Valor3, valor4, valor5: instruccion2; . . . ValorI, valorN: instruccionJ;
Vladimir Rodrguez
37
Seleccin: Cuando el usuario elija una opcin su programa habr de realizar las tareas pertinentes y en caso contrario notificar el error. No continen hasta no lograr hacer esto.
Vladimir Rodrguez
38
Vladimir Rodrguez
39
Anidacin del FOR: Veamos un ejemplo donde el usuario ingresar un nmero que indicar el largo del lado de un cuadrado y dibujaremos este con el carcter *: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 PROGRAM Cuadrado; Const caracter= '*'; Var i, j, lado: integer; BEGIN write('Ingresa el largo del lado: '); readln(lado); writeln; For i:=1 to lado do begin For j:=1 to lado do write(caracter); writeln; end; END.
Lo interesante en este programa es analizar lo que sucede desde la lnea 15 en adelante. Tenemos dos FOR, uno dentro de otro. Lo primero a resaltar es el uso de dos variables de control diferentes, una para cada FOR. Si usramos la misma variable para ambos uno modificara la del otro y se ocasionara un caos. Si quieren prueben este programa as como est y luego con la misma variable de control. Es probable que el compilador los regae por eso y se produzca un error en tiempo de compilacin. Veamos, cuando i vale 1 entramos al FOR principal. All tenemos otro FOR el cual se ejecutar hasta el final antes de volver al primer FOR. Cuando j vale 1 imprimimos un * (est definido en la constante caracter). Luego j vale 2 y se imprime un segundo * en la misma lnea ya que estamos usando un procedimiento write. As continuamos hasta que se imprimen tantos * en una lnea como lo indique la variable largo. Ahora pasamos a la lnea 20 del programa donde est writeln para escribir una lnea en blanco, o sea, para bajar un rengln. Ahora i vale 2. Volvemos a entrar en el FOR principal y el FOR de dentro vuelve a comenzar con j en 1. Imprime los *, se baja un rengln y as hasta que i sea igual al valor de la variable lado.
Vladimir Rodrguez
40
Esto funciona exactamente igual a la forma anterior del FOR con la diferencia de que valor_inicial es mayor que valor_final. O sea que la variable de control ir decreciendo en vez de aumentar. Por ejemplo: For i:=20 downto 1 do Instruccion; En este caso i comenzar valiendo 20, luego 19, luego 18, y as hasta llegar al 1. NOTAS: Nunca asuman el valor que tendr la variable de control una vez finalizado el FOR. En este ejemplo de FOR que va de 20 a 1 uno podra decir que i vale 1 al final, pero esto puede no ser as y depende del compilador. Si van a volver a usar esta variable debern inicializarla ustedes mismos con el valor apropiado. A continuacin les planteo una serie de ejercicios de aplicacin del FOR y algunos temas ya vistos. Por favor, no continen si no son capaces de resolver estos ejercicios. Si realmente se trancan con alguno envenme un e-mail a mstrvladi@hotmail.com explicndome cual ejercicio les est dando problemas y les ayudar o les enviar la solucin. Ejercicio 1: Determinen cules de los siguientes fragmentos de cdigo producen la misma salida: a) FOR i:=1 TO 3 DO FOR j:= i+1 TO 3 DO write(i,j) b) FOR i:=1 TO 3 DO write (i, i+1) c) FOR i:=1 TO 4 DO IF (i=1) OR (i=4) THEN write(i) ELSE write(i,i)
Ejercicio 2: Escriban un programa que lea desde el teclado un valor entero n. A continuacin, el programa deber leer n enteros y luego desplegar el mayor y el menor de ellos. Ejemplo de entrada: n=8 Ingrese 8 enteros: 5 12 36 4 21 95 12 18 Ejemplo de salida: El mayor entero ingresado es: 95 El menor entero ingresado es: 4
Vladimir Rodrguez
41
Ejercicio 4: Escriban un programa que lea de la entrada estndar un carcter c y un entero n. El programa debe desplegar un tringulo de n lneas formado por el carcter c segn se muestra en el siguiente ejemplo: Ejemplo de entrada: n=8 c=$ Ejemplo de salida: $$$$$$$$ $$$$$$$ $$$$$$ $$$$$ $$$$ $$$ $$ $
Vladimir Rodrguez
42
Vladimir Rodrguez
43
Vladimir Rodrguez
44
Vladimir Rodrguez
45
Vladimir Rodrguez
46
NOTAS: En el ejemplo que trabajamos, nosotros usamos un IF para decidir si cambibamos el valor de un booleano a TRUE. Sin embargo no es necesario hacerlo as, la siguiente instruccin If codigo_ingresado=codigo_secreto then adivino:= true equivale a escribir adivino:= codigo_ingresado=codigo_secreto. De este modo estamos diciendo que adivino es true si se cumple la condicin codigo_ingresado=codigo_secreto. Si esto no es as el booleano ser FALSE. Esto es exactamente igual que la instruccin con IF.
Vladimir Rodrguez
47
Bien, en la lnea 8 leemos el primer valor que debemos sumar. En la lnea 10 inicializamos la variable suma con el valor 0 ya que an no ha sumado nada. En la lnea 13 comienza el WHILE solo si el nmero que ingresamos es mayor que 0, en caso contrario no se ejecutar. Aqu vemos por qu es mejor utilizar un WHILE que un REPEAT, ya que si el usuario ingresa como primer valor un nmero negativo ya no habr nada para sumar. En caso de entrar en el WHILE aumentamos el valor de la variable suma sumando su valor anterior al valor del nmero ledo, o sea, al valor de numero. En la lnea 18 volvemos a leer un nmero y el WHILE volver a su encabezado. Si el nuevo nmero ledo es mayor que 0 ya no habr iteracin y en caso contrario s. Luego de haber salido del WHILE mostramos al usuario el resultado de la suma. Bien, esto era para mostrar la idea de centinela. En este caso nuestro centinela es cualquier nmero negativo ingresado por el usuario ya que ste ser el que detenga la iteracin del WHILE. Veremos muchos ms ejemplos de centinelas de aqu en adelante.
Vladimir Rodrguez
48
writeln('La cantidad de palabras en el texto es: ',cantidadPalabras); 36 END. Les dejar como tarea que estudien este cdigo hasta que lo entiendan completamente. Fjense como implementamos distintos REAPEAT dentro de un WHILE y como usamos el procedimiento READ para siempre ir leyendo sobre la misma lnea. No olviden las constantes declaradas al inicio del programa. Los comentarios son su gran gua y eso es algo muy importante para un programador. Si deben trabajar con el cdigo de su compaero de trabajo debern ser capaces de entender sus cdigos basados en que saben la tarea que desempea su programa, las variables que declar tienen nombres mnemotcnicos y sus comentarios indican bien cada accin aunque sin extenderse o abusar de ellos. Todo esto es realmente muy importante que aprendan a implementarlo. Por si alguno de ustedes no lo sabe, un nombre mnemotcnico es aquel que ayuda a saber el propsito de una variable, por ejemplo, en este programa la variable que guarda el valor de cuantas palabras se cuentan se llama cantidadPalabras. Bien poda haberse llamado cont que es una abreviacin de contador o podra haber tenido cualquier otro nombre. Usen nombres mnemotcnicos aunque sean largos, esto ayuda a prescindir un poco de los comentarios y a que el cdigo sea ms entendible Ejemplo de captura de error: En el siguiente ejemplo veremos un programa simple que le pedir al usuario que ingrese un valor entero. En esta ocasin veremos como leer estos valores como caracteres y luego si son correctos pasarlos a enteros tal como debera ser. Primero veamos algo que debera haber nombrado ya hace bastante pero sin embargo decid aplazarlo hasta ahora.
Vladimir Rodrguez
49
En esa imagen el cursor est posicionado sobre el smbolo @. Como vemos, a este carcter le corresponde el valor 64. Ese valor es el ordinal de @. Si nosotros tenemos un carcter cualquiera y queremos saber su ordinal debemos usar la funcin ord. Por ejemplo si escribiramos ord(@) obtendramos el entero 64. A la inversa tenemos la funcin chr que dado un entero nos devuelve su correspondiente carcter en la tabla. Si escribiramos chr(64) obtendramos como valor devuelto el carcter @. La funcin ORD transforma un carcter en entero, o sea, de un tipo char obtenemos un tipo integer. Esto es lo que usaremos para, dado un carcter numrico, obtener el entero de ese carcter, o sea, dado el carcter 1 obtener el nmero 1. Sin embargo no es tan fcil, observen ustedes en la tabla que al carcter 0 le corresponde el entero 48 ya que est en la posicin 49 de la tabla (la primera posicin es la 00). Al carcter 1 le corresponde el entero 49 y as sucesivamente. Por lo tanto si nosotros leyramos un carcter de la entrada estndar, suponiendo que el usuario ingres el 1, al hacer ORD(1) obtendramos el nmero 49 y no el nmero 1. A alguno de ustedes se les ocurre como, dado un carcter numrico, obtener su valor en entero? O sea, si me dan el 2 obtener el 2, si me dan el 8 obtener el 8, y as. Como pueden observar, los lugares de la tabla estn ordenados ascendentemente, por lo cual si al 0 le corresponde el 48, al 1 le corresponde el 49, al 2 el 50 y as hasta el 9 que le corresponde el 57. Entonces que pasa si al ordinal de cualquier carcter numrico le resto el ordinal de 0? Por ejemplo, si me dan el 0, resto ese ordinal con el ordinal de 0: Ord(0)-Ord(0)= 48 48= 0. Obtuve el entero 0. Ahora si me dieran el carcter 1 y a su ordinal le resto el ordinal de 0: Ord(1)-Ord(0)= 49 48= 1. Obtuve el entero 1. De tener el 9 y restarle su ordinal al ordinal de 0 tendra: Ord(9)-Ord(0)= 57 48= 9. Obtuve el entero 9. NOTA: El ordinal de un entero es el mismo entero: Ord(57)= 57. El ordinal de un boolean es 1 para TRUE y 0 para FALSE.
Vladimir Rodrguez
50
Como primer ejercicio tienen que entender este programa y modificarlo para que funcione con REPEAT. No continen hasta no ser capaces de resolver todos estos ejercicios. Ejercicio2: Determinen cules de los siguientes segmentos de cdigo producen la misma salida al ser ejecutados: a) i := 1; j := 2; REPEAT Write(i, j); i := i + 1; j := j + 1 UNTIL j <= 3; c) i := 1; REPEAT Write(i, i + 1); i := i + 1 UNTIL i <= 3; b) i := 1; j := 1; WHILE (i <= 3) AND (j <= 2) DO BEGIN Write(i, j + 1); i := i + 1; j := j + 1 END;
Vladimir Rodrguez
51
Ejercicio5: Realicen las funciones de una calculadora simple. Los datos de entrada sern una secuencia de dgitos enteros y los operadores +, *, / y -, seguida de un signo =. Hagan caso omiso de los espacios en blanco. Los operadores se aplican en el orden en que aparecen en los datos de entrada, o sea que olvdense de la precedencia, y producen resultados enteros, o sea, si bien se ingresa el operador de la divisin con el smbolo /, el comportamiento es el de DIV. Asuman que se ingresa al menos un nmero. Ej. de entrada: 4 + 3 / 2 * 8 - 4 = Ej. de salida: 20 Como ven, las operaciones se fueron realizando en el orden en el que aparecan. Primero 4+3 que es 7, eso lo dividimos entre 2 (div) que es 3, luego eso multiplicado por 8 que es 24 y luego eso menos 4 que es 20. Una verdadera calculadora habra hecho primero 3/2 que con DIV da 1 y luego eso por 8 que da 8. Luego hara 4+8-4 que es igual a 8. Sin embargo programar eso requiere ms herramientas de las que poseen. Ejercicio6 (DIFICIL): Escriban un programa que lea una letra y una oracin de la entrada estndar (de una sola lnea), y calcule la cantidad de palabras que terminan con la letra y estn en la oracin dada. Asuman que la oracin siempre terminar con un punto y que las palabras estarn separadas solo por espacios (no habr comas ni nada de eso). Asuman tambin que la oracin tendr siempre al menos una palabra. Ejemplos: Letra: s Oracin: La cualidad indispensable para un buen cocinero es la puntualidad pero es tambin la de los invitados. Salida: 4 Letra: r Oracin: Un discpulo de quien jams se pide nada que no pueda hacer nunca hace todo lo que puede. Salida: 1 Letra: o Oracin: No basta tener buen ingenio lo principal es aplicarlo bien. Salida: 4 Modifiquen su programa para que cuente las palabras que comienzan con la letra dada. Este es un ejercicio que los har pensar y luchar mucho. Intenten hacerlo bajo todos los medios posibles. Todo lo dado hasta ahora basta y sobra para lograrlo. Si no logran realizarlo escrbanme y les enviar la solucin para que la estudien, pero no tiene caso leer un programa ya hecho si ustedes no intentan realizarlo primero poniendo todas las ganas de aprender. Este manual es para ustedes y requiere todo su esfuerzo. No hay otro medio para aprender a programar. Mi esfuerzo fue escribir esto, ustedes practiquen y practiquen, pregntenme sus dudas a mi cacilla de correo y sigan intentando. Los frutos se vern al final.
Vladimir Rodrguez
52
TERCERA PARTE
Vladimir Rodrguez
53
Vladimir Rodrguez
54
Como ven es la misma sintaxis que para declarar una variable de tipo primitivo, le damos un identificador, dos puntos y luego el nombre del tipo. Esto significa que la variable letraMin podr tomar valores de la a a la z ya que es del tipo Minusculas. Como el Subrango Minusculas corresponde a un Subrango de caracteres, letraMin se comportar como una variable char con la diferencia de que no podr tomar valores fuera del rango indicado por Minusculas. Si esto sucede tendremos un error en tiempo de ejecucin. Si han activado Range checking como se los indiqu al inicio del tutorial, el compilador verificar esto en la medida de lo posible y se los advertir, pero no siempre podr hacerlo. Por ejemplo, si ustedes hacen read(letraMin) el compilador no podr asumir que el usuario puede ingresar un carcter fuera del rango y por tanto compilar correctamente, pero si hacen, letraMin:= A les advertir su error. Hay ms casos de esto, pero lo vern en la prctica. Lo mismo sucede para los subrangos Decimal, Mes y Naturales, que son subrangos de integer. Las variables de estos tipos funcionarn como variables enteras con la diferencia de que deben tomar valores dentro de los rangos establecidos. Visto de esta manera no puedo incentivarlos mucho con el uso de los subrangos, sin embargo veremos que resultan tiles para definir Arreglos, que es el tema que viene a continuacin. Lo que puedo decirles es que al definir un Subrango ustedes podrn estar seguros de que sus variables tendrn los valores adecuados. Claro que ustedes deben encargarse de que estas no tomen valor por fuera del rango establecido.
Vladimir Rodrguez
55
Vladimir Rodrguez
56
En la lnea 3 indicamos que comenzar la declaracin de tipos para, luego, en la lnea 4 declarar el tipo arr que ser un arreglo de 10 celdas numeradas del 1 al 10 del tipo entero. Como arr es un tipo su propsito es declarar variables de ese tipo, por lo tanto en la lnea 7 declaramos la variable arreglo1 del tipo arr. Podemos, al igual que con los tipos primitivos, declarar tantas variables como queramos de cualquier tipo que nosotros mismos definamos; en este caso tenemos solo una, pero podra tener ms y cada una sera independiente. Bien, en la lnea 9 comienza nuestro programa, el cual consta de diez lneas de asignacin donde en cada una le damos un valor a cada celda del arreglo. Veamos esto con detalle, es bien sencillo. Cada celda del arreglo funciona como una variable independiente, por tanto, en ese arreglo tenemos diez variables del tipo integer. Para referirnos a una celda debemos dar el nombre de nuestro arreglo (no el nombre del tipo sino el de la variable de ese tipo) seguido por el ndice entre parntesis rectos de la celda a la que queremos ir. De este modo, en la lnea 10, al escribir arreglo1[1]:= 1 estamos diciendo que vaya a la primera celda de arreglo1 y le asigne el valor 1. La sintaxis genrica sera Variable_del_tipo_arreglo[indice_de_la_celda] con lo cual nos referiramos a cualquier celda. Recordar que cada una es una variable independiente, por tanto esa declaracin es como si fuera el nombre de la variable y funciona igual que cualquier otra variable, valga la redundancia. Todo lo que sigue del programa es asignar a las celdas restantes el valor que queremos y termina nuestro programa. Sin embargo no es muy til tener que escribir instruccin por instruccin para dar un valor a cada celda. Imaginen un arreglo de 1000 celdas, tendramos un programa de 1000 lneas solo en asignaciones. Veamos el mismo programa pero asignando los mismos valores a cada celda de una forma ms inteligente:
Vladimir Rodrguez
57
Hemos sustituido las 10 lneas de asignacin por una instruccin FOR que va de 1 a 10. Fjense que hemos colocado la variable de control i dentro los parntesis rectos que indican el ndice de nuestras celdas. De este modo, cuando i valga 1 estaremos hablando de la primera celda, cuando i pase a valer 2 estaremos hablando de la segunda celda, y as sucesivamente hasta 10. En este caso hemos asignado a cada celda el mismo valor de su ndice, pero esto podra no ser as. Este arreglo dibujado sera: *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----* | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----* 1 2 3 4 5 6 7 8 9 10 Ahora veamos lo mismo pero asignando el doble del ndice a cada celda: 1 2 3 4 5 6 7 8 9 10 11 12 13 PROGRAM arreglos; Type arr= array[1..10] of integer; Var arreglo1: arr; i: integer; BEGIN For i:=1 to 10 do arreglo1[i]:= i*2; END.
De este modo arreglo1 ahora quedara as: *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----* | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----* 1 2 3 4 5 6 7 8 9 10 Dentro de los parntesis rectos que indican el ndice del arreglo es posible, como hemos visto ya, colocar el valor del ndice, una variable del tipo correcto o, como no hemos visto an, una expresin que d cmo resultado un valor del tipo correcto y que est dentro del rango de ndices posibles. Es muy importante que lo que est dentro de los parntesis rectos nunca exceda el rango en que est numerado el arreglo. Si en este ejemplo nosotros escribiramos For i:=1 to 11 do arreglo1[i]:= i*2; se producira un error cuando i alcanzara el valor 11 y el programa se cerrara abruptamente ya que la celda 11 no existe. Si han activado Range checking lo ms probable es que el compilador les avise antes, pero esto no es siempre seguro ya que no siempre es detectable que podemos estar salindonos del arreglo. Veamos un nuevo ejemplo del mismo programa, solo que ahora los ndices sern caracteres y a cada celda le asignamos el valor del ordinal de su ndice. No olviden que el arreglo es de enteros.
Vladimir Rodrguez
58
De este modo nuestro arreglo quedara as: *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----* | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | *-----*-----*-----*-----*-----*-----*-----*-----*-----*-----* a b c d e f g h i j
Hacer WRITE y READ de un arreglo: Si quisiramos desplegar un arreglo en la pantalla, o sea, que se nos muestren todos sus valores, debemos escribir celda por celda. En el ejemplo anterior tenamos el arreglo llamado arreglo1. Como primer impulso para escribirlo en pantalla uno tendera a hacer algo como esto write(arreglo1); y sin embargo eso no es posible. De hacerlo tendrn un error en tiempo de compilacin en el cual el compilador se les quejar por no poder escribir o leer variables de ese tipo. Esto es porque un arreglo es una estructura de datos y no un valor especfico. Por este motivo es que debemos escribir celda por celda. No se olviden que cada celda es como una variable ms y funciona del mismo modo. Veamos entonces como mostrar el arreglo de nuestro ejemplo anterior: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 PROGRAM arreglos; Type arr= array[a..j] of integer; Var arreglo1: arr; i: char; BEGIN For i:=a to j do arreglo1[i]:= ord(i); For i:=a to j do Write(arreglo1[i], ); END.
La nica diferencia entre este programa y el anterior es que agregamos un FOR que en cada iteracin escribe el valor de una de las celdas. Verifiquen esto ustedes mismos. Ejercicio: Modifiquen este programa para que con un solo FOR asigne los valores al arreglo y lo muestre en pantalla.
Del mismo modo, si quisiramos hacer read de un arreglo para que el usuario ingrese los valores de cada celda debemos hacerlo para cada una por separado. Ejercicio: Realicen un programa en el cual exista un arreglo de enteros de cinco celdas de modo que el usuario sea quin ingrese los valores para cada celda. Deben utilizar una instruccin FOR para ello.
Vladimir Rodrguez
59
Recorriendo arreglos: Ya sabemos como crear un arreglo y acceder a sus celdas para asignarles valores, leer estos desde la entrada o escribirlos. Tambin sabemos que cada celda funciona como una variable independiente y que siempre podemos acceder a la que queramos si sabemos como estn numeradas las celdas. Es por esto que conviene siempre que los ndices vayan de 1..N, o sea de 1 hasta N donde N es la cantidad de celdas del arreglo. Esto ayuda a evitar confusiones y hace todo mucho ms entendible. Sin embargo pueden hacerlo de otra manera si consideran que para cierto caso les resulta ms til. Muchas veces resulta necesario recorrer un arreglo, ya sea para buscar un elemento de l o para trabajar de algn modo con los valores de sus celdas, de otro modo para qu lo habramos creado entonces? Aunque les dej esto como ejercicio, espero lo hayan hecho antes de llegar aqu, veremos un ejemplo donde el usuario ingresa 10 valores enteros para un arreglo y luego nosotros desplegaremos stos para mostrrselos y adems le mostraremos la suma de ellos: 1 2 3 4 5 6 7 8 9 10 11 PROGRAM sumaCeldas; Const N= 10; //Dimensin del arreglo. Type arr= array[1..N] of integer; Var arreglo1: arr; i, suma: integer;
Vladimir Rodrguez
60
Vladimir Rodrguez
61
Vladimir Rodrguez
62
En la pgina siguiente hay una serie de ejercicios. No continen con el tutorial hasta que sean capaces de realizarlos todos, o de lo contrario, no podrn con los temas que vendrn a continuacin
Ejercicio1: Escriban un programa que lea diez enteros de la entrada estndar y guarde estos en un arreglo. El programa debe indicar el mayor de ellos y el ndice de la posicin en la que aparece, as como tambin, el menor de ellos y su posicin. En caso de que se repita un valor mostrarn el ndice de la primera celda en que aparezca. Asumimos que siempre se ingresan enteros en la entrada estndar. Ejemplo1: Ingrese 10 enteros: 2 3 1 4 5 10 6 7 8 9 El mayor entero es 10 en la posicin 6 El menor entero es 1 en la posicin 3 Ejemplo2: Ingrese 10 enteros: 2 3 2 20 5 20 6 7 8 9 El mayor entero es 20 en la posicin 4 El menor entero es 2 en la posicin 1
Ejercicio2: Este ejercicio conlleva cierta dificultad, sobretodo porque trabajarn con dos arreglos a la vez, cada uno de distinto tamao. Dada la definicin de tipo para representar cadenas de caracteres de largo M y N : CONST N = . . .; CONST M = . . .; { M < N } . . . TYPE CadenaM = ARRAY[1..M] Of Char;
Vladimir Rodrguez
63
Ejercicio3: Tiene una dificultad similar al anterior. Dada la definicin de tipo para representar cadenas de caracteres de largo N y M:
1. 2.
Escriban un programa que lea dos cadenas de largo M y N respectivamente, e imprima un mensaje en la salida estndar indicando si alguna letra en la primera palabra ocurre en la segunda. Escriban un programa que lea dos cadenas de largo M y N respectivamente, y determine si todas las letras en la primera palabra ocurren en la segunda.
Como ya saben, siempre que un ejercicio les de verdaderos problemas no tienen ms que escribirme a mstrvladi@hotmail.com explicndome sus dudas y les responder en breve. Siempre intenten todo lo que puedan y prueben bien sus programas antes de dar por terminado un ejercicio.
Vladimir Rodrguez
64
Arreglos bidimensionales: Comencemos por ver arreglos de dos dimensiones. Estos dibujarn una tabla de datos del mismo tipo, tambin conocidas matemticamente como matrices. Hay dos maneras de declarar arreglos bidimensionales, una es declarar un arreglo de un arreglo, o sea, un arreglo cuyas celdas sean tambin arreglos. Esta es la forma ms fea de hacerlo y la que menos recomiendo, pero existe. Vemoslo en un ejemplo: 1 PROGRAM BIDIMENSIONAL; 2 3 Type 4 arr1= array[1..5] of integer; 5 Tabla= array[1..7] of arr1; 6 7 Var 8 i, j: integer; 9 matriz: Tabla; 10 11 BEGIN 12 For i:= 1 to 7 do 13 For j:= 1 to 5 do 14 matriz[i,j]:= 0; 15 END. En la lnea 4 vemos un tipo arr1 que es un arreglo de 5 celdas de enteros. En la lnea 5 vemos el tipo Tabla que es un arreglo de 7 celdas del tipo arr1, o sea que cada celda es un arreglo de 5 celdas. Si dibujramos Tabla al igual que lo hicimos antes tendramos 7 celdas del 1 al 7, esto estara indicando cuantas filas tiene nuestra tabla. Si desplegamos cada celda de Tabla estaramos abriendo hacia la derecha 5 lugares con lo cual estaramos indicando las columnas de nuestra tabla. Columnas(j)
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
Filas(i)
Ese sera el dibujo de Tabla, como ven hay 7 filas (lneas horizontales) y 5 columnas (lneas verticales). El 0 corresponde a la posicin (3,3) de la tabla ya que est en la fila 3 y en la columna 3. Siempre para una tabla se especificarn las coordenadas de una celda nombrando primero el nmero de filas y luego el de columnas. En la lnea 9 del ejemplo declaramos la variable matriz del tipo Tabla. El trabajo con los arreglos bidimensionales es idntico al de los arreglos unidimensionales. Cada celda es una variable independiente y para acceder a cada una simplemente nombramos nuestro arreglo y damos entre parntesis rectos las coordenadas de nuestra celda separadas por comas. La forma genrica sera as: Variable_arreglo[fila,columna] de este modo, si quisiramos ir a la celda que est en la fila 7 y en la columna 4 deberamos escribir matriz[7,4].
Vladimir Rodrguez
65
Ejercicio2: Modifiquen los programas anteriores para que, si el usuario no ingresa un entero, notifiquen el error y vuelvan a pedir el valor correcto.
Ejercicio3 Se llama matriz cuadrada a toda aquella matriz que tiene tantas filas como columnas, o sea, si tiene 5 filas tiene 5 columnas. La transpuesta de una matriz cuadrada es aquella matriz que tiene como columnas lo que antes eran filas. O sea, si tenemos una matriz cuadrada A, una matriz cuadrada B es transpuesta de A si las columnas de B son iguales a las filas de A, o sea, si se satisface que B[i,j]=A[j,i] para todos los valores posibles de i y j.
Vladimir Rodrguez
66
Como ven, lo que en A eran filas, en B son columnas. La primera fila de A que contiene los valores 1, 2, 3, 4 y 5 es la primera columna de B 1, 2, 3, 4 y 5, y as sucesivamente. Escriban un programa que calcule la transpuesta de un arreglo de nmeros reales con cinco renglones y cinco columnas. Los valores de la matriz se leen desde la entrada estndar. El resultado se debe imprimir en la salida estndar. Recuerden la relacin B[i,j]=A[j,i] para que sean transpuestas. Este ejercicio no es muy complicado. Tal vez lo peor de esto es que trabaja con una propiedad matemtica de las matrices y para aquellos que no lo han estudiado puede resultar difcil de comprender. An as creo que no deberan tener problemas. Arreglos tridimensionales: Esto es bsicamente lo mismo. Se declaran exactamente igual solo que ahora habr tres subrangos entre parntesis rectos y por ende deberemos colocar tres coordenadas para indicar cual celda deseamos ver (una coordenada para la fila (altura), otra para la columna (largo) y otra para el eje (ancho)). Hago esta aclaracin porque si bien un arreglo bidimensional dibuja una tabla, uno tridimensional dibuja un prisma y por tanto debemos decir cuan a lo alto, largo y ancho queremos posicionarnos.
Esto es un arreglo bidimensional de 5 filas y 8 columnas. Visto de este modo es un rectngulo donde la cantidad de filas son su altura y la cantidad de columnas su largo. Este es un arreglo de 5x8.
As podramos visualizar un arreglo tridimensional de 5x8x6. Como ven es un arreglo de tablas que estaran todas bien pegadas formando un prisma. Nosotros debemos indicar en qu fila y columna est nuestra celda y en cual tabla de todas. Siempre indicamos [fila, columna, tabla]. Esto es un intento de que puedan visualizar un arreglo tridimensional. El punto rojo correspondera a la posicin [4,8,3] por estar en la cuarta fila, octava columna de la tercera tabla. El azul correspondera a la posicin [1,3,6]. Hasta aqu llegamos con arreglos. Ahora les propondr un PROYECTO.
Vladimir Rodrguez
67
En este caso el adivinador ha ganado. Noten que si suman Buenos + Regulares el resultado siempre ser menor o igual al nmero de letras en el cdigo (en este caso 4) y slo ser igual si todas las letras en el cdigo del adivinador estn en el del pensador. Nunca puede ser mayor. Dadas las siguientes declaraciones: Const primeraLetra= A; ultimaLetra= F; largoCodigo= 4; maxIntentos= 10; Type Codigo= array[1..largoCodigo] of char; deben programar el Master Mind de modo que la CPU haga de pensador y el usuario de adivinador. El programa deber crear un cdigo al azar y luego el usuario intentar adivinarlo. Cada vez que el usuario ingrese un cdigo el programa le dar las notas correspondientes. Si el usuario adivina el programa debe mostrarle un mensaje de felicitaciones. Si el usuario llega la cantidad mxima de intentos sin adivinar, el programa debe notificarle que perdi y mostrarle cual era el cdigo que deba adivinar. Cada vez que el usuario ingrese un caracter que no corresponda al rango de letras del cdigo (A a F), o que ingrese un cdigo ms largo o ms corto que 4 caracteres el programa debe mostrar un mensaje de error y volver a pedir el ingreso del cdigo sin aumentar el intento actual del usuario. En cada momento el programa ir mostrando el intento actual. Veamos unos ejemplos de ejecucin: Dispones de 10 intentos para adivinar:
Vladimir Rodrguez
68
Crear un cdigo al azar y calcular los Buenos de un cdigo mostrado por el usuario no es muy difcil. La verdadera dificultad de esto estar a la hora de calcular los Regulares ya que cuando se repitan letras tendrn problemas para contabilizarlas una nica vez. Por ejemplo si el cdigo a adivinar es ADFD y se nos muestra el ABDB tenemos un Bueno y un Regular. La D est dos veces en el cdigo del pensador y una en el del adivinador, pero no corresponde a dos regulares, sino a uno. Otro caso es ADFD y ADBB. En este caso la A est bien y la D coincide en la segunda posicin, por tanto es un Bueno y no un Regular a pesar de que hay una D en la cuarta posicin del cdigo del pensador. Estas y muchas otras dificultades se les presentarn con este programa. Realmente deben ser capaces de realizarlo. Les llevar como mnimo una semana hacerlo bien, no se maten por hacerlo en menos tiempo, realmente cuesta. Esta vez tendrn muchas consultas que hacerme, as que escrbanme a mstrvladi@hotmail.com y pregunten todas las dudas que tengan. Existen muchsimas formas de realizar este programa. Su imaginacin es el lmite. Mucha suerte.
Vladimir Rodrguez
69
Vladimir Rodrguez
70
CUARTA PARTE
Vladimir Rodrguez
71
Vladimir Rodrguez
72
Vladimir Rodrguez
73
Vladimir Rodrguez
74
Vladimir Rodrguez
75
//Realizamos la cuarta separacin. Saltear(); //Escribimos la direccin del usuario. writeln('DIRECCIN: ',direccion);
Bien, este programa es idntico al anterior en el sentido de que realiza exactamente la misma tarea. Como suceda en la versin anterior, repetamos cuatro veces la declaracin de los FOR que se ocupan de dibujar los guiones y saltear lneas. Ahora lo haremos solo una vez dentro del procedimiento Saltear. Las declaraciones de constantes y variables quedan idnticas a las anteriores. Ahora la diferencia principal est en que luego de las variables declaramos a nuestro procedimiento. Vemoslo detalladamente: Procedure Saltear(); Begin For i:= 1 to 20 do Write(separador); For i:= 1 to lineas_a_saltear do Writeln; End; Tenemos su encabezado que consta simplemente de la palabra reservada procedure y luego de su identificador Saltear seguido por los parntesis. En este caso estos parntesis no tienen nada dentro porque este procedimiento no utiliza parmetros, eso lo veremos ms adelante. A pesar de que no existan parmetros, es conveniente incluir los parntesis vacos, aunque pueden no hacerlo si lo prefieren. Luego del encabezado podramos haber declarado constantes, variables, tipos y/u otros procedimientos, pero no hace falta para este ejemplo tan sencillo, as que procedemos directamente a iniciar el bloque principal con la palabra Begin. A partir de aqu se escriben todas las instrucciones que realizar el procedimiento, las cuales pueden ser lo que a ustedes se les ocurra, lo que les haga falta y que saben utilizarn en ms de una ocasin. No olviden que un procedimiento es un pequeo programa dentro del programa principal y por lo tanto admite exactamente las mismas reglas. En este caso nuestro procedimiento contiene a los dos FOR de antes y nada ms. Cerramos su bloque principal con End; con lo cual nuestro programa sabe que all termina la declaracin de Saltear. Bien, ya tenemos nuestro procedimiento que se encarga de dibujar la lnea de guiones y de dejar lneas en blanco, ahora solo debemos llamarlo cada vez que lo necesitemos. Esto es bien fcil, solo escribimos su nombre en el lugar en el que deseamos que realice su tarea y entre parntesis colocamos los parmetros que utilizaremos, en este caso como no hay parmetros solo dejamos los parntesis vacos aunque bien podramos no incluirlos. En este ejemplo vemos las cuatro llamadas en las lneas 31, 36, 42 y 48. En este caso en particular no ahorramos muchas lneas de cdigo al utilizar el procedimiento, pero como ya dije, un verdadero ejemplo sera muy complejo y en realidad los procedimientos y funciones utilizados llegan a estar formados por cientos y cientos de lneas. Como ven, utilizando un procedimiento solo hace falta escribir una nica vez una tarea especfica volvindose el cdigo ms legible y ms fcil de mantener en caso de errores o actualizacin. Cuando nuestro programa principal llega, por ejemplo, a la lnea 31, regresa a la 12 (o a aquella donde est el BEGIN del bloque principal del procedimiento) y realiza una por una las instrucciones all indicadas hasta llegar al END que finaliza el bloque del procedimiento. Pueden ver esto si utilizan el DEBUGER paso a paso utilizando F7 en vez de F8 para avanzar. Como ya expliqu antes, F7 ingresa dentro de los subrprogramas en el paso a paso y F8 no.
Variables Globales y Locales. Introduccin al Alcance de Identificadores: En nuestro ejemplo vimos que nuestro procedimiento no posea ninguna variable declarada dentro. Sin embargo tambin vemos que la nica variable que usa es i y la nica constante es lineas_a_saltear adems de que ambas solo son usadas por el procedimiento y por nadie ms. Dado este caso bien podramos haberlas declarado adentro del procedimiento para que se entienda que solo sern usadas por este y por nadie ms. Las variables declaradas en el programa principal son llamadas variables globales y las que se declaran dentro de un procedimiento o una funcin son llamadas variables locales o internas. Las variables globales son visibles dentro de todo el programa, o sea, uno puede hacer referencia a ellas en cualquier parte del cdigo. Por ejemplo, si tenemos una variable global entera llamada x podremos asignarle un valor en cualquier parte de nuestro programa, o sea, escribir x:=10 en la lnea nmero 5 y luego en la lnea nmero 1258 escribir x:= 25 o utilizarla en expresiones o para que el usuario ingrese un valor, etc., por citar ejemplos tontos. Las variables locales son visibles solo dentro del procedimiento o la funcin en que son declaradas. Esto implica que solo podamos hacer referencia a variables locales dentro del bloque principal de su subprograma y n fuera. Por ejemplo, si tenemos una variable string llamada nombre dentro de un procedimiento podremos utilizarla como queramos solo dentro su bloque principal, por ejemplo con una instruccin readln(nombre), pero si escribimos la misma instruccin u
Vladimir Rodrguez
76
Vladimir Rodrguez
77
Vladimir Rodrguez
78
Vladimir Rodrguez
79
Sombreado de Identificadores: Bien, continuemos con esto del alcance de los identificadores. Hasta ahora simplemente hemos visto que cada variable es visible dentro del cuerpo del subprograma en que ha sido declarada y no fuera. Ahora bien, la complicacin con esto comienza cuando tenemos una variable global con cierto nombre y luego en un subprograma tenemos una variable local con el mismo nombre. Pero como? No era que no pueden repetirse identificadores? En efecto, no pueden repetirse, pero como hemos visto, una variable local es visible solo dentro del subprograma donde se ha declarado y por tanto fuera de este es como si no estuviera declarada, como si no existiera, por lo tanto es posible declarar otra con el mismo nombre. Pero si la variable global es visible dentro de un subprograma, cuando adems declaramos dentro de este una variable local con el mismo nombre cmo sabemos a cual nos estamos refiriendo dentro del subprograma? Esto es simple pero genera muchas confusiones. Si tenemos una variable global con un nombre idntico al de una local, cuando dentro del subprograma nos referimos a ese identificador tiene precedencia la variable local, o sea, esta sombrea a la global dentro del subprograma. Veamos un ejemplo bien sencillo de entender: 1 PROGRAM sombreo_identificadores; 2 3 Var 4 X: integer; 5 6 Procedure sombreo(); 7 Var 8 X: integer; 9 10 Begin 11 X:= 10; 12 End; 13 14 BEGIN 15 Sombreo(); 16 END. En este ejemplo tenemos una variable global X y una local dentro del procedimiento sombreo tambin llamada X. Como vemos, dentro del procedimiento he inicializado X en 10. El programa principal lo nico que hace es llamar al procedimiento. Cul de las dos X vale 10? Como la inicializacin fue dictada dentro del procedimiento, al referenciar a X nos estamos dirigiendo a la X local y no a la global, por lo tanto la X que vale 10 es la interna al procedimiento y la otra no sabemos cuanto vale porque no le hemos dado ningn valor an. Ahora veamos otro ejemplito sencillo: 1 PROGRAM sombreo_identificadores; 2 3 Var 4 X: integer; 5 6 Procedure sombreo(); 7 Var 8 X: integer; 9 10 Begin 11 X:= 10; 12 Writeln(X); 13 End; 14 15 BEGIN 16 X:= 5; 17 Sombreo();
Vladimir Rodrguez
80
Vladimir Rodrguez
81
Declaracin de parmetros de tipos diferentes: El encabezado del procedimiento anterior era Procedure calcularArea(b, h: real); pero bien poda haber sido Procedure calcularArea(b: real; h: real); Es posible declarar los parmetros por separado indicando su tipo y utilizando el punto y coma para comenzar con la declaracin siguiente. Sin embargo resulta ms til declarar todos los parmetros de un mismo tipo especificando solo una vez dicho tipo, tal y como lo hice en el ejemplo del tringulo. Ahora veamos ejemplos de encabezados sencillos donde hay parmetros de tipos distintos: Procedure procedimiento1(a, b: real; x, y: integer; letra: char; nombre: string); Como ven tenemos seis parmetros, dos reales, dos enteros, un carcter y una cadena. Ese orden de declaracin deber respetarse para llamar al procedimiento: procedimiento1(un real, otro real, un entero, otro entero, un carcter, una cadena); Si cambian dicho orden tendrn un error de compilacin. Otro ejemplo: Procedure procedimiento2(a, b: integer; n: char; x: integer); Aqu hay cuatro parmetros, dos enteros, un carcter y otro entero. Por qu no declar todos los enteros en una sola declaracin? O sea, este encabezado bien podra ser Procedure procedimiento2(a, b, x: integer; n: char); pero aunque son los mismos parmetros el orden est en lugares diferentes y por tanto los valores se pasarn en dicho orden. En el primer ejemplo la llamada sera as: Procedimiento2(un entero, otro entero, un carcter, un entero); y en el segundo ejemplo sera as: Procedimiento2(un entero, otro entero, un entero, un carcter); Tengan mucho cuidado con esto ya que el orden de declaracin y el de invocacin deben ser iguales. A veces por una cuestin de entendimiento es mejor utilizar un encabezado como el del primer ejemplo. Veamos algo tonto pero tal vez les ayude a entender esto: Supongamos que tenemos un procedimiento del cual no nos interesa su funcionamiento pero toma como parmetros el nombre de dos usuarios y sus edades. Un encabezado podra ser este: Procedure usuarios(nombre1, nombre2: string; edad1, edad2: real);
Vladimir Rodrguez
82
Asignaciones de parmetros: Como ya dije antes, los parmetros funcionan como variables locales al procedimiento al que pertenecen y por lo tanto pueden usarse como tales. O sea, podemos asignarle valores a un parmetro, utilizarlo en una expresin, hacer READ y WRITE, etc. No creo que esto requiera de ejemplos ya que ustedes deben trabajar muy bien con las variables a estas alturas.
Parmetros Nominales y parmetros Efectivos: Llamamos parmetros nominales a los nombres (identificadores) que aparecen en el encabezado del procedimiento o funcin y parmetros efectivos a los que usamos en la llamada al subprograma, ya sean valores, variables o expresiones. En nuestro ejemplo del tringulo los parmetros nominales eran b y h, y los efectivos eran base y altura.
Hasta ahora hemos visto el mtodo de pasaje de parmetros por valor, o sea, nosotros le pasamos valores a los parmetros en la llamada al procedimiento y sus parmetros se inicializan con dichos valores para trabajar. Lo que sucede luego con los parmetros no influye en el resto del programa, solo en el interior del procedimiento, excepto claro que modifiquemos a propsito variables globales, cosa que no recomiendo. Entonces, si dices que no es recomendable modificar variables globales, cmo podemos obtener resultados de un procedimiento? Esa sera una buena pregunta. Si luego de calcular el rea de tringulo quisiramos seguir trabajando con ese valor, pero no queremos utilizar variables globales qu hacemos? All es donde entra el pasaje de parmetros por referencia, pero no hablar de ello todava, ya que es lo ms complicado de este tema. En vez de eso hablemos primero de las funciones.
Vladimir Rodrguez
83
Vladimir Rodrguez
84
Funciones Booleanas: El tipo Boolean es un tipo simple que toma uno de dos valores posibles: TRUE (verdadero) o FALSE (falso). Esto por supuesto ustedes ya lo saben. Las funciones pueden ser del tipo boolean ya que como acabo de decir es un tipo simple. El hecho de que me tome un trabajo extra para tratarlas es para que ustedes puedan ver las formas ms comunes en las que estas son utilizadas. Ahora veremos el ejemplo del rea del tringulo donde tendremos dos funciones, una para verificar que lo ingresado por el usuario corresponde a dimensiones adecuadas para el tringulo, en tal caso la funcin devolver TRUE y en caso contrario FALSE. Si las dimensiones son correctas entonces llamaremos a la funcin para calcular el rea del tringulo para la cual solo nos limitaremos a hacer el clculo ya que siempre la llamaremos en caso de que los datos ingresados sean correctos. 1. PROGRAM triangulo; 2. 3. Var 4. base, altura, area: real; 5. 6. Function verificarDatos(b, h: real): boolean; 7. Begin 8. verificarDatos:= (b>0) and (h>0); 9. End; 10. 11. Function calcularArea(b, h: real): real; 12. Begin 13. calcularArea:= b*h/2; 14. End; 15. 16. BEGIN 17. //Pedimos el ingreso de datos al usuario. 18. write('Ingaresa el valor de la base: '); 19. readln(base); 20. write('Ingresa el valor de la altura: '); 21. readln(altura); 22. 23. If verificarDatos(base,altura) then 24. writeln('El rea es: ',calcularArea(base,altura):1:2) 25. else 26. writeln('Has ingresado alguna dimensin menor o igual a 0.'); 27. END. Este ejemplo debera de resultarles bien sencillo de entender. Tenemos dos funciones declaradas. Solo explicar el funcionamiento de la funcin verificarDatos: Como pueden ver tiene dos parmetros reales y la funcin es del tipo boolean. La sentencia de la lnea 8 es igual a la que se usa para las variables del tipo boolean, o sea, dicha sentencia es equivalente a escribir: If (a>0) and (h>0) then verificarDatos:= true else verificarDatos:= false;
Vladimir Rodrguez
85
Vladimir Rodrguez
86
Como habrn visto, hasta el momento hemos usado solo el pasaje de parmetros por valor tanto para funciones como para procedimientos. La prxima seccin de este manual explicar el pasaje de parmetros por referencia. Esto llevar un poco de trabajo para su entendimiento, pero si se esmeran vern que no es tan complicado. Espero que hasta ahora hayan entendido todo. Como ya saben pueden escribirme a mi casilla de correo mstrvladi@hotmail.com para consultar cualquier duda que tengan y yo se las responder a la brevedad. Tambin pueden sugerir ejercicios o sealar errores en el manual, cosas que tal vez resulten inentendibles, etc.
Vladimir Rodrguez
87
Vladimir Rodrguez
88
Figura 1
Var
x, y: integer; a: char;
Figura 2
15 ? ? ?
x y a
10 c
x y a
Figura 3
y:= x;
Como pueden ver all lo nico que hemos hecho fue asignarle a y el valor de x y por lo tanto lo que sucedi fue que se copi el valor que estaba en la celda de x hacia la celda de y. Tambin puede verse que cada celda sigue siendo independiente y cada variable sigue manteniendo su misma celda, o sea, cada flecha sigue apuntando al mismo lugar que antes.
10
x y a
10 c
Vladimir Rodrguez
Eso es lo que no suceder en el pasaje por referencia ya que lo que hacemos en ese caso es cambiar la direccin de las flechas. Es importante notar que las celdas no se guardan en ningn orden especfico, por eso la celda de x est ms abajo que la de y a pesar de que la primera variable se declara antes. Veamos un ejemplo de un procedimiento llamado proc1 con tres parmetros, num1, num2 y letra, a los cuales les pasamos sus valores con el pasaje por valor tal y como lo hemos venido haciendo hasta ahora:
89
Figura 4 10 15 c 10 15 c
x Y a num1 num2 letra
Var x, y: integer; a: char; Procedure proc1(num1, num2: integer; letra: char); (. . .) //Bloque del procedimiento. BEGIN x:= 10; y:= 15; a:= c; proc1(x,y,a); END.
Pues bien, veamos lo que ilustra esta figura. Declaramos las variables globales x, y como enteros y a como carcter. El procedimiento tiene los tres parmetros que ya nombre los cuales tambin funcionan como variables (eso ya lo expliqu antes). Al comenzar el programa le asignamos a cada variable global un valor especfico de acuerdo a su tipo y luego llamamos al procedimiento indicando qu valor de cada variable debe asignarse a cada parmetro. Eso lo hacemos al colocar las variables entre parntesis al llamar al procedimiento ya que el orden en que esas se colocan es el que indica cual variable corresponde a cual parmetro. De ese modo copiamos el valor de x a num1, el de y a num2 y el de a a letra. A los efectos de lo que sucede en memoria eso es lo mismo que hacer las siguientes asignaciones: num1:= x; num2:= y; letra:= a;
Ahora veamos el mismo ejemplo pero pasaremos por referencia el valor del parmetro letra, los otros dos seguirn siendo por valor. Aqu podrn apreciar qu es lo que le indica al programa cul parmetro se toma por referencia y cual no. Usar dos figuras para este ejemplo, una para antes de la llamada al procedimiento y otra para luego de la misma:
Vladimir Rodrguez
90
Figura 5 10 15 c ? ? ?
x Y a num1 num2 letra
Var x, y: integer; a: char; Procedure proc1(num1, num2: integer; var letra: char); //Bloque del procedimiento. BEGIN x:= 10; y:= 15; a:= c; END.
Vean que el encabezado del procedimiento sigue siendo igual excepto porque usamos la palabra reservada VAR antes de declarar el parmetro letra. Usar esta palabra antes de declarar los parmetros indica que los valores de estos sern tomados por referencia. Ahora veamos lo que sucede luego del llamado al procedimiento:
Figura 6 10 15 c 10 15
x Y a num1 num2 letra
Var x, y: integer; a: char; Procedure proc1(num1, num2: integer; var letra: char); //Bloque del procedimiento. BEGIN x:= 10; y:= 15; a:= c; proc1(x,y,a); END.
Vladimir Rodrguez
91
Veamos ahora dos ilustraciones ms. En la Figura 7 vemos lo que sucede si los parmetros num1 y num2 se toman por referencia y letra por valor, y en la Figura 8 vemos lo que sucede si todos son parmetros tomados por referencia:
10 15 c
x Y a num1
Var
Figura 7
x, y: integer; a: char;
Procedure proc1(var num1, num2: integer; letra: char); //Bloque del procedimiento. BEGIN x:= 10; y:= 15; a:= c; proc1(x,y,a); END.
num2 letra
Vladimir Rodrguez
92
10 15 c
Var
Figura 8
x, y: integer; a: char;
Procedure proc1(var num1, num2: integer; letra: char); //Bloque del procedimiento. BEGIN x:= 10; y:= 15; a:= c; proc1(x,y,a); END.
Dado que utilizar pasaje de parmetros por referencia modifica al parmetro efectivo usamos estos como parmetros de entrada/salida a un procedimiento. Esto es muy til ya que a diferencia de una funcin podemos obtener muchos datos de salida en vez de uno solo. NOTA: No es recomendable utilizar el pasaje de parmetros por referencia para las funciones. Con esto quedara abarcado todo lo referente a los subprogramas y por tanto es hora de practicar todo lo que han ledo ya que sin prctica no avanzarn un pelo. Es totalmente imprescindible que los hagan todos o de lo contrario no servir de nada todo lo dado hasta ahora. Los ms importantes son los ltimos, escritos en letra roja los que corresponden a un parcial de facultad. Todo lo dado en las clases es lo que yo les he explicado aqu y los ejercicios que les he puesto, incluso los enlaces de inernet, perteneces a la facultad y por tanto ustedes son totalmente capaces de resolver estos ejercicios habiendo ledo este manual y habindose esforzado en realizar los ejercicios planteados y estudiar los ejemplos que les he puesto.
Ejercicios: Ejercicio 1: Expliquen la diferencia entre un parmetro formal (o nominal) y un parmetro verdadero (o efectivo).
Ejercicio 2: Escriban un procedimiento con solo tres parmetros enteros: a, b y c. El efecto del procedimiento debe ser girar los valores de los parmetros hacia la derecha de manera que, despus de la ejecucin, el valor que originalmente estaba en a quede en b, el que estaba en b quede en c y el que estaba en c quede en a. Ejemplos de entrada: 4 1 7 -12 6 2 Ejemplos de salida: 7 4 1 2 -12 6
Vladimir Rodrguez
93
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1. 2. 3. 4. 5. 6.
PROGRAM alcance; Var tum, num, temp: Integer; Procedure Prog (a, b: integer; VAR c: integer); Var reloj: Integer; Begin reloj:= a * b; reloj:= reloj + 1; c:= reloj + a; WriteLn(a, b, c, reloj) End; (*Programa principal*) BEGIN tum:= 1; num:= 2; Prog(tum, num, temp); WriteLn(temp); tum:= 0; num:= 1; Prog(tum, num, temp); WriteLn(temp) END.
Identifiquen la/s variable/s globales del programa principal. Identifiquen la/s variable/s local/es declarada/s en el procedimiento Prog. Identifique los parmetros formales del procedimiento Prog. Determine los parmetros de entrada (de valor) y de salida (por variable). Determine los parmetros por valor verdaderos (efectivos) en la primera invocacin del procedimiento. Determine los parmetros por valor verdaderos (efectivos) en la segunda invocacin del procedimiento. Determine la salida estndar del programa. Cuntas lneas se escribirn?
PROCEDURE Prueba (x: Real; y: Integer; VAR z: Real); y las siguientes variables
VAR tiempo : Real; tiempo_i : Integer; Determinen cules de las siguientes invocaciones son correctas. Justifiquen. Prueba( 1, 2.0, tiempo ); Prueba( tiempo_i, 3, tiempo ); Prueba( tiempo_i, 3, 2.0 ); Prueba( 1, 3, tiempo_i ); Prueba( 5 * tiempo_i, round( 7.3 ), tiempo ); (* tiempo_i ya fue inicializada *) Prueba( tiempo, 3, tiempo ); Prueba( Prueba( 5, 33.8, tiempo ), 92, tiempo ); Cules son los errores en las invocaciones incorrectas? Ejercicio 5: Implementen un procedimiento que dado un arreglo de enteros devuelva el valor ms grande y el ndice en que ste ocurre. El largo del arreglo es dado por una constante N global al procedimiento. Ejercicio 6: Escriban una funcin llamada conv que convierta un arreglo de caracteres, cada uno de los cuales contiene un dgito decimal, al entero equivalente.
Vladimir Rodrguez
94
Ejercicio 7: Implementen una funcin que dados 2 arreglos de caracteres, determine si el primero es prefijo del segundo. Luego reescriban el Ejercicio 2 de la pgina 63 utilizando esta funcin. En caso de que la cadena ms corta pertenezca a la ms larga, despliegue en la salida estndar la posicin en que comienza la coincidencia. Tomando el primer ejemplo dado en la letra de dicho ejercicio, la salida sera: El texto 'tor' se encuentra dentro del texto 'totora' ( posicin de comienzo: 3 ). En caso de que no haya coincidencia, el mensaje deber ser el mismo que en el ejercicio 2 de la pgina 63.
Ejercicio 8: Dado el siguiente programa: PROGRAM principal (input, output); VAR a, b : Integer; PROCEDURE a1 (VAR x : Real); VAR c : char; PROCEDURE a2 (y : char); VAR d : Integer; BEGIN . . . END; BEGIN . . . END; PROCEDURE b1; VAR e : Integer; BEGIN . . . END; BEGIN . . . END; Indiquen las variables a las que se puede hacer referencia dentro del alcance del procedimiento a2, las que se pueden referenciar dentro de a1, las que se pueden referenciar dentro de b1 y las variables globales.
Ejercicio 9: Determinen la salida del siguiente programa si se obtiene de la entrada estndar el entero 4 (elegir la opcin a, b, c o d). PROGRAM Ppal (input, output); VAR x : Integer; PROCEDURE a (VAR y : Integer); BEGIN y := y * 2; END; PROCEDURE b (x : Integer); BEGIN x := x + 5;
Vladimir Rodrguez
95
Ejercicio 10: Determinen la salida del siguiente programa: PROGRAM Detectar (input, output); VAR a, b, c, d : Integer; PROCEDURE pal (y: Integer; VAR z: Integer); VAR c : Integer; BEGIN b := y * z; c := b + z; y := y + 1; z := z * (z + 1); WriteLn (b:3, c:3, y:3, z:3) END; BEGIN a := 2; c := 3; d := 5; pal (c, WriteLn b := 4; pal (b, WriteLn END.
d); (a:3, b:3, c:3, d:3); a); (a:3, b:3, c:3, d:3)
El simbolo - representa el salto de lnea. A: 15 20 4 30- 2 15 4 30- 9 10 9 6- 6 9 4 30 B: 15 20 4 30- 2 15 4 5- 9 10 9 6- 2 9 4 5 C: 15 20 4 30- 2 15 3 30- 8 10 5 6- 6 8 3 30 D: 15 20 4 30- 2 15 3 5- 8 10 5 3- 2 8 3 5
Ejercicio 11: Indiquen cual sera la salida del siguiente programa suponiendo que como entrada se le proporciona el ltimo dgito de su nmero de cdula de identidad. Por ejemplo, si su nmero de cdula es el 1.234.567-8, entonces el nmero que se ingresa es el 7. Program Ej1 (input,output); var z: integer; function f (y : integer) : boolean; begin z := z + y; f := y mod 2 = 0 end; procedure a (var y : integer);
Vladimir Rodrguez
96
Ejercicio 12: Indique cual sera la salida del siguiente programa suponiendo que como entrada se le proporciona el ltimo dgito de su nmero de cdula de identidad. Por ejemplo, si su nmero de cdula es el 1.234.567-8, entonces el nmero que se ingresa es el 7. El orden de evaluacion de las expresiones numericas depende de cada compilador. En este caso, asuma que ese orden es de izquierda a derecha. program jul2002 (input,output); var z: integer; function g (var x: integer):integer; begin x := x + 1; z := z + 1; writeln('Salida en g1:',x + z); g := x + z end; function f (x: integer; var y: integer): integer; function g(x: integer): integer; begin x := x + 1; writeln('Salida en g2:',x + z); g := x + z end; begin x := x + 1; z := z + x - y + 2; if x mod 2 = 0 then y := y + 1 else y := y - 1; x := g(x) + y; writeln('Salida en f:',x); f := x end; procedure p (var x,y: integer); begin y := f(x,y) + z; y := y + g(x) + x; x := x + 1; writeln('Salida en p:',y) end; begin readln(z); p(z,z); writeln('Salida final:',z + 1); end.
Vladimir Rodrguez
97
Vladimir Rodrguez
98
QUINTA PARTE
Vladimir Rodrguez
99
Vladimir Rodrguez
100
Recuerden siempre que la funcin ORD devuelve un valor del tipo integer. Ahora dganme cul sera la salida que obtendramos de las siguientes sentencias si pertenecieran a un programa donde estn declarados los enumerados anteriores: writeln(ord(Mes(0))); writeln(puntoCardinal(ord(domingo))); Es sumamente importante que al intentar obtener un elemento de un enumerado dado por su ordinal jams utilicemos un entero que est por fuera del rango de elementos del enumerado. Esto significa que, por ejemplo, para el tipo diasSemana no escribamos algo como esto: diasSemana(7), diasSemana(-1),
Vladimir Rodrguez
101
Read y Write con enumerados: Como primera cosa les digo que... NO ES POSIBLE HACER READ Y/O WRITE con los enumerados. Esto significa que, declarada una variable de un tipo enumerado, no podemos incluir esta dentro de un procedimiento READ, READLN, WRITE o WRITELN. Por ejemplo, para los enumerados anteriores, el siguiente fragmento de cdigo estara mal: Type diasSemana=(domingo, lunes, martes, miercoles, jueves, viernes, sabado); mesAo= (enero, febrero, marzo, abril, mayo, junio, julio, agosto, setiembre, octubre, noviembre, diciembre); puntoCardinal= (norte, sur, oeste, este); Var dia: diasSemana; mes: mesAo; punto: puntoCardinal; BEGIN read(dia); mes:= enero; writeln(' ',enero); punto:= sur; writeln('El da ',dia,' de ',mes,' fuimos al ',punto,'.'); END. Si han entendido lo que he hablado de enumerados hasta ahora podrn darse cuenta rpidamente de que los errores en este cdigo estn dados por el uso de variables de tipos enumerados en procedimientos READ y WRITE. Las asignaciones s son correctas. Noten que para inicializar una variable de tipo enumerado con un valor especfico lo hacemos como con cualquier otra, o sea, damos el nombre de la variable seguido del smbolo de asignacin := y luego el valor que le queremos dar escrito tal cual lo nombramos nosotros al incluirlo en el enumerado.
Comparaciones con enumerados: Segn el orden de enumeracin en que estn dados los elementos de un enumerado tenemos los mismos smbolos de comparacin que para los nmeros: < <= = >= > <> Menor que Menor o igual que Igual que Mayor o igual que Mayor que Distinto que
Por ejemplo, para el tipo puntoCardinal= (norte, sur, oeste, este) tenemos que norte<sur ya que el primero est en la posicin 0 y el segundo en la posicin 1. Lo mismo si la comparacin fuera de diferencia: norte<>sur. Supongo que no debera decir que no es posible comparar variables de tipos enumerados diferentes ya que seran variables de distintos tipos, o sea, para nuestro fragmento de cdigo anterior no sera posible comparar la variable dia con la variable mes ya que son de tipos diferentes, una del tipo diasSemana y la otra del tipo mesAo. Como debera resultar obvio ya, los tipos enumerados son tipos ordinales, o sea, tienen una cantidad finita de elementos y dado cualquiera de ellos es posible saber cul es el anterior y cual es el siguiente al mismo. Por lo tanto para estos tipos de datos podemos utilizar las funciones predefinidas de Pascal succ y pred, la primera para obtener el sucesor de un elemento siempre y cuando no nos estemos refiriendo al ltimo, y la segunda para obtener el
Vladimir Rodrguez
102
Vladimir Rodrguez
103
40. 41.{Escribimos la fecha en formato de oracin---------------------------------} 42. //Primero escribimos el da. 43. Case dia of 44. domingo: write('Domingo '); 45. lunes: write('Lunes '); 46. martes: write('Martes '); 47. miercoles: write('Mircoles '); 48. jueves: write('Jueves '); 49. viernes: write('Viernes '); 50. sabado: write('Sbado '); 51. End; 52. 53. //Ahora escribimos la fecha del mes. 54. write(fecha,' de '); 55. 56. //Ahora escribimos el mes. 57. Case mes of 58. enero: write('Enero del ao '); 59. febrero: write('Febrero del ao '); 60. marzo: write('Marzo del ao '); 61. abril: write('Abril del ao '); 62. mayo: write('Mayo del ao '); 63. junio: write('Junio del ao '); 64. agosto: write('Agosto del ao '); 65. setiembre: write('Setiembre del ao '); 66. octubre: write('Octubre del ao '); 67. noviembre: write('Noviembre del ao '); 68. diciembre: write('Diciembre del ao '); 69. End; 70. 71. //Escrbimos el ao. 72. write(anio,'.'); 73.END.
Analicemos un poco este cdigo desde el comienzo de su bloque principal. Primero pedimos al usuario que ingrese un ao y lo leemos como entero. Luego en la lnea 22 le indicamos que ingrese un nmero entre 1 y 12 para indicar un mes del ao, y en la lnea siguiente le asignamos a la variable mes el elemento cuyo ordinal es igual al valor indicado por el usuario menos 1. Esta resta se debe a que los meses del enumerado estn numerados del 0 al 11 y el usuario ingresa nmeros del 1 al 12. Aplicamos el mismo procedimiento para inicializar la variable dia. En ambos casos usamos una variable auxiliar llamada aux para leer los enteros ingresados por el usuario. Como ltimo dato pedimos que se ingrese la fecha del da, o sea, a qu da del mes nos estamos refiriendo y leemos este como un entero guardndolo en la variable fecha. En la lnea 39 escribimos la fecha en formato DD/MM/AAAA. Noten que en el procedimiento WRITELN de dicha lnea, para escribir el nmero del mes al que nos referimos escribimos el ordinal de la variable mes volvindole a sumar 1. Esto ya deberan entenderlo. Ahora comienza lo interesante ya que utilizamos dos veces la funcin CASE. En el CASE de la lnea 43 estamos diciendo: Si la variable da es igual al valor domingo entonces escribe 'Domingo ', sino, si es igual al valor lunes entonces escribe 'Lunes ',.... Esta funcin es equivalente a una anidacin de IF como la que sigue: If dia=domingo then write('Domingo ') else if dia=lunes then write('Lunes ') else if dia=martes then write('Martes ')... Si entendieron eso deberan ser capaces de entender el CASE de la lnea 57 ya que funciona exactamente igual. Como siempre les digo que ante cualquier duda o inquietud que tengan as como ante cualquier sugerencia escrbanme a mstrvladi@hotmail.com y les responder a la brevedad. Ahora entretnganse con algunos ejercicios acerca de los enumerados.
Vladimir Rodrguez
104
b)
e)
c)
f)
Ejercicio 2: Determinen cuales de las siguientes declaraciones son vlidas: a) TYPE letra = ('X', 'Y', 'Z'); b) TYPE lenguaje = (Pascal, Fortran, Basic); c) TYPE materias = (matematicas, historia, computacion, biologia); carrera = (matematicas, computacion); d)TYPE estado = (residente, ciudadano, extranjero); nacionalidad = (americano, europeo, africano, asiatico, otra); e) TYPE codigo = (1, 2, 3, 4, 5); f) TYPE codigo = (c1, c2, c3, c4, c5); g)TYPE estado = (soltera, casada, comprometida, divorciada); VAR type : estado; h) TYPE ciudad = (Paysandu, SanJose, Tacuarembo, Canelones); VAR ciudad : type;
i) VAR ciudad : (Rivera, Salto, Soriano, Rocha); j) PROCEDURE encontrar (VAR ciudad : (Minas, Florida, Flores)); k) TYPE trabajo = (obrero, oficinista, indefinido); . . . PROCEDURE buscar (VAR empleo : trabajo);
Vladimir Rodrguez
105
Ejercicio 4: Examinen la siguiente definicin : TYPE raro = (gugol, nudol, brudol, cudol, zudol, budol); Determinen el valor de las siguientes expresiones:
1. 2. 3. 4. 5. 6.
ord (gugol) ord (zudol) succ (brudol) succ (budol) pred (gugol) ord (succ (zudol))
1. 2. 3. 4.
ord ('7') - ord ('0') ord ('1') - ord ('1') chr (3 + ord ('0')) chr (0 + ord ('0'))
Ejercicio 6:: Examinen la siguiente declaracin: TYPE vocal = (a, e, i, o, u); VAR letra : vocal; uncar : char; Determinen si se ejecutar sin error el siguiente cdigo en PASCAL:
letra := a; WHILE letra <= u DO BEGIN Read (uncar); WriteLn (`El caracter capturado es ', uncar); letra := succ (letra) END letra := u; REPEAT Read (uncar); WriteLn (`El caracter capturado es ', uncar); letra := pred (letra) UNTIL letra = a
Vladimir Rodrguez
106
Declaracin: Bien, primero les dir como se declara genricamente un registro, luego veremos un ejemplo de programa donde guardaremos datos de dos personas, primero sin usar registros y luego usndolos. Type NombreDelRegistro= record campo1: tipo; campo2: tipo; . . . campoN: tipo; End; Como estamos hablando de un tipo resulta obvio que declararemos un registro debajo de la palabra TYPE. Al igual que a los dems tipos definidos por ustedes mismos debemos darle un nombre seguido del signo de igual y luego utilizamos la palabra reservada RECORD (registro en ingls). Despus definimos cada uno de los campos que contendr tal como si fueran variables (en efecto eso son), o sea, le damos un nombre seguido por dos puntos y luego le asignamos un tipo cualquiera recordando que si usaremos uno definido por nosotros mismos debemos haber declarado este antes. Veamos un ejemplo de declaracin de un registro llamado ESTUDIANTE en el cual guardaremos el nombre de un estudiante, su edad, su documento de identidad y el grado que cursa: Type Grados= (primero, segundo, tercero, cuarto, quinto, sexto); Estudiante= record nombre: string; edad, documento: integer; grado: Grados; End; Como ven primero tenemos un tipo llamado GRADOS que no es ms que un enumerado de los grados posibles que un estudiante puede cursar. Nuestro registro contiene cuatro campos (variables), una del tipo string, dos del tipo integer y una ltima del tipo grados, el cual fue declarado previamente por nosotros mismos. Ahora veamos los dos ejemplos de los que habl para ver cmo funcionan los registros. En el siguiente programa guardaremos el nombre, la edad, el documento de identidad y el grado de dos estudiantes distintos. Todos estos datos los asignaremos nosotros manualmente internamente en el programa, o sea, no haremos READ de nada, simplemente le daremos valores a nuestras variables y ya, como si nuestros estudiantes vinieran precargados en el programa: 1. PROGRAM Reg1; 2. 3. TYPE 4. Grados= (primero, segundo, tercero, cuarto, quinto, sexto); 5. 6. VAR 7. NombreEstudiante1, NombreEstudiante2: String; 8. EdadEstudiante1, EdadEstudiante2: Integer; 9. DocumentoEstudiante1, DocumentoEstudiante2: Integer;
Vladimir Rodrguez
107
Vladimir Rodrguez
108
Una estructura compleja: Teniendo los registros podemos crear estructuras muy complejas. Simplemente citar un ejemplo para que les quede ya que lo necesitarn para trabajar en lo que se viene. Supongan las siguiente declaracin TYPE Registro= Record Nombre: String; Documento: Integer; Edad: Integer; End; ListaRegistro= Array[1..7] of Registro; VAR MiLista: ListaRegistro; Como pueden observar simplemente he declarado un registro de tres campos y luego un arreglo de siete celdas donde cada una corresponde al registro. Si lo dibujara podramos tener algo as: MiLista Nombre Documento Edad 4
Si cada celda es un registro, entonces para acceder al campo Nombre de la celda nmero 1 tendramos lo siguiente: MiLista[1].Nombre Recuerden que cada celda de un arreglo es como una variable del tipo del arreglo, por lo tanto, cada celda de este arreglo es del tipo Registro por lo cual funciona como tal. Espero que este simple ejemplo les sirva para visualizar esto. Ahora les propongo un ejercicio para comprender mejor lo bsico de los registros para luego continuar con un programa ms complejo: Ejercicio 1: Determinen cules de las siguientes definiciones de registro son vlidas: a) TYPE estado = RECORD nombre : ARRAY[1..30] OF CHAR; edad : 0..120; sexo : (Fem, Masc); b) TYPE ejemplo = RECORD prueba : 0..100; final : 'A'..'F'; orden : 1..100 END; c) TYPE franco = RECORD prueba : 0..100; examen : 0..10; prueba : 0..100 END;
Vladimir Rodrguez
109
Ejercicio 2 Programa Complejo: En pos de comprender realmente lo que hemos aprendido hasta ahora, les propondr ir trabajando gradualmente en el programa que describir a continuacin. Su dificultad no se comparar con la de Master Mind, sin embargo ser de gran utilidad trabajar con l. Lo programaremos de forma sencilla, o mejor dicho, lo programarn de forma sencilla para luego ir agregando cosas. De este modo vern, adems de lo que refiere a los temas en s, lo complicado que puede resultar agregar cosas a un programa ya escrito. Por este motivo y por otros ms, yo les ir marcando ciertas estructuras que debern utilizar obligatoriamente ms las que ustedes mismos deseen agregar, de modo que todo sea bastante ms legible que si se hace a lo bruto. Vamos a ello: BASE DE PRODUCTOS Parte 1 Comenzaremos a armar algo as como una base de datos de un comercio pequeo. Dicho comercio tiene a su disposicin elementos que nicamente pueden corresponderse con las siguientes categoras: Comestibles, Frutas y Verduras, Higiene del hogar e Higiene personal. Como ven son solo cuatro. Asimismo, para cada elemento tenemos los siguientes datos que deseamos almacenar: Nombre: Simplemente una palabra que identifique al producto. ID: Un nmero que es nico para cada producto y lo identifica entre los dems. Stock: Qu cantidad de este producto tenemos a disposicin. Precio: Cuanto vale el producto. Categora: A cual de las cuatro categoras anteriores corresponde el producto. Para comenzar supondremos que el comercio tiene nicamente cinco productos a la venta, ni ms ni menos, y que no agregar ni quitar ninguno. Nuestro programa almacenar los datos de cada producto en un arreglo de cinco celdas, una para cada producto, cada una de las cuales almacenar todos los datos mencionados anteriormente. Los datos de cada producto sern ingresados mediante la entrada estndar por un usuario. Asumiremos para todos los casos que los datos ingresados son correctos, o sea que: ID es un nmero entero positivo y que no se repetir para ningn producto, que stock es un entero positivo o 0, que precio es un real positivo y que categora se corresponde con alguna de las anteriores. De este modo tendremos las siguientes declaraciones obligatorias: Const N= 5; Type Categorias= (comestibles, frutas_verduras, higiene_h, higiene_p); TNaturales= 1..MAXINT; Elemento= Record Nombre: String; Stock: TNaturales; Precio: Real; Categoria: Categorias; ID: TNaturales; End; Productos= Array[1..N] of Elemento; Al iniciar nuestro programa debe mostrarnos un men como el siguiente: MEN PRINCIPAL: 1) 2) 3) 4) Ingresar datos de productos. Ver lista de productos. Modificar productos. Vender.
Vladimir Rodrguez
110
Opcin: Para el Men Principal usaremos un procedimiento cuya firma ser la siguiente: PROCEDURE MenuPrincipal(VAR listaProductos: Productos); Luego entendern mejor el funcionamiento de esto, ya que para cada men tendremos un procedimiento distinto que ser llamado por el procedimiento MenuPrincipal. El nico parmetro all presente define a nuestra lista de productos, o sea, nuestro arreglos de 5 celdas. Veamos cada men por separado y su declaracin:
1)
Ingresar datos de productos: Este men nos pedir que ingresemos los datos de todos los productos en nuestro arreglo (lista) sin importar si ya haban datos o no, o sea, sobrescribiremos los datos existentes. Al seleccionar esta opcin veremos una pantalla como la siguiente:
|-----------Producto 1-----------| ID >> 1 Nombre >> Azcar Stock >> 10 Precio >> 50,5 Categoras: (1) (2) (3) (4)
Ingrese la opcin deseada >> 1 All pueden apreciar en negro lo escrito por el programa y en azul lo que ingresa el usuario. Cada opcin aparecer a medida que ingresamos la anterior, o sea, una vez ingresemos el valor de ID y presionemos ENTER, veremos la opcin de ingreso de Nombre. All ven el orden en el que se ingresan los datos. Como las categoras fueron declaradas como un enumerado y estos no pueden leerse desde la entrada estndar, debemos dar una lista de opciones a seleccionar por el usuario y luego en base a esta debemos asignar nosotros mismos el valor de la categora. Si continuramos ingresando datos tendramos algo como esto: |-----------Producto 1-----------| ID >> 1 Nombre >> Azcar Stock >> 10 Precio >> 50,5 Categoras: (1) (2) (3) (4)
Ingrese la opcin deseada >> 1 |-----------Producto 2-----------| ID >> 2 Nombre >> Harina Stock >> 10 Precio >> 42,5 Categoras: (1) (2) (3) (4)
Vladimir Rodrguez
111
Ver lista de productos: 0) 1) 2) 3) 4) Ver todos Comestibles Frutas y verduras Higiene del hogar Higiene personal
9) Volver al Men Principal Opcin >> La primera opcin de este men nos desplegar un listado con todos los elementos de la lista y las dems solo aquellos que se correspondan con la categora seleccionada. En todos los casos la salida ser as: Lista de elementos seleccionados (TODOS): Azucar Harina Manzana Banana Fideos ID: ID: ID: ID: ID: 1 2 3 4 5
Presione ENTER para volver al Men Principal... Lista de elementos seleccionados (FRUTAS y VERDURAS): Manzana Banana ID: 3 ID: 4
Presione ENTER para volver al Men Principal... La opcin 9 simplemente nos regresa al men anterior. No deberan tener problemas con eso. Para esto deben utilizar un procedimiento como el siguiente: Procedure MostrarListaProductos(listaProductos: Productos; opcion: char); donde opcion le indicar al procedimiento el criterio por el que tiene que mostrar los productos.
Vladimir Rodrguez
112
Modificar Productos: 1) 2) 3) 4) 5) 6) 7) Todos Segn ID Segn Nombre Comestibles Frutas y verduras Higiene del hogar Higiene personal
9) Volver al Men Principal Opcin >> Para esto debern implementar un procedimiento con la siguiente firma: PROCEDURE ModificarProductos(VAR listaProductos: Productos; opcion: char); el cual funcionar del mismo modo que MostrarListaProductos. Una vez elegida la opcin de criterio segn el cual modificaremos los productos, el programa nos mostrar los datos del primer producto a modificar y luego debajo nos pedir que ingresemos los nuevos datos. Supongamos que seleccionamos la ocpin 1: Modificando productos (TODOS): Nombre: Azucar ID: 1 Stock: 15 Precio: 50,5 Categora: Comestibles Ingrese los nuevos datos: Nombre >> Azcar ID >> 123 Stock >> 18 Precio >> 50,5 Categoras (1) Comestibles (2) Frutas y verduras (3) Higiene del hogar (4)Higiene personal Ocpion >> 1 Luego de lo cual pasaramos a modificar el siguiente producto, adecuado al criterio elegido en las siete opciones disponibles. Una vez terminados todos los productos adecuados se nos mostrar el siguiente mensaje Ha terminado de modificar los productos (TODOS). Presione ENTER para volver al Men Principal... Para mostrar los datos de un producto en pantalla deben implementar un procedimiento como el siguiente: PROCEDURE MostrarDatosProducto(listaProductos: Productos; indice: integer); donde indice indicar el lugar en la lista donde se encuentra nuestro producto. Si se le pasa indice como valor igual a 0 entonces el procedimiento mostrar el mensaje: No existen datos para mostrar. Cualquier otro valor debe ser una posicin existente en nuestro arreglo de elementos. Asimismo, para buscar un producto y obtener su ndice utilizarn las siguientes funciones segn el criterio de bsqueda (ID, Nombre o Categoras): FUNCTION BuscarPorID(listaProductos: Productos; ID: integer): integer; FUNCTION BuscarPorNombre(listaProductos: Productos; nombre: string): integer; FUNCTION BuscarPorCategoria(listaProductos: Productos; categoria: categorias): integer;
Vladimir Rodrguez
113
Vladimir Rodrguez
114
4) Vender: Esta opcin nos pedir el ID del producto que deseamos vender, nos mostrar sus datos luego, para pedirnos el Stock que deseamos vender, modificando esa cantidad en los datos del producto. Veremos el equivalente en dinero a la venta realizada para luego ver el mensaje de regreso al men principal. Veamos un ejemplo: Ingrese el ID del producto que desea vender: 1 Datos del producto: Nombre: Azucar
Vladimir Rodrguez
115
Buscar productos: 1) Segn ID 2) Segn Nombre 9) Volver al Men Principal Opcin >> La primera opcin nos pedir el ID del producto que deseamos buscar y luego nos mostrar sus datos en pantalla. La segunda opcin har lo mismo pero mediante el nombre. Si el nombre se repite en ms de un producto se nos desplegarn los datos de todos ellos en el orden en el que fueron ingresados o modificados. Veamos ejemplos: Buscando segn ID: Ingrese el ID del producto que desea buscar: 2 Nombre : Harina ID: 2 Stock: 10 Precio: 42,5 Categora: Comestibles Presione ENTER para volver al Men Principal... Buscando segn NOMBRE: Ingrese el nombre del/los producto/s: Manzana Nombre: Manzana ID: 3 Stock: 10 Precio: 30,00 Categora: Frutas y Verduras Fin de la lista. Presione ENTER para volver al Men Principal... Buscando segn NOMBRE: Ingrese el nombre del/los producto/s: Banana Nombre: Banana ID: 4 Stock: 10 Precio: 37,50 Categora: Frutas y Verduras Nombre: Banana ID: 24 Stock: 8 Precio: 42,5
Vladimir Rodrguez
116
Vladimir Rodrguez
117
Como ven, su nico cometido es evitarnos tener que escribir constantemente el nombre de nuestra variable del tipo registro seguido de un punto para luego especificar la variable del registro a la que queremos acceder. Utilizando WITH... DO simplemente escribimos el nombre de nuestra variable del tipo registro una nica vez y luego nos dirigimos a las variables internas directamente. Claro est que esto funciona solo dentro de los lmites del BEGIN y el END correspondientes a la funcin WITH, o sea, en nuestro programa accedemos directamente a las variables de Alumno solo dentro de las lneas 16 y 22 que corresponden al bloque de WITH. Del mismo que en estos ejemplos utilizamos los campos del registro para asignarles valores, podemos utilizarlos dentro de un WITH para hacer cualquier cosa de las que ya conocemos.
Vladimir Rodrguez
118
Como ven, solo conviene utilizar WITH cuando necesitamos trabajar nicamente con todos los datos del registro sin involucrar otros. Claro est que si no los identificadores no se repiten no hay problema. Lo que sucede en este programa es que el registro tiene un campo llamado Grado y a su vez he declarado una variable global llamada Grado. Esto es totalmente posible ya que en realidad no estoy repitiendo identificadores, la variable del registro en realidad se llama Alumno.Grado y mi variable global solo Grado. Sin embargo, dentro de WITH tendremos el problema que se ve en el ejemplo de la lnea 21, ya que intentamos asignar el valor 10 a nuestra variable global, pero para el compilador all nos estamos refiriendo a la variable Alumno.Grado que es de tipo Grados y no puede asumir valores enteros.
Vladimir Rodrguez
119
Si categoria toma el valor encargado entonces definimos la variable grado como un enumerado que corresponde a los grados de un encargado; si toma el valor peon entonces nos definimos la variable rango como otro enumerado con los rangos de un pen y su vez definimos la variable cargaHoraria como un subrango de enteros que va de 40 a 48 inclusive. Como en este caso definimos dos variables diferentes las separamos por punto y coma. Todas las variables declaradas para cada caso van entre parntesis. Las variables de un mismo tipo pueden declararse todas juntas separndolas por coma. Esto funciona como en los encabezados de un procedimiento o una funcin. A pesar de darle esta forma, el registro funciona como si estuviera declarado de esta manera:
Funcionario= Record Nombre: String; Documento: Integer; categoria: Categorias; grado: (Bajo, Medio, Alto); rango:(Novato, Practico); cargaHoraria: 40..48); End; Sin embargo, darle una aplicacin correcta a la estructura anterior hace que un cdigo sea mucho ms legible, corto, entendible y no van a creer cuantos errores de programacin nos ahorramos. Adems, la estructura del registro con variante nos da una visin mucho ms clara de la informacin que estamos manejando y de cmo utilizarla. El acceso a las variables (campos) del registro sigue siendo como siempre a pesar de que su declaracin ha cambiado, por eso mismo les he aclarado que el registro con variante funciona como si fuera este ltimo que ven aqu, sin embargo en este ltimo no es posible visualizar qu tipo de informacin corresponde a los distintos casos me comprenden? En el primero se ve claramente que si el funcionario es encargado debemos asignarle un grado, y si es pen le asignamos un rango y una carga horaria. Declarando el registro como uno normal da la impresin de que todos esos datos irn para todos los funcionarios, o sea, todos tienen una categora, un grado, un rango y una carga horaria, pero en realidad no es as. Veamos entonces un simple programa en el que tenemos un arreglo de tres celdas el cual contendr los datos de los empelados en cada una de ellas, o sea, tenemos tres personas. Los datos sern ledos de la entrada estndar y asumiremos que son correctos siempre. Utilizar la estructura del FOR para esto ya que ingresaremos tres funcionarios s o s, no me interesa nada ms:
Vladimir Rodrguez
120
Vladimir Rodrguez
121
48 hs): '); 67. readln(MisFuncionarios[i].cargaHoraria); 68. end; 69. End; 70. 71. writeln; 72. end; 73. END. Explicar un poco este programa, aunque no debera darles problemas ya que en s no es nada complejo, solo leemos y asignamos datos, nada ms. En la lnea 17 declaramos el tipo Personal que no es ms que un arreglo del tipo Funcionario, o sea, un arreglo donde cada celda representa a un registro. En la lnea 20 declaro una variable del tipo Personal, o sea, creo mi arreglo para trabajar; luego declaro la tpica variable i del tipo integer para iterar en las celdas del arreglo. Finalmente declaramos la variable opcion del tipo char, esta la usaremos para asignar valores a los tipos enumerados ya que estos no pueden leerse de la entrada estndar. El bloque principal de nuestro programa es un enorme FOR que iterar desde 1 a N en nuestro arreglo, o sea en todas sus celdas. Como N vale 3, nuestro FOR iterar 3 veces con lo cual terminar nuestro programa. En la lnea 30 leemos directamente el campo Nombre de la celda i de nuestro arreglo; hacemos lo mismo en la lnea 32 para el campo Documento. Luego desplegamos en pantalla las opciones para la categora de nuestro funcionario i, las cuales las leeremos como caracteres en la variable opcion. Dependiendo e la opcin elegida por el usuario ser la informacin que pediremos. Si se ingresa un 1 entonces pediremos los datos para un encargado y se ingresa un 2 pediremos los datos para un pen. Esto lo ven con el CASE opcion OF que inicia en la lnea 38 y cierra en la lnea 69. El funcionamiento de esta estructura no debera darles problemas. Lo que cabe destacar aqu es que, por ejemplo, si la opcin elegida es la 1, o sea, si estamos ingresando los datos de un encargado, solo usaremos las variables que guardan la informacin pertinente a esa categora y no tocamos las dems porque no nos interesan. Lo que quiero decir es que, por ejemplo, para un encargado no tocamos en ningn momento las variables rango o cargaHoraria porque no nos interesan y no trabajaremos con ellas en un posterior tratamiento de la informacin. Es un error asignar valores a campos que no se corresponden con la informacin que estamos tratando, o sea, si vamos a trabajar con un pen entonces nos aseguramos de ello y luego asignamos los valores solo y nicamente a las variables que son pertinentes a la categora de pen. Podramos, si queremos, asignar un valor de grado a pesar de estar trabajando con la categora pen, el compilador lo permitir tranquilamente y no habr ningn tipo de error en el programa en s, pero est mal porque un pen no tiene grado sino rango y no nos importa los valores que grado pueda tomar en este caso.
Vladimir Rodrguez
122
Vladimir Rodrguez
123
Vladimir Rodrguez
124
a)
grupo [1] := juanita;
b) c)
d) e)
f)
WITH juanita DO BEGIN nombre := grupo [50].nombre; salario := grupo [1].salario END;
Ejercicio 2: Supongan que una sociedad genealgica tiene un arreglo de registros que indican la fecha de nacimiento y subndice en el arreglo de los registros de los padres y madres de varias personas. El arreglo de registros y las variables correspondientes podran ser parecidos a stos :
Vladimir Rodrguez
125
Ejercicio 3: Escriba una funcin en PASCAL llamada alfa que tenga como parmetro una cadena de caracteres (representada con un arreglo con tope) llamada frase y una variable de tipo carcter llamada letra. La funcin alfa produce el nmero de apariciones del carcter letra en la frase.
Ejercicio 4: (Requiere conocimientos de matemtica bsica de nivel secundario, concretamente deben saber hallar las races de cualquier polinomio de segundo grado (funcin cuadrtica)) Implementen un procedimiento que calcule las races de una ecuacin de segundo grado. El cabezal del procedimiento debe de ser el siguiente: Procedure Raices(a, b, c : Real; Var r : TipoRaices); 1. Definan el tipo TipoRaices utilizando la estructura de registro con variante. 2. Implementen el procedimiento pedido.
Ejercicio 5: (Este conocimiento est basado en una notacin matemtica comn para cualquier estudiante pero avanzada para su tratamiento. El conocimiento necesario se corresponde con los ltimos aos de secundaria y/o inicios de estudios terciarios) Se considera el tipo de dato Nerr que es la unin de los nmeros naturales (N) con el conjunto Err. El conjunto Err se define como {diverr, reserr, argerr}, donde diverr es el error de la divisin por cero,reserr es el error de la resta con resultado negativo y argerr es el error de cualquier operacin donde alguno de sus argumentos no es natural. Se definen las siguientes operaciones sobre elementos del tipo Nerr:
Vladimir Rodrguez
126
Ejercicio 6: (Matemtica de nivel terciario (lgebra lineal de matrices) Se desea implementar en PASCAL el producto de matrices de elementos de tipo Nerr. Las matrices a operar podrn tener diferentes dimensiones. Se sabe que si una matriz tiene dimensin mxn, m y n son enteros positivos que nunca superarn a constantes X e Y respectivamente. El producto entre matrices de tipo Nerr se define de manera anloga al producto de matrices de nmeros naturales con la suma y el producto para el tipo Nerr dado en el ejercicio anterior. Sea la matriz m1 de dimensin m x n y la matriz m2 de dimensin p x q. Si n = p el producto m1 x m2 tendr dimensin m x q, en caso contrario diremos que el producto falla. Definir en PASCAL el tipo MNerr, que representa las matrices de dimensiones m x n de tipo Nerr, para cualquier m entre 1 e Y, y para cualquier n entre 1 y X. Puede asumir que X e Y son mayores o iguales que 1. Adems MNerr debe tener un valor de error merr para el caso en que el producto de matrices falle. Implementar el procedimiento: PROCEDURE mprod (m1:MNErr; m2:MNerr; VAR m3:MNerr); El procedimiento mprod recibe dos matrices m1 y m2, retornando en m3 el producto m1 x m2 o merren caso de que dicho producto falle.
Ejercicio 7: En una isla del Caribe una banda de piratas se gana la vida asaltando barcos mercantes. Anualmente en la isla se realiza un evento multitudinario llamado la entrega de los premios Calavera. Para este ao los piratas estn pensando en entregar el premio Calavera de Oro al pirata que haya conseguido mas dinero asaltando barcos para la banda. Como usualmente los piratas discuten a quin le corresponden los premios en trifulcas interminables y sangrientas, este ao la comisin directiva de la banda ha decidido informatizar el registro de logros de los piratas en los distintos asaltos, con la esperanza de terminar as con algunas de las discuciones sobre los crditos que le corresponden a cada pirata.
Vladimir Rodrguez
127
(* Limite de piratas que pueden vivir en la isla. *) (* Limite de asaltos a los que estadisticamente puede sobrevivir (* Cantidad de digitos para almacenar un numero de cdula. *)
TYPE TipoCI=ARRAY[1..MAXDIGITOSCI] OF CHAR; TipoFecha=RECORD dia:1..31; mes:1..12; anho:INTEGER END; TipoAsalto=RECORD nombre_barco:ARRAY[1..30] OF CHAR; fecha:TipoFecha; botin:INTEGER banda *) END;
(* Nombre del barco asaltado *) (* Fecha del atraco *) (* Suma de dinero obtenida para la
ConjuntoAsaltos=RECORD (* Contiene informacin sobre los asaltos asaltos:ARRAY[1..MAXASALTOS] OF TipoAsalto; tope:0..MAXASALTOS END; TipoPirata=RECORD nombre:ARRAY[1..30] OF CHAR; CI:TipoCI; CASE estavivo:BOOLEAN OF pirata *) TRUE: (asaltos:ConjuntoAsaltos); asaltos *) *) FALSE: () END; Banda=RECORD pirata:ARRAY[1..MAXPIRATAS] OF TipoPirata; piratas de la banda *) tope:0..MAXPIRATAS *) END;
*)
(* Nombre del pirata *) (* Cdula de identidad *) (* Indica el estado vital actual del (* Contiene informacin sobre los (* realizados por el pirata
Se pide:
Parte a)
Implementar la funcin: FUNCTION dinero_obtenido_por_pirata(pirata:TipoCI, anho:INTEGER, b:Banda) : INTEGER; la cual debe recibir la CI de un pirata, un ao y una banda, y retornar la suma de dinero obtenida por el pirata para la banda en ese ao. En caso de que el pirata se encuentre muerto o no se encuentre en la banda debe retornar 0. Se sugiere implementar primero funciones:
Vladimir Rodrguez
128
Parte b)
Implementar la funcin: PROCEDURE hallar_ganadores(piratas:Banda, anho:INTEGER; VAR piratas_merecedores:ConjuntoCIs) la cual dada una banda de pirata y un ao, devuelve en piratas_merecedores las cdulas de los piratas vivos merecedores del premio Calavera de Oro. Tengamos en cuenta, que varios piratas pueden coincidir en la cantidad de dinero obtenido para la banda y que no hayan otros piratas que los superen. ConjuntoCIs se declara como sigue: TYPE ConjuntoCIs=RECORD cedulas:ARRAY[1..MAXPIRATAS] OF TipoCI; (* Arreglo de cedulas *) tope:0..MAXPIRATAS (* Cantidad de cdulas en el array *) END;
Ejercicio 8: Examen Agosto 2001 Se desea trabajar con una aritmtica de Naturales de hasta 100 dgitos. Los Enteros de PASCAL no soportan dicha aritmtica, por lo que se piensa utilizar la siguiente representacin de Naturales basada en arreglos con tope: CONST MaxDig = 100; TYPE Digito = 0..9; Natural = RECORD digitos : ARRAY[1..MaxDig] OF Digito; tope : 0..MaxDig; END; Implementar la suma de Naturales representados en trminos de la estructura anterior. Utilizar el siguiente cabezal: Procedure Suma(a, b : Natural: Var c : Natural);
Vladimir Rodrguez
129
Nombre: El nombre de un producto debe estar formado nicamente por nmeros y/o letras maysculas y/o minsculas. Cualquier otro carcter se tomar como invlido. ID: El ID debe ser un nmero entero positivo, o sea, un natural. Si el dato ingresado no se corresponde con un nmero entero positivo o directamente no es un nmero lo consideraremos como error. Para el ID tambin deberemos corroborar que no se repita en todos los ingresos anteriores, ya que eso tambin sera un error. Stock: Debe ser un nmero entero positivo. Tiene las mismas restricciones que el ID con la excepcin de Stock s puede repetirse. Precio: Este dato debe ser un nmero real positivo, en caso de ser negativo o no ser un nmero lo consideraremos como error. Este dato les traer problemas de captura ya que leer la entrada como carcter y luego pasarla a real no es tan sencillo como pasarla a entero. Categora: Para este dato solo debemos corroborar que lo ingresado se corresponda con una de las opciones mostradas en pantalla, de lo contrario ser un error. Esto se aplicar a todas las pantallas de nuestro programa en las que el usuario deba elegir una de varias opciones.
Para controlar el nombre ingresado deben implementar una funcin como la siguiente: FUNCTION NombreCorrecto(nombre: CadenaChar): Boolean; donde CadenaChar ser un tipo global declarado de la siguiente manera: CONST MaxLargoNombre: 20; TYPE CadenaChar= RECORD nombre: Array[1..MaxLargoNombre] of Char; tope: 0..MaxLargoNombre; END; La funcin NombreCorrecto devolver TRUE si la cadena ingresada cumple las condiciones de ms arriba, FALSE en caso contrario.
Vladimir Rodrguez
130
1 : Parte entera 1, parte real 0. 1,000: Parte entera 1, parte real 0. 01,0: Parte entera 1, parte real 0. 12,52: Parte entera 12, parte real 52.
El truco est entonces en obtener estas dos partes por separado como nmeros enteros, de este modo si tenemos el 12,52 tendremos por un lado el 12 y por otro lado el 52, ambas partes como enteros, lo cual es fcil llevar a valor integer con la funcin StringToInt. Una vez tenemos el entero 12 por un lado y el entero 52 por otro debemos establecer el 52 como el real 0,52 y sumrselo al 12 con lo cual obtenemos 12,52. 12 + 0,52= 12,52.
Vladimir Rodrguez
131
Espero que con esto puedan implementar correctamente la funcin RealToInt. Cualquier duda estoy a su disposicin en mi correo mstrvladi@hotmail.com. Para comprobar que la opcin de la categora seleccionada es correcta, o para comprobar que la opcin de un men se corresponde con una de las disponibles deben implementar una funcin como la siguiente: FUNCTION OpcionCorrecta(inicioRango, finRango: char): Boolean; la cual recibir en sus parmetros el inicio del rango de opciones y el final del mismo, por ejemplo, viendo un men como los que tiene nuestro programa: Modificar Productos: 1) 2) 3) 4) 5) 6) 7) Todos Segn ID Segn Nombre Comestibles Frutas y verduras Higiene del hogar Higiene personal
9) Volver al Men Principal Opcin >> inicioRango sera '1' y finRango sera '9'. Ahora bien, si son obseravdores deberan preguntarse qu pasa si el usuario ingresa el valor '8'. Pues sera una buena pregunta. Para estos casos debemos modificar nuestro men de modo que todas las opciones sean consecutivas: Modificar Productos: 1) 2) 3) 4) 5) 6) 7) Todos Segn ID Segn Nombre Comestibles Frutas y verduras Higiene del hogar Higiene personal
Estas modificaciones debern hacerlas con todos los mens que hagan falta. Asumiremos siempre que inicioRango<finRango. Finalmente queda por ver el hecho de que la lista de elementos sea variable. Para esto implementaremos un arreglo con tope de la siguiente manera:
Vladimir Rodrguez
132
Ingrese la opcin deseada >> 1 Ingresar otro producto (1= SI/2= NO): j ERROR: Dato inesperado, ingrese un dato vlido. Ingresar otro producto (1= SI/2= NO): 1 |-----------Producto 2-----------| ID >> 2 Nombre >> Harina Stock >> 10 Precio >> 42,5 Categoras: (1) Comestibles (2) Frutas y verduras
Vladimir Rodrguez
133
6) Volver al Men Principal Opcin: Podemos eliminar un elemento determinado dando su ID, o podemos eliminar toda una categora completa para agilizar las cosas. Veamos dos ejemplos: Supongamos que seleccionamos la opcin 1, luego se nos pedir el ID. Supondremos que el usuario ingresa un ID que no existe, lo cual es un error, se pedir de nuevo el dato, tras lo cual el usuario ingresar un ID correcto. Como podrn ver, para no eliminar nada y volver al men principal debe ingresarse la letra M, cualquier otra cosa ser un error. Ingrese el ID del producto a eliminar o M para volver al men principal: 12 ERROR: Dato inesperado, ingrese un dato vlido.
Vladimir Rodrguez
134
Presione ENTER para volver al Men Principal... Como ven, se mostrar un listado de todos los elementos eliminados. Estarn mostrados en el orden que fueron ingresados. Para cualquiera de estas opciones debern implementar el siguiente procedimiento: PROCEDURE EliminarProducto(VAR Lista: Productos; indice: integer); donde lista ser nuestro listado de elementos e indice ser el lugar de la lista donde est el elemento a eliminar. Cada vez que se elimina un elemento debemos correr todos los siguientes a ese un lugar a la izquierda y reducir el tope un nmero. Veamos una ilustracin:
Producto 1
Producto 2
Producto 3
Producto 4
Producto 5
Vaco
Vaco
5
TOPE= 5
All vemos una lista de elementos donde el arreglo tiene 7 celdas, 5 de las cuales contienen productos, las otras dos no, por lo tanto nuestro tope es 5. En este ejemplo eliminaremos el producto nmero 3, con lo cual nuestro arreglo quedara as.
Producto 1
Producto 2
Producto 4
Producto 5
Vaco
Vaco
Vaco
El producto nmero 3 fue eliminado con lo cual ahora el producto 4 y 5 se corren un lugar hacia la izquierda y el tope se reduce en 1, o sea, TOPE= 4 Producto 4 estaba en la celda 4 pero ahora est en la celda 3, Producto 5 estaba en la celda 5 pero ahora est en la 4, y donde estaba Producto 5 no hay nada que nos interese, por lo tanto corremos nuestro tope hacia la ltima celda que contiene informacin. Nuestro procedimiento debe hacer justamente esta tarea cada vez que se elimina un elemento de nuestra lista. Si por ejemplo se eliminaran todos los Productos menos el Producto 5 entonces quedara as, donde TOPE= 1: Producto 5 Vaco Vaco Vaco Vaco Vaco Vaco
1
Vladimir Rodrguez
7 135
Hasta aqu llegamos por ahora con nuestro proyecto. La siguiente parte de este tutorial trata el ltimo tema y, a mi gusto, el ms interesante, con lo cual modificaremos por ltima vez nuestro programa de modo que quede perfecto y utilice solo los recursos que necesita para funcionar, pero ya veremos esto. Deben intentar realizar todo lo que detallo aqu antes de continuar, no importa cuanto tiempo les lleve, cuantas veces se tranquen, si tienen que escribirme a m o buscar ms info en Internet, deben lograr hacerlo. No hay nada aqu que no puedan hacer con lo que saben hasta ahora, es ms, todo est pensado para que lo hagan justamente con estas herramientas. Si han logrado resolver la mayora de los ejercicios anteriores ms este programa, entonces han aprendido lo bsico de la programacin de forma excelente y podrn resolver cualquier tipo de situacin que no requiera estructuras mucho ms complejas que estas que hemos visto ms las que veremos a continuacin. Existe una herramienta de implementacin de funciones llamada RECURSIN, sin embargo no la veremos en esta entrega.
Vladimir Rodrguez
136
SEXTA PARTE
Vladimir Rodrguez
137
Hemos llegado al fin al tema ms importante de este tutorial, el que he venido nombrando desde hace tiempo y el que tal vez les haga quemar bastante ms las neuronas, los Punteros. Antes que nada djenme aclarar que el tipo Puntero no tiene nada que ver con el puntero del ratn.... jeje... Bueno, pongmonos serios. Como dije en la introduccin a la tercera parte de este tutorial, todos los tipos que hemos visto hasta ahora son estticos, o sea que una vez declaradas las variables de cierto tipo se asignar en tiempo de compilacin la memoria RAM necesaria para guardar el mayor valor de dicho tipo y mantendremos siempre esa memoria reservada para nuestro uso, la utilicemos o no. Un ejemplo bien sencillo son los arreglos con tope. Declarado un arreglo de N cantidad de celdas, al compilar el programa se dir que reservemos en memoria el espacio suficiente para guardar datos en todas ellas. Supongan que N es igual a 1000 en un arreglo con tope para guardar quin sabe qu cosa; al compilar el programa se asignar memoria para mil celdas aunque usemos solo una o dos, lo cual es un desperdicio. Es aqu donde entra en juego el tipo Punteros, ya que nos permitir pedir memoria en tiempo de ejecucin, tanta como necesitemos, y devolverla cuando queramos. Por eso llamamos a este tema Memoria Dinmica. Utilizando la estructura de los punteros, por ejemplo, podemos hacer que nuestro programa de productos de almacn pida memoria para guardar los datos de un producto cada vez que el usuario ingresa uno nuevo, y liberar dicha memoria cuando se eliminan los productos. Ese tipo de tareas lo haremos con una estructura llamada Listas Encadenadas, pero an nos faltan datos para llegar a ello, as que vallamos all con los punteros.
Vladimir Rodrguez
138
Vladimir Rodrguez
139
Vladimir Rodrguez
140
Alias de variables: Veamos lo siguiente paso a paso: TYPE puntero= ^integer; VAR p1, p2: puntero; Tenemos hasta este momento lo siguiente:
donde ninguna de nuestras variables est inicializada y por lo tanto no podemos hacer nada con ellas. Sigamos entonces y veamos lo que sucede: NEW(p1);
Como pueden ver hemos solicitado un lugar de memoria para p1, o sea lo inicializamos de la nada y le damos su espacio para almacenar un entero; dicho espacio contiene algn valor desconocido (basura). La variable p2 sigue sin inicializar por lo que contiene una direccin que no sirve para nada y que solo nos traer problemas hasta que le demos un valor. Ahora hacemos lo siguiente: p2:= p1 con lo cual obtenemos el siguiente resultado: Hemos asignado a p2 el valor de p1, o sea, le hemos dicho a p2 que apunte hacia la misma celda que p1. Cuando dos o ms punteros apuntan a una misma celda decimos que son alias. Esto puede ser muy til ya pero a la vez es muy peligroso ya que al modificar la celda de uno modificamos la del otro, tal como sucede con el pasaje de parmetros por referencia. Ahora veamos una asignacin similar pero muy diferente:
Vladimir Rodrguez
141
Noten ahora la diferencia entre estas dos asignaciones suponiendo que las mismas son escritas luego del cdigo anterior, o sea, o escribimos una o la otra, no una seguida de la otra: p1^:=p2^ p1:=p2
Como podrn observar, en el primer caso hemos copiado el contenido de la celda de p2 en la de p1 con lo cual ambas variables (p1^ y p2^) valen lo mismo. Esto es igual a hacer una asignacin del tipo X:= Y siendo X e Y variables de un mismo tipo. Ahora vean el segundo caso donde en vez de usar p1^ y p2^ usamos p1 y p2 directamente. Como podrn observar p1 pasa a apuntar al mismo lugar que p2 transformndose ambos en alias, sin embargo la celda a la que apuntaba p1 originalmente queda sin nadie que la apunte por lo cual ya no es posible acceder a ella, pero sigue conteniendo su valor guardado y por tanto sigue ocupada, no ha sido liberada. La forma correcta de hacer esto habra sido liberar primero la celda de p1 de modo que este quede indefinido y luego hacer la asignacin p1:= p2. Este tipo de errores es muy comn y pronto veremos alguno ms. El trabajo con la memoria dinmica requiere muchsima atencin para hacerlo bien y controlar todo correctamente. Por eso muchos programadores sostienen que es mejor tener un sistema que se encargue de liberar la memoria automticamente cada cierto tiempo como lo hace Java y otros no. Yo me inclino por los que no, pero es una simple opinin personal. Un uso incorrecto de NEW: Si hacemos NEW de un puntero ya inicializado estaremos pidiendo un nuevo espacio de memoria para l, con lo cual apuntaremos al nuevo lugar y dejaremos el otro en la nada. Sea p un puntero a caracteres: NEW(p); p^:= 'a'; Ahora, luego de estas instrucciones hacemos NEW(p);
Como vern, al hacer NEW de un puntero obtenemos un nuevo lugar en la memoria sin importar lo que hubiera antes. De este modo si el puntero estaba indefinido lo definimos, y si ya estaba definido lo redefinimos sin importar lo que haba antes. De este modo, si ya tenamos un lugar de la memoria asignado, obtenemos uno nuevo a costa de abandonar el otro pero dejndolo ah, con o sin valor asignado, pero no liberado y sin posibilidad de volver a acceder a l. Tengan esto siempre en mente: NEW nos da un nuevo espacio de memoria y nos apunta hacia l, no importa lo que haya sucedido hasta el momento, NEW define nuevamente de cero al puntero y ya. Si hacemos treinta NEW seguidos de un mismo puntero habremos creado treinta lugares de memoria pero solo apuntaremos al ltimo creado y los otros veintinueve quedarn en nuestra memoria, ocupando lugar, pero sin posibilidad de ser accedidos o usados. Por suerte los sistemas operativos se encargan de liberar la memoria mal usada.
Vladimir Rodrguez
142
Como vern, primero solicitamos memoria inicializando puntero mediante NEW con lo cual obtenemos nuestro espacio para almacenar un carcter as como la direccin de ese espacio. Luego liberamos ese espacio mediante DISPOSE con lo cual la informacin contenida en l se pierde, o sea, deja de existir puntero^ y puntero queda indefinido, o sea, su direccin es cualquier cosa. Luego nuevamente intentamos liberar memoria, sin embargo puntero no apunta a nada especfico, contiene una direccin errnea porque est indefinido, por lo tanto no podemos liberar memoria de un lugar al que no podemos llegar lo cual genera un error en tiempo de ejecucin y nuestro programa se cerrar abruptamente (si no es as depende del compilador, sin embargo dejar eso a suerte es un error en la metodologa de programacin). Como ven, utilizar DISPOSE con punteros no inicializados es un error grave y hay que tener mucho cuidado a la hora de utilizar este procedimiento. Por eso es complicado administrar bien la memoria que utiliza el programa, ms an cuando la cosa se vuelve compleja. Ahora que saben liberar memoria pueden hacer de forma correcta lo que seal como error anteriormente, o sea, cuando tenemos dos punteros inicializados pero queremos transformarlos en alias, o sea, que ambos apunten a lo mismo, debemos primero liberar la memoria de uno de ellos y luego cambiar su direccin, de este modo no habr porciones de memoria que se queden perdidas en el espacio:
Vladimir Rodrguez
143
Nota: Sea un puntero p hacia cualquier tipo especfico, si p no est inicializado no podemos referenciar a p^ porque tal lugar de memoria no existe. Hacerlo causa un error en tiempo de ejecucin. De este modo, referenciar al lugar que apunta un puntero luego de un procedimiento DISPOSE es un error de programacin. Seguiremos con un ltimo concepto para luego ver algunos ejercicios antes de continuar. El siguiente tema luego de esto ser Listas Encadenadas, lo cual est basado en el mero uso de punteros. UN ERROR COMN: Alias y DISPOSE: Supongan que tenemos dos punteros, p y q, apuntando a una misma celda, o sea, p y q son alias:
Ahora q qued tambin indefinida porque la porcin de memoria a la que apuntaba ya no existe. Tengan mucho cuidado con este tipo de situaciones.
El puntero nulo, la constante NIL: Hemos visto que una variable de un tipo puntero es una variable que contiene una direccin hacia una porcin de memoria en la que se almacenar un tipo de dato especificado. Vimos tambin que no es posible asignar valores especficos a un puntero excepto si le pasamos el valor de otro puntero ya inicializado anteriormente. Existe, sin embargo, una constante general para cualquier tipo de puntero la cual contiene el valor nulo y que podemos asignarle a cualquiera de nuestros punteros; esta constante es llamada NIL. Sea p un puntero a un tipo cualquiera, podemos hacer:
Vladimir Rodrguez
144
IF p=NIL THEN...
o por ejemplo
IF p<>NIL THEN...
o en cualquier cosa ya que podemos utilizarlo como condicin:
Asignar el valor NIL a un puntero ya inicializado sin antes hacer DISPOSE de l es un error ya que cambiaremos la direccin del puntero sin antes liberar la memoria con lo cual quedara esta perdida en la nada. Es el mismo caso que asignar a un puntero ya inicializado el valor de otro puntero sin antes liberar sus memoria. IMPORTANTE: Siempre que vayan a asignar a un puntero ya inicializado algn valor deben liberar la memoria asignada para l. Hacer DISPOSE de un puntero NIL es un error de programacin, puede no causar problemas, pero no debe hacerse jams. Para qu sirve el valor NIL? De qu sirve decir que un puntero no apunta a nada? Pues lo veremos dentro de poco, primero realicen estos ejercicios simples antes de continuar. Ejercicio1: a) Determinen cules de las siguientes proposiciones son vlidas:
new(apun1) new(apun1^) apun1 := apun3 apun2^ := apun2^ + apun1^ apun1 := NIL apun4^ := NIL writeln(apun2, apun3) read(apun1^, apun4^) b) Determinen cules de las siguientes proposiciones son vlidas:
1.apun1 := NIL 2.apun2 := new(apun1) 3.dispose(apun3) 4.apun3^ := NIL 5.apun3 := apun4 AND (apun3 6.apun4^ := NIL
= NIL)
Vladimir Rodrguez
145
Ejercicio 3: Consideren las declaraciones realizadas en el Ejercicio 1, determinen la salida del siguiente cdigo: new(apun3); new(apun1); apun3^ := Z; apun2 := NIL; apun4 := NIL; IF (apun3 <> NIL) AND (apun2 = NIL) THEN writeln (Cdigo A); IF apun3^ = Z THEN writeln (Cdigo Z) ELSE writeln (Cdigo X)
Ejercicio 4 Dado el siguiente cdigo en Pascal Type TipoVehiculo = (barco, camion); Transporte = record capacidad : INTEGER; case vehiculo : TipoVehiculo of barco : (habitaciones : INTEGER); camion : (); end; var a, b, c : ^Transporte; begin new(a); a^.capacidad := 30; a^.vehiculo := barco; a^.habitaciones := 4;
Vladimir Rodrguez
146
new(b); b^.capacidad := 4; b^.vehiculo := camion; new(c); c^.capacidad := 5; c^.vehiculo := camion; end. 1- Cul de las siguientes figuras representa el estado de la memoria? (tengan en cuenta que se omiten los nombres y valores de algunos campos para no recargar el dibujo).
2- Dada la instruccin: b := c 2.1 Cul de las siguientes figuras representa el estado de la memoria posterior a la instruccin? (tengan en cuenta que en las figuras se omiten los nombres y valores de algunos campos para no recargar el dibujo).
2.2 Sera correcto hacer dispose(b) antes de la instruccin? Justifiquen. 3- Dada la instruccin b^ := c^ 3.1 Dibujen el estado de la memoria posterior a la instruccin. 3.2 Qu valor tiene b^.capacidad? 3.3 Teniendo en cuenta que se quiere mantener las referencias a los componentes referenciados sera correcto hacer dispose(c ) despus de la instruccin? Justifiquen.
Realicen todos estos ejercicios antes de continuar. Pregntenme acerca de todo aquello que les provoque dudas, les responder a la brevedad. Si no logran comprender estos conceptos no podrn trabajar con lo que viene a continuacin. Adelante!!!
Vladimir Rodrguez
147
All pueden ver un dibujo sencillo de lo que sera una lista encadenada. Cada elemento contiene lo que tiene que contener y adems nos dice donde est el elemento siguiente a l en la lista, hasta que algn elemento nos diga que ya no existe nada ms despus de l con lo cual diremos que estamos al final de la lista. Veamos como aplicar este concepto en PASCAL. Para empezar diremos que cada elemento de una lista es un nodo, por lo tanto una lista puede ser vaca (cuando no existe nodo alguno), contener un nodo o varios. La gracia de esto ser poder modificar los nodos, quitarlos, agregarlos y moverlos. En este primer ejemplo definiremos lo que es un nodo de lo que ser un alista de nmeros enteros. Pongan mucha atencin para comprender esto porque causa confusin y a veces cuesta entender lo que se est escribiendo: TYPE Lista= ^NodoLista; NodoLista= Record numero: integer; siguiente: Lista; End; Veamos: Definimos el tipo Lista como punteros hacia el tipo NodoLista, el cual declaramos ms abajo como un registro de dos campos, uno del tipo entero que ser nuestra informacin, y otro con un puntero del tipo Lista, o sea, un puntero que apuntar a elementos del tipo NodoLista. Al tener un puntero del tipo Lista, estaremos apuntando a un registro que dentro de l tendr, adems de un nmero entero, otro puntero del tipo Lista, y as por cada uno. Cada elemento del tipo Lista podr apuntar a otro con la misma estructura. Veamos esto en un dibujo:
Vladimir Rodrguez
148
En la lnea 16 utilizo un puntero adicional i para apuntar tambin al primer elemento de la lista transformando a MiLista y a i en alias:
En la lnea 18 solicito el espacio necesario para alojar a un nuevo elemento. El puntero i^.siguiente es del tipo Lista, por lo tanto la sentencia New(i^.siguiente) crear un nuevo registro y nos dar su direccin de memoria:
Vladimir Rodrguez
149
En la lnea 22 creo el tercer elemento del mismo modo que fue hecho para crear el segundo. Luego hago que i apunte hacia dicho elemento para asignarle el valor 10 tal como hice anteriormente:
De este modo he logrado construir una lista de tres nodos, cada uno con el valor 10, donde el puntero MiLista apunta al primero de ellos y todos estn encadenados; as, solo obteniendo la direccin de MiLista puedo recorrer todos los nodos uno por uno. Veamos ahora un cdigo que hace lo mismo pero mediante un FOR que iterar tres veces. Este cdigo es el ms correcto ya que ser igual de largo para crear tres nodos que para crear cien comprenden?:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 PROGRAM PrimeraLista; TYPE Lista= ^NodoLista; NodoLista= Record numero: integer; siguiente: Lista; End; VAR MiLista, i: Lista; j: integer; //Un iterador entero para el FOR.
BEGIN New(MiLista); For j:= 1 to 3 do //Crear tres nodos. begin If j=1 then i:=MiLista else new(i^.siguiente); If j<>1 then i:= i^.siguiente; i^.numero:= 10; end; END.
Vladimir Rodrguez
150
Fin de lista: En la primera lista que hemos visto aqu creamos tres nodos. Ahora haremos un pequeo programa que cree tantos nodos como el usuario quiera y a cada uno le asigne el nmero que el usuario quiere. Al terminar nos dir cuantos nodos hemos creado. Asumimos en todo momento que el usuario siempre ingresa informacin correcta: PROGRAM PrimeraLista; USES crt; TYPE Lista= ^NodoLista; NodoLista= Record numero: integer; siguiente: Lista; End; VAR MiLista, i: Lista; j: integer; //Un iterador entero para el FOR. opcion: char; //Para leer opciones de teclado. BEGIN //En esta seccin de cdigo solo mostramos opciones al usuario, creando //la lista vaca si el usuario dice que SI. // //----------------------------------------------------------------------clrscr; write('Desea crear una lista de nmeros enteros? S/N: '); readln(opcion); j:= 0; //Simplemente damos opciones de inicio al usuario. Case opcion of 'S': begin MiLista:= NIL; writeln('Hemos creado una lista vaca.'); writeln; end; 'N': begin write('Presione ENTER para salir...'); readln; exit; end; End;//Fin del CASE OF. {Crearemos tantos nodos como quiera el usuario. Cada vez que elija la opcin 1 crearemos un nuevo nodo y leeremos un nmero de la entrada estndar. Terminaremos cuando el usuario lo desee. De este modo la lista ser de un largo totalmente aleatorio en cada ejecucin} //-----------------------------------------------------------------------
Vladimir Rodrguez
151
Vladimir Rodrguez
152
Veamos, de forma genrica, ejemplos de cdigos para el trabajo habitual con listas encadenadas, o sea, contabilizar sus elementos, agregar un elemento al principio, agregar un elemento al final, buscar un elemento, borrar el primero, borrar la lista completa:
function largo(l: lista): integer; var contador: integer; p: lista; begin contador:= 0; p:= l; while p <> nil do begin contador:= contador + 1; p:= p^.siguiente; (* avanzar a la siguiente celda *) end; largo:= contador; end;
Bsqueda de un elemento:
var
p: lista;
begin p:= l; (* notar: evaluacin por circuito corto *) while (p <> nil) and (p^.elemento <> elem) do p:= p^.siguiente;
procedure agregar_al_principio(var l: lista; elem: T); var p : lista; begin new(p); (*crear nueva celda*) p^.elemento:= elem; (*cargar el elemento*)
Vladimir Rodrguez
153
procedure agregar_al_final(var l: lista; elem: T); var p,q : lista; begin new(p); p^.elemento:= elem; p^.siguiente:= nil;
if l = nil then l:= p else begin (*busco el ltimo de l*) q:= l; while q^.siguiente <> nil do q:= q^.siguiente; (*engancho p a continuacion del ltimo*) q^.siguiente:= p;
end; end;
procedure borrar_primero(var l: lista); var p: lista; begin p:= l; l:= l^.siguiente; dispose(p); end;
Para liberar todo el espacio ocupado por una lista es necesario liberar celda por celda.
procedure borrar_lista(l: lista); var p: lista; begin while l <> nil do begin p:= l; l:= l^.siguiente; dispose(p); end; end;
Vladimir Rodrguez
154
Continuemos ahora entonces con los ejercicios acerca de punteros, estos estn orientados a las listas: Ejercicio 5: Dado el siguiente cdigo que permite crear una lista encadenada: TYPE apuntador = ^registro; registro = RECORD dato : ...; (* integer, char, real, ... *) sigreg : apuntador; END; VAR apuntaInic : apuntador; apuntaActual : apuntador; indice : integer; BEGIN new(apuntaInic); apuntaActual := apuntaInic; FOR indice := 1 TO 3 DO BEGIN new(apuntaActual^.sigreg); apuntaActual := apuntaActual^.sigreg END; apuntaActual^.sigreg := NIL; END. Cmo modificara el cdigo anterior si se desea, mediante la inclusin de una tercera variable de tipo apuntador, apuntar al ltimo registro de la lista? Suponiendo que el tipo del campo dato en el registro definido anteriormente es de tipo char, escriban un procedimiento en Pascal llamado Buscar que exhiba un mensaje que indique si un valor de tipo char se encuentra o no en la lista. El cabezal del procedimiento es el siguiente: procedure Buscar(c : char; apuntaInic : apuntador) donde c es el char a buscar y apuntaInic es la referencia del comienzo de la lista. Ejercicio 6: 1.Definan un tipo llamado ListEnt, el cual representa una lista encadenada de enteros. 2. Escribir un procedimiento en Pascal que permita insertar un entero al principio de la lista encadenada. El cabezal del procedimiento es el siguiente: Procedure InsertarPrincipio(valor: integer; VAR lista: ListEnt); 3. Escribir un procedimiento en Pascal que permita eliminar el primer elemento de una lista encadenada. El cabezal del procedimiento es el siguiente: procedure EliminarPrincipio (VAR lista: ListEnt);
Ejercicio 7: Dada la siguiente definicin: Type ListChar = ^registro; registro = RECORD campo: char; siguiente : ListChar END;
Vladimir Rodrguez
155
BASE PRODUCTOS Parte 3 Nuestro programa seguir haciendo las mismas cosas que en su segunda parte solo que esta vez utilizar la estructura de listas encadenadas para almacenar los elementos de modo que solo utilizar la memoria necesaria para la cantidad de productos almacenados en el momento. Ser tarea de ustedes modificar los cabezales de los procedimientos y las funciones que utilizaban la estructura del arreglo con tope para trabajar con la lista de elementos as como es tarea de ustedes modificar la declaracin de los tipos principales del programa. No necesitan trabajar con listas para corregir errores de la entrada estandar, solo quiero que lo hagan con la lista de productos. Si necesitaran que yo les enve los cabezales de todos los subprogramas no tendr problema en hacerlo ya que por lo general es lo que se hace, sin embargo ser un buen ejercicio para ustedes el tener que pensarlos.
Vladimir Rodrguez
156
Les propondr aqu el trabajo con lo que fue la segunda tarea de Programacin 1 en la Facultad de Ingeniera donde estudio. Estos proyectos estn diseados para que apliquen todo lo dado aqu y para que piensen de forma estructurada, la misma forma que he enseado a lo largo de este manual. Es de alto nivel pero podrn hacerla, yo estar abierto a que pregunten todas y cada una de las dudas que tengan al respecto ya que por lo general estos trabajos se acompaan con alguna clase de consulta. Le dar aqu la letra tal y como fue dada a los estudiantes incluyendo hasta las reglas de no copiar y dems, mucha suerte. Espero que lancen a realizar esto: Deben leer todo antes de continuar, ya que luego de la letra del problema existen las instrucciones para implementarlo ya que se les brindar un archivo con el cdigo ya escrito para la interfaz grfica y un archivo con el cual debern trabajar ustedes creando el motor del juego. Aqu ven un ejemplo de un programa donde un archivo se encarga de realizar la interfaz grfica y otro de hacer las tareas del programa, luego ambos trabajan juntos. 1. Introduccin Este documento presenta el problema que deber resolverse para la aprobacin de la segunda tarea del laboratorio del curso 2010. Se presenta informacin acerca de: normas, recursos, plazos, una presentacin general del problema, las especificaciones del mismo, ejemplos de ejecucin y la forma de entrega.
2. Informacin general El estudiante que no respete alguna de las consideraciones que siguen corre el riesgo de que su trabajo sea invalidado, con la consiguiente prdida del curso. Compilador Todos los programas deben ser compatibles con el compilador del curso (Free Pascal). No se aceptarn como vlidas aquellas tareas que pudieran funcionar con algn otro compilador Pascal, pero no funcionen con Free Pascal, versin 2.2.2 para windows. Grupos Esta tarea se deber realizar en forma individual. Para todas las tareas rige el Reglamento del Instituto de Computacin ante Instancias de No Individualidad en los Laboratorios. A continuacin se adjunta un fragmento del mismo; ante cualquier duda se recomienda leer el documento completo (http://www.fing.edu.uy/inco/cursos/prog1/pm/field.php/Laboratorio/NoIndividuali dad)
Los laboratorios deben ser realizados nicamente por los integrantes del grupo establecido La realizacin de los laboratorios es estrictamente individual, sea a nivel unipersonal en el primer caso, o del grupo establecido en el segundo. Se entiende que compartir total o parcialmente cualquier actividad del laboratorio atenta contra la integridad del estudiante universitario y de su formacin, y por lo tanto constituye una falta grave. Especficamente no es posible compartir por ninguna va entre integrantes de grupos distintos las tareas de codificacin, digitacin, compilacin, depuracin y documentacin de los programas u objetos (o entregas) del laboratorio. Adems de que no se pueden compartir actividades del laboratorio, no se pueden compartir los productos de las mismas. Cada grupo es responsable de su trabajo de laboratorio y de que el mismo sea individual, independientemente de las causas que pudiesen originar la no individualidad. A modo de ejemplo y sin ser exhaustivos: utilizacin de cdigo realizado en cursos anteriores (por otros estudiantes) u otros cursos, perder el cdigo, olvidarse del cdigo en lugares accesibles a otros estudiantes, prestar el cdigo o dejar que el mismo sea copiado por otros estudiantes, dejar la terminal con el usuario abierto al retirarse, enviarse cdigo por mail, utilizar cdigo Vladimir Rodrguez
157
3. Presentacin 3.1 Juego Iguales Iguales es un juego tipo puzle. El objetivo es eliminar fichas obteniendo la mayor cantidad de puntos posible. Las fichas que estn adyacentes las unas de las otras se eliminan como un bloque. Las dems fichas se colapsan para rellenar los huecos vacos y se forman nuevos bloques. No se puede eliminar fichas que no estn en un bloque.
El tablero se inicia como una rejilla llena de fichas. Habr cuatro tipos de fichas. Si las fichas adyacentes de un bloque son todas del mismo tipo, entonces pueden ser eliminadas simplemente seleccionando una de ellas. El nmero de fichas en el bloque y los puntos que se obtendrn al eliminar ese bloque se muestran en la parte inferior. Cuanto ms fichas tenga un bloque ms puntos se obtendrn. Una vez que el bloque ha sido eliminado, las fichas encima de l comenzarn a caer para rellenar el espacio. Si se elimina una columna entera, entonces las fichas se desplazarn hacia la izquierda para rellenar el espacio. Dos fichas se consideran adyacentes: si estn en la misma columna y en filas consecutivas, o si estn en la misma fila y en columnas consecutivas.
Vladimir Rodrguez
158
Se decide eliminar el bloque de fichas verdes. 1. Las fichas se desplazan hacia abajo. 2. Como quedan columnas libres a la izquierda, se desplazan todos las fichas hacia la izquierda. 3. Resultado de eliminar el bloque de fichas verdes. El juego termina cuando no hay bloques a eliminar. 3.2 Fichas Cada celda del tablero contendr una ficha del conjunto {A, B, C, D} o estar vaca. Al comenzar el juego, el tablero se llena de fichas de forma aleatoria. 3.3 Puntajes La puntuacin est basada en el nmero de fichas que se eliminen:
0 1 4 159
4. Arquitectura del sistema El sistema que implementa el juego se construir de acuerdo con la siguiente arquitectura:
usuario
^ | | v +-------+ +----------+
estructura Este es un modelo simple donde tenemos dos capas o mdulos: la interfaz y el motor. La interfaz se encarga de realizar el dilogo con el usuario, capturar sus entradas e invocar las operaciones asociadas. La interfaz tambin maneja la imagen del juego en pantalla actualizndola cada vez que hay cambios. En resumen, se encarga de todo lo que tenga que ver con entrada y salida. La interfaz no realiza ninguna modificacin directa sobre la estructura de datos. El motor es el mdulo que trata con la estructura de datos que representa el juego en un estado determinado. Este mdulo estar compuesto por todos los subprogramas necesarios para ejecutar las acciones del usuario y reflejar estas en la estructura de datos. El motor no realiza ninguna operacin de entrada y salida. En esta tarea, el estudiante implementar solamente el motor. La interfaz ser provista por los docentes. En las siguientes secciones explicamos los detalles del mdulo a ser implementado.
5. Estructura La estructura de datos que representa el juego es la siguiente: 5.1 Estado del Juego El TipoEstadoJuego es un registro con los siguientes campos:
Vladimir Rodrguez
160
5.2 Tablero, Celdas y Fichas El tablero se representa con la siguiente estructura: TipoFicha = (A, B, C, D); TipoEstadoCelda = record case vacia : boolean of true : (); false : (ficha : TipoFicha); end; RangoFila = 1..MAX_FILAS; RangoColumna = 1..MAX_COLUMNAS; TipoTablero = record celdas : array [RangoFila, RangoColumna] of TipoEstadoCelda; topeFila : RangoFila; topeColumna : RangoColumna; end; Esta estructura es una matriz con dos topes (ver array con tope). De esta manera se pueden representar tableros de diferentes tamaos. Las celdas vlidas de la matriz son aquellas cuyas coordenadas (i,j) son tales que:
1<= j <= topeColumna Las constantes MAX_FILAS y MAX_COLUMNAS se suponen definidas con valores apropiados. Estos valores estn definidos en la interfaz, de manera que el estudiante no necesita conocerlos. Cada celda del tablero puede estar vaca, o contener una ficha como indica el registro con variantesTipoEstadoCelda. Para esta implementacin las fichas son 4, y las distinguimos con las etiquetas: A, B, C o D. 5.3 Posicin y Lista de Posiciones Una posicin en el tablero se representa con la siguiente estructura: TipoPosicion = record fila : RangoFila; columna : RangoColumna end; Para representar una bloque (o conjunto de fichas) utilizaremos el tipo TipoListaPosicion, que es un arreglo con tope, como se muestra a continuacin: TipoListaPosicion = record lista : array [1..MAX_CELDAS] of TipoPosicion; tope : 0..MAX_CELDAS; end;
6. Los subprogramas
Vladimir Rodrguez
161
cuantas_columnas : cantidad de columnas que va a tener el tablero Este procedimiento debe retornar el parmetro estado configurado para iniciar el juego, esto es: para cada celda selecciona al azar una ficha: A, B, C D el juego no est finalizado el puntaje es cero 6.2 Procedimiento obtenerBloque Dado un tablero y la posicin de una celda, devuelve la lista de posiciones de celdas que estn en el mismo bloque segn las reglas del juego. procedure obtenerBloque( tablero :TipoTablero; posicion :TipoPosicion; var bloque :TipoListaPosicion) 6.3 Procedimiento obtenerBloqueMasGrande Dado un tablero, devuelve en bloque el bloque ms grande. Si hay ms de un bloque que cumpla con esta condicin, devuelve cualquiera de estos. procedure obtenerBloqueMasGrande( tablero :TipoTablero; var bloque :TipoListaPosicion); 6.4 Funcin puntosAGenerar Dado un bloque de posiciones a eliminar y un tablero, devuelve la cantidad de puntos que se generaran si se eliminara el bloque. No solo debe tomar en cuenta la cantidad de posiciones en el bloque, sino tambin debe tener en cuenta si el tablero queda vaco o no, pues puede corresponder sumar el puntaje extra. function puntosAGenerar( bloque :TipoListaPosicion; tablero: TipoTablero) : Integer; 6.5 Procedimiento ordenarBloque Este procedimiento ordena una lista de posiciones bloque de forma creciente. Esto es, una celda [i1, j1] es menor que [i2, j2] si (i1 < i2) o si (i1 = i2) y (j1 < j2). Por ejemplo, la lista ([1,1], [2,1], [3,4], [3,5], [3,6])est ordenada. procedure ordenarBloque(var bloque : TipoListaPosicion); 6.6 Procedimiento eliminarBloque Dado un bloque (o lista de posiciones de celdas a eliminar) y un estado del juego, elimina las celdas y hace los corrimientos necesarios para que el tablero del juego sea vlido nuevamente. Una vez que el bloque ha sido eliminado, las fichas ubicadas encima de l caen para rellenar el espacio. Si se elimina una columna entera, entonces las fichas se desplazan hacia la izquierda para rellenar el espacio. Se debe sumar el puntaje ganado al puntaje total del usuario. procedure eliminarBloque ( bloque :TipoListaPosicion; var estado :TipoEstadoJuego);
7. Se pide Escribir un archivo con todos los subprogramas que forman el motor del juego.
Vladimir Rodrguez
162
8. Apndices 8.1 Nmeros aleatorios En free pascal se pueden generar nmeros al azar utilizando la funcin random. Esta funcin recibe un parmetro entero positivo y retorna un nmero aleatorio mayor o igual que 0 y menor que el parmetro recibido. El procedimiento randomize debe invocarse una sola vez, previamente a cualquier invocacin de la funcin random. El nico objetivo de esta operacin es inicializar el generador de nmeros aleatorios. A continuacin se muestra un ejemplo donde se generan 10 nmeros al azar entre 1 y 6, como si fueran lanzamientos de un dado. { inicializacin } randomize; for i:= 1 to 10 do begin { sorteo } numero:= random(6) + 1; writeln(numero); end; Tener en cuenta que la funcin random puede repetir nmeros.
Vladimir Rodrguez
163
9. Referencias 1. http://library.gnome.org/users/swell-foop/2.31/swell-foop.html
2.
http://en.wikipedia.org/wiki/Same_Game
El programa final que implemente el juego debe funcionar sin que Ud. tenga que modificar el archivo iguales.pas. Todos los subprogramas que se pide implementar en la letra de la tarea 2, as como cualquier funcin, procedimiento, tipo, etc. extra que Ud. necesite definir, debern ser implementados en el archivo motor.pas. Las directivas, procedimientos y funciones no estndar usadas en el archivo iguales.pasfueron utilizadas con el fin de que Ud. tenga una interfaz ms amigable. Sin embargo, como es regla en este curso, Ud. no puede utilizar facilidades de Free Pascal que no forman parte del estndar y que no se dan en el curso para implementar los subprogramas que se piden o cualquier otro subprograma extra que necesite.
Vladimir Rodrguez
164
En el cabezal est el nombre del juego y se despliegan mensajes cuando el jugador ha ganado o perdido. En el tablero se ven: las celdas vacas (que se representan con el caracter .) o con fichas (que se representan con los caracteres A, B, C o D con un color distinto cada uno). la posicin en donde est parado el usuario (que se indica con el cursor que est parpadeando). el bloque de fichas adyacentes del mismo tipo que la ficha de la posicin en donde est parado el cursor (que se resaltan con un color ms brillante) En la barra de estado se puede ver: la fila y la columna donde est posicionado el usuario, la cantidad de fichas del bloque de fichas adyacentes del mismo tipo que la ficha de la posicin en donde est parado el usuario. los puntos a generar por el usuario, si borra el bloque de fichas adyacentes del mismo tipo que la ficha de la posicin en donde est parado los puntos ya generados en jugadas (o acciones) previas.
Usando las flechas del teclado y algunas teclas se consigue probar y jugar. 3.2 Pantalla inicial Cuando se inicia el juego aparece la siguiente pantalla, que nos permite elegir el tamao del tablero o cargar el estado del juego (nuevo o ya empezado) desde un archivo.
Vladimir Rodrguez
165
3.3 Ayuda Por ms informacin Ud. puede ver la ayuda que trae la interfaz. Presionando la tecla h (de help en ingls) se obtiene la siguiente pantalla:
4. Comunicacin interfaz-motor 4.1 Invocacin a inicializar Juego(...): Cuando se inicia un juego nuevo seleccionando el nivel (fcil, intermedio, difcil), la interfaz invoca al procedimiento inicializarJuego() pasando como parmetros cierta cantidad de filas y columnas dependiendo de cada nivel. 4.2 Invocacin a los otros subprogramas del motor Cuando el usuario se posiciona sobre una celda determinada y presiona algunas de las teclas correspondientes a eliminar bloque, eliminar bloque ms grande, etc., la interfaz invoca los procedimientos y funciones que correspondan pasando los parmetros que se necesiten, el estado del juego y/o la posicin, dependiendo del procedimiento o funcin del motor que est invocando.
Vladimir Rodrguez
166
Listar las posiciones del bloque de fichas seleccionado que devuelve el procedimientoobtenerBloque()
Listar las posiciones del bloque de fichas seleccionado que devuelve el procedimientoobtenerBloque(), ordenadas tal cual las ordena el procedimiento ordenarBloque() Volver atrs (o deshacer) una accin. Guardar la configuracin actual del estado del juego en un archivo de texto. Cargar la configuracin de un estado del juego desde un archivo de texto. Guardar en un archivo de texto el historial de los sucesivos estados del juego y las acciones que se realizaron. 6. Recomendaciones 6.1 Para compilar Cree una carpeta (o directorio) donde va a trabajar, descargue los archivos iguales.pas (que contiene la interfaz) y motor.pas (que solo contiene los encabezados de los subprogramas) y cpielos a dicha carpeta. Compile el archivo iguales.pas desde la lnea de comandos con: fpc -Co -Cr -Mtp iguales.pas, o desde el IDE teniendo abierta la ventana con el archivo iguales.pas. El compilador se da cuenta automticamente de que tiene que incluir el cdigo del archivo motor.pas donde est la directiva mencionada antes. Esto ya va a generar una interfaz sencilla que, si bien no permite crear juegos nuevos ni realizar acciones, s permite cargar archivos con juegos de prueba, como se explica a continuacin. A medida que implemente los procedimientos y funciones, podr realizar ms acciones del juego. 6.2 Para probar usando la interfaz Hasta que usted no implemente el procedimiento inicializarJuego(), no va a poder generar sus propios tableros. Pero s puede ir probando los otros procedimientos y funciones, por ejemplo cargar el juego desde uno de los archivo de ejemplo que le proporcionamos. A continuacin se proporcionan algunos archivos generados desde la interfaz, los cuales se pueden cargar sin necesidad de implementar los procedimientos y funciones del motor.
A continuacin se proporcionan algunos archivos con historiales de ejemplo. Los historiales contienen la secuencia de juegos que se generan a partir de un estado inicial y la aplicacin sucesiva de acciones eliminarBloque o eliminarBloqueMasGrande:
Adems Ud. puede probar los procedimientos y funciones implementando su propia interfaz y/o pequeos programas para probar cada procedimiento por separado. Recuerde que lo que Ud. debe implementar son los procedimientos, la interfaz es simplemente una ayuda para probar y jugar con su implementacin del motor.pas.
Vladimir Rodrguez
167
En esta hoja encontrarn casos de prueba de ejemplo (casos de prueba bsicos) y puntos que deberan tener en cuenta a la hora de probar. As que a hincarle los dientes a esto que ser de gran ayuda para ustedes!! 2. Cmo arman un puzzle? En general, uno empieza por el borde, luego arma las partes ms fciles, las difciles, y siempre, para cada parte, uno se va cerciorando que se est uniendo las piezas correctamente. Luego juntamos las partes prearmadas hasta llegar a la figura final. 3. Por qu estamos viendo cmo armar un puzzle? La idea de cmo armar un puzzle e implementar y probar el juego iguales tiene muchas similitudes. El borde y algunas partes ya estn prearmados y testeados (iguales.pas) y le pedimos que armen y testeen las partes faltantes (motor.pas) para completar el juego o puzzle. Parece una tarea fcil, no? Por lo tanto para probar su tarea deben tener en cuenta 3 puntos de prueba y en este orden: 1. Probar cada una de las partes armadas por ustedes por separado. O sea, cada subprograma o procedimiento por separado. 2. Probar que las partes encajan correctamente en lo que ya est armado. O sea, que al unir los subprogramas implementados por ustedes con los implementados por nosotros no generen errores. 3. Probar finalmente que todas las partes junto con el borde funcionan correctamente. O sea, nuestro puzzle es el que est en la imagen de la caja. Esto se hace probando desde la interfaz. Ac es dnde se pone ms divertido, es hora de jugar. Como bien dice la letra de la tarea Ud. puede probar los procedimientos implementando su propia interfaz, y/o pequeos programas para probar cada procedimiento por separado. Recuerde que lo que Ud. debe realizar son los procedimientos, la interfaz es simplemente una ayuda para probar y jugar con su motor. 4. Indicaciones y pautas a seguir para las pruebas 4.1 Probar por separado los subprogramas Prueben todos los procedimientos por separado. Esta tcnica les ser de gran ayuda al momento de hacer la prueba desde la interfaz. Es decir, disminuir significativamente la cantidad de errores que encontrarn y su gravedad. Pueden armarse un programa principal auxiliar que simplemente
Vladimir Rodrguez
168
PruebaInicializarJuego.pas PruebaObtenerBloque.pas
que sirven para probar en forma independiente los procedimientos incializarJuego y obtenerBloque. Recomendamos que armen programas similares para probar el resto de los subprogramas por separado. 4.2 Pautas para los casos de prueba Recomendamos que piensen los casos de pruebas antes de empezar a probar y que anoten sus ideas. Recuerden que un caso de prueba se puede decir que est formado por entradas (en este caso, parmetros del procedimiento), un resultado esperado y un resultado obtenido. Adems se puede agregar una descripcin para entender rpidamente qu se est atacando. La idea es que prueben lo que se les sugiere abajo. Si tienen algunas ideas ms, no las descarten, senlas para probar, incluso en combinacin con estas. inicializarJuego Diferentes dimensiones de tableros, chicos, medianos, grandes Casos lmites en las dimensiones: mximo tablero (MAX_FILAS * MAX_COLUMNAS), mnimo tablero (1 x 1) Verificar que el juego no est finalizado Verificar que el puntaje es cero Verificar que las fichas se distribuyen aleatoriamente sobre el tablero
obtenerBloque Diferentes ubicaciones de la posicin: en el centro del tablero, en el borde derecho, en el borde izquierdo, en el borde superior, en el borde inferior Diferentes fichas en la celda de la posicin: con ficha A, B, C, D, vaca Diferentes adyacentes a la celda de la posicin: con adyacentes a la derecha, a la izquierda, arriba, y abajo Sin adyacentes
obtenerBloqueMasGrande Diferentes estados, situaciones, posiciones (como en el caso anterior) Probar con un tablero con solo bloques unitarios Probar con un tablero lleno de fichas del mismo color (el bloque es todo el tablero) Probar con un tablero con bloques del mismo tamao
puntosAGenerar Siendo n la cantidad de fichas en el bloque: Probar cuando el resultado es 0 puntos (n=0, n=1, o n=2) Probar para n>2 Probar para un n grande
ordenarBloque Ordenar Ordenar Ordenar Ordenar un bloque un bloque un bloque un bloque de tamao>1 de tamao 1 de tamao cero que abarque todo el tablero
Vladimir Rodrguez
169
4.3 Integrar el motor.pas con iguales.pas Adems de manejar bien las estructuras ya definidas, deben estar seguros que los datos que les llegan son los esperados por ustedes y los datos que devuelven son los esperados por la interfaz. En otras palabras deben verificar que estn tomando correctamente los datos que les pasan por parmetro y devolvindolos correctamente tambin. 4.4 En el juego, desde la interfaz Esta es la ltima. Si hicieron bien las pruebas anteriores no deberan tener muchos errores en esta etapa. Les sugerimos que para esta parte lo que ms deben hacer es jugar. Recuerden que la intencin debe ser encontrar fallas y no salterselas. Lo mejor es enfocarse en buscar cierto tipo de errores durante varias partidas seguidas. Piensen que tienen una misin, encontrar defectos, entonces por varias partidas ponen el foco en buscar ciertos defectos, por ejemplo, eliminar bloques y verificar que las dems fichas se desplazan correctamente. Es importante que se guarden los historiales de cada partida(*), as cuando ocurre una falla, pueden reproducirla. Esto les servir al momento de verificar que hayan arreglado el defecto. Les podr ser til anotar en una planilla la correspondencia entre los errores y los archivos de historial. En las misiones, pueden intercalar los distintos niveles de tablero de la interfaz, o alguno armado por ustedes. (*) Recuerde que la interfaz proporcionada por los docentes, le permite guardar y cargar desde un archivo de texto el estado de un juego (recin iniciado, o ya comenzado). Tambin se puede guardar en un archivo de texto el historial de los estados por los que pas el juego y las acciones que se realizaron. 5. Indicaciones tiles Cuando utilicen casos de prueba Ejecuten todos los casos de prueba y anoten sus resultados antes de comenzar la revisin de los que fallaron. Esto les facilitar el trabajo. A la hora de empezar la revisin de los casos que fallaron. Se puede seguir estos pasos: Revisar la formulacin del caso de prueba. Puede que est mal el caso y no sea el programa. Si el caso de prueba es correcto, se pasa a detectar qu parte del cdigo est generando la falla. Esto lo pueden hacer debugueando por ejemplo. Cuando hayan arreglado todos los errores, ejecuten los casos de prueba que en un principio no fallaron. Esto es para asegurarse que los cambios hechos no introdujeron errores en lo que andaba bien.
Algunas indicaciones ms para cuando prueben desde la interfaz: Al igual que con los casos de prueba, ejecuten varias misiones, las que ustedes crean convenientes, antes de empezar a corregir los errores. 6. Conceptos de testing manejados en esta gua Testing Unitario (probar cada parte por separado) Testing de Integracin (probar cmo encajan las partes entre s) Testing de Sistema o Caja Negra (probar que el puzzle armado es igual al de la imagen en la caja)
Vladimir Rodrguez
170
Despedida y Agradecimientos:
Hasta aqu hemos llegado con este manual, de verdad espero que les haya servido para algo as como esperar sus correos para preguntar, corregir, sugerir y dems. La creacin de este documento est inspirada en el agradecimiento a todos aquellos que al igual que yo han decidido utilizar su tiempo para ayudar a quienes buscan informacin en la web, una ayuda sin fines de lucro sino ms bien todo lo contrario, una ayuda sin la cual yo hoy no sabra nada. Muchas gracias a todos ellos, muchas gracias a quienes publiquen y difundan este manual, a todas aquellas pginas web que permitan su descarga gratuita, a todos quienes se tomen enserio este curso y aprendan realmente. No es una despedida para siempre porque continuar con ms manuales, manuales para los cuales asumir que ya han ledo este. En la segunda entrega, como ya dije, abordaremos el lenguaje MODULA-2 el cual sigue siendo PASCAL pero orientado a la modularizacin. Con dicho lenguaje aprenderemos a crear programas separados por mdulos individuales, lo cual nos da infinitas posibilidades; abordaremos la recursin como herramienta y sin la cual no podrn solucionar muchos problemas, adems de que les proveer del poder de realizar taras que de otro modo seran imposibles; abordaremos estructuras como las de rboles, colas y otras tantas usadas en muchos tipos de programas; aprendern acerca del diseo de TAD (Tipos Abstractos de Datos) entre muchas otras cosas. Los programas seguirn siendo vistosamente horribles pero potencialmente competentes, lo cual es lo ms importante, porque de nada sirve saber crear hermosas interfaces grficas si nuestros programas no saben trabajar. Estoy pensando tambin crear un anexo a este manual, no para aprender conceptos nuevos, sino para traducir estos conceptos a un lenguaje moderno como Java, a modo de incentivo. El aprendizaje seguir orientado a la segunda entrega de este tutorial, por lo cual lo otro es un simple agregado a este, pero an lo estoy pensando ya que Java obliga a aprender conceptos de la programacin orientada a objetos y an no hemos trabajado con ello en absoluto. Habiendo ledo todo este manual cualquiera de ustedes debera poder iniciarse en otro lenguaje si leen manuales de l, podrn al menos hacer casi las mismas cosas que hasta ahora pero tal vez con un modo de trabajo mucho mejor y un tanto ms sencillo. Sin embargo esperar que lean la prxima entrega, la cual espero est lista para mediados de este ao, o sea, Junio o Julio de 2011. Gracias nuevamente a todos los que me han ledo, a todos los que me escribirn y a todos quienes decidan aportar algo ms a esto. Estar a su disposicin siempre que necesiten ayuda y tambin para recibirla ya que yo tambin estoy aprendiendo, recin ingresando a este mundo tan complejo y tan interesante. Gracias a todos y a todas. Un cordial saludo de su amigo Vladimir Rodrguez. Ha sido un gusto escribir esto y, aunque me ha llevado su tiempo, hoy me siento feliz de poder mostrarlo. Gracias.
Vladimir Rodrguez
171
Vladimir Rodrguez
172
4 5 6 8 9 10 11 13 14 15 15 16 17 18 19 20 20 20 21 21 22 23 26 26 26
28 29 30 30 32 32 33 33 36 38 39 40 41 43 44 46 47 48 48 49 49 49 50 51
Vladimir Rodrguez
173
54 55 56 56 57 59 59 60 60 62 63 65 65 66 67 68
72 73 73 76 79 80 82 83 83 84 84 85 87 88 93
100 101 102 102 105 107 107 109 109 110 117 118 119 120 123 124 130
Vladimir Rodrguez
174
138 139 139 139 142 144 145 148 151 151 153 153 153 154 154 154 155 156
Vladimir Rodrguez
175
Vladimir Rodrguez
176