Операции и выражения
Чтобы понять, какое значение имеют в языке 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.
Двоичное представление x сдвигается вправо на y разрядов. Сдвиг вправо логический, то есть освобождающиеся слева разряды будут заполняться нулями.
Двоичное представление x сдвигается влево на y разрядов; освобождающиеся справа разряды заполняются нулями.
Побитовая операция И двоичных представлений x и y. Значение выражения содержит 1 (ИСТИНА) во всех разрядах, в которых и x, и y содержат не ноль; и 0 (ЛОЖЬ) во всех
остальных разрядах.
Побитовая операция ИЛИ двоичных представлений x и y. Значение выражения содержит 1 во всех разрядах, в которых x или y не содержат 0, и 0 - во всех остальных разрядах.
Побитовая операция "исключающее ИЛИ" (eXclusive OR) двоичных представлений x и y. Значение выражения содержит 1 в тех разрядах, в которых x и y имеют разные двоичные значения, и 0 - во всех остальных разрядах.
Операция запятая
Выражения, разделённые запятыми, вычисляются слева направо. Все побочные эффекты вычисления левого выражения могут возникать до вычисления правого выражения. Тип
и значение результата совпадают с типом и значением правого выражения.
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 = "Северный";
string W2 = "Парикмахерская";
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 = "Северный";
double A = 2;
Вариант 5.1. Допустимое решение задачи:
string W1 = "Северный";
double A = 2;
string Sum = W1 + A;
Здесь в правой части складываются значения двух переменных, одна из которых имеет тип string, а другая - тип double. В соответствии с правилом неявного приведения типов значение переменной А сначала будет приведено к типу string (т.к. у этого типа более высокий приоритет), а после этого произойдёт сложение (конкатенация) однотипных значений. Типом вычисленного значения в правой части от знака операции присвоения будет тип string. На следующем этапе это значение будет присвоено строковой переменной Sum. В результате значением переменной Sum будет следующая строка:
Вариант 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;
int Res_2 = A*C/B;
Проследим процесс вычисления выражения 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 (три).
В данном примере рассмотрен небольшой фрагмент программы, в котором вычисляются значения переменных целого типа. Если эти переменные заменить константами, но использовать при этом те же их значения, то конечный результат от этого не изменится. При вычислении выражений, в которых используются целые числа, необходимо проявлять повышенное внимание к содержанию программных строк. В противном случае в программе может возникнуть ошибка, которую впоследствии (особенно в больших программах) очень трудно обнаружить. При вычислениях, в которых участвуют только действительные числа, подобная проблема не возникает. Но если в сложном выражении употребляются операнды разных типов, то конечный результат может полностью зависеть от случайно составленного фрагмента, где происходит деление целых чисел.
В разделе Операторы рассматривается понятие и общие свойства операторов, в главе Операторы раскрываются собственные свойства каждого оператора.