Порядок расчета логических выражений

Опубликовано в AmiBroker

Как вычисляются сложные логические условия в AFL?

 Существует 2 стратегии вычисления результатов логических выражений типа (УСЛОВИЕ1 AND УСЛОВИЕ2), а также (УСЛОВИЕ1 OR УСЛОВИЕ2). Необходимо четко понимать, какая именно стратегия заложена в реализации того языка программирования, которым Вы в текущий момент пользуетесь.

  • Полное вычисление: вычислить УСЛОВИЕ1, потом вычислить УСЛОВИЕ2, затем сравненить результаты каждого условия с нулём (или FALSE), затем выполнить операцию «И» для результатов. Так поступает, к примеру, Visual Basic.
  • Неполное вычисление: вычислить УСЛОВИЕ1, сравнить значение с нулём. Если результат сравнения — «истина» (то есть не ноль), то вычислять остальную часть выражения. Если же первое условие ложно, то второе условие рассчитывать не нужно, так как для операции «И» при ложности одного из операндов всё выражение заведомо ложно.

Второй вариант является наиболее распространённым для промышленных языков (в частности, для Алгола, Фортрана, С++, С, Java, JavaScript, ECMAScript, JScript, C#, Python). В этих языках действует жёсткое правило:

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

Это означает, что если выражение состоит из нескольких подусловий, объединённых оператором AND, то вычисление выражения прекратится, как только одно из подусловий окажется ложным (так как «ложь AND любое значение» в результате всегда даёт «ложь»), и, наоборот, если несколько подусловий объединены оператором OR, вычисление прекратится после первого же истинного подусловия, поскольку в этом случае всё выражение истинно, независимо от дальнейших вычислений. А вот выражение, содержащее оператор «Исключающее ИЛИ» (XOR) неполному вычислению не поддаётся, поскольку в нём одно из значений не может определить результат вычисления всего выражения.

 

Все было бы хорошо, если бы УСЛОВИЕ1 и УСЛОВИЕ2 были бы простыми, например, сравнение двух целочисленных переменных. Однако вместо каждого из них может фигурировать как сложное выражение, так и вызов функции. А функции кроме возвращения результата могут делать много разной побочной работы.

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

Некоторые языки (например Ада или Erlang) для разных вариантов используют разные ключевые слова: and и or означают полное вычисление, а and then, or else (Ада), andalso, orelse (Erlang) — неполное.

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

Как себя ведет в этом вопросе интерпретатор AFL? Напишем простейший тест

a= 1;

function f1()

{      a = 2;

        return False;

}

function f2()

{     a = 3;

       return True;

}

if (f1() and f2())

        ;

_Trace("a= " + a);

 

В теле программы определили глобальную переменную A и инициализировали ее значением 1. Узнаем, какие функции были выполнены, проанализировав ее значение в конце. После исполнения условного оператора IF в окне трассировки мы получим следующую запись:

а = 2

Функция f2() не выполнялась. Делаем вывод. Язык AFL принадлежит к кусту С-подобных языков и, как и следовало ожидать, применяет укороченный способ вычисления логических выражений.

А как обстоят дела у QPILE? А вот QPILE рассчитает оба условия, там реализована первая стратегия. Lua использует второй вариант.

Недостаточно прав для комментирования