Копирование и редактирование массивов
В данном разделе мы узнаем, как с помощью встроенных функций можно вставлять и удалять элементы массивов, менять их порядок, а также копировать массивы целиком.
bool ArrayInsert(void &target[], const void &source[], uint to, uint from = 0, uint count = WHOLE_ARRAY)
Функция вставляет в массив-приемник target указанное количество элементов из массива-источника source. Место вставки в массив target задается индексом в параметре to. Начальный индекс элемента, с которого начинается копирование из массива source, задается индексом from. Константа WHOLE_ARRAY ((uint)-1) в параметре count задает перенос всех элементов массива-источника.
Все индексы и количество относятся к первому измерению массивов. Иными словами, для многомерных массивов вставка производится не отдельными элементами, а всей конфигурацией, описанной "высшими" размерностями. Например, для двумерного массива значение 1 в параметре count означает вставку вектора длиной, равной второй размерности (см. пример).
Из этого следует, что конфигурации массива-приемника и массива-источника должны быть одинаковыми (иначе возникнет ошибка, и копирования не произойдет). Для одномерных массивов это не является ограничением, но для многомерных необходимо соблюдать равенство размеров по измерениям выше первого. В частности, элементы из массива [][4] нельзя вставить в массив [][5] и наоборот.
Функция применима только для массивов фиксированного или динамического размера. Изменить таймсерии (массивы с временными рядами) с помощью данной функции нельзя. Также не допускается указывать в параметрах target и source один и тот же массив.
При вставке в фиксированный массив новые элементы сдвигают имевшиеся элементы вправо и вытесняют count самых правых элементов за пределы массива. Параметр to должен быть в диапазоне от 0 до размера массива за вычетом 1.
При вставке в динамический массив также происходит сдвиг вправо старых элементов, но они не пропадают, потому что сам массив расширяется на count элементов. Параметр to должен лежать в пределах от 0 до размера массива. Если он равен размеру массива, новые элементы добавляются в конец массива.
Указанные элементы копируются из одного массива в другой, то есть они остаются в исходном массиве без изменений, а их "двойники" в новом массиве становятся самостоятельными экземплярами, никак не связанными с "оригиналами".
Функция возвращает true в случае успеха или false в случае ошибки.
Рассмотрим некоторые примеры (ArrayInsert.mq5). В функции OnStart описано несколько массивов разной конфигурации, фиксированных и динамических.
#define PRTS(A) Print(#A, "=", (string)(A) + " / status:" + (string)GetLastError())
|
Предварительно для удобства введен макрос, выводящий код ошибки (получаемый через функцию GetLastError) сразу после вызова тестируемой инструкции — PRTS. Это слегка модифицированная версия знакомого нам макроса PRT.
Попытки копирования элементов между массивами разной конфигурации заканчиваются ошибкой 4006 (ERR_INVALID_ARRAY).
// нельзя смешивать массивы 1D и 2D
|
Целевой индекс должен находиться в пределах массива.
// целевой индекс 10 выходит за пределы массива 'insert',
|
Далее идут успешные модификации массивов:
// копируется второй ряд из 'fixed', 'dynamic2Dx4' распределяется
|
Вот, что появится в журнале:
ArrayInsert(dynamic2Dx4,fixed,0,1,1)=true
|
bool ArrayCopy(void &target[], const void &source[], int to = 0, int from = 0, int count = WHOLE_ARRAY)
Функция копирует часть или весь массив source в другой массив target. Место в массиве target, куда записываются элементы, задается индексом в параметре to. Начальный индекс элемента, с которого начинается копирование из массива source, задается индексом from. Константа WHOLE_ARRAY (-1) в параметре count задает перенос всех элементов массива-источника. Если count меньше нуля или больше числа элементов, оставшихся от позиции from до конца массива source, копируется весь остаток массива.
В отличие от функции ArrayInsert, функция ArrayCopy не сдвигает имеющиеся элементы приемного массива, а записывает новые элементы в указанные позиции поверх старых.
Все индексы и количество элементов задаются с учетом сквозной нумерацией элементов вне зависимости от количества измерений в массивах и их конфигурации. Иными словами, элементы можно копировать из многомерных массивов в одномерные и обратно или между многомерными массивами с разными размерами по "высшим" размерностям (см. пример).
Функция работает с фиксированными и динамическими массивами, а также массивами временных рядов, назначенными в качестве индикаторных буферов.
Допустимо копировать элементы из массива в самого себя. Но если области target и source перекрываются, нужно иметь в виду, что обход делается слева направо.
Динамический массив-приемник при необходимости автоматически расширяется. Фиксированные массивы сохраняют свои размеры, причем копируемое должно уместиться в массиве, иначе возникнет ошибка.
Поддерживаются массивы встроенных типов и массивы структур с полями простых типов. При этом для числовых типов функция попытается выполнить конвертацию, если типы источника и приемника различаются.
Строковый массив можно скопировать только в строковый массив.
Объекты классов запрещены, однако можно копировать указатели на объекты.
Функция возвращает количество скопированных элементов (0 в случае ошибки).
В скрипте ArrayCopy.mq5 приведено несколько примеров использования функции.
class Dummy
|
Массивы с объектами генерируют ошибку компиляции о том, что такие элементы не поддерживаются ("structures or classes containing objects are not allowed"). Зато можно копировать указатели.
Dummy *pointers1[5], *pointers2[5];
|
Массивы структур с полями простых типов также копируются без проблем.
struct Simple
|
Для дальнейшей демонстрации работы с массивами разных типов и конфигураций определены следующие массивы (среди них есть фиксированные, динамические, с разным числом измерений):
int dynamic[];
|
При копировании одного элемента из массива fixed из позиции 1 (число 2) в приемном динамическом массиве dynamic2Dx4 выделяется целый ряд из 4 элементов, и поскольку копируется только 1 элемент, три оставшихся будут содержать случайный "мусор" (подсвечен желтым).
PRTS(ArrayCopy(dynamic2Dx4, fixed, 0, 1, 1)); // 1 / status:0
|
Далее копируем все элементы из массива fixed, начиная с третьего, в тот же массив dynamic2Dx4, но уже начиная с позиции 1. Поскольку копируется 5 элементов (общее количество в массиве fixed 8 минус начальная позиция 3), а помещаются они по индексу 1, всего в приемном массиве будет занято 1 + 5, итого 6 элементов. И поскольку массив dynamic2Dx4 имеет 4 элемента в каждом ряду (во втором измерении), выделить память под него можно только для числа элементов кратного 4-м, то есть будет распределено еще 2 элемента, в которых останутся случайные данные.
PRTS(ArrayCopy(dynamic2Dx4, fixed, 1, 3)); // 5 / status:0
|
При копировании многомерного массива в одномерный, элементы будут представлены в "плоском" виде.
PRTS(ArrayCopy(dynamic, fixed)); // 8 / status:0
|
При копировании одномерного массива в многомерный, элементы "раскладываются" по измерениям приемного массива.
PRTS(ArrayCopy(dynamic2Dx5, insert)); // 3 / status:0
|
В данном случае копировалось 3 элемента и они уместились в один ряд длиной 5 элементов (согласно конфигурации приемного массива). Память под два оставшихся элемента ряда была выделена, но не заполнена (содержит "мусор").
Мы можем перезаписать массив dynamic2Dx5 из другого источника, в том числе и из многомерного массива другой конфигурации. Поскольку в приемном массиве было выделено два ряда по 5 элементов, а в источнике было 2 ряда по 4 элемента, 2 дополнительных элемента осталось незаполненными.
PRTS(ArrayCopy(dynamic2Dx5, fixed)); // 8 / status:0
|
С помощью ArrayCopy можно менять элементы в фиксированных массивах-приемниках.
PRTS(ArrayCopy(fixed, insert)); // 3 / status:0
|
Здесь мы перезаписали первые три элемента массива fixed. А затем перезапишем 3 последних.
PRTS(ArrayCopy(fixed, insert, 5)); // 3 / status:0
|
Копировать в позицию, равную длине фиксированного массива, не получится (динамический массив-приемник при этом расширился бы).
PRTS(ArrayCopy(fixed, insert, 8)); // 4006, ERR_INVALID_ARRAY
|
Строковые массивы в сочетании с массивами других типов выдадут ошибку:
PRTS(ArrayCopy(texts, insert)); // 5050, ERR_INCOMPATIBLE_ARRAYS
|
Но между строковыми массивами копирование возможно:
PRTS(ArrayCopy(texts, message));
|
Массивы разных числовых типов копируются с необходимой конвертацией.
PRTS(ArrayCopy(insert, array, 1)); // 1 / status:0
|
Здесь мы записали число Пи в целочисленный массив, и потому получили значение 3 (оно заменило 11).
bool ArrayRemove(void &array[], uint start, uint count = WHOLE_ARRAY)
Функция удаляет из массива указанное количество элементов начиная с индекса start. Массив может быть многомерным и иметь любой встроенный тип или тип структуры с полями встроенных типов, за исключением строк.
Индекс start и количество count относятся к первому измерению массивов. Иными словами, для многомерных массивов удаление производится не отдельными элементами, а всей конфигурацией, описанной "высшими" размерностями. Например, для двумерного массива значение 1 в параметре count означает удаление целого ряда длиной, равной второй размерности (см. пример).
Значение start должно лежать в пределах от 0 до размера первого измерения минус 1.
Функцию нельзя применять к массивам с временными рядами (встроенным таймсериям или буферам индикаторов).
Для проверки работы функции подготовлен скрипт ArrayRemove.mq5. В нем, в частности, определены 2 структуры:
struct Simple
|
Массивы с простой структурой могут обрабатываться функцией ArrayRemove успешно, в то время как массивы объектов с деструкторами (даже с неявными, как в NotSoSimple) вызывают ошибку:
void OnStart()
|
Далее определены и проинициализированы массивы различной конфигурации.
int dynamic[];
|
При удалении из фиксированного массива, все элементы, находящиеся после удаляемого фрагмента, сдвигаются влево. Важно, что размер массива при этом не меняется и потому копии сдвинутых элементов оказываются в двух экземплярах.
PRTS(ArrayRemove(fixed, 0, 1));
|
Здесь мы удалили один элемент первого измерения двумерного массива fixed по смещению 0, то есть начальный ряд. Элементы следующего ряда сдвинулись вверх и остались в прежнем ряду.
Если проделать ту же операцию с динамическим массивом (идентичным по содержимому массиву fixed), его размер будет автоматически уменьшен на количество удаленных элементов.
PRTS(ArrayRemove(dynamic2Dx4, 0, 1));
|
В одномерном массиве каждый удаляемый элемент соответствует одиночному значению. Например, в массиве dynamic при удалении трех элементов начиная с индекса 2, получим следующий результат:
PRTS(ArrayRemove(dynamic, 2, 3));
|
Значения 3, 4, 5 были удалены, размер массива сокращен на 3.
bool ArrayReverse(void &array[], uint start = 0, uint count = WHOLE_ARRAY)
Функция изменяет порядок следования указанных элементов в массиве на обратный. Переворачиваемые элементы определяются по начальной позиции start и количеству count. Если start = 0, а count = WHOLE_ARRAY, обращается весь массив целиком.
Поддерживаются массивы произвольной размерности и типов, как фиксированные, так и динамические (включая временные ряды в буферах индикаторов). Массив может содержать объекты, указатели или структуры.
Для многомерных массивов разворот производится только по первому измерению.
Значение count должно быть в диапазоне от 0 до количества элементов в первом измерении. Учтите, что count меньше 2 не даст заметного эффекта, но это можно использовать в целях унификации циклов в алгоритмах.
Функция возвращает true в случае успеха или false в случае ошибки.
Для проверки функции был написан скрипт ArrayReverse.mq5. В его начале определен класс для порождения объектов, хранимых в массиве. Наличие строк и прочих "сложных" полей — не помеха.
class Dummy
|
Объекты идентифицируют по порядковому номеру (присваивается в момент создания).
void OnStart()
|
После применения ArrayReverse получим ожидаемый обратный порядок объектов.
PRTS(ArrayReverse(objects)); // true / status:0
|
Далее подготавливаются числовые массивы разной конфигурации и разворачиваются с разными параметрами.
int dynamic[];
|
В последнем случае значение start (2) превышает размер в первом измерении, поэтому возникает ошибка.