Arrays o vectores
Cuando empezamos a programar nuestros primeros programas pueden ocurrir dos cosas, que todo parezca muy sencillo porque somos unos cracks; o que todo se nos haga cuesta arriba y demasiado complicado al ver tantas palabras nuevas de programación.
De una manera u otra, aunque sepamos mucho o poco; siempre hay algo nuevo para aprender, aplicar y mejorar en nuestros programas con Arduino.
Hasta el momento, hemos utilizado métodos sencillos para el manejo de pines, entradas y salidas, funciones, variables, instaciación de objetos para nuestros motores, instalación de librerías.
Pero nos queda aún una parte muy importante; que es el manejo de arrays o también denominados vectores.
Array
Un array es una ordenación de un conjunto de datos del mismo tipo ordenados mediante un índice.
Si queremos tratar un objeto de forma individual, al que le debemos asociar un nombre; para tratar con 100 objetos del mismo tipo, tendremos que crear 100 nombres distintos. En lugar de eso vamos a crear un nombre del grupo de objetos y cuando queramos acceder a uno de ellos, solamente tendremos que identificarlo por un número.
La estructura a almacenar de datos sería la siguiente.
Un aspecto muy importante a tener en cuenta, es que antes de nada, hemos de definir el tamaño, o el espacio máximo que puede ocupar el array que vamos a crear.
A los arrays que tienen un tamaño variable, se les denomina arrays dinámicos y aunque se pueden implementar , debemos aprender un poco más acerca del lenguaje y crear diferentes estrategias asociadas con punteros.
En el momento de crear una array, tenemos dos opciones de instanciarlo fuera del Setup o el loop.
- Inicializarlo en vacio estableciendo el tamaño máximo
- Inicializarlo con valores sin establecer tamaño máximo.
Una de las cosas que nos tendremos que dar cuenta, es que los índices de los vectores empiezan por el 0. Esto quiere decir que el último valor de 10 elementos, no es el índice 10, sino el 9, ya que el 0 cuenta y recorrer los vectores siempre llega hasta el tamaño menos una unidad.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | int void_array[5]; int full_array[] ={ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; void setup() { Serial.begin(9600); Serial.print( "First element of void array: " ); Serial.println(void_array[0]); Serial.print( "First element of full array: " ); Serial.println(full_array[0]); Serial.print( "11th element of full array: " ); Serial.println(full_array[10]); full_array[10]=110; Serial.print( "11th element of full array with value: " ); Serial.println(full_array[10]); void_array[7]=123456; Serial.print( "8th element of void array with value: " ); Serial.println(void_array[7]); } void loop() { } |
Una cuestión interesante a considerar en este ejercicio, es que aunque el array tenga un tamaño definido, eso no quiere decir que no se pueda aumentar el tamaño dentro del programa mediante asignación de un valor a un elemento fuera de los límites definido por el tamaño.
Pero la lógica detrá de este hecho, es que la memoria de nuestro Arduino es finita y limitada. Cuando definimos el tamaño de un array, podemos estar seguros de que se va a dejar ese espacio para manipular las variables dentro de ese rango. Mientras que si nos vamos fuera del rango, existe una posibilidad de que haya un conflicto a la hora de guardar variables en un espacio de memoria que no le corresponde, como se puede ver en el siguiente caso.
Array – Control en bucle
Para operar con todos los elementos de un array, vamos a utilizar los bucles mediante un contador que define el índice. Esto es muy útil, ya que para recorrer todos los elementos podemos realizar en pocas lineas, sabiendo este formato de control en bucle.
En este ejemplo, vamos a calcular la media de todo el conjunto de elementos del array.
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 | int full_array[] ={ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}; int sum; int length; void setup() { Serial.begin(9600); length = sizeof (full_array)/ sizeof (full_array[ 0 ]); Serial.print( "Length: " ); Serial.println( length ); for ( int i = 0; i < length; i++ ){ sum = sum + full_array[ i ]; Serial.print( "Value: " ); Serial.print( full_array[ i ] ); Serial.print( " Sum: " ); Serial.println(sum); } Serial.print( " Mean: " ); Serial.println( sum/length ); } void loop() { } |
En este ejemplo, tenemos que conocer la longitud del vector para poder realizar la media.
El conjunto de datos array definido no nos ofrece un método que nos proporcione el valor de la longitud, a menos que la definamos como constante. Pero la podemos deducir gracias a la función sizeof. Sizeof realmente es una función del lenguaje C++, que proporciona cuántos bytes ocupa una variable dentro del programa.
Por lo que si dividimos cuantos bytes ocupa el array entero, con respecto a un elemento del mismo, obtendremos la longitud total.
Este formato es importante manipularlo de la misma manera, ya que si declaramos el tipo de dato del array diferente, siempres debe ir asociado el espacio que ocupa el array con respecto al espacio que ocupa uno solo de sus elementos.
Arduino Array Pins
Un truco muy habitual que podremos encontrar en algunos programas de ejemplo, es el de configuración de Pines en un array.
Imaginemos que tenemos una serie de pines que queremos poner en Output y otros en Input.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | int input_array[] ={ 2, 3, 4, 11, 13}; int ouput_array[] ={ 5, 6, 7, 8, 9, 10, 12}; void setup() { Serial.begin(9600); int input_length = sizeof (input_array)/ sizeof (input_array[ 0 ]); for ( int i = 0; i < input_length ; i++ ){ Serial.print( "Input: " );Serial.println( input_array[ i ]); pinMode( input_array[ i ], INPUT ); } int output_length = sizeof (ouput_array)/ sizeof (ouput_array[ 0 ]); for ( int i = 0; i < output_length ; i++ ){ Serial.print( "Output: " );Serial.println( ouput_array[ i ]); pinMode( ouput_array[ i ], OUTPUT ); } } void loop() { } |
Esta estrategia es muy útil si lo que queremos es que la configuración de los pines no dependan del contenido del programa, sino de la configuración a la hora de crear el array. En el caso de que tuvieramos que modificar los pines por equivocación o para adaptar de un proyecto a otro, no tendriamos que preocuparnos en cambiar nada en la función setup o loop, ya que el comportamiento es el mismo.
Solamente habría que cambiar los números iniciales de la declaración de los dos arrays.
Algoritmos de ordenación
Una de las complejidades en programación reside en la búsqueda y ordenación de un conjunto de datos dentro de una array extenso de forma optimizada reduciendo los tiempos al máximo.
Aunque veremos los algoritmos más simples, por contra los más ineficientes, consideraremos que para una placa Arduino es suficiente, ya que los datos que manejaremos no superarán en muchas ocasiones demasiados elementos debidas a la restricción en memoria dentro de la placa (1000 elementos como máximo).
En caso de disponer de demasiados elementos, podríamos considerar utilizar una librería con métodos más optimizados o una placa mejor.
Metodo de selección
El siguiente método consiste en buscar el menor elemento de izquierda a derecha e ir intercambiándolo desplazándose hacia la derecha, dejando los elementos ordenados a un lado del array.
Método de Inserción
El método de inserción consiste en ordenar los elementos desplazando en sustitución del elemento siguiente en función de si el elemento es mayor o menor hasta que tengamos una porción del array que consideramos que esté todo ordenado, hasta terminar con los que quedan desordenados.
Método de la burbuja
Este método se recomienda solamente para listas cortas de unos 1000 elementos, ya que se considera uno de los más simples de programar, pero por contra ineficiente en cuanto a su velocidad. Hay que tener en cuenta también que los datos a almacenar en una placa Arduino no superará casi nunca los 4000 datos si son simples caracteres o enteros hasta 255, y aún menor para cualquier tipo de dato con más capacidad por lo que este algoritmo es aplicable.
Algoritmo Quicksort
El algoritmo quicksort es uno de los algoritmos más eficientes para ordenar arrays. Se puede usar con Arduino a través de una librería que podemos descargar desde el siguiente enlace. Y desde la página web de Luis Llamas, todo un maker Arduino se explica con detalle cómo realizar este algoritmo de ordenación de forma intuitiva y práctica.
Conclusiones
Muchas veces no nos damos cuenta de que existen muchas opciones de programación más potentes que Arduino. En caso de realizar algoritmos de ordenamiento y búsqueda hay que pensar que existen otras opciones más robustas en el siglo XXI. Así que si solamente queremos aprender y aplicar estos modelos, solamente su uso es un buen ejercicio para comprender su fcuncionamiento.
Recomendamos en todo caso, medir los tiempos que nos cuesta aplicar cada algoritmo de ordenación. En este apartado del curso no profundizamos en punteros, pero es un concepto muy útil para seguir adentrándonos en modelos más complejos de ordenación.
Algoritmos eficientes de ordenación disponemos de los siguiente métodos:
¿Cómo suena un algoritmo de ordenación?
Para visualizar un poco los modelos de ordenación de forma gráfica dejamos este hipnotizante video para entender estos modelos, ya que a simple vista en un programa el resultado puede parecer imperceptible.
Una vez completado este tutorial, puedes acceder al siguiente nivel.
Los ejercicios de código, proyectos y recursos que desarrollaremos durante el curso se pueden consultar a través de nuestro Github.