четверг, 26 августа 2010 г.

Условия и переменные (заметки Кэпа)

Решил я как-то проверить каким образом вычисляются составные условия в 1С 8.1
Не думаю, что профи найдут для себя в этой статье нечто новое или интересное, но Капитан Очевидность продолжает вещать.
Первое, что пришлось проверить - не инициализированные переменные:

На код вроде:
    А = 1;

    Если (
А=1) и (Б=1) Тогда
       
Сообщить("Ок!");
    КонецЕсли;

Выругался сам "компилятор" при попытке сохранить обработку. Логично, так как переменная Б нигде не определена. Но, если обмануть синтаксический контроль следующим образом:
    А = 1;

    Если
0=1 Тогда  // заведомо ложное условие
       
Б=1;        // никогда не выполняющийся код
   
КонецЕсли;

    Если (
А=1) и (Б=1) Тогда
       
Сообщить("Ок!");
    КонецЕсли;

В этом случае при сохранении ошибки синтаксиса не будет, а при запуске надписи "Ок!" не появляется. По логике следования кода мы нигде не определяли переменную Б, но код успешно выполняется.
Что говорит нам отладчик?
Обратите внимание, что в самом начале, когда ни одна переменная еще не определена память под них уже зарезервирована и им назначен тип "Неопределено", в отличии от переменной В, которая вообще не упоминается в процедуре.
Сделаем пару шагов:

Теперь все очевидно. Условие совершенно корректно и дает на выходе ложь, так как единица не равна "Неопределено" и вообще разных типов.

Итак, предварительный вывод:
Под переменные, которые могут быть инициализированы, внутри своей области видимости память выделяется в самом начале и они неявно инициализируются типом "Неопределено".

Чисто теоретически следующий код мог бы работать:
    А = 1;

    Если (
А=1) и (Б=1) Тогда
       
Сообщить("Ок!");
    КонецЕсли;

    Если
0=1 Тогда
       
Б=1;
    КонецЕсли;

Но нет. Не проходит синтаксический контроль, что вообщем-то правильно.

Чем же эта информация может быть полезна? Например тем, что если в процессе отладки у вас какая-то переменная имеет тип "Неопределено", то возможно она была инициализирована неявно и вам стоит проверить свои условия. Хотя, вносить инициализацию переменной в условие вообще-то плохой тон. Да, да, как раз из-за вышеизложенного.

Однако, неявная инициализация это не совсем то, что я собирался выяснить с самого начала и я вернулся к условиям.

Следующий код работает как положено и выдает правильный результат:
    Товар = Справочники.Номенклатура.НайтиПоКоду("00000026");

    Если  (
ТипЗнч(Товар) <> Тип("Неопределено")) и (Товар.Наименование = "Тест") Тогда
       
Сообщить("Ок!");
    КонецЕсли;

В описании функции "НайтиПоКоду" написано, что:
Если не существует ни одного элемента с требуемым кодом, то будет возвращена пустая ссылка.
Если код не задан, то будет возвращено Неопределено.
Если честно, я не смог сразу добиться от функции возврата значения "Неопределено" и потому поступил проще:
    //Товар = Справочники.Номенклатура.НайтиПоКоду("00000026");
   
Товар = Неопределено;

    Если  (
ТипЗнч(Товар) <> Тип("Неопределено")) и (Товар.Наименование = "Тест") Тогда
       
Сообщить("Ок!");
    КонецЕсли;

Запускаем - бинго! Все работает, несмотря на то, что в составном условии есть обращение к реквизиту переменной "Товар", к "Наименованию", которого у типа "Неопределено" быть не может.
Для проверки попробуем еще два вида условия:
1.
    Если  (ТипЗнч(Товар) <> Тип("Неопределено")) или (Товар.Наименование = "Тест") Тогда
2.
    Если (Товар.Наименование = "Тест") и (ТипЗнч(Товар) <> Тип("Неопределено")) Тогда

В первом случае мы поменяли "и" на "или" в условии, во-втором поменяли местами части условия.

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

В нашем случае, когда мы сначала проверяли не является ли переменная "Товар" неопределенной и получали "ложь" смысл проверки остальных операндов утрачивался, так как в операции "И" достаточно одному операнду быть ложным, чтобы остальные можно было не проверять - все-равно конечный результат будет "ложь".

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

Во-втором варианте, результат выполнения так же изменился, несмотря на то, что операция "и" коммутативная. Теперь проверка начинала выполняться с первого, заведомо провального операнда.

Такое поведение, на мой взгляд оправдано и удобно. Сравните, например с похожим кодом в платформе 7.7:
    //Товар = СоздатьОбъект("Справочник.Номенклатура");
    //Товар.НайтиПоКоду(" 52200");
   
Товар = ПолучитьПустоеЗначение();

    Если (ПустоеЗначение(
Товар) = 0) и (Товар.Наименование = "Тест") Тогда
        Сообщить(
"Ок!");
    КонецЕсли;

В отличии от 8.1 он не будет работать, так как проверяются все операнды операции "и", а второй у нас будет выдавать ошибку.
 

Для платформы 7.7 пришлось бы применить следующее:
    Если (ПустоеЗначение(Товар) = 0) Тогда
        Если (
Товар.Наименование = "Тест") Тогда
            Сообщить(
"Ок!");
        КонецЕсли;
    КонецЕсли;

Что на мой взгляд является неоправданным (хоть и небольшим) усложнением структуры кода.

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

Спасибо за внимание и хорошего вам кода!

среда, 18 августа 2010 г.

Сила слова

На секлабе очередная статья про ужасного хакера подозреваемого во взломе банка. Статья уныла, но каменты жгут:

Гость: гик 17.08.2010 10:00:06

Уж сколько раз твердили миру, что если подразумевается негативный смысл, то надо говорить "кракер" или "крекер", или "крэкер".

Как всегда всё опошлили, на этот благородное слово "хакер", теперь все думают, что "хакер" - это плохо.
Гость: 23404 17.08.2010 11:31:47
если подразумевается негативный смысл, то нужно говорить: "кракен".

вторник, 17 августа 2010 г.

it-jam 2010

Очередной IT-Jam, на этот раз в Харькове.

среда, 4 августа 2010 г.

Сам я считаю, что дефектом многих существующих учебников по технологиям программирования (это относится не только к COM, но и вообще к любой технологии излагаемой в современных учебниках) является то, что они начинаются "сверху" - "вызовите Wizard, отметьте в нём... поставьте... Wizard сгенерировал вам код...". Но в учебнике очень невнятно объясняется, почему Wizard сгенерировал именно такой код! Что означают те или иные макросы по ходу текста, как выглядит и сам протокол к которому Wizard строит реализацию. Словом, современные учебники пытаются обучить сложению используя в качестве наглядного пособия калькулятор - а как калькулятор выполняет сложение учебник не объясняет. С моей точки зрения это - тяжелейший порок, поскольку квалификация программиста определяется прежде всего пониманием философии, основ, концепций. Писать реально работающие программы, конечно, нужно с применением соответствующих инструментов. Писать вручную - анахронизм, часто выдающий дремучесть программиста. Но и владея распрекрасным инструментом всё равно нужно знать, как работает механизм, потому, что в какой-то момент времени может обнаружиться ошибка в самом инструменте, произойти несчастливое стечение обстоятельств и параметров, а тогда программист должен суметь, пользуясь своим общим знанием, отыскать то самое место, в котором возникает ошибка и исправить её.
(с) Михаил Безверхов, "Технология COM"