Состав функции Хаскелла
Я читаю этот учебник по Haskell. Они определяют состав функций следующим образом:
(.) :: (b->c) -> (a->b) -> (a->c)
f . g = \ x -> f (g x)
Никаких примеров не было предоставлено, что, я думаю, просветит меня относительно того, что здесь определено.
Может ли кто-нибудь представить простой пример (с объяснением) того, как используется композиция функции?
Ответы
Ответ 1
Состав функций - это способ "скомпоновать" две функции вместе в одну функцию. Вот пример:
Скажите, что у вас есть следующие функции:
even :: Int -> Bool
not :: Bool -> Bool
и вы хотите определить свою собственную функцию myOdd :: Int -> Bool
, используя приведенные выше.
Очевидным способом сделать это является следующее:
myOdd :: Int -> Bool
myOdd x = not (even x)
Но это можно сделать более кратко с использованием композиции функций:
myOdd :: Int -> Bool
myOdd = not . even
Функции myOdd
ведут себя точно так же, но второй создается путем "склеивания" двух функций вместе.
Сценарий, в котором это особенно полезно, - это удалить необходимость явной лямбда. Например:
map (\x -> not (even x)) [1..9]
можно переписать на:
map (not . even) [1..9]
Немного короче, меньше места для ошибок.
Ответ 2
Замечательная сторона. Состав функции является эквивалентом силлогизма в логике:
Все люди смертны. Сократ - это человек. Поэтому Сократ смертен.
Силлогизм составляет два существенных значения в одном:
(Man => Mortal), (Socrates => Man), therefore (Socrates => Mortal)
Поэтому...
(B => C) => (A => B) => (A => C)
... который является типом функции .
.
Ответ 3
состав f
и g
- это функция, которая сначала применяет g
к своему аргументу, а затем f
к значению, возвращаемому g
. Затем он возвращает возвращаемое значение f
.
Это тождество может быть просветляющим:
f (g x) = (f . g) x
Если у вас есть фон Java/C, рассмотрите этот пример:
int f(int x);
int g(int x);
int theComposition(int x) { return f(g(x)); }
Ответ 4
Этот пример надуман, но предположим, что
sqr x = x * x
inc x = x + 1
и мы хотим написать функцию, вычисляющую x ^ 2 + 1. Мы можем написать
xSquaredPlusOne = inc . sqr
(что означает
xSquaredPlusOne x = (inc . sqr) x
что означает
xSquaredPlusOne x = inc(sqr x)
так как f = inc и g = sqr).
Ответ 5
Из страница HaskellWiki о составе функции:
desort = (reverse . sort)
Теперь desort
- это функция, сортирующая список в обратном порядке. В принципе, desort
передает свои аргументы в sort
, а затем возвращает возвращаемое значение из sort
в reverse
, возвращает это. Поэтому он сортирует его, а затем меняет отсортированный список.
Ответ 6
Состав функций - способ объединения двух или более функций вместе. Он часто сравнивается с оболочкой. Например, в оболочке в стиле Unix вы можете написать что-то вроде
cat foo.txt | sort -n | less
Это запускает cat
, передает свой вывод на sort
и подает вывод с этого на less
.
Строго, это похоже на оператор Haskell $
. Вы можете написать что-то вроде
sum $ sort $ filter (> 0) $ my_list
Обратите внимание, что в отличие от примера оболочки это читается справа налево. Итак, мы начинаем с my_list
в качестве входных данных, затем выполняем над ним filter
, тогда мы sort
it, а затем вычисляем его sum
.
Оператор композиции функции .
делает нечто подобное. В приведенном выше примере выдается число; в приведенном ниже примере создается функция:
sum . sort . filter (> 0)
Обратите внимание, что мы фактически не подавали список. Вместо этого мы только что создали новую функцию, и мы можем подать несколько разных списков этой функции. Например, вы можете назвать эту функцию:
my_function = sum . sort . filter (> 0)
Или вы можете передать его как аргумент другой функции:
map (sum . sort . filter (> 0)) my_lists
Вы можете использовать его в любом месте, где вы можете использовать любую другую функцию. Это просто быстрый и понятный способ сказать: "Я хочу объединить эти функции вместе".