Вначале изложу немного теории относительно вычисления логических выражений. То, что будет написано в первом разделе, есть в любом самоучителе по любому современному языку программирования. И если вам покажется, что я описываю “элементарные вещи, которые и так все знают”, то можете смело пропустить первый раздел.
Любое выражение, написанное в коде вычисляется слева направо и согласно приоритету операторов. При вычислении логических выражений приоритет операторов следующий (в порядке убывания):
-
Операции сравнения (=, >, <, <>)
-
НЕ
-
И
-
ИЛИ
Три логических оператора: НЕ И ИЛИ для удобства понимания можно представить как арифметические: унарный минус, умножить, плюс (соответственно). Например, если мы хотим посчитать сумму покупок: 5 яблок по 12 рублей, 3 килограмма гречки по 50 рублей, 2 рулона туалетной бумаги по 25 рублей - запишем в калькулятор все это одной строкой в естественном порядке и без всяких скобок: 5 * 12 + 3 * 50 + 2 * 25. Ведь еще в школе нас научили, что сначала множим, потом плюсуем. Так и здесь мы сначала получим сумму каждой позиции, а затем общую сумму.
Аналогично работает приоритет в логическом выражении. Сначала будут вычислены все И, затем все ИЛИ. Раньше всех применится НЕ, оно инвертирует результат выражения справа от себя, аналогично унарному минусу: -5 - это как 5 только в другую сторону от нуля).
Операторы И и ИЛИ являются бинарными, то есть сравнивают 2 выражения слева и справа от себя и возвращают результат сравнения: Истина или Ложь. Цепочка операторов обрабатывается слева направо. При этом работает сокращенный цикл вычисления логических выражений. Такую фишку добавили в платформе 1с 8. В учебнике по javascript я вычитал замечательную аллегорию на этот механизм:
"Оператор И спотыкается на лжи, а оператор ИЛИ спотыкается на правде”.
Иными словами, когда у нас цепочка из нескольких И, то они будут вычисляться до тех пор пока следующее И не вернет ложь. Все И, что правее от этого выражения вычисляться не будут. С другой стороны, когда у нас цепочка из нескольких ИЛИ, они будут вычисляться до первой истины. Платформа справедливо считает, что если в куче И мы наткнулись на ложь, то все выражение ложное и незачем тратить ресурсы на его вычисление. Эту особенность мы можем использовать для наших нужд.
А теперь практический пример.
Рассмотрим задачу загрузки элемента справочника “Номенклатура” из внешнего источника. Алгоритм действия примерно такой:
-
Создать карточку номенклатуры
-
Создать единицы измерения
-
Записать базовую единицу измерения в карточку номенклатуры
-
Загрузить штрихкоды номенклатуры
-
Загрузить цены номенклатуры.
Простой линейный алгоритм. При этом выполняться он должен именно в этой последовательности. И кроме того должен прерываться, если очередной этап выдал сбой. При этом желательно в конце вернуть общий результат выполнения операции. Мы можем оформить каждый этап в отдельную функцию, которая в зависимости от успешности возвращает Истину или Ложь, а затем написать следующий код:
Если НЕ СоздатьКарточкуНоменклатуры() Тогда
Возврат Ложь;
КонецЕсли;
Если НЕ СоздатьЕдиницыИзмерения() Тогда
Возврат Ложь;
КонецЕсли;
Если НЕ ЗаписатьБазовуюЕдиницу() Тогда
Возврат Ложь;
КонецЕсли;
Если НЕ ЗагрузитьШтрихкоды() Тогда
Возврат Ложь;
КонецЕсли;
Если НЕ ЗагрузитьЦены() Тогда
Возврат Ложь;
КонецЕсли;
Возврат Истина;
Вроде получилось наглядно и понятно. Но смущает часто повторяющиеся блоки Если Тогда КонецЕсли. Теперь вспомним теорию: как себя ведет цепочка И при вычислении, и перепишем код следующим образом:
Возврат СоздатьКарточкуНоменклатуры()
И СоздатьЕдиницыИзмерения()
И ЗаписатьБазовуюЕдиницуИзмерения()
И ЗагрузитьШтрикоды()
И ЗагрузитьЦены();
Код получился значительно короче, при этом сохранил прежний функционал. Выполнение будет происходить в том же порядке и прервется на той функции, которая вернет Ложь.
Теперь немного усложним задачу. У нас есть разная номенклатура с характеристиками и без, весовая и штучная. Для номенклатуры с характеристиками надо загружать характеристики номенклатуры, для весовой - коды весового товара. В этом случае мы из внешнего источника получаем булевы признаки: ИспользуютсяХарактеристики и ВесовойТовар. Теперь самая длинная цепочка алгоритма должна выглядеть так:
-
Создать карточку номенклатуры
-
Создать единицы измерения
-
Записать базовую единицу измерения
-
Создать характеристики
-
Загрузить коды весового товара
-
Загрузить штрикоды
-
Загрузить цены
Алгоритм перестал быть линейным. Набор операций теперь зависит от флагов: ИспользуютсяХарактеристики и ВесовойТовар. Попробуем записать этот алгоритм в одну строчку:
Возврат СоздатьКатрочкуНоменклатуры()
И СоздатьЕдиницыИзмерения()
И ЗаписатьБазовуюЕдиницуИзмерения()
И (НЕ ИспользуютяХарактеристики ИЛИ СоздатьХарактеристики())
И (НЕ ВесовойТовар ИЛИ ЗагрузитьКодыВесовогоТовара())
И ЗагрузитьШтрихкоды()
И ЗагрузитьЦены();
Для замены конструкции:
Если ИспользуютсяХарактеристики Тогда
Если НЕ СоздатьХарактеристики() Тогда
Возврат Ложь;
КонецЕсли;
КонецЕсли;
Мы использовали конструкцию с ИЛИ. Поскольку мы помним, что ИЛИ запинается на правде, значение флага инвертировали с помощью НЕ. Такое выражение остановится на вычислении значения флага, если он не установлен либо пройдет дальше и вычислит правое выражение и создаст характеристики для товара. Поскольку приоритет ИЛИ ниже, чем И, то эту конструкцию мы заключили в скобки.
Чтобы избавиться от круглых скобок, можно перевернуть все выражение и заменить И на ИЛИ так чтобы вложенные выражения содержали конструкцию И и выполнялись согласно приоритету первыми:
Результат = НЕ СоздатьКарточкуНоменклатуры()
ИЛИ НЕ СоздатьЕдиницыИзмерения()
ИЛИ НЕ ЗаписатьБазовуюЕдиницуИзмерения()
ИЛИ ИспользуютсяХарактеристики
И НЕ СоздатьХарактеристики()
ИЛИ ВесовойТовар
И НЕ ЗагрузитьКодыВесовогоТовара()
ИЛИ НЕ ЗагрузитьШтрихкоды()
ИЛИ НЕ ЗагрузитьЦены();
Возврат НЕ Результат;
Поскольку ИЛИ запинается на правде, то для сохранения функционала мы инвертировали каждый вызов операции. Вложенное условие с конструкцией И выполняется в первую очередь, поэтому круглые скобки уже не нужны. Поскольку И запинается на Лжи, то при снятом флаге ИспользуютсяХарактеристики выражение НЕ СоздатьХарактеристики() вычисляться не будет, и соответственно характеристики не создадутся. В таком виде вложенное выражение читается естественнее. Все полученное выражение возвращает Ложь в случае успешного выполнения всех операций, поэтому значение результата в конце инвертируется. Кстати дополнительный отступ перед И визуально отделяет вложенное условие, что в условии отсутствия круглых скобок облегчает анализ кода.