sum $ filter (> 10) $ map (*2) [2..10]

Но кроме избавления от скобок оператор $ означает, что само применение функции может использоваться как и любая другая функция. Таким образом, мы можем, например, применить функцию к списку функций:

ghci> map ($ 3) [(4+), (10*), ( 2), sqrt]

[7.0,30.0,9.0,1.7320508075688772]

Функция ($ 3) применяется к каждому элементу списка. Если задуматься о том, что она делает, то окажется, что она берёт функцию и применяет её к числу 3. Поэтому в данном примере каждая функция из списка применится к тройке, что, впрочем, и так очевидно.

<p>Композиция функций</p>

В математике композиция функций определяется следующим образом:

(f ° g)(x) = f (g (x))

Это значит, что композиция двух функций создаёт новую функцию, которая, когда её вызывают, скажем, с параметром x, эквивалентна вызову g с параметром x, а затем вызову f с результатом первого вызова в качестве своего параметра.

В языке Haskell композиция функций понимается точно так же. Мы создаём её при помощи оператора (.), который определён следующим образом:

(.) :: (b –> c) –> (a –> b) –> a –> c

f . g = \x –> f (g x)

По декларации типа функция f должна принимать параметр того же типа, что и результат функции g. Таким образом, результирующая функция принимает параметр того же типа, что и функция g, и возвращает значение того же типа, что и функция f. Выражение negate . (* 3) возвращает функцию, которая принимает число, умножает его на три и меняет его знак на противоположный.

Одно из применений композиции функций – это создание функций «на лету» для передачи их другим функциям в качестве параметров. Конечно, мы можем использовать для этого анонимные функции, но зачастую композиция функций понятнее и лаконичнее. Допустим, что у нас есть список чисел и мы хотим сделать их отрицательными. Один из способов сделать это – получить абсолютное значение числа (модуль), а затем перевести его в отрицательное, вот так:

ghci> map (\x –> negate (abs x)) [5,–3,–6,7,–3,2,–19,24]

[–5,–3,–6,–7,–3,–2,–19,–24]

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

ghci> map (negate . abs) [5,–3,–6,7,–3,2,–19,24]

[–5,–3,–6,–7,–3,–2,–19,–24]

Невероятно! Композиция функций правоассоциативна, поэтому у нас есть возможность включать в неё много функций за один раз. Выражение f (g (z x)) эквивалентно (f . g . z) x. Учитывая это, мы можем превратить

ghci> map (\xs –> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]

[–14,–15,–27]

в

ghci> map (negate . sum . tail) [[1..5],[3..6],[1..7]]

[–14,–15,–27]

Функция negate . sum . tail принимает список, применяет к нему функцию tail, суммирует результат и умножает полученное число на -1. Получаем точный эквивалент анонимной функции из предыдущего примера.

<p>Композиция функций с несколькими параметрами</p>

Ну а как насчёт функций, которые принимают несколько параметров? Если мы хотим использовать их в композиции, обычно мы частично применяем их до тех пор, пока не получим функцию, принимающую только один параметр. Запись

sum (replicate 5 (max 6.7 8.9))

может быть преобразована так:

(sum . replicate 5) (max 6.7 8.9)

или так:

sum . replicate 5 $ max 6.7 8.9

Функция replicate 5 применяется к результату вычисления max 6.7 8.9, после чего элементы полученного списка суммируются. Обратите внимание, что функция replicate частично применена так, чтобы у неё остался только один параметр, так что теперь результат max 6.7 8.9 передаётся на вход replicate 5; новым результатом оказывается список чисел, который потом передаётся функции sum.

Если вы хотите переписать выражение с кучей скобок, используя функциональную композицию, можно сначала записать самую внутреннюю функцию с её параметрами, затем поставить перед ней знак $, а после этого пристраивать вызовы всех других функций, записывая их без последнего параметра и разделяя точками. Например, выражение

replicate 2 (product (map (*3) (zipWith max [1,2] [4,5])))

можно переписать так:

replicate 2 . product . map (*3) $ zipWith max [1,2] [4,5]

Перейти на страницу:

Похожие книги