Выбор if

Оператор if имеет несколько форм. В простейшем случае он выполняет зависимую инструкцию, если истинно заданное условие:

if ( условие )
   инструкция

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

Инструкцией может быть простая или составная инструкция. Условие представляет собой выражение логического или приводимого к нему типа.

Вторая форма позволяет указать две ветки действий: не только для истинного условия (инструкция_А), но и для ложного (инструкция_Б):

if ( условие )
   инструкция_А
else
   инструкция_Б

Какая бы из управляемых инструкций ни выполнилась, алгоритм затем продолжится, следуя инструкциям ниже оператора if/else.

Например, скрипт может придерживаться разной стратегии в зависимости от таймфрейма графика, на котором он размещен. Для этой цели достаточно анализировать значение, возвращаемое встроенной функцией Period. Значение имеет тип перечисления ENUM_TIMEFRAMES. Если оно меньше PERIOD_D1, подразумеваем краткосрочную торговлю, а в противном случае — долгосрочную (StmtSelectionIf.mq5).

if(Period() < PERIOD_D1)
{
   Print("Intraday");
}
else
{
   Print("Interday");
}

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

string s = "Hello, " + Symbol();
int capital = 0punctuation = 0;
for(int i = 0i < StringLen(s); ++i)
{
   if(s[i] >= 'A' && s[i] <= 'Z')
      ++capital;
   else if(!(s[i] >= 'a' && s[i] <= 'z'))
      ++punctuation;
      
}
Print(capital" "punctuation);

Цикл организован по всем символам строки (нумерация начинается с 0), функция StringLen возвращает длину строки. Первый if проверяет каждый символ на принадлежность диапазону от 'A' до 'Z' и в случае успеха увеличивает на 1 счетчик заглавных букв capital. Если символ не попадает в этот диапазон, запускается второй if, в котором условие на принадлежность диапазону строчных букв (s[i] >= 'a' && s[i] <= 'z') подвергается инверсии с помощью '!'. Иными словами, условие означает, что символ не лежит в заданном диапазоне. С учетом двух последовательных проверок, если символ не заглавная буква (else) и не строчная буква (второй if), можно заключить, что символ не является буквой латинского алфавита — в этом случае увеличиваем счетчик punctuation.

Те же проверки можно было бы записать в более развернутом виде и использовать блоки '{...}' для наглядности.

int capital = 0small = 0punctuation = 0;
for(int i = 0i < StringLen(s); ++i)
{
   if(s[i] >= 'A' && s[i] <= 'Z')
   {
      ++capital;
   }
   else
   {
      if(s[i] >= 'a' && s[i] <= 'z')
      {
         ++small;
      }
      else
      {
         ++punctuation;
      }
   }
}

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

Когда операторы if вложены друг в друга, иногда получается, что ветвей else меньше, чем if. Вот один пример:

factor = 0.0;
if(mode > 10)
   if(mode > 20)
      factor = +1.0;
else
   factor = -1.0;

Отступы указывают, какую логику подразумевал программист: factor должен стать равным +1 при mode больше 20, остаться равным 0 при mode от 10 до 20, и измениться на -1 в остальных случаях (mode <= 10). Но сработает ли код именно так?

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

factor = 0.0;
if(mode > 10)
   if(mode > 20)
      factor = +1.0;
   else
      factor = -1.0;

Значит, factor будет равен -1 в диапазоне mode от 10 до 20, и 0 при mode <= 10. Самое интересное, что программа не выдает никаких формальных ошибок, ни при компиляции, ни при исполнении. И все же она работает неверно.

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

if(mode > 10)
{
   if(mode > 20)
      factor = +1.0;
}
else
   factor = -1.0;

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

При использовании в качестве условия проверки на равенство следует учесть возможность опечатки, когда вместо двух символов '==' написан один '='. При этом сравнение превращается в присваивание, и присвоенное значение анализируется в качестве логического условия. Например,

// должно было быть x == y + 1, что дало бы false и пропустило if
if(x = y + 1// warning: expression not boolean
{
   // присвоило x = 5 и трактовало x как true, потому if выполняется
}

Компилятор в таких случаях выдает предупреждение "выражение не является логическим" ("expression not boolean").