Выбор if
Оператор if имеет несколько форм. В простейшем случае он выполняет зависимую инструкцию, если истинно заданное условие:
if ( условие )
|
Если условие ложно, инструкция пропускается, и сразу же происходит переход к оставшейся части алгоритма (последующим инструкциям, если они есть).
Инструкцией может быть простая или составная инструкция. Условие представляет собой выражение логического или приводимого к нему типа.
Вторая форма позволяет указать две ветки действий: не только для истинного условия (инструкция_А), но и для ложного (инструкция_Б):
if ( условие )
|
Какая бы из управляемых инструкций ни выполнилась, алгоритм затем продолжится, следуя инструкциям ниже оператора if/else.
Например, скрипт может придерживаться разной стратегии в зависимости от таймфрейма графика, на котором он размещен. Для этой цели достаточно анализировать значение, возвращаемое встроенной функцией Period. Значение имеет тип перечисления ENUM_TIMEFRAMES. Если оно меньше PERIOD_D1, подразумеваем краткосрочную торговлю, а в противном случае — долгосрочную (StmtSelectionIf.mq5).
if(Period() < PERIOD_D1)
|
В качестве инструкции в ветке else допускается указывать следующий оператор if, и таким образом нанизывать их в цепочку последовательных проверок. Например, следующий фрагмент подсчитывает в строке количество заглавных букв и символов пунктуации (точнее, не являющихся буквами латинского алфавита).
string s = "Hello, " + Symbol();
|
Цикл организован по всем символам строки (нумерация начинается с 0), функция StringLen возвращает длину строки. Первый if проверяет каждый символ на принадлежность диапазону от 'A' до 'Z' и в случае успеха увеличивает на 1 счетчик заглавных букв capital. Если символ не попадает в этот диапазон, запускается второй if, в котором условие на принадлежность диапазону строчных букв (s[i] >= 'a' && s[i] <= 'z') подвергается инверсии с помощью '!'. Иными словами, условие означает, что символ не лежит в заданном диапазоне. С учетом двух последовательных проверок, если символ не заглавная буква (else) и не строчная буква (второй if), можно заключить, что символ не является буквой латинского алфавита — в этом случае увеличиваем счетчик punctuation.
Те же проверки можно было бы записать в более развернутом виде и использовать блоки '{...}' для наглядности.
int capital = 0, small = 0, punctuation = 0;
|
Применение фигурных скобок помогает избежать логических ошибок, связанных с тем, что программисты в их отсутствии ориентируются на отступы в коде. В частности, наиболее распространена проблема под названием "висящий" else.
Когда операторы if вложены друг в друга, иногда получается, что ветвей else меньше, чем if. Вот один пример:
factor = 0.0;
|
Отступы указывают, какую логику подразумевал программист: factor должен стать равным +1 при mode больше 20, остаться равным 0 при mode от 10 до 20, и измениться на -1 в остальных случаях (mode <= 10). Но сработает ли код именно так?
В MQL5 каждый else считается относящимся к ближайшему предыдущему if (у которого нет else). В результате компилятор воспримет инструкции следующим образом:
factor = 0.0;
|
Значит, factor будет равен -1 в диапазоне mode от 10 до 20, и 0 при mode <= 10. Самое интересное, что программа не выдает никаких формальных ошибок, ни при компиляции, ни при исполнении. И все же она работает неверно.
Исключить такие трудноуловимые логические проблемы позволяет расстановка фигурных скобок.
if(mode > 10)
|
Для единообразия оформления желательно использовать блоки во всех ветвях инструкции, если в ней уже потребовался хотя бы один блок.
При использовании в качестве условия проверки на равенство следует учесть возможность опечатки, когда вместо двух символов '==' написан один '='. При этом сравнение превращается в присваивание, и присвоенное значение анализируется в качестве логического условия. Например,
// должно было быть x == y + 1, что дало бы false и пропустило if
|
Компилятор в таких случаях выдает предупреждение "выражение не является логическим" ("expression not boolean").