Основы языка MQL 4. Сложные вопросы простым языком.
Часть 2
Многомерные массивы
Вы часто будете использовать массивы при программировании, и в большинстве случаев вам будет достаточно одномерных массивов. Но иногда вам понадобятся и двухмерные, и трехмерные. Сейчас вы научитесь ими пользоваться.
Для начала давайте наглядно представим себе одномерный массив, вспомним объявление, инициализацию, индексы и значения:

Как видно, любой одномерный массив удобно представить себе в виде ряда значений единого типа. Посмотрите как будут обрабатываться различные обращения к одномерному массиву:


Двухмерные массивы подобны обычным таблица, посмотрите:

Как видно из рисунка, у двухмерных массивов имеется уже два индекса для обращения к значению: первый индекс определяет ряд,
а второй - столбец. Для инициализации используются перечень значений как и в одномерном массиве. Посмотрите как происходит обращение к значениям ячеек таблицы:




Как видно, все достаточно просто. Давайте посмотрим как можно "пройтись" по всем значениям двухмерного массива. Для этого нужно использовать 2 цикла:
int array2D[3][3]={10,20,30,
40,50,60,
70,80,90};
for(int y=0;y<3;y++)
for(int x=0;x<3;x++)
MessageBox("array2D["+y+"]["+x+
"]="+array2D[y][x]);
В этом примере обращение к каждой ячейке будет проходить сверху вниз слева направо. В качестве упражнения попробуйте изменить направление проходов, например, снизу вверх.
Трехмерные массивы отличаются лишь наличием еще одного индекса для обращения к значениям ячеек. Можно легко представить трехмерный массив в виде нескольких таблиц (двухмерных массивов). Вот как можно "пройтись" по всем элементам трехмерного массива:
int array3D[3][3][3] = {11, 12, 13,
14, 15, 16,
17, 18, 19,
21, 22, 23,
24, 25, 26,
27, 28, 29,
31, 32, 33,
34, 35, 36,
37, 38, 39};
for(int z = 0; z < 3; z++)
for(int y = 0; y < 3; y++)
for(int x = 0; x < 3; x++)
MessageBox("array3D["+z+"]["+y+
"]["+x+"]=" +
array3D[z][y][x]);
Основательно разберитесь в двухмерных и трехмерных массивах - это очень важно. Внимательно еще раз просмотрите пояснительные рисунки. Очень много различных задач решается с использованием массивов, так что, уделите им достаточно внимания, они еще не раз вас выручат. Если вы поймете принцип работы с двухмерным массивом, то вам будет легко разобраться в любом n-мерном массиве.
Некоторые функции для работы с массивами
Начнем с простых функций.
int ArraySize(object array[]);
Эта функция возвращает количество элементов, которые вмещает массив. Работает со всеми типами. Например:
// создаем два разных массива
int arrayInt[] = {1, 2, 3, 4};
double arrayDouble[] = {5.9, 2.1, 4.7};
// здесь будем хранить количество элементов
int amount;
amount = ArraySize(arrayInt); // обратите внимание:
// что бы указать конкретный
// массив, нужно указать только
// его название.
// Теперь amount равно 4
amount = ArraySize(arrayDouble); // amount равно 3
Следующая функция:
int ArrayInitialize(object array[],double value);
ArrayInitialize
присваивает всем элементам массива значение value. Возвращает количество элементов, которым было присвоено значение. Используйте эту функцию с массивами типа int и double.
Дальше:
int ArrayMaximum(double array[], int count = WHOLE_ARRAY,
int start = 0);
int ArrayMinimum(double array[], int count = WHOLE_ARRAY,
int start = 0);
Эти две функции возвращают индекс максимального и минимального значения ячейки. Что бы воспользоваться ними просто укажите в каком массиве искать:
int array[] = {10, 100, 190, 3, 1}; // будет возвращено 1, так как array[1] - максимальное значение
ArrayMaximum(array); // будет возвращено 4, потому что, array[4] - минимальное значение ArrayMinimum(array);
int ArrayDimension(object array[]);
С помощью этой функции можно узнать размерность массива. То есть определить одномерный он, двухмерный или n-мерный. Пример:
int array1D[15];
int array4D[3][3][3];
ArrayDimension(array1D); // получим 1
ArrayDimension(array3D); // 3
А вот более сложные и полезные функции:
int ArraySort(double&array[], int count = WHOLE_ARRAY, int start = 0,
int sort_dir = MODE_ASCEND);
Эта функция
сортирует
элементы. Если явно не указывать аргументы по умолчанию, например так:
int array[5] = {1, 10, 5, 7, 8};
ArraySort(array);
То элементы будут отсортированы
по возрастанию
. Также можно воспользоваться дополнительными параметрами, что бы уточнить поведение функции:
- int count - количество элементов, которые вы хотите отсортировать
- int start - индекс элемента, с которого следует начать сортировку
- int sort_dir - направление сортировки (по возрастанию - MODE_ASCEND или по убыванию - MODE_DESCEND)
Тут у вас должен возникнуть вопрос: что это за MODE_ASCEND и MODE_DESCEND?? Ведь указано, что должно быть целое число! Не волнуйтесь, все стает на свои места в следующем разделе - "Препроцессор". Если вам нужно, например, отсортировать 5 элементов, начиная с второго по убыванию следует написать, что-то вроде этого:
ArraySort(array, 5, 1, MODE_DESCEND);
И последняя на сегодня функция:
int ArrayCopy(object&dest[], object source[], int start_dest = 0,
int start_source=0, int count=WHOLE_ARRAY);
Она предназначена для
копирования
одного массива в другой. Рассмотрим обязательные параметры:
- dest[] - в какой массив копировать
- source[] - из какого массива копировать
Необязательные параметры:
- start_dest - индекс элемента массива, в который будет произведено копирование
- start_source - индекс элемента массива, из которого будет произведено копирование
- int count - количество элементов для копирования
Функция возвращает количество скопированных элементов. Используйте ArrayCopy с осторожностью: убедитесь, что массивы обладают достаточной вместимостью, когда что-то копируете в них!
Препроцессор
Что же это такое? Препроцессор - это специальный механизм, который предназначен для обработки исходного кода. То есть сначала препроцессор подготавливает код, а потом передает его для компиляции. Сегодня мы ознакомимся с одной полезной возможностью - константами.
В чем суть? Что бы разобраться, давайте вспомним пример из раздела про switch:
switch(marketState)
{
case 1:
// стратегия торговли для восходящего тренда
break;
case 2:
// стратегия для нисходящего тренда
break;
case 3:
// стратегия для флета
break;
default:
// ошибка: такого состояния не предусмотрено!
break;
}
Здесь мы реализовывали механизм, который действует по разному в зависимости от состояния рынка. Вспомнили? Так вот, было бы намного удобнее и нагляднее вместо 1, 2 и 3 написать, что вроде TREND_UP, TREND_DOWN, FLET:
switch(marketState)
{
case TREND_UP:
// стратегия торговли для восходящего тренда
break;
case TREND_DOWN:
// стратегия для нисходящего тренда
break;
case FLET:
// стратегия для флета
break;
default:
// ошибка: такого состояния не предусмотрено!
break;
}
В таком случае исходный код выглядит намного понятнее и нагляднее, согласны? Так вот, константы позволяют заменить TREND_UP, TREND_DOWN и FLET на соответствующие значения 1,2 и 3, до начала компиляции. Все что вам нужно, это указать что на что заменять препроцессору. Это делается с помощью директив препроцессора, которые начинаются с специального символа "#". Директивы препроцессора следует размещать в начале исходного файла вместе с другими директивами. Давайте посмотрим на законченный пример с использованием констант:
//+------------------------------------------------------------------+
//| preprocessor.mq4 |
//| Copyright © 2007, Antonio Banderass. All rights reserved |
//| banderassa@ukr.net |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, Antonio Banderass. All rights reserved"
#property link "banderassa@ukr.net"
#define TREND_UP 1
#define TREND_DOWN 2
#define FLET 3
//+------------------------------------------------------------------+
//| script program start function |
//+------------------------------------------------------------------+
int start()
{
MessageBox("TREND_UP=" + TREND_UP + " TREND_DOWN=" + TREND_DOWN +
" FLET=" + FLET);
return(0);
}
Обратите внимание, мы разместили объявления констант в начале файла, под остальными директивами препроцессора. Рассмотрим объявление детальнее:
#define TREND_UP 1
Сначала следует ключевое слово #define. Оно указывает препроцессору, что дальше пойдет объявление константы. Потом идет название константы, ее идентификатор, то есть слово, по которому вы будете обращаться к значению константы. У нас это - TREND_UP. Потом следует само значение - 1. Теперь когда препроцессор увидит в исходном коде TREND_UP, он заменит это на 1 и так со всеми константами. Посмотрите на исходный код нашего примера до обработки препроцессором:
int start()
{
MessageBox("TREND_UP=" + TREND_UP + " TREND_DOWN=" +
TREND_DOWN + " FLET=" + FLET);
return(0);
}
и
после
:
int start()
{
MessageBox("TREND_UP=" + 1 + " TREND_DOWN=" + 2 +
" FLET=" + 3);
return(0);
}
Теперь вы должны понимать, что это за MODE_ASCEND и MODE_DESCEND из предыдущего раздела. Это были всего лишь константы, которым соответствуют определенные значения.
Заключение
Итак, в этой статье вы почерпнули много свежего материала: новый вид циклов - while; новый вид условий - switch; операторы break и continue, вы научились писать собственные функции и работать с многомерными массивами, а также узнали как использовать константы. Все это - ваш основной инструмент, фундамент для написания более продвинутых вещей, таких как пользовательские индикаторы и советники. Поэтому убедитесь, что основательно разобрались в этом, так как материал, изложенный в этой статье очень важен; и в будущем будет использоваться постоянно.
|