Не думаю, что профи найдут для себя в этой статье нечто новое или интересное, но Капитан Очевидность продолжает вещать.
Первое, что пришлось проверить - не инициализированные переменные:
На код вроде:
А = 1;
Если (А=1) и (Б=1) Тогда
Сообщить("Ок!");
КонецЕсли;
Выругался сам "компилятор" при попытке сохранить обработку. Логично, так как переменная Б нигде не определена. Но, если обмануть синтаксический контроль следующим образом:Если (А=1) и (Б=1) Тогда
Сообщить("Ок!");
КонецЕсли;
А = 1;
Если 0=1 Тогда // заведомо ложное условие
Б=1; // никогда не выполняющийся код
КонецЕсли;
Если (А=1) и (Б=1) Тогда
Сообщить("Ок!");
КонецЕсли;
Если 0=1 Тогда // заведомо ложное условие
Б=1; // никогда не выполняющийся код
КонецЕсли;
Если (А=1) и (Б=1) Тогда
Сообщить("Ок!");
КонецЕсли;
В этом случае при сохранении ошибки синтаксиса не будет, а при запуске надписи "Ок!" не появляется. По логике следования кода мы нигде не определяли переменную Б, но код успешно выполняется.
Что говорит нам отладчик?
Обратите внимание, что в самом начале, когда ни одна переменная еще не определена память под них уже зарезервирована и им назначен тип "Неопределено", в отличии от переменной В, которая вообще не упоминается в процедуре.
Сделаем пару шагов:
Теперь все очевидно. Условие совершенно корректно и дает на выходе ложь, так как единица не равна "Неопределено" и вообще разных типов.
Итак, предварительный вывод:
Под переменные, которые могут быть инициализированы, внутри своей области видимости память выделяется в самом начале и они неявно инициализируются типом "Неопределено".
Чисто теоретически следующий код мог бы работать:
А = 1;
Если (А=1) и (Б=1) Тогда
Сообщить("Ок!");
КонецЕсли;
Если 0=1 Тогда
Б=1;
КонецЕсли;
Если (А=1) и (Б=1) Тогда
Сообщить("Ок!");
КонецЕсли;
Если 0=1 Тогда
Б=1;
КонецЕсли;
Но нет. Не проходит синтаксический контроль, что вообщем-то правильно.
Чем же эта информация может быть полезна? Например тем, что если в процессе отладки у вас какая-то переменная имеет тип "Неопределено", то возможно она была инициализирована неявно и вам стоит проверить свои условия. Хотя, вносить инициализацию переменной в условие вообще-то плохой тон. Да, да, как раз из-за вышеизложенного.
Однако, неявная инициализация это не совсем то, что я собирался выяснить с самого начала и я вернулся к условиям.
Следующий код работает как положено и выдает правильный результат:
Товар = Справочники.Номенклатура.НайтиПоКоду("00000026");
Если (ТипЗнч(Товар) <> Тип("Неопределено")) и (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
В описании функции "НайтиПоКоду" написано, что:Если (ТипЗнч(Товар) <> Тип("Неопределено")) и (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
Если не существует ни одного элемента с требуемым кодом, то будет возвращена пустая ссылка.Если честно, я не смог сразу добиться от функции возврата значения "Неопределено" и потому поступил проще:
Если код не задан, то будет возвращено Неопределено.
//Товар = Справочники.Номенклатура.НайтиПоКоду("00000026");
Товар = Неопределено;
Если (ТипЗнч(Товар) <> Тип("Неопределено")) и (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
Товар = Неопределено;
Если (ТипЗнч(Товар) <> Тип("Неопределено")) и (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
Запускаем - бинго! Все работает, несмотря на то, что в составном условии есть обращение к реквизиту переменной "Товар", к "Наименованию", которого у типа "Неопределено" быть не может.
Для проверки попробуем еще два вида условия:
1.
Если (ТипЗнч(Товар) <> Тип("Неопределено")) или (Товар.Наименование = "Тест") Тогда
2. Если (Товар.Наименование = "Тест") и (ТипЗнч(Товар) <> Тип("Неопределено")) Тогда
В первом случае мы поменяли "и" на "или" в условии, во-втором поменяли местами части условия.
В обоих случая при попытке выполнения мы получили сообщение об ошибке. Почему? Потому, что "компилятор" вычисляя выражение условия идет от начала к концу по-очереди вычисляя каждое вложенное выражение (по правилам арифметики, ага) и как только становится очевидно, что все выражение имеет определнный результат (истина/ложь) вычисление прекращается.
В нашем случае, когда мы сначала проверяли не является ли переменная "Товар" неопределенной и получали "ложь" смысл проверки остальных операндов утрачивался, так как в операции "И" достаточно одному операнду быть ложным, чтобы остальные можно было не проверять - все-равно конечный результат будет "ложь".
В первом случае, когда мы заменили "и" на "или" необходимость проверять все выражение возникла снова так как в определении "или" говорится, что истинным должен быть "хотя бы один" из операндов. Но пытаясь вычислить второй операнд мы сталкивались с ошибкой о которой я писал выше.
Во-втором варианте, результат выполнения так же изменился, несмотря на то, что операция "и" коммутативная. Теперь проверка начинала выполняться с первого, заведомо провального операнда.
Такое поведение, на мой взгляд оправдано и удобно. Сравните, например с похожим кодом в платформе 7.7:
//Товар = СоздатьОбъект("Справочник.Номенклатура");
//Товар.НайтиПоКоду(" 52200");
Товар = ПолучитьПустоеЗначение();
Если (ПустоеЗначение(Товар) = 0) и (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
В отличии от 8.1 он не будет работать, так как проверяются все операнды операции "и", а второй у нас будет выдавать ошибку.//Товар.НайтиПоКоду(" 52200");
Товар = ПолучитьПустоеЗначение();
Если (ПустоеЗначение(Товар) = 0) и (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
Для платформы 7.7 пришлось бы применить следующее:
Если (ПустоеЗначение(Товар) = 0) Тогда
Если (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
КонецЕсли;
Что на мой взгляд является неоправданным (хоть и небольшим) усложнением структуры кода.Если (Товар.Наименование = "Тест") Тогда
Сообщить("Ок!");
КонецЕсли;
КонецЕсли;
Итак, теперь вы можете смело записывать в одно условие конструкции, которые могут вызывать ошибки, в случае расхождения типов, главное не забывайте предварять их проверками на соответствие.
Спасибо за внимание и хорошего вам кода!
2 комментария:
Забавно!
Получается 8 стала более гибкой в операции "И". Код с А и Б отрабатывает и в 7 версии. :) Только вот к переменной В я так и не смог добраться ;)
Странно, но в посте нет ссылки на т.н. "Ленивые вычисления".
Исправляюсь.
Отправить комментарий
Примечание. Отправлять комментарии могут только участники этого блога.