preview
Изучение MQL5 — от новичка до профи (Часть II): Базовые типы данных и использование переменных

Изучение MQL5 — от новичка до профи (Часть II): Базовые типы данных и использование переменных

MetaTrader 5Примеры | 20 февраля 2024, 16:59
847 7
Oleh Fedorov
Oleh Fedorov

Введение

В моей предыдущей статье мы рассмотрели основные программы, которые используются программистами на MQL5 (и пришли к выводу, что для начинающих вполне достаточно IDE MetaEditor). Кроме того, бегло рассмотрели понятие функции и создали простой скрипт, выводящий сообщение для пользователя в системный журнал. Эти записи можно посмотреть внизу окна терминала на вкладке "Эксперты".

Напомню: функция — это описание действия.

Мы пользовались только предопределёнными функциями: OnStart и Print, причем первую мы "начиняли" сами, вторую же, которая выводила нужную нам информацию, мы взяли готовой и лишь передавали ей параметры. А вообще программист может создавать свои, пользовательские функции, которые ему нужны для решения его задач.

Каждая функция состоит из совсем элементарных шагов-действий, которые называются операторы. Такие действия совсем просты: сравнить два числа, повторить фрагмент кода несколько раз, склеить вместе два фрагмента текста, вызвать другую функцию... Ну, и так далее. Их совсем немного, и некоторые я рассмотрю в этой статье.

Из последовательности операторов складываются алгоритмы.

Алгоритм — четкие и понятные исполнителю (в данном случае — компьютеру) предписания выполнить определенные действия, приводящие к решению конкретной, более глобальной, задачи. И вот алгоритмов существует огромное количество, поскольку одну и ту же задачу, как правило, можно решить многими разными способами.

Например, тот же трейдинг. Вход в сделку, выход из сделки, вывод соответствующих логов — всё это может происходить по-разному. И компьютеру надо абсолютно четко объяснить, чего хотите именно вы (ну, или ваш заказчик) в данном конкретном случае.

Можно сказать, что более сложные алгоритмы состоят из более простых, и каждый алгоритм реализуется какой-то функцией, выполняя какие-то действия. И действия эти совершаются над данными. В качестве данных могут выступать цены Ask и Bid или объёмы сделок, приходящие несколько раз в секунду. А могут быть точки на экране, между которыми надо нарисовать прямую или кривую. Или звук, который надо проиграть в момент сделки. Или текст, например, список котировок за определённый период. Или... Надеюсь, идея понятна.

Главное то, что эти данные надо где-то хранить.

Сегодня мы поговорим о том, как хранятся данные в оперативной памяти. И хранятся в памяти данные в переменных или константах.

Отличия очевидны:

  • переменные меняются, программа имеет право их перезаписывать;
  • константы всё время жизни программы остаются неизменными, и если программист попытается перезаписать их значения, он получит сообщение об ошибке компиляции.

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

Компилятор потом эти имена, конечно, уберёт, но, если у нас есть доступ к исходному коду (нашему текстовому файлу), мы всегда в будущем по имени сможем понять, для чего нужна та или иная переменная. Если, конечно, они описаны правильно.

В некоторых случаях константы не имеют имён. Программист просто пишет, что именно он хочет обработать (как, например, те строки, которые мы передавали функции Print). Такие безымянные константы называют литералы.

В этой статье мы подробно рассмотрим основные типы данных, способы описания переменных и констант и основные операторы, которые может использовать программист для создания своих алгоритмов. Это в свою очередь, позволит создавать более полезные программы, чем просто "Hello, World".


Базовый код для проверки всех выражений из статьи

В прошлой статье мы создали простейшую программу: скрипт, который выводит данные во вкладку "Experts" в нижней панели терминала. Вот он:

//+------------------------------------------------------------------+
//|                                                   HelloWorld.mq5 |
//|                                       Oleg Fedorov (aka certain) |
//|                                   mailto:coder.fedorov@gmail.com |
//+------------------------------------------------------------------+
#property copyright "Oleg Fedorov (aka certain)"
#property link      "mailto:coder.fedorov@gmail.com"
#property version   "1.00"
//#property script_show_inputs

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
  Print("Hello, MQL5 world!");
  }

//+------------------------------------------------------------------+

Пример 1. Полный текст простейшего скрипта

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


Литералы

Давайте разберёмся, как именно мы можем вывести данные на печать.

Начнём со строк.

Литерал строки заключается в двойные кавычки <">. Однако не все символы можно просто взять и вывести. Некоторые из них имеют специальное значение в языке (те же кавычки), некоторые вообще не отображаются, как, например, символ перевода строки.

Для таких символов существует специальное соглашение: их записывают с помощью обратного слэша:

Print(
    "Symbol <\"> can't be placed"
    " without (\\).\n  And we can carrying over the string now."
  );

Пример 2. Использование обратных слэшей для вывода специальных символов

В примере 3 зелёным выделены кавычки, описывающие выводимые строки, а желтым — специальные символы.Так, символ "\n" обозначает перенос строки.

Пример вывода строкового литерала

Рисунок 1. Пример вывода строкового литерала

И еще обратите внимание на один нюанс. В MQL5 очень длинные строки можно разбивать в произвольном месте (взяв каждую часть в кавычки). При этом не обязательно вставлять какие-либо другие действия для склеивания этих строк. Если компилятор обнаружит два строковых литерала, идущих подряд (как в примере), он автоматически склеит строки в одну большую строку, а потом в ней уже будет искать специальные символы.

Полную таблицу специальных символов, а также более подробное описание символьных констант можно посмотреть в официальной справке.

Вторые по частоте использования — числа.

Целые числа выглядят обычно:

Print(5);

Пример 3. Вывод целого числа

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

Print(5.5);

Пример 4. Вывод десятичной дроби

И для очень больших или очень малых чисел можно использовать запись "с плавающей точкой" (иногда говорят "экспоненциальная форма"):

Print(5.5e3);
Print(5.5e-3);

Пример 5. Использование литералов с плавающей точкой

Результат работы скрипта, использующего все эти формы чисел, показан на рисунке 2.

Вывод чисел функцией Print

Рисунок 2. Результат вывода чисел функцией Print

Обратите внимание, как функция преобразовала переданные ей данные в двух последних строках. Это говорит о том, что данные были восприняты именно как числа и адекватно обработаны.

Иногда программистам приходится иметь дело с шестнадцатеричными числами. Начинающим это редко надо, но иногда может всё же оказаться полезным, например, для описания цвета. На этот случай знайте, что шестнадцатеричные  числа состоят из цифр (0..9) и/или букв (a..f).

Чтобы программа точно поняла, что это именно шестнадцатеричный литерал, в начале числа нужно добавить символы "0x" (цифра ноль и буква икс).

Регистр букв не важен.

Print(0xAF35);

Пример 6. Использование шестнадцатеричного числа

Вывод шестнадцатиричного числа

Рисунок 3. Результат вывода шестнадцатеричного числа

Результат работы скрипта показан на рисунке 3. Число опять было преобразовано (в обычное целое), значит, снова компьютер понял нас именно так, как нужно.

И еще программисту на MQL5 очень часто приходится использовать даты.

Для полной записи даты и времени необходимо начать с прописной буквы "D", затем поставить апостроф < ' >, записать нужную дату через точки или слэши, если нужно, через пробел указать время, отделяя минуты и секунды двоеточиями, и поставить еще один апостроф:

  Print(D'24/12/23');
  Print(D'24.12');
  Print(D'24.12.2023 7:55:34');

Пример 7. Использование даты и времени

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

Предупреждение о неполном литерале

Рисунок 4. Предупреждение (MetaEditor) о неполном литерале даты

Результаты работы скрипта (вывод дат)

Рисунок 5. Результаты работы скрипта вывода дат (терминал)

Обратите внимание, что при компиляции первой строки было подставлено время начала дня, а при компиляции второй — время компиляции. Ну, и преобразование формата снова показывает, что программа поняла нас правильно...

И над любыми литералами можно проводить любые — допустимые для данного типа данных — действия.

Например, числа можно сравнивать, проводить над ними арифметические операции, передавать функциям в качестве параметров.

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

Давайте рассмотрим следующий пример:

Print( "This will be calculated: "+4+9 );
Print( "This will be calculated: "+(4+9) );

Пример 8. Использование скобок при записи выражений.

Предупреждения компилятора об использовании чисел в строковом выражении

Рисунок 6. Предупреждение компилятора об использовании чисел в строковом выражении

Результаты вычислений

Рисунок 7. Функция вывела результат вычислений

Обратите внимание на то, что там, где скобок не было, числа просто "приклеились" к тексту, а там, где они были, всё было вычислено правильно. Это довольно трудноуловимая ошибка, поэтому и компилятор нас о ней предупреждает сразу. Есть специальные функции для преобразования чисел в строки явным образом. Но пока просто помните: скобки — это важно.


Описание констант с помощью директивы препроцессора #define

Если вы не хотите запутаться в собственном коде, а, наоборот, хотите понимать, "для чего вот эта 5" и "что значат эти 72", всем вашим константам лучше дать осмысленные имена. Для этого чаще всего используют директиву препроцессора #define:
#define название значение

Пример 9. Директива #define

Напомню, язык препроцессора — это как бы "язык в языке", и этот язык описывает действия до начала компиляции.

Как правило, задачей препроцессора является поменять одни фрагменты кода на другие. Так, директива #define при первом проходе говорит компилятору заменять во всём нашем коде "название" на "значение", и только после этого начинать всякие проверки синтаксиса и т.д.

Например, можно было бы написать:

  #define MY_SUPER_CONSTANT 354
  Print(MY_SUPER_CONSTANT);

Пример 10. Директива #define

Программа выводит значение, а не имя

Рисунок 8. Программа выводит значение константы, а не имя

Программа при работе выведет именно число 354, а не его название.

Обратите внимание, что после литерала, описывающего число, не стоит точка с запятой.

При описании констант с помощью директивы препроцессора точка с запятой не нужна.

Если бы мы её написали, препроцессор подставил бы этот знак вместе с числом — внутри скобок Print — и мы бы получили сообщение об ошибке компиляции.

Итак, запомнили: любую константу называем, и в выражениях используем имя, а не значение.

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


<Описание переменных

Помним: если данные в памяти нужно менять в процессе работы, мы используем переменные.

Описание переменных тоже не вызывает никаких сложностей. Мы просто записываем, что именно мы хотим хранить:

тип имя_переменной;

Пример 11. Шаблон описания переменной

Такая запись означает, что компилятор должен выделить определённое количество памяти под наши данные. Подробнее про типы и их размеры будет ниже.

Теперь к этим данным можно обращаться по имени (имя_переменной).


Соглашения об идентификаторах

Имя (часто говорят "идентификатор") переменной — и вообще любое, придуманное нами — должно

  • быть информативным для нас (лучше "chartNumber", чем "sss");
  • состоять из букв латинского алфавита, цифр и знака подчеркивания (_), 

Имя НЕ должно:

Регистр букв в имени важен. Так, myVariable и MyVariable — разные имена. (И, естественно использовать оба этих имени в одном файле сильно не рекомендуется).

Если вы случайно где-то в тексте программы перепутаете регистр, компилятор выдаст сообщение об ошибке: "Неописанная переменная", — так что вы легко сможете это исправить. Но если описать обе переменные по всем правилам, но при этом у них будет различаться только регистр одной буквы, в них будет слишком легко запутаться.

В остальном — никаких ограничений. Вы даже можете назвать свою переменную именем какой-нибудь встроенной функции, если захотите (надеюсь, нет).


Операция присваивания

Чтобы записать в переменную какие-то данные, используют операцию присваивания. Иногда это делают сразу при описании, тогда говорят об инициализации:

// Инициализация (при создании)
int counter = 0;
// Обычное присваивание
counter = 10;

Пример 12. Простое присваивание

Слово int обозначает, что переменная может содержать данные только целого типа (см. ).

Знак "=" в этом примере обозначает оператор присваивания. В данном случае в ячейку с названием counter записывается целое число.

Все данные, что были в ячейке до присваивания, теряются.

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


Операция присваивания — нюансы

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

  • Приоритет операции присваивания — самый низкий, поэтому она выполняется справа налево. То есть справа идёт выражение, а слева — переменная, куда записываются данные, и вычисляется сначала выражение:
a = b + c;

Пример 13. Присваивание имеет самый низкий приоритет.

В выражении выше сначала выполнится сложение b и с, и затем результат этого выражения запишется в переменную a.

  • как следствие предыдущего замечания, значение переменной можно использовать в выражении, а потом получившийся результат записать в эту же переменную:
a = a — c;

    Пример 14. В выражении можно использовать предыдущее значение переменной

    • В MQL5, выражения, где одна и та же переменная по одному разу встречается справа и слева (как в примере выше), можно сократить, перенеся знак выражения в левую часть операции:
    a -= c;

    Пример 15. Сокращенное использование присваивания

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

    Скажем, для выражения a = a*(1+1/a) этот фокус уже не пройдёт, если не раскрыть скобки, а вот для a = a*(b+c) — запросто: a *= b+c.

    • Если нужно увеличить или уменьшить целое число на 1, то присваивание вообще можно не использовать. Вместо него можно использовать операции инкремента и декремента:
    a++; // Инкремент. Увеличит а на 1
    b--; // Декремент. Уменьшит b на 1

    Пример 16. Инкремент и декремент

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

    Если использовать их в префиксной форме, то сначала выполнится действие, а потом результат будет использован в выражении.

    А вот если в постфиксной, то сначала будет использовано старое значение переменной, а потом уже переменная изменится на 1:

    int a = 1;
    Print (++a); // 2, и a == 2
    Print (a++); // 2, но a == 3

      Пример 17. Префиксная и постфиксная форма инкремента (декремент используется абсолютно  аналогично)

      • Можно использовать несколько операторов присваивания подряд, "каскадом". Последовательность действий "справа налево" сохраняется.
      int a=1, c=3;
      a = c = a+c; // сначала a+c (4), потом c = 4, потом a = c (то есть a = 4)

      Пример 18. "Каскадное" присвоение


      Базовые типы данных

      Типов данных их относительно много.

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

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

      Целые типы

      Прежде всего нужно понимать, что целые числа — основной способ компьютера "мыслить".

      Операции с целыми числами для компьютера просты и выполняются быстро. Но если результат операции превысит определённый диапазон значений, возможна потеря данных.

      Второй важный момент: целые числа бывают "знаковыми" и "беззнаковыми".

      Если число "беззнаковое", то можно использовать числа от 0 до максимума. Скажем, числа, которые занимают 1 байт, могут расти от 0 до 28-1 = 255 — всего 256 значений.

      Если кто-то попытается записать в такую переменную число "256" или "-1", произойдёт "переполнение", результат всё равно останется в тех же пределах [0..255] , а остальные данные потеряются. В принципе, иногда такое может быть полезно, но в абсолютном большинстве случаев для таких преобразований  лучше пользоваться другими методами. Например, можно использовать операцию остатка от деления (далее в этой статье). А переменные лучше использовать тех типов, которые гарантированно примут все ваши данные без потерь.

      Перед названием типов, которые начинаются с 0 (и, по сути, описывают натуральные числа), ставится буква "u" (от английского unsigned — "беззнаковый").

      "Знаковые" числа используют тот же диапазон значений, поделенный пополам. В первой половине хранятся отрицательные числа, во второй — положительные.Так, те же однобайтные числа будут корректными в диапазоне [-128..127].

      Таблица 1. Целые типы данных.

      Название
      Размер, байт
       Минимальное значение
      Максимальное значение
      char
      1 (8 бит)
      -128
      127
      uchar
      1 (8 бит)
      0
      255
      short
      2 (16 бит)
      -32 768
      32 767
      ushort
      2 (16 бит)
      0
      65 535
      int
      4 (32 бита)
      -2 147 483 648
      2 147 483 647
      uint
      4 (32 бита)
      0
      4 294 967 295
      long
      8 (64 бита)
      -9 223 372 036 854 775 808
      9 223 372 036 854 775 807
      ulong
      8 (64 бита)
       0  18 446 744 073 709 551 615

      На практике наиболее часто используются типы int (потому, что это слово быстро писать и данные этого типа имеют достаточно большой размер) и long (размер достаточен для абсолютного большинства задач, компилятор может оптимизировать байткод для наилучшего быстродействия на современных компьютерах).

      Тем не менее, другие целые типы тоже бывают полезны.

      Булевский (логический) тип

      Обозначается ключевым словом bool, занимает в памяти 1 байт и может принимать всего два значения: true или false ("истина" или "ложь").

      При крайней необходимости можно использовать в качестве логических данных любое число. Если число равно 0 — то это "ложь", не равно — "истина". Но применять эту возможность стоит с крайней осторожностью...

      Вещественные числа (они же — числа "с плавающей точкой")

      Таблица 2. Вещественные типы данных

      Название
       Размер, байт Минимальное положительное значение  Максимальное значение  
      float
      4 (32 бита)
      1.175494351e-38
      3.402823466e+38
      double
      8 (64 бита)
      2.2250738585072014e-308
      1.7976931348623158e+308

      В современной практике используется, в основном, тип double. Типа float я уже очень давно в чужих кодах не встречал. Вероятно, он остался для совместимости со старыми версиями. Хотя в очень больших массивах данных, вероятно, он будет всё-таки полезен — для экономии места в памяти.

      Вещественные числа идеально представляют цены, количество нужной валюты и другие полезные понятия.

      Они охватывают гораздо больший диапазон значений, чем целые.

      Однако компьютеру с этими типами работать не очень "удобно". Во-первых, операции с вещественными числами чуть медленнее, чем с целыми, а во-вторых из-за особенностей формата вычисления почти всегда получаются с погрешностями в последних знаках. Поэтому вместо 1.0 в какой-то операции можно получить 1.00000001, а в другом случае 0.99999999.

      Поэтому, если необходимо сравнить два вещественных числа, обычно берут их разницу и сравнивают с какой-нибудь малой величиной, однозначно большей, чем погрешность. Так надёжнее.

      Дата и время

      Обозначается словом datetime, занимает в памяти 8 байт.

      Каждая переменная этого типа содержит количество секунд, прошедших с 1 января 1970 года до нужной даты, то есть — обычное целое число.

      Последняя возможная дата — 31 декабря 3000 года. Пожалуй, на наш век хватит, и "ошибка 2000" (кто помнит :-) ) нам пока не грозит...

      Существуют специальные предопределённые константы:

      • __DATE__ — дата компиляции;
      • __DATETIME__ — дата и время компиляции;
      • можно использовать выражение __DATETIME__ — __DATE__ — оно описывает только время компиляции, без даты.

      При записи литерала можно вообще опустить всё и записать значение в виде D'' (D и два апострофа). Такая запись эквивалентна __DATETIME__. Однако читаемость кода при этом сильно падает.

      Цвета

      Цвет в MQL5 определяется отдельным типом, который называется, конечно же, color. При описании цвета можно воспользоваться литералом:

        color myColor1=C'100,200,30';
        color myColor2=C'0xFF,0x00,0x5A';

      Пример 19. Описание цвета с помощью десятичных или шестнадцатеричных чисел

      Можно также использовать предопределённые для web-цветов константы. Имена цветовых констант начинаются с букв clr (например, clrBlue — синий). Полный список констант можно посмотреть, набрав clr в MetaEditor, или почитать официальную справку.

      В примере 12 видно, что каждое описание цвета состоит из трёх фрагментов. Каждый такой фрагмент описывает интенсивность свечения красной, зелёной или синей точки на экране (Red, Green, Blue = RGB). Вместе они дают всё многообразие цветов.

      Смешивая красный и зелёный, получаем все оттенки жёлто-коричнево-оранжевой гаммы.

      Красный и синий дают фиолетово-розовые тона.

      Зелёный и синий дают разные варианты бирюзового, голубого и т.д.

      Смешение всех трёх оттенков в равных пропорциях даёт гамму серых оттенков: от черного — когда все компоненты "выключены", то есть имеют интенсивность, равную 0, до белого — когда все имеют максимальную интенсивность 255 (0xFF).

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

      Все эти правила проиллюстрированы в таблице 3, где я просто раскрасил ячейки теми цветами, которые доступны в редакторе.

      На практике чаще всего не обязательно знать числовые значения для каждого цвета, достаточно выбрать цвет из специальной палитры или передать функции предопределённую в языке константу. Но понимать, как всё устроено "изнутри", по-моему всё равно полезно.

      Цветовые данные занимают в памяти 4 байта, хотя используются только 3 из них. Так сложилось исторически, и это — общее соглашение для любых программ, умеющих работать с этой моделью описания цвета.

      Таблица 3. Примеры использования цветов

      0, 0, 0 156, 15, 15 106, 0, 86 0, 49, 110 0, 110, 41 56, 37, 9 56, 37, 9
      51, 51, 51 191, 3, 3 133, 2, 108 0, 67, 138 0, 137, 44 243, 195, 0 87, 64, 30
      102, 102, 102 226, 8, 0 160, 39, 134 0, 87, 174 55, 164, 44 255, 221, 0 117, 81, 26
      153, 153, 153 232, 87, 82 177, 79, 154 44, 114, 199 119, 183, 83 255, 235, 85 143, 107, 50
      204, 204, 204 240, 134, 130 193, 115, 176 97, 147, 207 177, 210, 143 255, 242, 153 179, 146, 93
      255, 255, 255 249, 204, 202 232, 183, 215 164, 192, 228 216, 232, 194 255, 246, 200 222, 188, 133

      При желании с цветом можно работать так же, как с обычными целыми числами.

      Например, вот такой код:

        color a = C'255,0,0';
        color b = C'0,255,0';
        color d = a+b;
        Print(a," ",b," ",d);

      Пример 20. Использование цветов в арифметических выражениях

      Даст вот такой результат:

      Результата использования цветовв арифметических выражениях

      Рисунок 9. Результата использования цветов в арифметических выражениях

      Перечисления

      Последний базовый тип — перечисления.

      Бывают ситуации, когда по смыслу задачи переменная должна принимать только определённые значения. Например, тренд бывает нисходящий, восходящий или боковой (флет). Или приказы на покупку: купить по рынку (Buy), отложить, пока цена не дойдёт до определённого уровня на пробой (Buy Stop) или на отскок (Buy Limit). Или дни недели. В общем, думаю, принцип понятен.

      Вот для таких случаев и используются перечисления (англ. enumeration).

      Описание перечислений состоит из трёх этапов.

      1. На первом этапе нужно создать сам список и как-то его назвать. Полученное имя — это имя типа для любых переменных или функций. Единственное отличие этого имени от предопределённых типов в том, что мы его придумали сами.
      2. На втором этапе нужно создать переменную этого типа.
      3. И, наконец, на третьем этапе данную переменную можно использовать.
      Пример 15 показывает, как описать и использовать перечисление. Для иллюстрации я взял описание направления (DIRECTION), которое может использовать только три значения: "Подъём", "Спуск", "Боковик".

      //--- Первый этап: создание нового списка (нового типа данных)
        enum ENUM_DIRECTION
         {
          Upward,
          Downward,
          Aside
         };
      
      //--- Второй этап: описание (и, при необходимости, инициализация) переменной этого типа
        ENUM_DIRECTION next=Upward;
      
      //--- Третий этап: использование переменной
        Print(next);

      Пример 21. Пример описания и использования перечисления

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

      Хотя можно описывать его и локально, внутри какой-нибудь функции. Тогда из других функций это перечисление видно не будет. Чаще всего большого смысла в этом нет, но мало ли, какие перед вами будут стоять задачи...

      Названия элементов списка перечисляются в фигурных скобках через запятую.

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

      Предопределённые в языке перечисления имеют имена, написанные заглавными буквами, причем эти имена начинаются с приставки ENUM_. Вы можете называть свои перечисления как угодно (в пределах ), однако хорошим тоном будет придерживаться этих же стандартов.

      Внутреннее представление для перечислений — это знаковое целое число, которое занимает в памяти 4 байта.

      Если попытаться выполнить код из примера 21, то мы увидим число 0. Значит, когда мы предоставляем MQL5 самому подбирать номера для элементов перечисления, он начинает с нуля.

      Но можно указать и какое-то другое число, просто нужно задать его явно:

      //--- Первый этап: создание нового списка (нового типа данных)
        enum DIRECTION
         {
          Upward = 1,
          Downward = -1,
          Aside = 0
         };

      Пример 22. Явное задание значений перечисления

      Необязательно указывать все значения.

      Если некоторые значения указаны, а некоторые — нет, MQL5 сам подберёт номера, основываясь на порядке элементов и последнем наибольшем номере. Это значит, что если в примере 15 я задам Upward = 1 и уберу все остальные инициализации, то Downward будет равен 2, а Aside — 3. Рекомендую проверить это самостоятельно.


      Выражения и простые операторы

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

      Операторы сравнения

      Эти операторы осмыслены для всех типов данных.

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

      Существуют следующие операторы сравнения:

      • больше( > ), 
      • меньше ( < ), 
      • больше или равно ( >= ), 
      • меньше или равно ( <= ), 
      • равно ( == ), 
      • не равно ( != )

      Приоритет всех этих операций одинаков.

      При сравнении строк компьютер будет следовать расположению символов в кодировке. Например, прописная буква "А" идёт раньше строчной "а", поэтому она будет меньше, и, соответственно,

      "Ассоль" < "а соль?" //true

      Пример 23. Сравнение строк. Прописные меньше строчных

      Если подряд идут несколько одинаковых символов, выбирается для сравнения первый неодинаковый.

      "Ассоль" > "А соль?" //true

      Пример 24. Первые буквы совпадают

      Выражение в примере 24 верно, поскольку пробел в кодировке идёт раньше алфавитных символов, а первые буквы совпадают.

      Если одна строка уже закончилась, а вторая продолжается, и символы до сих пор совпадали, то меньшей будет считаться та, что закончилась. Например:

      "Песня" < "Песня про зайцев" //true

      Пример 25. Различная длина строки

      Арифметические действия

      Результат определяется типом данных, участвующих в выражении (см. раздел "").

      С числами можно проводить арифметические действия:

      • знак перед числом ( -3 ) (иногда говорят "унарный" минус);
      • умножение ( * ), деление ( / ) (для целых чисел даёт округление вниз), остаток от деления ( % ) (только для целых, 5%2 == 1);
      • сложение ( + ), вычитание ( - );
      • инкремент ( ++ ), декремент ( -- )

      Список приведён в порядке приоритета.

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

      Для строк тоже определена операция ( + ), но здесь она обозначает склейку (сделать из двух коротких строк одну длинную).

      Побитовые операции

      Результат — целое число.

      Для целых чисел есть еще побитовые операции:

      • побитовое отрицание ( ~ );
      • сдвиг вправо ( >> );
      • сдвиг влево ( << );
      • побитовое "и" ( & );
      • побитовое "или" ( | );
      • исключающее "или" ( ^ ).

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

      Список приведён в порядке приоритета.

      Логические операторы

      Результат — логический.

      • логического отрицания ( ! );
      • логического умножения — логическое "и" ( && );
      • логического сложения — логическое "или" ( || ).

      Список приведён в порядке приоритета.

      Существуют и другие операции, и о них будет разговор в других статьях. Также в других статьях будут более подробно рассмотрены примеры использования всех вышеперечисленных операторов. Пока пользуйтесь тем, что вам понятно, или смотрите в справку. Тут особых сложностей быть не должно...


      Приведение типов

      Иногда в арифметическом выражении участвуют данные нескольких типов. Например, с помощью функции Print мы постоянно выводим строки вместе с числами, а в этой статье даже цвета попадались.

      Какой тип будет у результата? Тут возможен выбор из двух вариантов: либо мы сами определяем, к чему в итоге приходим, либо мы доверяем компилятору.

      Компилятор, конечно, умный, он разберётся... Но не всегда.

      Поэтому давайте разберёмся сами, что делает компилятор, и что можно делать "вручную", чтобы не потерять важных данных, не получать предупреждения при компиляции и быть уверенными в результате.

      Для начала — что делает компилятор?

      Во-первых, если в выражении используются данные одного типа, то и результат будет того же типа. Это самый лёгкий случай.

      Если участвуют разные типы, компилятор попытается расширить результат до наиболее точного. Скажем, если мы пытаемся сложить 4-байтное целое ( int ) c датой ( datetime ), то мы получим дату (поскольку её диапазон шире).

      Целый литерал принадлежит к типу int, литерал с плавающей точкой — обычно к типу double, если в конце не поставлена строчная буква "f":

      5 + 3.4f + 4.25 // Результат — double, так как сначала 5 преобразуется к типу float, а потом 4.25 задаёт двойную точность.

      Пример 26. Приведение типов при использовании литералов

      В справке приведена схема приоритетов преобразований:

      Приоритеты преобразований данных

      Рисунок 10. Приоритеты преобразований данных

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

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

      Если нужно преобразовать результат (или конкретное значение) к определённому типу, можно:

      • Просто записать результат в переменную определённого типа. Метод, по сути, является вариантом автоматического приведения, поэтому пользоваться им нужно с осторожностью;
      • Использовать специальные функции преобразования данных;
      • Использовать краткую форму приведения типов.
      (int)a+b // преобразует а к целому. b остаётся неизменным
      double (c+d) // запись абсолютно аналогична предыдущей. В данном случае преобразуется результат суммирования
      
      // и т.д. — можно использовать любой подходящий тип
      

      Пример 27. Краткая форма приведения типов

      Естественно, вы помните, что скобки позволяют изменить порядок операций, так как имеют наивысший приоритет. Не уверены — ставьте скобки, и будет счастье...


      Заключение

      Мы рассмотрели большой кусок теории про базовые типы данных, переменные и выражения. Если вы поймёте материал этой и следующей статей, то новичком вы быть уже почти перестанете, и перейдёте на уровень выше :-) Если понимать, как работают переменные (материал этой статьи) и функции (будет в следующей), то дальше вам никакой ООП не будет страшен...

      ООП — объектно ориентированное программирование, считается сложным материалом. Но на самом деле, если понимать базу, там сложности больше идеологические, чем технические

      Для тех, кто не понял каких-то моментов, рекомендую перечитать статью ещё раз (или не раз), только очень медленно, по одному понятию за раз, с проверкой всего сказанного в коде.

      Для тех, кто всё понял и не хочет ждать, пока я созрею со следующей статьёй, рекомендую скрасить ожидание, написав свой скрипт, который будет выводить полезную для вас информацию о ваших рабочих инструментах и балансе. Большую часть этой информации можно получить из семейства функций AccountInfo... и SymbolInfo...

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

      P.S. Пример такого скрипта есть в стандартной библиотеке. Если вам лень писать свой, попробуйте разобраться с готовым. Тем, кто всё же напишет что-то своё, рекомендую сравнить...

      Последние комментарии | Перейти к обсуждению на форуме трейдеров (7)
      Oleh Fedorov
      Oleh Fedorov | 21 февр. 2024 в 08:06
      Константин Сандалиди #:
      Спасибо за попытку... для меня лично это очередная серия абсолютно бесполезных статей, как бы я не хотел научиться...
      Константин, а можете развернуть? Почему "бесполезных"? И что нужно, чтобы наоборот? Сразу скажу, скорость выхода хочу сильно повысить. Есть что-то ещё?
      Yuriy Bykov
      Yuriy Bykov | 21 февр. 2024 в 08:56

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

      Также приходилось сталкиваться с людьми, у которых вызывало разочарование выяснение, что для овладения навыками программирования понадобится приложить гораздо больший объем усилий и временных затрат на обучение, чем им представлялось изначально. Тут успех в большей мере зависит от конкретного человека, но если не опускать руки и продолжать находить и изучать другие материалы для обучения, то всё возможно.

      Константин Сандалиди
      Константин Сандалиди | 29 февр. 2024 в 05:46
      Oleh Fedorov #:
      Константин, а можете развернуть? Почему "бесполезных"? И что нужно, чтобы наоборот? Сразу скажу, скорость выхода хочу сильно повысить. Есть что-то ещё?

      Потому что я не смог самостоятельно освоить MQL 4, так же не могу самостоятельно освоить  MQL 5. И что бы я ни читал, не смотрел мне не понятно! Уровень моей подготовки 0!!! А все статьи для продвинутых пользователей, которые могут просто в документации посмотреть... Перечитывание непонятного по нескольку раз это тупиковая ветвь обучения (познания), "учебник" С.Ковалева я выучил наизусть как стихотворение мог рассказать, но я не понимаю про что в нем говорится (пишется)... Тем более не могу хранить в голове непонятное и использовать на практике. Возможно конечно же есть уникальные личности, которые налету все схватывают, но у меня это не так. Только фундаментальные знания могу помнить и использовать. Учиться по прежнему хочу, но не могу найти УЧИТЕЛЯ.

      MrBrooklin
      MrBrooklin | 29 февр. 2024 в 06:51
      Константин Сандалиди #:

      Потому что я не смог самостоятельно освоить MQL 4, так же не могу самостоятельно освоить  MQL 5. И что бы я ни читал, не смотрел мне не понятно! Уровень моей подготовки 0!!! А все статьи для продвинутых пользователей, которые могут просто в документации посмотреть... Перечитывание непонятного по нескольку раз это тупиковая ветвь обучения (познания), "учебник" С.Ковалева я выучил наизусть как стихотворение мог рассказать, но я не понимаю про что в нем говорится (пишется)... Тем более не могу хранить в голове непонятное и использовать на практике. Возможно конечно же есть уникальные личности, которые налету все схватывают, но у меня это не так. Только фундаментальные знания могу помнить и использовать. Учиться по прежнему хочу, но не могу найти УЧИТЕЛЯ.

      До УЧИТЕЛЯ мне, как до Пекина ползком, но кое-что уже смогу рассказать. Сам был в такой же ситуации, несколько лет назад, когда начинал изучать язык программирования MQL5 с полного нуля.

      Вопрос №1: что именно Вам не понятно в изучении языка программирования? Назовите, хотя бы, одну проблему и конкретно опишите, что в ней непонятного.

      С уважением, Владимир.

      Oleh Fedorov
      Oleh Fedorov | 29 февр. 2024 в 10:50
      Константин Сандалиди #:

      Потому что я не смог самостоятельно освоить MQL 4, так же не могу самостоятельно освоить  MQL 5. И что бы я ни читал, не смотрел мне не понятно! Уровень моей подготовки 0!!! А все статьи для продвинутых пользователей, которые могут просто в документации посмотреть... Перечитывание непонятного по нескольку раз это тупиковая ветвь обучения (познания), "учебник" С.Ковалева я выучил наизусть как стихотворение мог рассказать, но я не понимаю про что в нем говорится (пишется)... Тем более не могу хранить в голове непонятное и использовать на практике. Возможно конечно же есть уникальные личности, которые налету все схватывают, но у меня это не так. Только фундаментальные знания могу помнить и использовать. Учиться по прежнему хочу, но не могу найти УЧИТЕЛЯ.

      Сколько раз убеждался, что в непонятном материале, если он излагается последовательно, обычно есть какой-то фрагмент, на котором происходит "затык": до него - всё понятно, а после - тёмный лес... Недопонятое понятие, недоусвоенная концепция...

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

      Посему задам пару вопросов, которые, естественно, можно самостоятельно развивать до бесконечности. :-)

      1. Понятно ли Вам, что делает программист в принципе? (Если нет, можно глянуть в мою первую статью этого цикла - и позадавать там вопросы в комментах по непонятным фразам).
      2. Понятно ли Вам, куда складывать текстовые файлы, которые Вы пишете? И что с ними делать после написания?
      3. Понятна ли Вам концепция функции? Концепция переменной?

      Если ответы на все вопросы - "да", то уровень подготовки у Вас уже НЕ 0!!! Если хоть на один вопрос не знаете ответа или ответ "нет" - попробуйте еще раз медленно перечитать хоть начало Ковалева, хоть мои уже вышедшие статьи...

      И - практикуйте! Это важно. Открывайте редактор, пишите код примеров (можно копировать, но лучше самостоятельно). Компилируйте и проверяйте, что получилось.

      А после этого задавайте вопросы. Хоть в моих темах, хоть в параллельных на форуме... По запросу "вопросы новичков" поиск по сайту выдал мне 202 страницы ссылок, но никто не запретит Вам создавать свою тему, если она не дублирует существующие.

      Освоение ONNX: Переломный момент для MQL5-трейдеров Освоение ONNX: Переломный момент для MQL5-трейдеров
      Погрузитесь в мир ONNX - мощного открытого формата для обмена моделями машинного обучения. Узнайте, как использование ONNX может произвести революцию в алгоритмической торговле на MQL5, позволяя трейдерам беспрепятственно интегрировать передовые модели искусственного интеллекта и поднять свои стратегии на новый уровень. Раскройте секреты кросс-платформенной совместимости и узнайте, как раскрыть весь потенциал ONNX в своей торговле на MQL5. Улучшите свою торговлю с помощью этого подробного руководства по ONNX.
      Нейросети — это просто (Часть 77): Кросс-ковариационный Трансформер (XCiT) Нейросети — это просто (Часть 77): Кросс-ковариационный Трансформер (XCiT)
      В своих моделях мы часто используем различные алгоритмы внимание. И, наверное, чаще всего мы используем Трансформеры. Основным их недостатком является требование к ресурсам. В данной статье я хочу предложить Вам познакомиться с алгоритмом, который поможет снизить затраты на вычисления без потери качества.
      DoEasy. Элементы управления (Часть 33): вертикальный "ScrollBar" DoEasy. Элементы управления (Часть 33): вертикальный "ScrollBar"
      В статье продолжим разработку графических элементов библиотеки DoEasy, и добавим вертикальную прокрутку элементов управления объекта-формы и некоторые полезные функции и методы, которые потребуются в дальнейшем.
      Разрабатываем мультивалютный советник (Часть 3): Ревизия архитектуры Разрабатываем мультивалютный советник (Часть 3): Ревизия архитектуры
      Мы уже несколько продвинулись в разработке мультивалютного советника с несколькими параллельно работающими стратегиями. С учетом накопленного опыта проведем ревизию архитектуры нашего решения и попробуем ее улучшить, пока не ушли слишком далеко вперед.