Изучай Haskell во имя добра! (Липовача) - страница 33

>  | 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 = "По части веса ты в норме.

>                                   Зато, небось, уродец!"