jardínBit

Arreglos: Fundamentos

Los arreglos (arrays) son una manera sistematizada de almacenar múltiples variables del mismo tipo.

En combinación con ciclos while o for, nos permiten programar rutinas que actúan sobre diversos valores a la vez.

[Ciclos while y for]

1 Planteamiento del problema

1.1 Una variable

Partimos de este ejemplo de código de animación básica con acumuladores, en el que un punto se mueve hacia la derecha y al llegar al borde vuelve a empezar su camino en la izquierda.

[Animación básica con acumuladores]

// 1) variable para almacenar la posición horizontal del punto
float px; 

void setup(){
  size(400, 400);
  strokeWeight(10);
  stroke(255);

  px = 0; // 2) valor inicial de posición
}

void draw(){
 background(0);
 // 3) dibuja el punto con pos. horizontal: px
 // y posición vertical: 200
 point(px, 200);
 
 // 4) incrementa la posición horizontal
 px = px + 2;
 // 5) y si llega al borde derecho, reinicia
 if(px > width){
  px = 0; 
 }
}

1.2 Múltiples variables, sin arreglos

Supongamos que queremos tener más puntos moviéndose de la misma manera, pero empezando en diferentes posiciones.

Previo a utilizar arreglos, lo que haríamos sería crear una variable por cada punto y repetir por cada una los 5 pasos del ejemplo previo.

Un ejemplo con tres puntos se vería así:

// 1) variables para almacenar las posiciones horizontales de los puntos
float px1, px2, px3; 

void setup(){
  size(400, 400);
  strokeWeight(10);
  stroke(255);

// 2) valor inicial de posición
  px1 = 0; 
  px2 = 50; 
  px3 = 200; 
}

void draw(){
 background(0);
 // 3) dibuja los puntos con pos. horizontal: px
 // y posición vertical: 200
 point(px1, 200);
 point(px2, 200);
 point(px3, 200);
 
 // 4) incrementa las posiciones horizontales
 px1 = px1 + 2;
 px2 = px2 + 2;
 px3 = px3 + 2;

 // 5) y si alguna llega al borde derecho, reinicia
 if(px1 > width){
  px1 = 0; 
 }
 if(px2 > width){
  px2 = 0; 
 }
 if(px3 > width){
  px3 = 0; 
 }
}

Tal vez con tres puntos todavía es manejable, pero imagina cómo se vería el código con 5, 10, o 100 puntos.

Los arreglos nos permitirán tener un código con una extensión al primero, aunque tengamos miles de puntos.

1.3 Con arreglos

El mismo programa, con arreglos, podría quedar de la siguiente forma:

// 1) arreglo para almacenar las posiciones horizontales de los puntos
float[] px;

void setup(){
  size(400, 400);
  strokeWeight(10);
  stroke(255);

// 2) crea e inicializa arreglo con 3 puntos:
  px = new float[3];
  px[0] = 0; 
  px[1] = 50; 
  px[2] = 200; 
}

void draw(){
  background(0);

  // por cada elemento del arreglo...
  for (int i=0; i<px.length; i++) {
    // 3) dibuja los puntos con pos. horizontal: px[i]
    // y posición vertical: 200
    point(px[i], 200);

    // 4) incrementa la posición horizontal
    px[i] = px[i] + 2;

    // 5) y si alguna llega al borde derecho, reinicia
    if (px[i] > width) {
      px[i] = 0;
    }
  }
}

2 Notación

Los símbolos que nos van a indicar que estamos usando arreglos son los corchetes [ ].

2.1 Declaración

Declaramos un arreglo con su tipo de dato y los corchetes:

float[] posiciones; // declara arreglo

2.2 Creación

A diferencia de otras variables básicas, antes de poder usar un arreglo necesitamos crearlo.

Al crearlo, indicamos el número de elementos que contendrá:

posiciones = new float[100]; // crea arreglo de 100 elementos

2.3 Atributo .length

Los arreglos tienen un atributo .length que nos indica el número de elementos que contiene.

println( posiciones.length ); // imprime tamaño del arreglo

2.4 Asignación y lectura

Cada elemento del arreglo tiene un índice numérico con el cual lo podemos ubicar. Podemos visualizarlo también como una dirección.

Los elementos están numerados a partir de 0, y llegan hasta .length - 1.

Por ejemplo, un arreglo de 5 elementos tendrá los índices 0, 1, 2, 3, 4.

Accederemos a cada índice con los corchetes [ ]:

posiciones[0] = 200; // asigna 200 a la primera posición del arreglo
posiciones[1] = 10; // asigna 10 a la segunda posición del arreglo

Esa notación la podemos utilizar en lugar de cualquier variable.

Por ejemplo, aquí la tercera posición del arreglo se comporta como acumulador, leyendo el valor del elemento, sumándole 5, y guardando el resultado en la misma posición:

posiciones[2] = posiciones[2] + 5;

Cuando queremos utilizar un índice fuera del rango del arreglo, recibiremos un error ArrayIndexOutOfBoundsException.

2.5 Declaración, creación y asignación

Alternativamente, podemos declarar y crear un arreglo directamente con una lista de elementos:

float[] posiciones = { 200, 100, 50, 20 }; // declara y crea arreglo

El arreglo tendrá el tamaño correspondiente a la cantidad de elementos dados.

2.6 Ciclos for

Una de las principales ventajas de los arreglos es que como podemos acceder a sus elementos a través de un valor numérico (el índice), podemos hacer que ese sea una variable que se incrementa de uno en uno (un contador).

La notación más utilizada es con una variable de tipo int llamada i que funciona como índice, y que recorre cada posición del arreglo con un ciclo for de la siguiente forma:

for( int i=0; i<posiciones.length; i++){
  // realiza acciones con posiciones[i]
}

Lo que escribiremos dentro del ciclo for requiere un trabajo previo de identificar qué acciones son las que tomamos por cada elemento del arreglo, qué tienen en común, para entonces abstraerlas en términos de i.

2.6.1 Inicializaciones

Este ejemplo inicializa todos los valores del arreglo en 0:

for( int i=0; i<posiciones.length; i++){
  posiciones[i] = 0;
}

Y este asigna un valor aleatorio distinto a cada elemento:

for( int i=0; i<posiciones.length; i++){
  posiciones[i] = random(width);
}

Aquí le asignamos un valor dado por un acumulador:

float x = 0;
for( int i=0; i<posiciones.length; i++){
  posiciones[i] = x;
  x = x + 10;
}

2.6.2 Lectura

Este ejemplo dibuja tantos puntos como elementos del arreglo, usando el valor leído como posición horizontal:

for( int i=0; i<posiciones.length; i++){
  point( posiciones[i], 200 );
}

2.6.3 Incrementos, condicionales

Este ciclo fue extraído del ejemplo de arriba. Aquí cada elemento del arreglo se incrementa y cuando supera un límite es reiniciado a 0

for( int i=0; i<posiciones.length; i++){
  posiciones[i] = posiciones[i] + 10;
  if( posiciones[i] > width ){
    posiciones[i] = 0;
  }
}

3 Multiplicidades

Podemos crear tantos arreglos como queramos. Si todos tienen el mismo tamaño, los podemos utilizar para referirse a diferentes características de una misma entidad.

Por ejemplo, los puntos del ejemplo de arriba pueden tener diferente posición vertical además de diferente posición horizontal. Este sketch inicializa 100 de ellos con posición aleatoria y los mueve en la misma dirección, reiniciándolos al llegar al final:

// 1) arreglos para almacenar posiciones x,y de los puntos
float[] px;
float[] py;


void setup() {
  size(400, 400);
  strokeWeight(10);
  stroke(255);

  // 2) crea e inicializa arreglos con 100 elementos:
  px = new float[100];
  py = new float[100];

  for (int i=0; i<px.length; i++) {
    px[i] = random(width);
    py[i] = random(height);
  }
}

void draw() {
  background(0);

  // por cada elemento del arreglo...
  for (int i=0; i<px.length; i++) {
    // 3) dibuja los puntos con pos. horizontal: px[i]
    // y posición vertical py[i]
    point(px[i], py[i]);

    // 4) incrementa la posición horizontal
    px[i] = px[i] + 2;

    // 5) y si alguna llega al borde derecho, reinicia
    if (px[i] > width) {
      px[i] = 0;
    }

  }// cierra ciclo for
  
}

¿Cómo harías que los puntos se muevan en el eje vertical en vez del horizontal?

¿Cómo harías que al llegar al borde derecho, los puntos cambien su posición vertical a una nueva posición aleatoria?

¿Cómo harías que cada punto tuviera una velocidad distinta?