sábado, 26 de agosto de 2017

Curso de C#: arrays

Arrays


Un array es un indicador que puede almacenar varios valores simultáneamente. Cada uno de estos valores se identifica mediante un número al cual se llama índice. Así, para acceder al primer elemento del array habría que usar el índice cero, para el segundo el índice uno, para el tercero el índice dos, y así sucesivamente. A continuación se muestra cómo se declara un array:

tipo[] variable;

Curso de C#: arrays



Es muy parecido a como se declara una variable, sólo que hay que poner corchetes detrás del tipo. Los arrays son objetos derivados de la clase System.Array. Por lo tanto, y esto es muy importante, cuando declaramos un array en C# este, aún no se habrá creado, no se habrá reservado aún memoria para él. En consecuencia, los arrays de C# son todos dinámicos, por lo que es necesario instanciarlos antes de poder utilizarlos. Un ejemplo:

string[] nombres; // Declaración del array
nombres = new string[3]; // Instanciación del array

El array nombres será utilizable únicamente a partir de su instanciación. En este ejemplo, el número 3 que está dentro de los corchetes indica el número total de elementos de que constará el array. Todos los arrays de C# están basados en cero, esto es, el primer elemento del array es cero. Por lo tanto, en este caso, el último elemento sería 2 y no 3, ya que son tres los elementos que lo componen (0, 1 y 2). Un ejemplo:

    class Arrays
    {
        static void Main()
        {
            string[] nombres; // Declaración del array
            ushort num=0;

            do
            {
                try
                {
                    Console.Write("¿Cantidad de nombres a introducir? ");
                    num=UInt16.Parse(Console.ReadLine());
                }
                catch
                {
                    continue;
                }
            } while (num==0);

            nombres=new string[num]; // Instanciación del array

            for (int i=0; i<num; i++)
            {
                Console.Write("Introduzca el nombre para cada elemento {0}: ", i);
                nombres[i]=Console.ReadLine();
            }

            Console.WriteLine("Introducidos los {0} nombres", num);
            Console.WriteLine("Pulse INTRO para listarlos");

            string a=Console.ReadLine();

            for (int i=0; i<num; i++)
            {
                Console.WriteLine("Elemento {0}: {1}", i, nombres[i]);
            }

            a=Console.ReadLine();
        }
    }


Veamos la salida en la consola:

¿Cantidad de nombres a introducir? 3
Escriba el nombre para el elemento 0: Antonio
Escriba el nombre para el elemento 1: Ana
Escriba el nombre para el elemento 2: Raquel
Introducidos los 3 nombres
Pulse INTRO para listarlos
Elemento 0: Antonio
Elemento 1: Ana
Elemento 2: Raquel

Este programa declara un array y lo instancia después de haber preguntado al usuario cuántos elementos va a tener. Utiliza un bucle for para recoger los valores a introducir en el array. Dichos nombres se han introducido en la línea "nombres[i] = Console.ReadLine()" lo que hace es que al elemento "i" del array le asigna lo que devuelva el método ReadLine. Como "i" tomará valores entre 0 y el número total de elementos menos uno, rellena el array completo (la condición del bucle, es i<num, si i es igual a num el bucle ya no se itera). Después tiene otro bucle for para recorrer todo el array y escribir sus valores en la consola. Para acceder a un elemento del array se utiliza la sintaxis "array[índice]".

También es posible inicializar un array en la propia declaración, ya sea instanciándolo o bien asignándole los valores directamente. Reescribimos el ejemplo anterior para instanciar el array en la declaración del mismo:

    class Arrays2
    {
        static void Main()
        {
            ushort num=3;

            do
            {
                try
                {
                    Console.Write("¿Cantidad de nombres a introducir? ");
                    num=UInt16.Parse(Console.ReadLine());
                }
                catch
                {
                    continue;
                }
            } while (num==0);

            string[] nombres=new string[num]; // Declaración e instanciación del array

            for (int i=0; i<num; i++)
            {
                Console.Write("Escriba el nombre del elemento {0}: ", i);
                nombres[i]=Console.ReadLine();
            }

            Console.WriteLine("Introducidos los {0} nombres", num);
            Console.WriteLine("Pulse INTRO para listarlos");

            string a=Console.ReadLine();

            for (int i=0; i<num; i++)
            {
                Console.WriteLine("Elemento {0}: {1}", i, nombres[i]);
            }

            a=Console.ReadLine();
        }
    }


Se puede observar, que el array se instancia en la misma línea en la que se declara. El funcionamiento sería el mismo que el del ejemplo anterior. En el ejemplo siguiente de inicialización del array, le asigna los valores en la declaración:

    class Arrays3
    {
        static void Main()
        {
            // Declaración e inicialización del array
            string[] nombres={"Antonio", "Ana", "Raquel"};

            for (int i=0; i<nombres.Length; i++)
            {
                Console.WriteLine("Elemento {0}: {1}", i, nombres[i]);
            }

            string a=Console.ReadLine();
        }
    }


En este caso, el array nombres se inicializa en la propia declaración del mismo, asignándole los tres valores que va a contener. Dichos valores están entre llaves y separados por comas. Las comillas son necesarias, ya que el array es de tipo string. ¿Dónde se encuentra en este caso la instanciación del array? la instanciación la hace por debajo el compilador, de forma implícita. Ahora se utiliza la propiedad Length del array nombres en lugar de una variable. Esta propiedad devuelve el número de elementos de un array. De este modo, la salida en consola de este programa sería esta:

Elemento 0: Antonio
Elemento 1: Ana
Elemento 2: Raquel

El hecho de que un array haya sido inicializado no quiere decir que sea inamovible. En el caso de que un array que ya contienga datos se vuelva a instanciar, el array volverá a estar vacío, y obtendrá las dimensiones de la nueva instanciación.


Arrays multidimensionales


Los arrays multidimensionales constan de dos o más dimensiones, cada elemento del array viene definido por dos o más índices. En este caso vamos a definir un array de tres dimensiones):

tipo[,,] variable;

Hay dos comas dentro de los corchetes, esto indica que el array es tridimensional, los tres índices del mismo se separan uno de otro por comas. Un pequeño ejemplo:

string[,] nombres = new string[2,4];

Este array es bidimensional y sirve para almacenar una lista de nombres por habitación, de este modo, si tenemos dos salones y cuatro personas en cada una, podemos representarlo en una tabla para hacernos una idea:

nombres[0,0]="Manuel";
nombres[0,1]="Ana";
nombres[0,2]="Juan";
nombres[0,3]="Raquel";
nombres[1,0]="Lola";
nombres[1,1]="Antonio";
nombres[1,2]="Juana";
nombres[1,3]="Vicente";

Esto sería como almacenar los datos en esta tabla:


Sala 0
Sala 1
NOMBRE 0
Manuel
Lola
NOMBRE 1
Ana
Antonio
NOMBRE 2
Juan
Juana
NOMBRE 3
Raquel
Vicente

Un array multidimensional se reccorre con bucles anidados. A continuación un programa pregunta al usuario por el número de columnas que quiere generar de una quiniela de fútbol, y después las rellena al azar y las muestra en pantalla:

    class Quinielas
    {
        static void Main()
        {
            const char local='1';
            const char empate='X';
            const char visitante='2';
            const byte numFilas=14;
            byte numColumnas=0;
            char[,] quiniela;
            byte azar;
            Random rnd=new Random(unchecked((int) DateTime.Now.Ticks));
            
            do
            {
                try
                {
                    Console.WriteLine("Mínimo una columna y máximo ocho");
                    Console.Write("¿Cuántas columnas quiere generar? ");
                    numColumnas=Byte.Parse(Console.ReadLine());
                }
                catch
                {
                    continue;
                }
            } while (numColumnas<1 || numColumnas>8);

            quiniela=new char[numColumnas, numFilas];

            for (byte i=0; i<numColumnas; i++)
            {
                for (byte j=0; j<numFilas; j++)
                {
                    azar=(byte) (rnd.NextDouble()*3D);
                    switch (azar)
                    {
                        case 0:
                            quiniela[i,j]=local;
                            break;
                        case 1:
                            quiniela[i,j]=empate;
                            break;
                        case 2:
                            quiniela[i,j]=visitante;
                            break;
                    }
                }
            }

            Console.WriteLine("Quiniela generada. Pulse INTRO para mostrarla");
            string a=Console.ReadLine();

            for (byte i=0; i<numColumnas; i++)
            {
                Console.Write("Columna {0}: ", i+1);
                for (byte j=0; j<numFilas; j++)
                {
                    Console.Write("{0} ", quiniela[i,j]);
                }
                Console.WriteLine();
                Console.WriteLine();
            }

            a=Console.ReadLine();
        }
    }


La clase Random utilizada en este programa, es para generar números aleatorios. En la instanciación de la clase, se ha puesto algo que puede resultar algo confuso:

Random rnd=new Random(unchecked((int) DateTime.Now.Ticks));

El constructor de esta clase tiene dos sobrecargas: una de ellas es sin argumentos, y la otra acepta un argumento de tipo int, que es la que se utiliza en este ejemplo. De no ser así, siempre generaría los mismos números en cada ejecución del programa. El número lo generamos al ejecutar el método NextDouble, el cual retorna un número mayor o igual a 0 y menor que 1. Esta es la línea:

azar=(byte) (rnd.NextDouble()*3D);

Se multiplica por 3D? pues como se necesitan números enteros entre 0 y será suficiente con multiplicar este número por 3. La D es un sufijo literal, para indicar si se debe considerar si el número es de un tipo o de otro. Dado que el método NextDouble retorna un valor double, tenemos que multiplicarlo por otro valor double. Por eso se pone el sufijo "D" al número tres. Posteriormente todo ese resultado se convierte a byte y se asigna a la variable azar, que es la que se comprueba en el switch y le asigna el carácter necesario según su valor a cada elemento del array.

También hay un par de bucles anidados para asignar los valores al array y después otros dos bucles anidados para recorrer dicho array y mostrar su contenido en la consola.
  
El número de dimensiones de un array se llama rango. Para conocer el rango de un array a través de código, se invoca la propiedad Rank del mismo (heredada de la clase System.Array). Un ejemplo:

 class Rangos
    {
        static void Main()
        {
            int[] array1=new int[2];
            int[,] array2=new int[2,2];
            int[,,] array3=new int[2,2,2];
            int[,,,] array4=new int[2,2,2,2];

            Console.WriteLine("Rango de array1: {0}", array1.Rank);
            Console.WriteLine("Rango de array2: {0}", array2.Rank);
            Console.WriteLine("Rango de array3: {0}", array3.Rank);
            Console.WriteLine("Rango de array4: {0}", array4.Rank);

            string a=Console.ReadLine();
        }
    }

La salida en la consola sería la siguiente:

Rango de array1: 1
Rango de array22
Rango de array33
Rango de array44

Arrays de Arrays


Son arrays que pueden contener otros arrays. Un programa en el que el usuario tiene que manejar simultáneamente múltiples objetos de distintas clases derivadas de una clase base, por ejemplo, triángulos y cuadrados derivados de la clase figura. Si solamente estuviera permitido utilizar arrays unidimensionales o multidimensionales declararíamos un array distinto para cada tipo de objeto (uno para triángulos y otro para cuadrados). Pero en el caso de que haya que redibujar todos los objetos, ya sean cuadrados o triángulos, habría que escribir un bucle para cada uno de los arrays para poder invocar los métodos Redibujar de cada uno de los elementos. Sin embargo, introduciendo todos los arrays dentro de un array de arrays bastaría con escribir un par de bucles anidados para recorrer todos los objetos y dejar el resto en manos del polimorfismo. A continuación un ejemplo.

    class ArraysDeArrays
    {
        static void Main()
        {
            object[][] numeros; // Declaración del array de arrays
            numeros=new object[2][]; // Instanciación del array de arrays
            
            numeros[0]=new object[3]; // Instanciación del primer array
            numeros[1]=new object[4]; // Instanciación del segundo array
            
            numeros[0][0]=3.325D;
            numeros[0][1]=6.25D;
            numeros[0][2]=3D;

            numeros[1][0]=3u;
            numeros[1][1]=7u;
            numeros[1][2]=4u;
            numeros[1][3]=87u;

            for (int i=0;i<numeros.Length;i++)
            {
                for (int j=0;j<numeros[i].Length;j++)
                {
                    Console.WriteLine(numeros[i][j].ToString());
                }
            }

            string a=Console.ReadLine();
        }
    }


En este ejemplo se utiliza un array que incluye dos arrays: uno para números de tipo double y otro para números de tipo ulong. Como estos dos tipos están derivados de la clase System.Object, se declara un array de este tipo en la primera línea del método Main, y después se instancia para  que contendrá dos arrays (en la segunda línea). Después se instancian los dos arrays como tipo object, y se les asignan los valores: al array numeros[0] se le asignan valores de tipo double, y al array numeros[1] se le asignan valores de tipo ulong. Posteriormente  se utiliza un par de bucles anidados que recorren todos los elementos del array de arrays con objeto de invocar el método ToString() de todos ellos (heredado de la clase System.Object). El bucle "i" recorre el array de arrays (la condición, i<numeros.Length), y el bucle "j" recorre cada uno de los elementos del array numeros[i], según sea el valor de i en cada iteración.

No hay comentarios:

Publicar un comentario