Русский

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

Операции и выражения


Чтобы понять, какое значение имеют в языке MQL4 операции и выражения, не требуется никаких особых аналогий. Практически это - то же самое, что операции и выражения в простой арифметике. Каждому человеку понятно, что в записи f = n + m элементы f, n и m являются переменными, значки = и + являются знаками операций, а n + m - выражением.

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

Понятия "операнд", "операция", "знак операции" и "выражение"


Операнд - это константа, переменная, элемент массива или значение, возвращаемое функцией (понятие "функция" рассматривается в разделе Функции, понятие "массив" - в разделе Массивы; на данном этапе обучения достаточно понимать под операндами уже известные нам константы и переменные).

Операция - это действие, производимое над операндами.

Знак операции - предопределённый символ или группа символов, предписывающие выполнить некоторую операцию.

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

Виды операций


В языке MQL4 различают следующие виды операций:

  • арифметические операции;
  • операции присваивания;
  • операции отношения;
  • логические операции;
  • побитовые операции;
  • операция запятая;
  • вызов функции.

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

Арифметические операции


К символам арифметических операций относятся следующие:

Символ Операция Пример Аналог
+ Сумма величин x + 2
- Вычитание величин или изменение знака x - 3, y = - y
* Умножение величин 3 * x
/
Частное от деления x / 5
% Остаток от деления minutes = time % 60
++ Добавление 1 к значению переменной y++ y = y + 1
-- Вычитание 1 из значения переменной y-- y = y - 1

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


К символам операций присваивания относятся следующие:

Символ Операция Пример Аналог
= Присваивание значения x переменной y у = x
+= Увеличение значения переменной у на x у += x y = y + x
-= Уменьшение значения переменной y на x y -= x y = y - x
*= Умножение значения переменной y на x y *= x y = y * x
/= Деление значения переменной y на x y /= x y = y / x
%= Остаток от деления значения переменной y на x y %= x y = y % x

Операции отношения


К символам операций отношения относятся следующие:

Символ Операция Пример
== Истина, если x равно y x == y
!= Истина, если x не равно y x != y
< Истина, если x меньше y x < y
> Истина, если x больше y x > y
<= Истина, если x меньше или равно y x <= y
>= Истина, если x больше или равно y x >= y

Логические операции


К символам логических операций относятся следующие:

Символ Операция Пример Пояснения
! НЕ (логическое отрицание) ! х ИСТИНА(1), если значение операнда ЛОЖЬ(0), и ЛОЖЬ(0), если значение операнда не ЛОЖЬ(0).
|| ИЛИ (логическое ИЛИ) x < 5 || x > 7 ИСТИНА(1), если истинно любое из значений
&& И (логическое И) x == 3 && y < 5 ИСТИНА(1), если истинны все значения

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


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

Дополнение значения переменной до единицы. Значение выражения содержит 1 во всех разрядах, в которых значения переменной содержат 0, и 0 во всех разрядах, в которых значения переменной содержат 1.

b = ~n;

Двоичное представление x сдвигается вправо на y разрядов. Сдвиг вправо логический, то есть освобождающиеся слева разряды будут заполняться нулями.

x = x >> y;

Двоичное представление x сдвигается влево на y разрядов; освобождающиеся справа разряды заполняются нулями.

x = x << y;

Побитовая операция И двоичных представлений x и y. Значение выражения содержит 1 (ИСТИНА) во всех разрядах, в которых и x, и y содержат не ноль; и 0 (ЛОЖЬ) во всех остальных разрядах.

b = ((x & y) != 0);

Побитовая операция ИЛИ двоичных представлений x и y. Значение выражения содержит 1 во всех разрядах, в которых x или y не содержат 0, и 0 - во всех остальных разрядах.

b = x | y;

Побитовая операция "исключающее ИЛИ" (eXclusive OR) двоичных представлений x и y. Значение выражения содержит 1 в тех разрядах, в которых x и y имеют разные двоичные значения, и 0 - во всех остальных разрядах.

b = x ^ y;

Операция запятая


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

for(i=0,j=99; i<100; i++,j--) Print(array[i][j]); // Оператор цикла

В качестве примера можно рассматривать список передаваемых параметров (будет рассмотрено далее).

My_function (Alf, Bet, Gam, Del) // Вызов функции с аргументами

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

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

Вызов функции подробно рассматривается в разделе Вызов функции.

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

Операции над однотипными операндами


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


Задача 1. У Васи 2 карандаша, у Пети 3 карандаша. Сколько всего карандашей у мальчиков?:)

Решение. Обозначим количество карандашей у Васи как переменную А, количество карандашей у Пети – как переменную В, а результат – через переменную С.

Ответ будет таким: С = А + В

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

Программный код можно записать, например, так:

   double A = 2.0;                       // Количество карандашей у Васи
double B = 3.0; // Количество карандашей у Пети
double C = A + B; // Общее количество

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

Типом значения выражения

   A + B

будет тип тех переменных, которые составляют выражение, в данном случае - тип double.

Аналогичным будет ответ для разности величин (на сколько больше карандашей у Пети, чем у Васи?):

   double A = 2.0;                       // Количество карандашей у Васи
double B = 3.0; // Количество карандашей у Пети
double C = B - A; // Разность действительных чисел

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

   double C = B * A;                     // Умножение действ. чисел
double C = B / A; // Деление действительных чисел

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


Задача 2. Ученики выходят к доске для ответа. Вася выходил 2 раза, Петя выходил 3 раза. Сколько всего раз мальчики выходили к доске?

Решение. Обозначим количество ответов Васи как переменную Х, количество ответов Пети как переменную Y, а результат - через Z.

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

Решение задачи можно записать так:

   int X = 2;                           // Количество ответов Васи
int Y = 3; // Количество ответов Пети
int Z = X + Y; // Общее количество

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

   int X = 2;                           // Целое число
int Y = 3; // Целое число
int Z = Y - X; // Разность целых чисел
int Z = Y * X; // Произведение целых чисел
int Z = Y / X; // Частное от деления целых чисел

С переменными типа string возникает несколько иная ситуация:

Задача 3. На одном углу дома расположен гастроном с названием "Северный". На другом углу дома расположено заведение под названием "Парикмахерская". Требуется определить, что написано на доме.

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

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

 string W1  = "Северный";                     // Строка 1
string W2 = "Парикмахерская"; // Строка 2
string Ans = W1 + W2; // Сумма строк

Значением переменной Ans будет строка следующего вида:

СеверныйПарикмахерская

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

Любые другие арифметические операции с переменными строкового типа запрещены:

 string Ans= W1 - W2;                        // Не допускается
string Ans= W1 * W2; // Не допускается
string Ans= W1 / W2; // Не допускается

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


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

Рассмотрим несколько задач, касающихся приведения типов.

Задача 4. У Васи 2 карандаша, Петя выходил для ответа 3 раза. Сколько всего?

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

Задача 5. На одном углу дома расположен гастроном с названием "Северный", а у Васи 2 карандаша.:)

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

1. Сколько всего? 2. Что написано на доме?

Чтобы правильно решить две последние задачи в рамках языка MQL4, необходимо обратиться к правилам приведения типов значений. Для этого необходимо указать, как переменные разных типов представлены в памяти компьютера.

Типы данных int, bool, color, datetime и double относятся к числовому типу. Внутренним (машинным) представлением констант и переменных типов int, double, bool, color и datetime является число. При этом переменные типов int, bool, color и datetime представлены в памяти машины как целые числа, а переменные типа double - как числа двойной точности с плавающей точкой, т.е. действительные числа. Значением констант и переменных типа string является набор символов (рис. 16).

Значения типов int, bool, color и datetime представлены в памяти машины как целые числа. Значения типа double представлены в памяти машины как действительные числа. Значения типа string представлены в памяти машины как последовательность символов. Значения типов int, bool, color, datetime и double являются значениями числового типа. Значения типа string являются значениями символьного типа.


Рис. 16. Представление разных типов данных в памяти машины.

Выше указывалось, что значения переменных типов int, bool, color и datetime представлены в памяти машины как целые числа, а double - как действительные числа. Выясняя вопрос о типе выражения, в котором участвуют переменные разных типов, допустимо вести разговор только относительно трёх типов данных: int, double и string. Значения типов bool, color и datetime будут проявлять себя в выражении так же, как значения типа int.

Итак, каким будет значение выражения, если его составляют операнды разных типов? В языке MQL4 принято правило неявного приведения типов:

  • если в выражении содержатся операнды разных типов, то тип выражения преобразовывается в тип, имеющий более высокий приоритет; типы int, bool, color и datetime имеют одинаковый приоритет, тип double - более высокий приоритет, тип string - самый высокий приоритет;
  • если тип выражения справа от знака операции присвоения не совпадает с типом переменной слева от знака операции присвоения, то значение выражения приводится к типу переменной слева от знака операции присвоения; это называется приведением к целевому типу;
  • приведение значений типа string к любому другому целевому типу запрещено.

Вернёмся к Задаче 4. Возможны два варианта решения.

Вариант 4.1. Вычисляется результат целого типа:

   double A = 2.0;                      // Количество карандашей у Васи
int Y = 3; // Количество ответов Пети
int F = A + Y; // Общее количество

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

   A + Y

участвуют операнды двух типов данных: А - действительного типа double и Y - целого типа int.

В соответствии с правилом неявного преобразования типов значением этого выражения будет число действительного типа double. Обратите внимание: мы говорим только о типе выражения A+Y, но не о типе переменной F, стоящей слева от знака операции присвоения. Значением этого выражения будет действительное число 5.0. Для определения типа выражения A+Y применена первая часть правила неявного приведения типов.

После вычисления выражения A+Y исполняется операция присвоения. В данном случае также имеется несовпадение типов: тип выражения A+Y - double, а тип переменной F - int. В процессе выполнения операции присвоения: сначала тип выражения A+Y будет приведен к типу int (в соответствии с правилом вычисления целых чисел) и равен целому числу 5, а затем этот результат станет значением целой переменной F. Вычисления выполнены в соответствии со второй частью правила неявного приведения типов - приведения к целевому типу. Конечный результат вычислений и преобразований таков: значением целой переменной F является целое число 5.

Вариант 4.2. Аналогичная ситуация возникает и в случае, если искать результат в виде значения действительного типа:

   double A = 2.0;                   // Количество карандашей у Васи
int Y = 3; // Количество ответов Пети
double F = A + Y; // Общее количество

Представленный случай отличается от предыдущего тем, что целевой тип переменной F (слева от знака операции присвоения), в данном случае - тип double, совпадает с типом double выражения A+Y, поэтому целевого преобразования типов не происходит. Результатом вычислений (значением действительной переменной F) будет действительное число 5.0.

Теперь посмотрим, каким будет решение Задачи 5. При инициализации переменных вопросов не возникает:

   string W1 = "Северный";             // Строка 1
double A = 2; // Количество карандашей у Васи

Вариант 5.1. Допустимое решение задачи:

   string W1  = "Северный";           // Строка 1
double A = 2; // Количество карандашей у Васи
string Sum = W1 + A; // Неявное преобразование справа

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

Северный2.00000000

Вариант 5.2. Такое решение является ошибочным:

   string W1 = "Северный";             // Строка 1
double A = 2; // Количество карандашей у Васи
double Sum = W1 + A; // Это недопустимо

В данном случае нарушен запрет приведения к целевому типу значения типа string. Типом значения выражения W1+A, как и в предыдущем примере, является тип string. При исполнении операции присвоения должно быть произведено целевое приведение типа. Однако, в соответствии с правилом, целевое понижение типа string запрещено. Это - ошибка, которая будет обнаружена редактором MetaEditor на этапе создания программы (при компиляции).

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

Особенности вычисления значений целых чисел


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

   int X = 2;                        // Первая целая переменная
int Y = 3; // Вторая целая переменная

и при этом:

   int Z = X + Y;                    // Операция сложения

то не возникает никаких проблем с вычислением значения переменной Z: 2 + 3 = 5

Аналогично, если выполняется операция умножения:

   int Z = X * Y;                    // Операция умножения

то результат также легко предсказуем: 2 * 3 = 6

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

   int Z = X / Y;                    // Операция деления

Нетрудно написать 2 / 3. Но это - не целое число. Каким же будет значение выражения X/Y и переменной Z?

Правило вычисления значений целых чисел состоит в том, что дробная часть отбрасывается.

В этом примере справа от знака равенства находится выражение, содержащее только целые числа, т.е. в данном случае приведение типов не происходит. Это значит, что типом выражения X/Y является тип int. Поэтому результатом вычисления целого значения выражения X/Y (= 2/3) будет 0 (ноль). В результате это значение (ноль) будет присвоено переменной Z.

Соответственно, при других значениях переменных X и Y результат будет другим. Например, если:

   int X = 7;                      // Значение переменной целого типа
int Y = 3; // Значение переменной целого типа
int Z = X / Y; // Операция деления

то значение 7 / 3 выражения X / Y и переменной Z будет равно 2 (двум).

Порядок вычисления выражений


Правило вычисления выражений состоит в следующем:

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

Рассмотрим порядок вычисления выражения в следующем примере:

   Y = 2.0*( 3*X/Z - N) + D;                          // Пример выражения
Выражение в правой части от знака равенства состоит из двух слагаемых: 2.0*( 3*X/Z - N) и D. Слагаемое 2.0*( 3*X/Z - N) состоит из двух множителей, а именно, 2 и (3*X/Z - N). Выражение в скобках 3*X/Z - N, в свою очередь, состоит из двух слагаемых, причём слагаемое 3*X/Z состоит из трёх множителей, а именно, 3, X и Z.

Для вычисления значения выражения в правой части от знака равенства сначала будет вычислено значение выражения 3*X/Z. Это выражение содержит две операции (умножение и деление), имеющих одинаковый приоритет, поэтому вычисление выражения будет выполняться слева направо. Сначала будет вычислено значение выражения 3*X, причём тип этого значения будет таким, какой тип имеет переменная X. Затем будет вычислено значение выражения 3*X/Z, его тип также будет вычислен на основании правил приведения типов. После этого программа вычислит значение и тип выражения 3*X/Z - N, потом выражения 2.0*( 3*X/Z - N) и в последнюю очередь - значение и тип всего выражения 2.0*( 3*X/Z - N) + D.

Легко заметить, что порядок вычисления выражений в программе аналогичен порядку подобных вычислений в математике, но отличается вычислением типов значений промежуточных выражений, что существенно влияет на конечный результат вычислений. В частности (в отличие от правил, принятых в математике), немаловажное значение имеет порядок операндов в выражении. Чтобы продемонстрировать это, рассмотрим небольшой пример.
Задача 6. Вычислить значения выражений А/В*С и А*С/В для целых чисел А, В и С.

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

   int A = 3;                          // Значение целого типа
int B = 5; // Значение целого типа
int C = 6; // Значение целого типа
int Res_1 = A/B*C; // Результат 0 (ноль)
int Res_2 = A*C/B; // Результат 3 (три)

Проследим процесс вычисления выражения A/B*C:

1. Сначала (слева направо) будет вычислено значение выражения A/B. В соответствии с указанными выше правилами значением выражения (3/5) будет целое значение 0 (ноль).

2. Вычисление выражения 0*С (ноль умножить на С). Результат - целое значение 0 (ноль).

3. Общий результат (значение переменной Res_1) - целое значение 0 (ноль).

Теперь посмотрим, как будут развиваться события при вычислении выражения A*C/B.

1. Вычисление A*C. Значением этого выражения будет целое число 18 (3*6=18).

2. Вычисление выражения 18/B. Ответ очевиден: (18/5) после отбрасывания дробной части получится целое число 3(три).

3. Общий результат (значение переменной Res_2) - целое значение 3 (три).

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

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