Охрана (программирование)

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

Термин охраняющее утверждение является шаблоном проектирования программного обеспечения, приписываемым Кенту Беку, который формализовал многие ранее безымянные практики программирования в виде именованных шаблонов. Однако практика использования этого приёма восходит как минимум к началу 1960-х годов. Охраняющее утверждение чаще всего добавляется в начале процедуры и «охраняет» остальной код, обрабатывая граничные случаи заранее.

Использование

Типичный пример — проверка того, что ссылка, которая будет обработана, не равна null, что предотвращает ошибки нулевого указателя.

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

public String foo(String username) {
    if (username == null) {
        throw new IllegalArgumentException("Username is null.");
    }

    // Остальной код метода...
}

Более плоский код с меньшей вложенностью

Охрана обеспечивает ранний выход из подпрограммы и является распространённым отклонением от структурного программирования, устраняя один уровень вложенности и делая код более плоским:[1] заменяя if guard { ... } на if not guard: return; ....

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

Например, на Python:

from typing import Any, Optional

# Эта функция не использует охраняющее утверждение
def f_noguard(x: Any) -> Optional[int]:
    if isinstance(x, int):
        # код здесь
        return x + 1
    else:
        return None

# Эквивалентная функция с охраняющим утверждением. Обратите внимание, что большая часть кода менее вложена, что облегчает чтение и понимание
def f_guard(x: Any) -> Optional[int]:
    if not isinstance(x, int):
        return None
    # код здесь
    return x + 1

Другой пример на C:

// Эта функция не использует охраняющее утверждение
int funcNoGuard(int x) {
    if (x >= 0) {
        // код здесь
        return x + 1; 
    } else {
        return 0;
    }
}

// Эквивалентная функция с охраняющим утверждением
int funcGuard(int x) {
    if (x < 0) {
        return 0;
    }

    // код здесь
    return x + 1; 
}

Терминология

Термин используется в специфическом значении в языках APL, Haskell, Clean, Erlang, occam, Promela, OCaml, Swift[2], Python начиная с версии 3.10, и Scala.Citation needed[en] В Mathematica охраны называются ограничениями (constraints). Охраны — фундаментальное понятие в , используемом в формальных методах. Охраны могут использоваться для расширения сопоставления с образцом с возможностью пропуска образца, даже если структура совпадает. Булевы выражения в условных операторах обычно также соответствуют этому определению охраны, хотя чаще называются условиями.

Математика

В следующем примере на Haskell охраны располагаются между каждой парой символов "|" и "=":

f x
 | x > 0 = 1
 | otherwise = 0

Это похоже на соответствующую математическую запись:

В данном случае охраны находятся в частях "если" и "иначе".

Несколько охран

Если имеется несколько параллельных охран, они обычно проверяются сверху вниз, и выбирается ветвь первой прошедшей охраны. Охраны в списке случаев, как правило, параллельны.

Однако в Haskell при списковых включениях охраны идут последовательно, и если хотя бы одна из них не выполняется, элемент списка не создаётся. Это эквивалентно объединению отдельных охран с помощью логического И, за исключением того, что между охранами могут быть и другие части выражения спискового включения.

Эволюция

Простое условное выражение, уже присутствовавшее в CPL в 1963 году, содержит охрану в первой подвыражении и другое подвыражение, используемое, если первое не может быть применено. Некоторые распространённые способы записи:

(x>0) -> 1/x; 0
x>0 ? 1/x : 0

Если второе подвыражение также может быть простым условным выражением, можно задать больше альтернатив до последнего fall-through:

(x>0) -> 1/x; (x<0) -> -1/x; 0

В 1966 году ISWIM имел форму условного выражения без обязательного случая по умолчанию, тем самым отделяя охрану от выбора между альтернативами. В случае ISWIM, если ни одна альтернатива не могла быть использована, значение считалось неопределённым, что означало невозможность вычисления значения.

KRC, «миниатюрная версия»[3] SASL (1976), был одним из первых языков программирования, использовавших термин «охрана». Его определения функций могли содержать несколько предложений, и выбиралось то, для которого выполнялась охрана, следующая за предложением:

 fac n = 1,               n = 0
       = n * fac (n-1),   n > 0

Использование охраняющих утверждений и сам термин «охраняющее утверждение» восходят как минимум к практике Smalltalk 1990-х годов, формализованной Кентом Беком[1].

В 1996 году Dyalog APL принял альтернативный чисто функциональный стиль, в котором охрана является единственной управляющей структурой[4]. Пример на APL, вычисляющий чётность числа:

parity{
        2 : 'odd'
              'even'
        }

Охрана с образцом

В дополнение к охране, связанной с образцом, охрана с образцом может означать использование сопоставления с образцом в контексте охраны. По сути, совпадение с образцом считается прохождением охраны. Это значение было введено в предложении для Haskell Саймоном Пейтоном Джонсом под названием A new view of guards в апреле 1997 года и было реализовано в языке. Эта возможность позволяет использовать образцы в охранах образца.

Пример на расширенном Haskell:

 clunky env var1 var2
 | Just val1 <- lookup env var1
 , Just val2 <- lookup env var2
 = val1 + val2
 -- ...другие уравнения для clunky...

Это читается так: «clunky для окружения и двух переменных, если поиск переменных в окружении возвращает значения, равен сумме этих значений...». Как и в списковых включениях, охраны идут последовательно, и если хотя бы одна из них не выполняется, ветвь не выбирается.

См. также

Примечания

Ссылки