P RÁCTICAS DE F UNDAMENTOS DE I NFORMÁTICA DIPLOMATURA EN ESTADÍSTICA CURSO 2009 – 2010 MARTES 15 Y JUEVES 10 DE DICIEMBRE DE 2009 SESIÓN 06 ARRAYS UNIDIMENSIONALES (VECTORES) SESIÓN 07 ARRAYS BIDIMENSIONALES (MATRICES) Hasta ahora hemos estado trabajando principalmente con cuatro tipos de datos básicos: enteros (integer), reales (real), caracteres (char) y valores lógicos (boolean). Sin embargo, existen otros tipos de datos más complejos como los tipos estructurados que permiten almacenar una colección de valores bajo un único identificador de variable, es decir, la colección tiene un único nombre que engloba todos esos valores. Por supuesto, se sigue podiendo acceder individualmente a cada valor o componente de la colección. Por ejemplo, si en uno de nuestros programas necesitásemos almacenar diez valores de tipo entero, en lugar de crearnos diez variables con diez identificadores diferentes num1, num2, ..., num10, podríamos almacenar esos diez valores utilizando una única variable de un tipo estructurado, y luego acceder a cada valor concreto a través del identificador de la variable general y el índice concreto del valor, que podría ser por ejemplo de la posición 1 a la posición 10. Los arrays son un caso concreto de tipo estructurado (como los registros, los conjuntos y los ficheros). Básicamente son variables capaces de almacenar una colección de valores que sean del mismo tipo (por ejemplo, como decíamos antes, diez enteros). Como veremos a lo largo de esta sesión, los arrays pueden tener una o más dimensiones. Cuando tienen una sola dimensión se les suele llamar vectores, y cuando tienen dos dimensiones se les conoce como matrices (lo mismo que en matemáticas o física). Aunque como también veremos, el número de dimensiones no tiene por qué estar limitado a dos como máximo, sino que en general hablaremos de arrays multidimensionales. ARRAYS UNIDIMENSIONALES (VECTORES) El caso más sencillo de arrays son los unidimensionales, que no son otra cosa que vectores, es decir, una fila de valores. En la figura de abajo puedes ver un ejemplo de una variable de ese tipo que por ahora llamaremos vector (luego veremos cómo se llama en Pascal). A esa variable la hemos llamado numeros, y almacena en este caso diez valores de tipo entero. Para acceder a cada valor concreto utilizamos su índice o posición, que va en este caso desde 1 hasta 10. Así, el primer elemento del vector será numeros[1], el segundo numeros[2], y así sucesivamente. numeros 4 27 215 49 -15 11 -23 0 8 16 1 2 3 4 5 6 7 8 9 10 Cada uno de estos elementos se trata como si fuera una variable individual, es decir, cuando Eduardo E isman – [email protected] – http://decsai.ug r.es/~eisman Depart amento de Ciencias de la Computación e Inteligencia Art if icial – ETSIIT – Un iversid ad de Granada 1 escribimos numeros[1] es como si tuviéramos nuestra variable num1 de tipo entero, por lo que podemos asignarle cualquier valor entero a la variable utilizando el operador de asignación := o hacer cualquier operación que se nos ocurra con ella. Hasta ahora, para declarar una variable escribíamos su nombre, seguido de dos puntos, y después el tipo de esa variable acabado en punto y coma (por ejemplo, num : integer;). Para declarar una variable de tipo vector en Pascal tenemos que hacer uso de la palabra reservada array, tal y como se muestra a continuación nombreDeLaVariable : array [índiceInicial..índiceFinal] of tipoBásico; donde índiceInicial e índiceFinal son los índices (posiciones) inicial y final de la colección de valores, y tipoBásico es cualquiera de los tipos básicos que hemos visto hasta ahora (integer, real, char, boolean, incluso string). ¿Cómo declararíamos el vector numeros del que hablamos anteriormente? numeros : array [1..10] of integer; De esta forma tendremos una variable cuyo identificador es numeros y que es de tipo array de enteros, cuyos índices van desde el 1 hasta el 10, por lo que tendremos diez enteros. Veamos un ejemplo concreto en Pascal de cómo podríamos leer por teclado un vector de diez enteros, calcular la media de todos ellos y mostrarla por pantalla Lo primero que tenemos que hacer es declarar el vector junto con las otras variables que vayamos a utilizar. Después, ya en el cuerpo de nuestro programa principal, podemos leer todas las 2 Práct icas de Fundamentos de Informátic a – Curso 2009 – 2010 Diplomatu ra en Estadíst ica – Universidad de Granada componentes del vector. Pero no hace falta poner diez instrucciones read, sino que podemos utilizar un bucle for para que resulte mucho más fácil, claro y conciso. Una vez leídos todos los valores, pasamos a procesarlos. En este caso inicializamos la suma al valor cero, y utilizando otro bucle for vamos añadiéndole el valor de cada componente del vector, a las cuales accedemos mediante su índice. Finalmente mostramos por pantalla la suma dividida entre el número de valores. En realidad, los índices del vector no tienen por qué empezar por el valor 1. En el ejemplo anterior podrían haber ido perfectamente desde el 11 hasta el 20. Podrían ser incluso negativos. La única restricción es que el índice inicial debe ser menor o igual que el final. Prueba a cambiar la declaración del vector del ejercicio anterior para que los índices sean negativos y vayan desde el -10 hasta el -1. Una vez hechas las modificaciones, comprueba si sigue funcionando el programa (se compila y se ejecuta correctamente). Es posible que hayas recibido un mensaje de error por pantalla al empezar a introducir los números (program exited with exitcode = 201). Si es así, la razón es que en la declaración del array has cambiado los índices para que vayan del -10 al -1, pero sin embargo los bucles for siguen accediendo a las componentes utilizando los índices del 1 al 10. Estás intentando acceder por tanto a un elemento con un índice que está fuera del rango declarado, lo cual causa ese error en tiempo de ejecución. Podemos evitar tener que estar cambiando los valores inicial y final de los bucles cada vez que se modifican el tamaño o los índices del vector de la siguiente manera. En lugar de iterar desde 1 hasta 10 podemos iterar desde low(nombreDeLaVariable) hasta high(nombreDeLaVariable) tal y como se muestra en la siguiente imagen Estas funciones low y high lo que hacen es devolver el índice menor y mayor, respectivamente, del array que se les pasa como argumento. Además, observa cómo también hemos utilizado en el ejemplo anterior la función length (que ya vimos en la segunda práctica) para calcular cuántos Eduardo E isman – [email protected] – http://decsai.ug r.es/~eisman Depart amento de Ciencias de la Computación e Inteligencia Art if icial – ETSIIT – Un iversid ad de Granada 3 elementos tiene el vector, en lugar de poner directamente el valor 10. Así, si cambiamos la longitud del vector en la declaración, el programa seguiría funcionando sin tener que realizar ninguna otra modificación. Otra de las cosas que podemos hacer con los arrays es inicializar el valor de cada componente en la propia declaración. Para ello, sólo tenemos que poner, después del tipo de array, un igual seguido de la lista de valores iniciales separados por comas y en entre paréntesis numeros : array [-10..1] of integer = (4, 27, 215, 49, -15, 11, -23, 0, 8, 16); Además de números, los índices de los vectores pueden ser caracteres. El siguiente vector por ejemplo podría almacenar un valor entero para cada uno de los caracteres comprendidos entre la ‘a’ y la ‘z’ frecuencias : array ['a'..'z'] of integer; Dicho vector podría utilizarse para almacenar con qué frecuencia (cuántas veces) aparece cada letra en un texto determinado frecuencias 54 9 17 ... 2 ‘a’ ‘b’ ‘c’ ... ‘z’ Así, podríamos almacenar en frecuencias[‘a’] cuántas letras ‘a’ contiene el texto. De esta forma podríamos intentar adivinar en qué idioma está escrito el texto sin necesidad de conocer ni una sola palabra de ese lenguaje, ya que cada letra tiene una frecuencia de aparición diferente en cada idioma Porcentaje de aparición de cada letra en un texto 16,000 14,000 12,000 10,000 8,000 6,000 4,000 2,000 0,000 a b c d e f g h i j k l m n o p q r s t u v w x y z Español 4 Inglés Francés Práct icas de Fundamentos de Informátic a – Curso 2009 – 2010 Diplomatu ra en Estadíst ica – Universidad de Granada Como vemos, letras como la h, la k, y la w son muchísimo más comunes en inglés que en español y en francés, por lo que si la frecuencia de aparición relativa de estas letras en un texto es alta, cabría la posibilidad de pensar que el texto está escrito en inglés. Lo mismo pasa en español con la vocal a o en francés con las letras u y v. Pero hay que tener en cuenta que para que este método de detección del idioma funcione más o menos bien, el texto debe tener un tamaño aceptable. STRING Un caso concreto de vectores son las cadenas de caracteres, es decir, los string, que no son otra cosa que arrays unidimensionales de caracteres (cada posición del vector es un carácter, un char). Nosotros, siempre que hemos declarado un string lo hemos hecho de la siguiente manera cadena : string; Esta declaración reserva espacio para una cadena de tamaño máximo, es decir, de 255 caracteres. Si quisiéramos tener una cadena de menor tamaño para almacenar por ejemplo un DNI, podríamos hacer perfectamente dni : string[9]; Así reservaríamos espacio solamente para 9 caracteres. Para acceder a la primera cifra del DNI escribiríamos dni[1], para la segunda dni[2], y así hasta la última, que sería dni[8]. Pascal cuenta con un conjunto de operadores, funciones y procedimientos para manipular cadenas de caracteres. Ya hemos visto en alguna ocasión la función length, que calcula la longitud de una cadena. También podemos concatenar cadenas utilizando la función concat, o simplemente juntándolas con el operador + como se muestra a continuación cadenaResultado := concat(‘Hola’, ‘ mundo’, ‘!!!’); cadenaResultado := ‘Hola’ + ‘ mundo’ + ‘!!!’; Ambas instrucciones almacenarían en la variable cadenaResultado la cadena ‘Hola mundo !!!’. Por otro lado, si queremos copiar una subcadena podemos hacer la siguiente llamada cadenaResultado := copy(‘Hola mundo!!!‘, 1, 4); En este caso estaríamos copiando 4 caracteres de la cadena ‘Hola Mundo!!!’ empezando por la posición 1, con lo que almacenaríamos en cadenaResultado la cadena ‘Hola’. Si lo que queremos es insertar una cadena dentro de otra, podemos usar la función insert cadenaResultado := insert(‘Hola !!!‘, ‘mundo’, 6); Esto insertaría la cadena ‘mundo‘ en la posición 6 de la cadena ‘Hola !!!’, es decir, justo donde comienzan los signos de exclamación. Para eliminar una subcadena, podemos hacer uso de delete cadenaResultado := delete(‘Hola mundo!!!‘, 1, 4); Como vemos, la sintaxis es muy parecida a la de la función copy, sólo que en este caso, además de Eduardo E isman – [email protected] – http://decsai.ug r.es/~eisman Depart amento de Ciencias de la Computación e Inteligencia Art if icial – ETSIIT – Un iversid ad de Granada 5 devolver la subcadena, esta se elimina de la cadena original. También podemos buscar una cadena dentro de otra utilizando la función pos posicionInicio := pos(‘Hola mundo!!!’, ‘mundo’); Esta función devolvería la posición a partir de la que comienza la subcadena ‘mundo’ dentro de la cadena ‘Hola mundo!!!’. Finalmente, para convertir cadenas en números y viceversa podemos usar los procedimientos str y val str(134.56, cadena) val(‘134.56’, numero, variableDeError) str asigna a la variable cadena la representación, en forma de cadena de caracteres, del número que se le pasa como primer argumento, mientras que val hace exactamente lo contrario, asigna a la variable numero el valor numérico que representa la cadena que se le pasa como primer parámetro. En este último caso, si no se puede realizar la conversión (si por ejemplo estamos intentando convertir en número la cadena ‘hola’), la variable variableDeError tomará un valor distinto de 0. Por lo tanto, siempre que hagamos uso de val, deberemos comprobar a continuación el estado de dicha variable de error. ARRAYS DINÁMICOS Hasta ahora hemos visto un determinado tipo de arrays, conocidos como arrays estáticos, los cuales tienen siempre una longitud fija que se incluye en la declaración del array. Así, si queríamos calcular la media de diez enteros nos creábamos un vector de longitud diez donde íbamos almacenando los números que íbamos leyendo. Una vez que habíamos leído todos los valores, recorríamos el vector para sumarlos y al final dividíamos entre la longitud del vector. Pero, ¿qué pasa si lo que queremos es calcular la media de 20 valores? O peor aún, ¿qué pasa si es el usuario el que tiene que decidir de cuántos números quiere calcular la media? En ese caso los arrays estáticos ya no nos valen. Necesitamos poder definir dinámicamente, es decir, en tiempo de ejecución, cuál va a ser la longitud del vector. Este tipo de arrays se conocen como arrays dinámicos. Para declarar un array de forma dinámica simplemente tenemos que omitir el rango, es decir, los índices inicial y final en la declaración del array nombreDeLaVariable : array of tipoBásico; o para nuestro ejemplo concreto numeros : array of integer; A continuación, y ya en el cuerpo del programa principal, podemos definer la longitud concreta que va a tener el array (ya que la longitud inicial siempre es 0) realizando una llamada al procedimiento setLength setLength(numeros,10); donde numeros es nuestra variable array dinámico, y 10 es la longitud que va a tener el array. Veamos cómo quedaría el ejemplo del cálculo de la media utilizando un array dinámico 6 Práct icas de Fundamentos de Informátic a – Curso 2009 – 2010 Diplomatu ra en Estadíst ica – Universidad de Granada Observa cómo primero le pedimos al usuario que introduzca de cuántos números quiere realizar la media y luego ya hacemos que el vector tenga esa longitud. Copia el programa y ejecútalo para calcular la media de por ejemplo tres enteros. ¿Por qué número crees que empezarán a numerarse los índices en los arrays dinámicos? Compruébalo. Es muy importante que recuerdes que, a diferencia de lo que ocurre con las cadenas de caracteres (los string) cuyos índices empiezan por 1, en el caso de los arrays dinámicos los índices comienzan siempre por 0. Aunque no entraremos en detalle en el siguiente aspecto, merece la pena mencionar que cuando se realiza una asignación directa entre variables arrays dinámicas (por ejemplo A := B, siendo A y B dos arrays dinámicos), no se realiza una copia elemento a elemento, sino que los dos arrays pasan a compartir la misma zona de memoria, por lo que los cambios que realicemos en una variable se verán reflejados automáticamente en la otra variable. ARRAYS BIDIMENSIONALES (MATRICES) Como ya hemos comentado, en la declaración de un array hay que especificar de qué tipo van a ser sus componentes. Este tipo puede ser otro array, de forma que cada elemento del primer vector es a su vez otro vector. Así podemos definir arrays bidimensionales, o lo que es lo mismo, matrices. La siguiente declaración crea una variable llamada numeros de tipo array de cuatro elementos, en el Eduardo E isman – [email protected] – http://decsai.ug r.es/~eisman Depart amento de Ciencias de la Computación e Inteligencia Art if icial – ETSIIT – Un iversid ad de Granada 7 que cada elemento es a su vez un array formado por tres números enteros numeros : array [1..4] of array of [1..3] of integer; Este tipo de declaraciones de arrays de arrays se suele escribir de forma más abreviada de la siguiente manera (ambas son equivalentes) numeros : array [1..4,1..3] of integer; numeros será por tanto una matriz de 4 filas por 3 columnas en la que cada elemento será un número entero. Su representación gráfica sería la que se muestra a continuación índiceFila 2 3 4 4 27 215 49 1 numeros -10 8 16 61 2 51 15 57 -3 3 índiceColumna 1 numeros es un array de cuatro elementos que contiene en cada posición un array de tres elementos. Podemos acceder a cada una de estas celdas de la matriz de la siguiente manera numeros[índiceFila][índiceColumna] Por ejemplo, mediante numeros[1][1] accederíamos a la primera variable entera, que en este caso contiene el valor 4, numeros[1][2] devolvería un -10, y así sucesivamente. Al igual que ocurría con los vectores, podemos trabajar por separado con cada celda de la matriz y tratarlas como si fueran variables independientes, o también podemos trabajar con la matriz de forma global. Podríamos asignar por ejemplo el contenido de una matriz (o array en general, porque no tiene que ser necesariamente bidimensional) a otra matriz, de forma que se copia cada una de las celdas. En el caso de las matrices bidimensionales, para la lectura de los datos por teclado deberemos utilizar un doble bucle for anidado, de manera que el bucle más exterior se puede utilizar para recorrer cada una de las filas de la matriz, y el bucle más interior recorre cada una de las columnas de la fila que marca el bucle exterior. Sería algo similar a lo que aparece en el siguiente ejemplo. Piensa qué es lo que haría exactamente este programa. 8 Práct icas de Fundamentos de Informátic a – Curso 2009 – 2010 Diplomatu ra en Estadíst ica – Universidad de Granada ARRAYS MULTIDIMENSIONALES Los arrays multidimensionales no son más que una generalización de los vectores y matrices. Podríamos tener por ejemplo un array de tres dimensiones (como si fuera un cubo). Para declararlo simplemente tendríamos que especificar el rango de cada dimensión, separando cada rango por una coma array3D : array [1..5,1..10,1..15] of integer; En este caso tendríamos 5 filas, 10 columnas y una profundidad igual a 15. Es como si tuviéramos 15 tablas de tamaño 5 x 10, una detrás de otra. Para acceder a cada elemento haríamos referencia al índice que este tiene para cada dimensión. Por ejemplo, para acceder al elemento de la primera fila y segunda columna, de la tercera tabla podríamos utilizar cualquiera de estas dos instrucciones Eduardo E isman – [email protected] – http://decsai.ug r.es/~eisman Depart amento de Ciencias de la Computación e Inteligencia Art if icial – ETSIIT – Un iversid ad de Granada 9 array3D[1][2][3] array3D[1,2,3] EJERCICIOS PARA PRACTICAR CON VECTORES Ejercicio 01. Crea un programa que lea un vector por teclado y muestre su contenido por pantalla. Ejercicio 02. Escribe un programa que lea n temperaturas por teclado y las almacene en un vector. A continuación, el programa deberá calcular y mostrar por pantalla cuál es la temperatura media y cuántas temperaturas son mayores o iguales que la media. Ejercicio 03. Implementa un programa que calcule el número de elementos negativos, positivos y nulos de un vector de n elementos introducido por teclado. Ejercicio 04. Realiza una función que calcule el mayor elemento de una lista de tamaño n leída por teclado. Ejercicio 05. Crea una función que calcule el producto escalar de dos vectores. Ejercicio 06. Implementa una función que diga si una cadena es un palíndromo, es decir, si se lee igual de izquierda a derecha que de derecha a izquierda. Por ejemplo, la cadena ‘sos’, o ‘dabalearrozalazorraelabad’ son palíndromos. Ejercicio 07. Escribe un procedimiento que invierta el contenido de un vector, de manera que el primer elemento pase a ser el último y el último el primero, el segundo pase a ser el penúltimo y el penúltimo el segundo, etc. Ejercicio 08. Crea una función que cuente el número de veces que aparece una cadena dentro de otra. Ejercicio 09. Haciendo uso de las funciones de manipulación de cadenas, crea una función que elimine todos los espacios en blanco existentes en una cadena. EJERCICIOS PARA PRACTICAR CON MATRICES Ejercicio 10. Construye un programa que lea por teclado una matriz de tamaño 3 x 3 y muestre por pantalla la matriz opuesta. Ejercicio 11. Crea una función que devuelva la suma de todos los valores de una matriz, la cual se le pasa como parámetro. Ejercicio 12. Escribe una función que devuelva el mayor elemento de una matriz. Ejercicio 13. Crea una función que compruebe si dos matrices (que se le pasan como argumento) son idénticas. 10 Práct icas de Fundamentos de Informátic a – Curso 2009 – 2010 Diplomatu ra en Estadíst ica – Universidad de Granada Ejercicio 14. Realiza una función que diga si una matriz de tamaño m x n es simétrica, es decir, si tiene igual número de filas que de columnas (m = n) y además el elemento en la posición (i, j) es igual al elemento de la posición (j, i), para 1 ≤ i ≤ m, 1 ≤ j ≤ n. Ejercicio 15. Construye un programa que compruebe si una matriz de tamaño 3 x 3 es un cuadrado mágico, es decir, si las filas, las columnas y las diagonales suman lo mismo. Por ejemplo, la siguiente matriz es un cuadrado mágico, ya que todas las filas, columnas y diagonales suman 15. 4 3 8 9 5 1 2 7 6 Ejercicio 16. Escribe un procedimiento que obtenga la matriz suma de dos matrices. El procedimiento recibirá tres parámetros, los dos primeros corresponderán a las matrices que se quieran sumar, y el tercero corresponderá a la matriz en la que se desea guardar la suma. Recuerda que debes pasar la matriz resultado por referencia en lugar de por copia. Ejercicio 17. Crea un procedimiento que calcule la matriz traspuesta de una matriz cualquiera. Este documento está protegido bajo una licencia Creative Commons. No se permite un uso comercial de la obra ni la generación de obras derivadas. Eduardo E isman – [email protected] – http://decsai.ug r.es/~eisman Depart amento de Ciencias de la Computación e Inteligencia Art if icial – ETSIIT – Un iversid ad de Granada 11