понедельник, 26 октября 2020 г.

Short circuit evaluation в php

Сегодня поближе познакомимся с short circuit evaluation: выясним что это за зверь такой, посмотрим примеры и выясним, как он нам может помочь.

Для начала немного теории. Short circuit evaluation (не могу предложить простого русского перевода) - это стратегия в языках программирования, которая используется, чтобы избежать ненужных вычислений.
Лучше всего это понять на примере булевых выражений. Допустим, мы проверяем условие вида if (checkSomething() && checkSomethingElse()). Если checkSomething() вернет false, то true в итоге уже никак не получить, следовательно, вычислять второе значение в checkSomethingElse() не имеет смысла - любое вычисленное значение никак не повлияет на итоговый результат.

Теперь примеры - short circuit evaluation в действии. Определим четыре функции, две из них возвращают true, две других - false. Для проверки поместим в тела этих функций вывод сообщений:

function returnTrue(): bool
{
    echo 'Вызван метод ' . __METHOD__ . PHP_EOL;
    
    return true;
}

function returnTrueToo(): bool
{
    echo 'Вызван метод ' . __METHOD__ . PHP_EOL;
    
    return true;
}

function returnFalse(): bool
{
    echo 'Вызван метод ' . __METHOD__ . PHP_EOL;
    
    return false;
}

function returnFalseToo(): bool
{
    echo 'Вызван метод ' . __METHOD__ . PHP_EOL;
    
    return false;
}

Проверяем:

if (returnFalse() && returnTrue()) {
    echo 'Эта строка не выведется';
} else {
    echo 'Эта строка выведется и это правильно';
}
// Вывод
Вызван метод returnFalse
Эта строка выведется и это правильно

Как видим - short circuit evaluation действительно работает: так как && вернет true только в случае если оба аргумента равны true, а первый вычисленный аргумент не равен true, то вычислять второй не имеет смысла, итоговое выражение от этого не станет true. Поэтому видим, что выполнилась только первая функция returnFalse.

Пример посложнее:

if ((returnTrue() || returnFalse()) && (returnFalseToo() && returnTrueToo())) {
    echo 'Эта строка не выведется';
} else {
    echo 'Эта строка выведется и это правильно';
}
// Вывод
Вызван метод returnTrue
Вызван метод returnFalseToo
Эта строка выведется и это правильно

Третья проверка показывает, что если все условия могут повлиять на результат - то все они и проверятся:

if (returnFalse() || returnFalseToo() || returnTrueToo()) {
    echo 'Эта строка выведется и это правильно';
} else {
    echo 'Эта строка не выведется';
}
// Вывод
Вызван метод returnFalse
Вызван метод returnFalseToo
Вызван метод returnTrueToo
Эта строка выведется и это правильно

Итак, short circuit evaluation действительно работает и не вызывает функции, если их выполнение не повлияет на результат вычисления. И стоит это учитывать в ваших условиях. Например, рассмотрим такой гипотетический случай:

if (returnsTrue() || checkSomethingFromApi()) {
    // more code
}

Здесь checkSomethingFromApi некая функция, которая достает результат из апи, что-либо проверяет в нем и возвращает какой-то результат. Если вы будете думать, что checkSomethingFromApi будет вызываться всегда, то увы. Так как первой части достаточно для всего результата, то функция checkSomethingFromApi не вызовется никогда. И если checkSomethingFromApi дополнительно делает какое-то еще, явно не обозначенное действие (например, пишет в БД или в кеш), то это действие не выполнится, и в БД не запишется ничего. Следовательно, вам гарантированы часы дебага и разочарование, что "ларчик просто открывался". Инспекции PHPStorm, кстати, могут обратить ваше внимание на такое поведение.

Теперь перейдем к другому моменту: что если мы объединим некоторые логические условия в скобки и возьмем от них отрицание. Будет ли применён short circuit evaluation или будут выполнены все вычисления в скобках, и только потом инвертируется результат? Смотрим в пример:

if (!(returnTrue() || returnFalse()) || !(returnFalse() && returnFalseToo())) {
    echo 'Эта строка выведется и это правильно';
} else {
    echo 'Эта строка не выведется';
}
// Вывод
Вызван метод returnTrue
Вызван метод returnFalse
Эта строка выведется и это правильно

Как видим, и тут срабатывает short circuit evaluation. В первом условии returnTrue() однозначно определяет результат всего условия как true, отрицание дает false, значит надо перейти к вычислению второго условия. Во втором условии returnFalse() также достаточно для определения результата, и отрицание false дает true. Как видим - вместо предполагаемых четырех функций выполнились всего лишь две.

Итак, мы разобрались что такое short circuit evaluation, посмотрели примеры с его использованием и даже выяснили один подводный камень его использования.

Комментариев нет:

Отправка комментария