Язык ST все еще поддерживает оператор GOTO. Не прямо так гоу-ту, а как в ассемблере — JMP куда-то там на метку в коде. Выглядит это так:
label_infinitum: (* полезный код *) JMP myLabel_4321; (* бесполезный кот *) myLabel_4321: // это метка для перехода (* еще полезный код *) JMP label_infinitum;
Избегайте бесконечно-вечных циклов.
CONTINUE
CONTINUE относится к расширению языка, а EXIT нет. Если вы не знали, то EXIT немедленно прекращает выполнение текущего цикла и выходит из него (текущая итерации FOR, WHILE или REPEAT не будет выполнена до конца). CONTINUE же прекращает текущую итерацию и немедленно переходит к началу цикла, выполнять следующую итерацию:
FOR i := 0 TO 100 BY 2 DO IF i MOD 3 = 0 THEN CONTINUE; // пропускаем инкремент переменной a и переходим к следующей итерации END_IF a := a + 1; END_FOR
Осторожнее с вложенными циклами: EXIT выходит только из одного вложения. Для выхода из нескольких вложенных циклов, помогает команда JMP.
FOR j := 0 TO 100 DO FOR i := 0 TO 100 DO IF ??? THEN EXIT; // завершит цикл FOR i ... ELSE JMP finita_incantata; END_IF END_FOR // EXIT приведет сюда END_FOR finita_incantata:
Оператор MOD находит остаток от деления, как оператор % в C++.
Оператор установки S=
Запись A S= X; аналогична:
IF X THEN A := TRUE; END_IF
Как только A станет равным TRUE, то никакое значение X уже не изменит A. Оператор S= никогда не сможет сбросить A в FALSE.
A X A FALSE S= FALSE → FALSE FALSE S= TRUE → TRUE TRUE S= FALSE → TRUE TRUE S= TRUE → TRUE
Оператор сброса R=
Запись F R= X; аналогична:
IF X THEN F := FALSE; END_IF
Как только F станет равным FALSE, то никакое значение X уже не изменит F. Оператор R= никогда не сможет установить F в TRUE.
F X F FALSE R= FALSE → FALSE FALSE R= TRUE → FALSE TRUE R= FALSE → TRUE TRUE R= TRUE → FALSE
Присваивание
Присваивание как выражение (assignment as expression) можно делать так:
IF bVar := (i = 2) THEN i := i + 1; END_IF
bVar — булева переменная, получает значение от выражения (i = 2), то есть будет равна TRUE когда (i = 2). Затем bVar оценивается в IF. Лучше не мудрить и не пихать в одну строку несколько команд сразу. Пишите так, чтобы было проще читать впоследствии. Компилятору все-равно как вы написали, работать это будет одинаково, а вероятность ошибиться намного выше в сложносочиненной записи:
bVar := (i = 2); IF bVar THEN i := i + 1; END_IF
Можно присваивать нескольких переменных одновременно:
a:= b:= c:= k + 12;
Но не дайте обмануть себя видом и формой записи — здесь нет присваивания, передающегося справа-налево по цепочке! Аналогичная, но более длинная запись, выглядит так:
a := k + 12; b := k + 12; c := k + 12;
Выражение (k + 12) будет вычисляться три раза, поэтому выгоднее записать:
c := k + 12;
a:= b:= c;
Теперь вспомним операторы установки/сброса. Как будет работать следующая строка? Ответ в конце поста.
A S= F R= X;
Типизированные литералы
Звучит жутко умно, но это всего-лишь способ задать точный тип числа.
A := 123; — не говорит нам какого типа число 123 (литерал). Это точно не BOOL и скорее всего не REAL / LREAL (нет десятичной запятой). Возможны SINT, USINT, BYTE, INT, UINT, WORD, DINT, UDINT, DWORD. Так кто же из них?
Обычно тип литерала будет зависеть от типа переменной A. Поэтому можно просто уточнить запись литерала: A := 123.0; и тогда мы получим REAL. Но что если хочется BYTE, а переменная типа UINT?
Для этого тип можно обозначить точно: A := BYTE#127;
Такую запись можно использовать везде, где допустимо использование констант. Если TwinCAT обнаружит потенциальную потерю данных, то выдаст сообщение об ошибке или просто предупреждение, если всё не так страшно.
CASE ... ELSE
Если конкретный шаг CASE для какого-то значения переменной не существует, то CASE игнорируется целиком, за исключением если... Блок ELSE в CASE позволяет задать действие по умолчанию, если номер шага в CASE не задан:
CASE state OF 0: DoSomething(); 100: DoSomething2(); ... 2000: DoSomethingAgainNAgain(); ELSE DontKnowWhatHappenedLastTime(); END_CASE
Если вы знакомы с C++, то это аналогично: switch case ... default.
OR_ELSE
Работает аналогично OR, но если левая часть OR_ELSE уже равна TRUE, то правая часть не проверяется и не выполняется:
VAR a, z, b : BOOL; i : INT; END_VAR z := a OR_ELSE (b := (i = 2));
Если А равно TRUE, то правая часть OR_ELSE не будет выполняться и B никогда не сможет стать TRUE. Можно оптимизировать производительность, исключив лишний вызов функций. И теперь мы знаем, что для "обычного" OR, независимо от значения операндов, вызываются и проверяются обе части: и левая, и правая.
AND_THEN
Как AND, но правая часть AND_THEN обрабатывается только при условии, что левая часть равна TRUE. Например, это удобно для проверок указателей:
IF (ptr <> 0 AND_THEN ptr ^= 99) THEN ...
Когда указатель не равен нулю и_тогда выполнить сравнение адреса указателя. Это позволяет избежать проблем с нулевым указателем. В обычном AND всегда обрабатываются обе части независимо от их значения.
Ответ
A S= F R= X; // работает как A S= X; F R= X;
В заголовке и тексте указана функция - AND_ELSE, а в листинге AND_THEN. Правильный вариант: AND_THEN
ReplyDeleteИсправил. Спасибо!
Deleteв тэгах еще подправьте
Delete