Русский

Учебник по MQL4  Основы языка  Функции

Функции


Понятие функции


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

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

Говоря о функциях, мы будем иметь в виду два аспекта: описание функции и вызов функции.

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

Вызов функции (обращение к функции) - это запись, исполнение которой приводит к исполнению функции.

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

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

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

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


Состав функции


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

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


Рис. 18. Код цельной программы pifagor.mq4.


Задача 8. Оформить часть представленного кода программы в виде функции.

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

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


Рис. 19. Код программы, содержащий описание и обращение к пользовательской функции gipo.mq4.

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

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

Описание функции


Описание функции состоит из двух основных частей - заголовка функции и тела функции.

Заголовок функции состоит из указания типа возвращаемого значения, названия функции и списка формальных параметров. Список формальных параметров заключается в круглые скобки и располагается после названия. Тип возвращаемого значения может быть одним из известных нам типов: int, double, bool, color, datetime и string. Если функция не возвращает никакого значения, то её тип можно обозначить void ("без типа") или любым другим типом.

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


Рис. 20. Описание функции.

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

Вызов функции


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


Рис. 21. Вызов функции (обращение к функции).

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

Типы функций


Различают три типа функций - специальные, стандартные (встроенные, предопределённые) и пользовательские.

Специальные функции


В языке MQL4 имеется всего 3 специальные функции. Они имеют предопределённые имена: init(), start() и deinit(), которые запрещено использовать для названия других функций. Специальные функции подробно рассматриваются в разделе Специальные функции. Здесь укажем лишь, что основной код программы размещается внутри этих функций (рис. 18, 19).

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

Стандартные функции


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

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

В нашем примере (рис. 18, 19) используется две стандартные функции: MathSqrt() и Alert(). Первая предназначена для вычисления квадратного корня, вторая - для сообщения на экран некоторого текста, заключённого в круглые скобки. Более подробно свойства функций рассматриваются в разделе Стандартные функции, а пока нам важно отметить, что эти записи представляют собой вызов стандартных функций, в то время как описания этих функций в программе отсутствуют. Стандартные функции ещё называют встроенными или предопределёнными. Можно использовать любой из указанных терминов.

Пользовательские функции


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

Таблица 1. Использование в программе описания и вызова для функций разных типов.

Тип функции Описание функции Вызов функции
Специальная Используется Не используется (*)
Стандартная Не используется Используется
Пользовательская Используется Используется

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

Свойства функций

Главное свойство всех функций состоит в том, что вызванные функции исполняются. Исполнение функций происходит в соответствии с их кодом.

Передаваемые параметры и возвращаемое значение


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

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

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

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

В нашем примере передаваемые параметры - это переменные A и В (рис. 21), а возвращаемое значение - переменная с (рис. 20). Требование совпадения типов передаваемых и формальных параметров показано на рис. 22.

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


Рис. 22. Совпадение количества, типов и порядка следования передаваемых и формальных параметров. В качестве передаваемых параметров используются только переменные.

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


Рис. 23. Совпадение количества, типов и порядка следования передаваемых и формальных параметров. В качестве передаваемых параметров используются константа, выражение и переменные соответствующего типа.

Формальные параметры


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

Формальные параметры - это список переменных, указанных в заголовке описания функции.

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

Обратимся ещё раз к рис. 20 и 21. Обратите внимание: имена передаваемых параметров (в скобках вызова функции указано A и В) не совпадают с именами параметров, указанных в описании функции (a и b). В разделе Константы и переменные указывалось, что язык MQL4 чувствителен к строчным и прописным буквам. Таким образом, рассматриваемые здесь A и a, В и b - разные имена переменных. Однако в этом коде нет ошибки.

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

Как это работает


  • В программе встречается вызов функции, в скобках которого указаны переменные А и В.
  • Вызывается одноимённая функция, в заголовке у которой указаны формальные параметры а и b.
  • Значение переменной А присваивается переменной а.
  • Значение переменной B присваивается переменной b.
  • Исполняемая функция производит вычисления, используя значения переменных а и b.

Допустимо использование любых названий формальных параметров (не совпадающих с названиями переменных, используемых в программе). В данном примере мы использовали идентификаторы формальных параметров а и b, но могли бы использовать любые другие, например, m и n или Kat_1 и Kat_2. Разумеется, при составлении программы в теле функции следует указывать вычисления, использующие те имена переменных, которые указаны в заголовке. Мы указали в заголовке а и b, значит, в функции необходимо использовать а и b, а не m и n.

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

Возвращаемое значение, вычисленное в функции, указывается в скобках оператора return(). В качестве возвращаемого значения могут быть использованы значение переменной, результат выражения или константа. В нашем случае это - значение локальной переменной c (локальная переменная - это переменная, объявленная на уровне функции; при выходе из функции значения всех локальных переменных теряются; более подробно с локальными переменными мы познакомимся в разделе Виды переменных). Функция возвращает в программу значение локальной переменной с (рис. 19). Это значит, что теперь это значение будет присвоено переменной С.

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

Пример использования стандартных функций в программе


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

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

   int A = 3;                               // Первый катет

1. В правой части оператора присваивания указана константа, значение которой равно 3.

2. Присвоение переменной А (находящейся в левой части от знака равенства в операторе присваивания) значения 3 (значения правой части).

Управление передаётся в следующую строку:

   int B = 4;                               // Второй катет

3. В правой части оператора присваивания указана константа, значение которой равно 4.

4. Присвоение переменной В значения 4.

Программа переходит к выполнению следующей строки:

  int C_2 = A*A + B*B;                      // Сумма квадратов катетов

5. Вычисление правой части оператора присваивания. Результатом вычислений является значение 25 (подробности обращения программы к переменным для получения их значений рассмотрены в разделе Константы и переменные, поэтому останавливаться на этом вопросе здесь мы не будем).

6. Присвоение переменной С_2 значения 25.

Следующая строка представляет собой оператор присваивания, в правой части которого содержится вызов стандартной функции:

   int C = MathSqrt( C_2);                  // Вычисление гипотенузы

Программа стремится выполнить оператор присваивания. Для этого она сначала выполняет вычисления справа от знака равенства.

7. Программа вызывает для исполнения стандартную функцию вычисления квадратного корня MathSqrt(). В качестве передаваемого параметра используется значение переменной С_2 (в нашем случае это значение равно 25). Обратите внимание на то, что нигде в программе нет описания этой стандартной функции. Описания стандартных функций не должны быть содержанием программ. В тексте программы вызов стандартной функции легко отличить по внешнему виду: в редакторе MetaEditor они выделяются фиолетовым цветом (программист может настроить цвет по своему выбору).

8. Производятся вычисления в стандартной функции MathSqrt().

9. Стандартная функция MathSqrt() закончила вычисления и возвращает полученное значение. В нашем случае это - значение 5 (квадратный корень из 25).

Возвращаемое функцией значение является теперь содержанием записи:

           MathSqrt( C_2)

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

10. В данном случае возвращаемое значение является значением правой части оператора присваивания. Продолжая выполнять оператор присваивания, программа присвоит значение 5 переменной С.

11. В следующей строке находится оператор обращения к стандартной функции Alert() (вызов функции).

   Alert("Гипотенуза = ", C);                // Сообщение на экран

Стандартная функция Alert() открывает диалоговое окно, в котором отображаются значения передаваемых параметров. В данном случае в качестве передаваемых параметров функция получила два значения:

- значение строковой константы: Гипотенуза =

- значение целой переменной С: 5

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

В результате исполнения стандартной функции Alert() в окне этой функции появится следующая строка:

Гипотенуза = 5

12. Последний в этой программе оператор завершает работу специальной функции start().

 return;                                     // Оператор выхода из функции

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


Может возникнуть вопрос: как узнать, какая функция возвращает значение, а какая - нет? Ответ на этот вопрос очевиден: для получения подробного описания стандартных функций необходимо обратиться к справочной документации на MQL4.community, сайте MetaQuotes Ltd., или к разделу "Справка" в редакторе MetaEditor. Свойства пользовательской функции задаются при её описании. Будет пользовательская функция возвращать значение или нет - зависит от заложенного в неё алгоритма (решение принимается программистом на этапе составления программного кода функции).


Пример применения в программе пользовательской функции


Теперь посмотрим, как те же вычисления выполняются в программе, содержащей пользовательскую функцию (рис. 19). Некоторая часть кода, ранее присутствовавшая в специальной функции start(), теперь отсутствует. Вместо неё там присутствует вызов пользовательской функции. Но вслед за специальной функцией start() появилось описание пользовательской функции.

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

   int A = 3;                            // Первый катет
int B = 4; // Второй катет

В третьей строке написан оператор присваивания, в правой части которого указан вызов пользовательской функции:

   int C = Gipo(A,B);                    // Вычисление гипотенузы

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

Обратите внимание: описание пользовательской функции должно обязательно присутствовать в программе и располагаться сразу после закрывающей фигурной скобки специальной функции start() (т.е. за её пределами).

В процессе обращения к пользовательской функции программа выполнит следующие действия:

6.1. Обращение к переменной А с целью получить её значение (в нашем случае - 3)

6.2. Обращение к переменной В с целью получить её значение (в нашем случае - 4)

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

7. Управление передаётся в пользовательскую функцию.

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

Первой строкой в описании пользовательской функции является её заголовок:

   int Gipo(int a, int b)                 // Пользовательская функция

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

7.1. Переменной а (первой по порядку в списке формальных параметров) будет присвоено значение 3 (первое по порядку в списке передаваемых параметров).

7.2. Переменной b (второй по порядку в списке формальных параметров) будет присвоено значение 4 (второе по порядку в списке передаваемых параметров).

Далее управление будет передано в тело функции для исполнения заложенного в неё алгоритма.

Первым оператором в теле функции стоит такой:

   int c2 = a*a + b*b;                   // Сумма квадратов катетов

7.3. Исполняя этот оператор, программа вычислит значение в правой части оператора присваивания, а после этого присвоит полученное значение (в нашем случае: 3*3 + 4*4 = 25) переменной с2.

Следующий оператор:

   int c = MathSqrt(c2);                 // Гипотенуза

7.4. Здесь вычисляется квадратный корень из значения переменной с2. Порядок вычислений такой же, как и в предыдущем примере. Описание стандартной функции также не используется. В результате выполнения оператора присваивания переменной с будет присвоено значение 5.

7.5. В следующей строке стоит оператор:

   return(c);                           // Оператор выхода из функции

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

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

8. Напомним, что вызов пользовательской функции произошёл из оператора

   int C = Gipo(A,B);                   // Вычисление гипотенузы

Запись (вызов пользовательской функции)

          Gipo(A,B)

на этапе возврата значения получает значение, вычисленное в функции (в нашем случае - это значение 5).

Заканчивая выполнение оператора присваивания, программа присвоит переменной С значение 5.

9. Следующий оператор

   Alert("Гипотенуза = ", C);           // Сообщение на экран

будет исполнен так же, как и в предыдущем примере, а именно - в специальном окне появится сообщение:

Гипотенуза = 5

10. Последний в этой программе оператор

 return;                                // Оператор выхода из функции

завершает работу специальной функции start(), а вместе с ней - и работу всей программы (более подробно свойства специальных функций рассматриваются в разделе Специальные функции).


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

Ранее рассмотренный вариант пользовательской функции Gipo()


В этой функции формальные параметры "напоминают" переменные, используемые в основной программе. Однако это - только внешнее сходство, в действительности А и а - разные имена переменных.

//--------------------------------------------------------------------
int Gipo(int a, int b) // Пользовательская функция
{
int c2 = a*a + b*b; // Сумма квадратов катетов
int c = MathSqrt(c2); // Гипотенуза
return(c); // Оператор выхода из функции
}
//--------------------------------------------------------------------

2-й вариант реализации пользовательской функции


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

//--------------------------------------------------------------------
int Gipo(int alpha, int betta) // Пользовательская функция
{
int SQRT = alpha*alpha + betta*betta; // Сумма квадратов катетов
int GIP = MathSqrt(SQRT); // Гипотенуза
return(GIP); // Оператор выхода из функции
}
//--------------------------------------------------------------------

3-й вариант реализации пользовательской функции Gipo()


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

//--------------------------------------------------------------------
int Gipo(int alpha, int betta) // Пользовательская функция
{
alpha= alpha*alpha + betta*betta; // Сумма квадратов катетов
alpha= MathSqrt(alpha); // Гипотенуза
return(alpha); // Оператор выхода из функции
}
//--------------------------------------------------------------------

4-й вариант реализации пользовательской функции Gipo()


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

//--------------------------------------------------------------------
int Gipo(int a, int b) // Пользовательская функция
{
return(MathSqrt(a*a + b*b)); // Оператор выхода из функции
}
//--------------------------------------------------------------------

Таким образом, применение пользовательских функций в практике программирования имеет неоспоримые достоинства:

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

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