Copiar y editar arrays
En esta sección aprenderemos a utilizar las funciones integradas para insertar y eliminar elementos de arrays, cambiar su orden y copiar arrays enteros.
bool ArrayInsert(void &target[], const void &source[], uint to, uint from = 0, uint count = WHOLE_ARRAY)
La función inserta el número especificado de elementos del array de origen 'source' en el array de destino target. La posición de inserción en el array target se establece mediante el índice del parámetro to. El índice inicial del elemento en el que empezar a copiar del array source viene dado por el índice from. La constante WHOLE_ARRAY ((uint)-1) del parámetro count especifica la transferencia de todos los elementos del array de origen.
Todos los índices y recuentos son relativos a la primera dimensión de los arrays. En otras palabras, en el caso de los arrays multidimensionales, la inserción no se realiza por elementos individuales, sino por toda la configuración descrita por las dimensiones «superiores». Por ejemplo, para un array bidimensional, el valor 1 en el parámetro count significa insertar un vector de longitud igual a la segunda dimensión (véase el ejemplo).
Por ello, el array de destino y el array de origen deben tener las mismas configuraciones. De lo contrario, se producirá un error y la copia fallará. Para arrays unidimensionales, esto no es una limitación, pero para arrays multidimensionales, es necesario observar la igualdad de tamaños en dimensiones superiores a la primera. En particular, los elementos del array [][4] no pueden insertarse en el array [][5] y viceversa.
La función sólo es aplicable a arrays de tamaño fijo o dinámico. Edición de series temporales (arrays con series temporales) no puede realizarse con esta función. Está prohibido especificar en los parámetros target y source el mismo array.
Cuando se insertan en un array fijo, los nuevos elementos desplazan los existentes hacia la derecha y desplazan count de los elementos situados más a la derecha hacia el exterior del array. El parámetro to debe tener un valor comprendido entre 0 y el tamaño del array menos 1.
Cuando se insertan en un array dinámico, los elementos antiguos también se desplazan a la derecha, pero no desaparecen, porque el propio array se amplía en count elementos. El parámetro to debe tener un valor comprendido entre 0 y el tamaño del array. Si es igual al tamaño del array, se añaden nuevos elementos al final del array.
Los elementos especificados se copian de un array a otro, es decir, permanecen inalterados en el array original, y sus «dobles» en el nuevo array se convierten en instancias independientes que no guardan relación alguna con los «originales».
La función devuelve true si tiene éxito o false en caso de error.
Veamos algunos ejemplos (ArrayInsert.mq5). La función OnStart proporciona descripciones de varios arrays de diferentes configuraciones, tanto fijos como dinámicos.
#define PRTS(A) Print(#A, "=", (string)(A) + " / status:" + (string)GetLastError())
|
Para empezar, por comodidad, se introduce una macro que muestra el código de error (obtenido mediante la función GetLastError) inmediatamente después de llamar a la instrucción bajo prueba: PRTS. Se trata de una versión ligeramente modificada de la conocida macro PRT.
Los intentos de copiar elementos entre arrays de configuraciones diferentes terminan con el error 4006 (ERR_INVALID_ARRAY).
// you can't mix 1D and 2D arrays
|
El índice de destino debe estar dentro del array.
// target index 10 is out of the range or the array 'insert',
|
Las siguientes son modificaciones de arrays realizadas con éxito:
// copy second row from 'fixed', 'dynamic2Dx4' is allocated
|
Esto es lo que aparecerá en el registro:
ArrayInsert(dynamic2Dx4,fixed,0,1,1)=true
|
bool ArrayCopy(void &target[], const void &source[], int to = 0, int from = 0, int count = WHOLE_ARRAY)
La función copia parte o la totalidad del array source en el array target. El lugar del array target en el que se escriben los elementos se especifica mediante el índice del parámetro to. El índice inicial del elemento desde el que empezar a copiar del array source viene dado por el índice from. La constante WHOLE_ARRAY (-1) en el parámetro count especifica la transferencia de todos los elementos del array de origen. Si count es menor que cero o mayor que el número de elementos que quedan desde la posición from hasta el final del array source, se copia todo el resto del array.
A diferencia de la función ArrayInsert, la función ArrayCopy no desplaza los elementos existentes del array receptor, sino que escribe nuevos elementos en las posiciones especificadas sobre los antiguos.
Todos los índices y el número de elementos se establecen teniendo en cuenta la numeración continua de los elementos, independientemente del número de dimensiones de los arrays y de su configuración. En otras palabras, se pueden copiar elementos de arrays multidimensionales a arrays unidimensionales y viceversa, o entre arrays multidimensionales con diferentes tamaños según las dimensiones «superiores» (véase el ejemplo).
La función funciona con arrays fijos y dinámicos, así como con arrays de series temporales designadas como búferes indicadores.
Se permite copiar elementos de un array a sí mismo. Pero si las áreas target y source se solapan, debe tener en cuenta que la iteración se realiza de izquierda a derecha.
Un array de destino dinámico se amplía automáticamente según sea necesario. Los arrays fijos conservan sus dimensiones, y lo que se copia debe caber en el array, de lo contrario se producirá un error.
Se admiten arrays de tipos integrados y arrays de estructuras con campos de tipo simple. Para los tipos numéricos, la función intentará convertir los datos si los tipos de origen y destino difieren. Un array de cadenas sólo puede copiarse en otro array de cadenas. Los objetos de clase no están permitidos, pero los punteros a objetos sí pueden copiarse.
La función devuelve el número de elementos copiados (0 en caso de error).
En el script ArrayCopy.mq5 hay varios ejemplos de uso de la función.
class Dummy
|
Los arrays con objetos generan un error de compilación que indica que «no se permiten estructuras o clases que contengan objetos», pero los punteros pueden copiarse.
Dummy *pointers1[5], *pointers2[5];
|
Los arrays de estructuras con campos de tipos simples también se copian sin problemas.
struct Simple
|
Para seguir demostrando cómo trabajar con arrays de diferentes tipos y configuraciones, se definen los siguientes arrays (incluyendo arrays fijos y dinámicos, así como arrays con un número diferente de dimensiones):
int dynamic[];
|
Cuando se copia un elemento del array fixed desde la posición 1 (número 2), se asigna una fila entera de 4 elementos en el array dinámico receptor dynamic2Dx4, y como sólo se copia 1 elemento, los tres restantes contendrán «basura» aleatoria (resaltada en amarillo).
PRTS(ArrayCopy(dynamic2Dx4, fixed, 0, 1, 1)); // 1 / status:0
|
A continuación, copiamos todos los elementos del array fixed, empezando por el tercero, en el mismo array dynamic2Dx4, pero empezando por la posición 1. Dado que se copian 5 elementos (el número total del array fixed es 8 menos la posición inicial 3) y se colocan en el índice 1, en total, 1 + 5 estarán ocupados en el array receptor, para un total de 6 elementos. Y como el array dynamic2Dx4 tiene 4 elementos en cada fila (en la segunda dimensión), es posible asignarle memoria sólo para el número de elementos que sea múltiplo de 4, es decir, se distribuirán 2 elementos más, en los que quedarán datos aleatorios.
PRTS(ArrayCopy(dynamic2Dx4, fixed, 1, 3)); // 5 / status:0
|
Al copiar un array multidimensional en un array unidimensional, los elementos se presentarán de forma «plana».
PRTS(ArrayCopy(dynamic, fixed)); // 8 / status:0
|
Cuando se copia un array unidimensional en otro multidimensional, los elementos se «expanden» según las dimensiones del array receptor.
PRTS(ArrayCopy(dynamic2Dx5, insert)); // 3 / status:0
|
En este caso, se han copiado 3 elementos que caben en una fila de 5 elementos (según la configuración del array receptor). La memoria para los dos elementos restantes de la serie se asignó, pero no se llenó (contiene «basura»).
Podemos sobrescribir el array dynamic2Dx5 desde otra fuente, incluso desde un array multidimensional de una configuración diferente. Dado que se asignaron dos filas de 5 elementos cada una en el array receptor y 2 filas de 4 elementos cada una en el array de origen, han quedado 2 elementos adicionales sin rellenar.
PRTS(ArrayCopy(dynamic2Dx5, fixed)); // 8 / status:0
|
Mediante ArrayCopy es posible cambiar los elementos de los arrays receptores fijos.
PRTS(ArrayCopy(fixed, insert)); // 3 / status:0
|
Aquí hemos sobrescrito los tres primeros elementos del array fixed. Y luego vamos a sobrescribir los tres últimos.
PRTS(ArrayCopy(fixed, insert, 5)); // 3 / status:0
|
Copiar a una posición igual a la longitud del array fijo no funcionará (el array dinámico de destino se expandiría en este caso).
PRTS(ArrayCopy(fixed, insert, 8)); // 4006, ERR_INVALID_ARRAY
|
Los arrays de cadenas combinados con arrays de otros tipos producirán un error:
PRTS(ArrayCopy(texts, insert)); // 5050, ERR_INCOMPATIBLE_ARRAYS
|
Pero entre arrays de cadenas, la copia es posible:
PRTS(ArrayCopy(texts, message));
|
Los arrays de distintos tipos numéricos se copian con la conversión necesaria.
PRTS(ArrayCopy(insert, array, 1)); // 1 / status:0
|
Aquí hemos escrito el número Pi en un array de enteros, y por lo tanto recibimos el valor 3 (sustituyó a 11).
bool ArrayRemove(void &array[], uint start, uint count = WHOLE_ARRAY)
La función elimina el número especificado de elementos del array a partir del índice start. Un array puede ser multidimensional y tener cualquier tipo de estructura o integrado con campos de tipos integrados, a excepción de las cadenas.
El índice start y la cantidad count se refieren a la primera dimensión de los arrays. En otras palabras: en el caso de los arrays multidimensionales, el borrado no se realiza por elementos individuales, sino por toda la configuración descrita por dimensiones «superiores». Por ejemplo, para un array bidimensional, el valor 1 en el parámetro count significa borrar toda una serie de longitud igual a la segunda dimensión (véase el ejemplo).
El valor start debe estar comprendido entre 0 y el tamaño de la primera dimensión menos 1.
La función no puede aplicarse a arrays con series temporales (series temporales o búferes indicadores integrados).
Para probar la función, preparamos el script ArrayRemove.mq5. En concreto, define dos estructuras:
struct Simple
|
Los arrays con una estructura simple pueden ser procesados con éxito por la función ArrayRemove, mientras que los arrays de objetos con destructores (incluso con destructores implícitos, como en NotSoSimple) provocan un error:
void OnStart()
|
A continuación, se definen e inicializan arrays de distintas configuraciones.
int dynamic[];
|
Al borrar de un array fijo, todos los elementos posteriores al fragmento que se elimina se desplazan hacia la izquierda. Es importante que el tamaño del array no cambie, por lo que las copias de los elementos desplazados aparecen por duplicado.
PRTS(ArrayRemove(fixed, 0, 1));
|
Aquí eliminamos un elemento de la primera dimensión de un array bidimensional fixed por el offset 0, es decir, la fila inicial. Los elementos de la fila siguiente se desplazan hacia arriba y permanecen en la misma fila.
Si realizamos la misma operación con un array dinámico (idéntico en contenido al array fixed), su tamaño se reducirá automáticamente en el número de elementos eliminados.
PRTS(ArrayRemove(dynamic2Dx4, 0, 1));
|
En un array unidimensional, cada elemento eliminado corresponde a un único valor. Por ejemplo, en el array dynamic, al eliminar tres elementos a partir del índice 2, obtenemos el siguiente resultado:
PRTS(ArrayRemove(dynamic, 2, 3));
|
Se han eliminado los valores 3, 4 y 5, el tamaño del array se ha reducido en 3.
bool ArrayReverse(void &array[], uint start = 0, uint count = WHOLE_ARRAY)
La función invierte el orden de los elementos especificados en el array. Los elementos que deben invertirse vienen determinados por una posición inicial start y una cantidad count. Si start = 0 y count = WHOLE_ARRAY, se accede a todo el array.
Se admiten arrays de dimensiones y tipos arbitrarios, tanto fijos como dinámicos (incluidas series temporales en búferes indicadores). Un array puede contener objetos, punteros o estructuras. En el caso de los arrays multidimensionales, sólo se invierte la primera dimensión.
El valor de count debe estar comprendido entre 0 y el número de elementos de la primera dimensión. Tenga en cuenta que count menos de 2 no producirá un efecto perceptible, pero puede utilizarse para unificar bucles en algoritmos.
La función devuelve true si tiene éxito o false en caso de error.
El script ArrayReverse.mq5 puede utilizarse para probar la función. Al principio, se define una clase para generar objetos almacenados en un array.. La presencia de cadenas y otros campos «complejos» no supone ningún problema.
class Dummy
|
Los objetos se identifican mediante un número de serie (asignado en el momento de su creación).
void OnStart()
|
Tras aplicar ArrayReverse obtenemos el orden inverso esperado de los objetos.
PRTS(ArrayReverse(objects)); // true / status:0
|
A continuación, se preparan arrays numéricos de distintas configuraciones y se despliegan con diferentes parámetros.
int dynamic[];
|
En este último caso, el valor de start (2) supera el tamaño en la primera dimensión, por lo que se produce un error.