>bmiTell :: Double -> Double -> String
>bmiTell weight height
> | weight / height ^ 2 <= 18.5 = "Слышь, эмо, ты дистрофик!"
> | weight / height ^ 2 <= 25.0 = "По части веса ты в норме.
> Зато, небось, уродец!"
> | weight / height ^ 2 <= 30.0 = "Ты толстый!
> Сбрось хоть немного веса!"
> | otherwise = "Мои поздравления, ты жирный боров!"
Ну-ка проверим, не толстый ли я…
>ghci> bmiTell 85 1.90
>"По части веса ты в норме. Зато, небось, уродец!"
Ура! По крайней мере, я не толстый! Правда, Haskell обозвал меня уродцем. Ну, это не в счёт.
ПРИМЕЧАНИЕ. Обратите внимание, что после имени функции и её параметров нет знака равенства до первого охранного выражения. Многие новички ставят этот знак, что приводит к ошибке.
Ещё один очень простой пример: давайте напишем нашу собственную функцию >max
. Если вы помните, она принимает два значения, которые можно сравнить, и возвращает большее из них.
>max' :: (Ord a) => a –> a –> a
>max' a b
> | a <= b = b
> | otherwise = a
Продолжим: напишем нашу собственную функцию сравнения, используя охранные выражения.
>myCompare :: (Ord a) => a –> a –> Ordering
>a `myCompare` b
> | a == b = EQ
> | a <= b = LT
> | otherwise = GT
>ghci> 3 `myCompare` 2
>GT
ПРИМЕЧАНИЕ. Можно не только вызывать функции с помощью обратных апострофов, но и определять их так же. Иногда такую запись легче читать.
Где же ты, where?!
Программисты обычно стараются избегать многократного вычисления одних и тех же значений. Гораздо проще один раз вычислить что-то, а потом сохранить его значение. В императивных языках программирования эта проблема решается сохранением результата вычислений в переменной. В данном разделе вы научитесь использовать ключевое слово >where
для сохранения результатов промежуточных вычислений примерно с той же функциональностью.
В прошлом разделе мы определили вычислитель ИМТ и «ругалочку» на его основе таким образом:
>bmiTell :: Double -> Double -> String
>bmiTell weight height
> | weight / height ^ 2 <= 18.5 = "Слышь, эмо, ты дистрофик!"
> | weight / height ^ 2 <= 25.0 = "По части веса ты в норме.
> Зато, небось, уродец!"
> | weight / height ^ 2 <= 30.0 = "Ты толстый!
> Сбрось хоть немного веса!"
> | otherwise = "Мои поздравления, ты жирный боров!"
Заметили – мы повторили вычисление три раза? Операции копирования и вставки, да ещё повторенные трижды, – сущее наказание для программиста. Раз уж у нас вычисление повторяется три раза, было бы очень удобно, если бы мы могли вычислить его единожды, присвоить результату имя и использовать его, вместо того чтобы повторять вычисление. Можно переписать нашу функцию так:
>bmiTell :: Double -> Double -> String bmiTell weight height
> | bmi <= 18.5 = "Слышь, эмо, ты дистрофик!"
> | bmi <= 25.0 = "По части веса ты в норме.
> Зато, небось, уродец!"
> | bmi <= 30.0 = "Ты толстый!
> Сбрось хоть немного веса!"
> | otherwise = "Мои поздравления, ты жирный боров!"
> where bmi = weight / height ^ 2