> | bmi <= 18.5 = "Слышь, эмо, ты дистрофик!"
> | bmi <= 25.0 = "По части веса ты в норме. Зато, небось, уродец!"
> | bmi <= 30.0 = "Ты толстый! Сбрось хоть немного веса!"
> | otherwise = "Мои поздравления, ты жирный боров!"
Охранные выражения обозначаются вертикальными чёрточками после имени и параметров функции. Обычно они печатаются с отступом вправо и начинаются с одной позиции. Охранное выражение должно иметь тип >Bool
. Если после вычисления условие имеет значение >True
, используется соответствующее тело функции. Если вычисленное условие ложно, проверка продолжается со следующего условия, и т. д.
Если мы вызовем эту функцию с параметром >24.3
, она вначале проверит, не является ли это значение меньшим или равным >18.5
. Так как охранное выражение на данном значении равно >False
, функция перейдёт к следующему варианту. Проверяется следующее условие, и так как >24.3
меньше, чем >25.0
, будет возвращена вторая строка.
Это очень напоминает большие деревья условий >if
– >else
в императивных языках программирования – только такой способ записи значительно лучше и легче для чтения. Несмотря на то что большие деревья условий >if
– >else
обычно не рекомендуется использовать, иногда задача представлена в настолько разрозненном виде, что просто невозможно обойтись без них. Охранные выражения – прекрасная альтернатива для таких задач.
Во многих случаях последним охранным выражением является >otherwise
(«иначе»). Значение >otherwise
определяется просто: >otherwise = True
; такое условие всегда истинно. Работа условий очень похожа на то, как работают образцы, но образцы проверяют входные данные, а охранные выражения могут производить любые проверки.
Если все охранные выражения ложны (и при этом мы не записали >otherwise
как последнее условие), вычисление продолжается со следующей строки определения функции. Вот почему сопоставление с образцом и охранные выражения так хорошо работают вместе. Если нет ни подходящих условий, ни клозов, будет сгенерирована ошибка времени исполнения.
Конечно же, мы можем использовать охранные выражения с функциями, которые имеют столько входных параметров, сколько нам нужно. Вместо того чтобы заставлять пользователя вычислять свой ИМТ перед вызовом функции, давайте модифицируем её так, чтобы она принимала рост и вес и вычисляла ИМТ:
>bmiTell :: Double -> Double -> String
>bmiTell weight height
> | weight / height ^ 2 <= 18.5 = "Слышь, эмо, ты дистрофик!"
> | weight / height ^ 2 <= 25.0 = "По части веса ты в норме.
> Зато, небось, уродец!"