2
*
3
==
1
+
(2
*
3)
1
+++
2
*** 3
==
(1
+++
2)
***
3
1
-
2
-
3
==
(1
-
2)
-
3
1 ‘neg‘ 2 ‘neg 3‘ ==
1 ‘neg‘ (2
‘neg‘ 3)
Также в Haskell есть директива infix это тоже самое, что и infixl.
Приоритет функции композиции
Посмотрим на приоритет функции композиции:
Prelude> :i (. )
(. ) :: (b -> c) -> (a -> b) -> a -> c
-- Defined in GHC.Base
infixr 9 .
Она имеет высший приоритет. Она очень часто используется при определении функции в бесточечном
стиле. Такая функция похожа на конвейер функций:
fun a = fun1 a . fun2 (x1 + x2) . fun3 . (+x1)
Приоритет функции применения
Теперь посмотрим на полное определение функции применения:
infixr 0 $
($) :: (a -> b) -> a -> b
f $ x
=
f x
Ответ на вопрос о полезности этой функции кроется в её приоритете. Ей назначен самый низкий прио-
ритет. Она будет исполняться в последнюю очередь. Очень часто возникают ситуации вроде:
Приоритет инфиксных операций | 77
foldNat zero succ (Succ b) = succ (foldNat zero succ b)
С помощью функции применения мы можем переписать это определение так:
foldNat zero succ (Succ b) = succ $ foldNat zero succ b
Если бы мы написали без скобок:
... = succ foldNat zero succ b
То выражение было бы сгруппировано так:
... = (((succ foldNat) zero) succ) b
Но поскольку мы поставили барьер в виде операции ($) с низким приоритетом, группировка скобок
произойдёт так:
... = (succ $ ((foldNat zero) succ) b)
Это как раз то, что нам нужно. Преимущество этого подхода проявляется особенно ярко если у нас
несколько вложенных функций на конце выражения:
xs :: [Int]
xs = reverse $ map ((+1) . (*10)) $ filter even $ ns 40
ns :: Int -> [Int]
ns 0
= []
ns n
= n : ns (n - 1)
В списке xs мы сначала создаём в функции ns убывающий список чисел, затем оставляем лишь чётные,
потом применяем два арифметических действия ко всем элементам списка, затем переворачиваем список.
Проверим работает ли это в интерпретаторе, заодно поупражняемся в композиционном стиле:
Prelude> let ns n = if (n == 0) then [] else n : ns (n - 1)
Prelude> let even x = 0 == mod x 2
Prelude> let xs = reverse $ map ((+1) . (*10)) $ filter even $ ns 20
Prelude> xs
[21,41,61,81,101,121,141,161,181,201]
Если бы не функция применения нам пришлось бы написать это выражение так:
xs = reverse (map ((+1) . (*10)) (filter even (ns 40)))
5.3 Функциональный калькулятор
Мне бы хотелось сделать акцент на одном из вступительных предложений этой главы:
За счёт развитых средств составления новых функций в Haskell пользователь определяет лишь
базовые функции, получая остальные “на лету” применением двух-трёх операций, это выглядит
примерно как (2+3)*5, где вместо чисел стоят базовые функции, а операции + и * составляют
новые функции из простейших.
Такие обобщённые функции как id, const, (. ), map filter позволяют очень легко комбинировать различ-
ные функции. Бесточечный стиль записи функций превращает функции в простые значения или значения-
константы, которые можно подставлять в другие функции. В этом разделе мы немного потренируемся в пе-
регрузке численных значений и превратим числа в функции, функции и в самом деле станут константами.
Мы определим экземпляр Num для функций, которые возвращают числа. Смысл этих операций заключается в
том, что теперь мы применяем обычные операции сложения умножения к функциям, аргумент которых сов-
падает по типу. Например для того чтобы умножить функции \t -> t+2 и \t -> t+3 мы составляем новую
функцию \t -> (t+2) * (t+3), которая получает на вход значение t применяет его к каждой из функций и
затем умножает результаты:
78 | Глава 5: Функции высшего порядка
module FunNat where
import Prelude(Show(.. ), Eq(.. ), Num(.. ), error)