Инструкции, блоки кода и функции

Итак, функция OnStart выглядит в скрипте, сгенерированном Мастером, следующим образом:

void OnStart()
{
}

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

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

  • определить переменные — именованные ячейки в памяти компьютера для хранения данных;
  • организовать ввод исходных данных;
  • выполнить обработку данных — прикладной алгоритм;
  • организовать вывод результатов.

Все эти этапы являются необязательными, если говорить о сохранении синтаксической правильности программы. Например, если мы создадим программу, которая считает результат умножения "2*2", то для неё, очевидно, не нужно никаких входных данных, потому что числа для умножения вшиты в текст программы. Кроме того, поскольку 2 и 2 в этом выражении являются постоянными значениями, то в программе не нужны и дополнительные именованные ячейки (переменные). А поскольку мы и так знаем, сколько будет дважды два, то, в принципе, можем не сообщать результат умножения. Конечно, такая программа не имела бы практического смысла, но была бы абсолютно корректной с технической точки зрения.

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

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

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

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

И здесь на помощь приходит принцип "разделяй и властвуй", который используется в программировании практически повсеместно и под разными "соусами". Мы изучим их все по мере продвижения по разделам книги, а пока отметим лишь суть.

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

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

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

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

Для тех, кто знаком с программированием на других языках (например, C, C++, Rust, Kotlin), очевидна аналогия данной функции с функцией main — основной точкой входа в программу.

Любой скрипт обязательно должен содержать функцию OnStart. В противном случае компиляция закончится с ошибкой.

Пустая функция OnStart, как у нас, начинает выполняться терминалом (как только скрипт запускается тем или иными способом) и тут же завершает работу. Строго говоря, прикладного алгоритма в нашем скрипте еще нет, но функция-заглушка для его добавления уже есть.

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

Синтаксис определения функций мы подробно рассмотрим во второй части книги. А для первого знакомства с ним, чтобы понять описание OnStart, достаточно упомянуть следующие основные моменты.

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

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

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

Круглые скобки после имени — обязательная часть описания любой функции: в них может содержаться список параметров функции. Например, если бы мы писали функцию, возводящую число в квадрат, то в ней нужно было бы предусмотреть один параметр — под то самое число. Затем мы могли бы вызвать эту функцию из любой части программы, передав в неё один аргумент — конкретное значение для параметра. Как описать список параметров, мы увидим позднее, а в текущем примере он отсутствует. Это требование наложено на функцию OnStart из-за того, что её вызывает сам терминал, и он никогда ничего в неё не передает в качестве параметров.

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

Приведенная последовательность слова void, имени OnStart, пустого списка параметров и пустого блока кода определяет для компилятора минимально возможную, пустую реализацию функции OnStart. Позднее, добавляя инструкции в тело функции, мы будем расширять определение функции OnStart.

Выполнив команду Компилировать, мы убедимся, что скрипт успешно компилируется, и готовая программа появляется в Навигаторе терминала в папке Scripts/MQL5Book/p1. Это результат того, что на диске в соответствующей папке теперь есть файл Hello.ex5, это легко проверить в любом файловом менеджере.

Мы можем запустить скрипт на чарте, но единственным подтверждением его выполнения будут записи в журнале терминала (вкладка Журнал в окне Инструменты, не путать с панелью инструментов):

Scripts        script Hello (EURUSD,H1) loaded successfully
Scripts        script Hello (EURUSD,H1) removed

То есть скрипт загрузился, управление передалось функции OnStart, но тут же вернулось терминалу, потому что функция ничего не делает, и после этого терминал выгрузил скрипт с чарта.