Присваивание и инициализация, выражения и массивы

Массив — это именованный набор однотипных ячеек, расположенных в памяти следом друг за другом, с возможностью доступа к каждой из них по индексу. В некотором роде, это составная переменная, характеризующаяся общим идентификатором, типом хранимых значений и количеством пронумерованных элементов.

Например, целочисленный массив из 5 элементов можно описать так:

int array[5];

Размер массива указывается в квадратных скобках после имени. Элементы нумеруются от 0 до N-1, где N — размер массива. Доступ к ним, то есть чтение значений, производится с помощью похожего синтаксиса. Например, чтобы вывести в журнал первый элемент указанного массива, можно было бы написать инструкцию:

Print(array[0]);

Обратите внимание, что индекс 0 соответствует самому первому элементу. А для вывода последнего элемента инструкция поменялась бы на такую:

Print(array[4]);

Конечно, предполагается, что перед печатью элемента массива в него когда-то было записано полезное значение. Такая запись выполняется с помощью специальной инструкции — оператора присваивания. Отличительной чертой этого оператора является использованием символа '=', слева от которого указывается элемент массива (или переменная), куда производится запись, а справа — записываемое значение или его "эквивалент". Под "эквивалентом" здесь скрывается возможность языка вычислять арифметические, логические и прочие типы выражений (мы изучим их во второй части книги). Синтаксис выражений во многом походит на правила записи уравнений из школьных курсов арифметики и алгебры. Например, в выражении можно использовать операции сложения ('+'), вычитания ('-'), умножения ('*'), деления ('/').

Вот, например, как могли бы выглядеть операторы для заполнения некоторых элементов указанного массива.

array[0] = 10;                       // 10
array[1] = array[0] + 1;             // 11
array[2] = array[0] * array[1] + 1;  // 111

Эти инструкции демонстрируют разные способы присваивания и конструирования выражений: в первой строке в элемент array[0] записывается литерал 10, во второй и третьей — используются выражения, вычисление которых приводит к получению результатов, указанных для наглядности в комментариях.

Когда элементы массивов (или переменные, в общем случае) участвуют в выражении, компьютер в процессе выполнения программы считывает из памяти их значения и выполняет над ними указанные операции.

Следует различать использование переменных и элементов массивов слева и справа от знака '=' в инструкции присваивания: слева стоит "получатель" обработанных данных (он всегда один), а справа — "источники" исходных данных для вычислений ("источников" может быть много в выражении, как в этом примере в последней строке, где перемножаются значения элементов array[0] и array[1]).

В приведенных примерах знак '=' был применен для присвоения значений элементам массива, который определен заранее. Но зачастую бывает удобно присваивать начальные значения переменным и массивам непосредственно в момент их определения. Это называется инициализацией. Для неё также используется знак "равно". Рассмотрим этот синтаксис в контексте нашей прикладной задачи.

Опишем массив строк с вариантами приветствия внутри функции Greeting:

string Greeting(int hour)
{
  string messages[3] = {"Good morning""Good day""Good evening"};
  return "Hello, ";
}

В добавленной инструкции производится не только определение массива messages с 3 элементами, но и его инициализация, то есть заполнение нужными начальными значениями. Инициализацию выделяет знак равно '=' после описания типа и имени переменной или массива. В случае переменной после '=' необходимо указать одно единственное значение (без фигурных скобок), а в случае массива, как мы видим, можно написать несколько значений, разделенных запятыми и заключенных в фигурные скобки.

Не путайте инициализацию и присваивание. Первая указывается при определении переменной/массива (и производится единожды), а второе встречается в отдельных инструкциях (причем одной и той же переменной или элементу массива могут многократно присваиваться разные значения). Присваивать элементы массива можно только по отдельности: MQL5 не поддерживает присваивание сразу всех элементов по аналогии с инициализацией.

Массив messages, будучи определен внутри функции, доступен только внутри неё, как и параметр hour. Далее мы увидим, как можно описать переменные, доступные во всем коде программы.

Каким же образом, преобразовать входящее значение hour с номером часа в один из трех элементов?

Напомним, что по задумке hour может быть равен числу от 0 до 23 (включительно). Если разделить его на 8 без остатка, получим значения от 0 до 2 (включительно). Например, деление 1 на 8 даст 0 и 7 на 8 даст 0 (дробная часть при делении без остатка отбрасывается). А вот деление 8 на 8 — это уже 1, и вплоть до 15 все числа будут давать 1 при делении на 8. Числа с 16 по 23 будут соответствовать результату деления 2. Полученные целые значения 0, 1, 2 следует использовать как индексы для чтения элемента массива messages.

В MQL5 деление без остатка для целых чисел позволяет вычислять операция '/'.

Выражение для получения результата деления похоже на те, что мы недавно рассмотрели для массива array, только в вычислении должен использоваться параметр hour и операция '/'. В качестве демонстрации возможной реализации преобразования hour в индекс элемента приведем такую инструкцию:

int index = hour / 8;

Здесь определяется некая новая целочисленная переменная index и инициализируется значением указанного выражения.

Однако, мы можем не сохранять промежуточное значение в переменной index, а сразу перенести данное выражение (справа от знака '=') внутрь квадратных скобок, где указывается номер элемента массива.

Тогда в инструкции с оператором return мы можем извлекать подходящее приветствие следующим образом:

string Greeting(int hour)
{
  string messages[3] = {"Good morning""Good day""Good evening"};
  return messages[hour / 8];
}

Функция в общих чертах готова. Кое-какие правки мы внесем через пару разделов. А пока сохраним наработки в файле под новым именем GoodTime0.mq5 и попробуем вызвать свою функцию. Для этого в OnStart используем обращение к Greeting внутри вызова Print.

void OnStart()
{
  Print(Greeting(0), ", "Symbol());
}

Мы оставили разделяющую запятую (стоявшую внутри литерала "Hello, ") между приветствием и названием инструмента. Теперь в вызове функции Print три аргумента: первый и последний будут вычислены "на лету" с помощью вызовов, соответственно, функций Greeting и Symbol, а запятая отправится на печать как есть.

В функцию Greeting мы пока передаем константу 0. Именно её значение попадет в параметр hour. Откомпилировав и запустив программу, мы можем убедиться, что она выводит в журнал требуемый текст.

GoodTime0 (EURUSD,H1)        Good morning, EURUSD

Однако, на практике выбор приветствия должен осуществляться динамически, в зависимости от времени, указанного пользователем.

Таким образом, мы подошли к необходимости организовать ввод данных.