Оператор Dot в Haskell: нужно больше объяснений
Я пытаюсь понять, что делает оператор точек в этом коде Haskell:
sumEuler = sum . (map euler) . mkList
Весь исходный код приведен ниже.
Мое понимание
Точечный оператор принимает две функции sum
и результат map euler
и результат mkList
в качестве входа.
Но, sum
не является функцией, это аргумент функции, правильно? и так, что здесь происходит?
Также, что делает (map euler)
?
код
mkList :: Int -> [Int]
mkList n = [1..n-1]
euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))
sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList
Ответы
Ответ 1
Проще говоря, .
- это композиция функции, как и в математике:
f (g x) = (f . g) x
В вашем случае вы создаете новую функцию sumEuler
, которая также может быть определена следующим образом:
sumEuler x = sum (map euler (mkList x))
Стиль в вашем примере называется "бессмысленным" стилем - аргументы функции опущены. Во многих случаях это обеспечивает более четкий код. (Это может быть трудно получить в первый раз, когда вы его видите, но вы привыкнете к нему через некоторое время. Это обычная икома Хаскелла.)
Если вы все еще запутались, это может помочь связать .
с чем-то вроде UNIX-канала. Если вывод f
становится g
, выход которого будет h
, вы должны записать это в командной строке, например f < x | g | h
. В Haskell .
работает как UNIX |
, но "назад" - h . g . f $ x
. Я считаю, что это обозначение очень полезно, если, скажем, обработать список. Вместо некоторой громоздкой конструкции, такой как map (\x -> x * 2 + 10) [1..10]
, вы можете просто написать (+10) . (*2) <$> [1..10]
. (И, если вы хотите применить эту функцию только к одному значению, это (+10) . (*2) $ 10
. Consistent!)
У Haskell wiki есть хорошая статья с более подробной информацией: http://www.haskell.org/haskellwiki/Pointfree
Ответ 2
. Оператор составляет функции. Например,
a . b
Если a и b являются функциями, это новая функция, которая запускает b для своих аргументов, а затем a для этих результатов. Ваш код
sumEuler = sum . (map euler) . mkList
точно так же, как:
sumEuler myArgument = sum (map euler (mkList myArgument))
но, надеюсь, легче читать. Причина, по которой вокруг карты euler есть параны, состоит в том, что она проясняет, что составляются 3 функции: sum, map euler и mkList - map euler - это одна функция.
Ответ 3
sum
- это функция в Прелюдии Хаскеля, а не аргумент sumEuler
. Имеет тип
Num a => [a] -> a
Оператор композиции функций .
имеет тип
(b -> c) -> (a -> b) -> a -> c
Итак, у нас есть
euler :: Int -> Int
map :: (a -> b ) -> [a ] -> [b ]
(map euler) :: [Int] -> [Int]
mkList :: Int -> [Int]
(map euler) . mkList :: Int -> [Int]
sum :: Num a => [a ] -> a
sum . (map euler) . mkList :: Int -> Int
Обратите внимание, что Int
действительно является экземпляром класса типов Num
.
Ответ 4
. оператор используется для составления функции. Подобно математике, если у вас есть функции f (x) и g (x) f. g становится f (g (x)).
map - это встроенная функция, которая применяет функцию к списку. Помещая функцию в круглые скобки, функция рассматривается как аргумент. Термин для этого currying. Вы должны посмотреть на это.
Что происходит, так это то, что он принимает функцию с двумя аргументами, она применяет аргумент euler. (карта Эйлера)? и результатом является новая функция, которая принимает только один аргумент.
сумма. (эйлер карты). mkList - это, по сути, причудливый способ собрать все это вместе. Должен сказать, мой Haskell немного ржавый, но, может быть, вы можете связать эту последнюю функцию самостоятельно?
Ответ 5
Точечный оператор применяет функцию слева (sum
) к выходу функции справа. В вашем случае вы объединяете несколько функций - вы передаете результат mkList
в (map euler)
, а затем передаете результат этого в sum
.
Этот сайт имеет хорошее представление о нескольких концепциях.
Ответ 6
Точка оператора в Хаскеле
Я пытаюсь понять, что делает оператор точки в этом коде на Haskell:
sumEuler = sum . (map euler) . mkList
Краткий ответ
Эквивалентный код без точек, это просто
sumEuler = \x -> sum ((map euler) (mkList x))
или без лямбды
sumEuler x = sum ((map euler) (mkList x))
потому что точка (.) указывает на состав функции.
Более длинный ответ
Прежде всего, давайте упростим частичное применение euler
к map
:
map_euler = map euler
sumEuler = sum . map_euler . mkList
Теперь у нас есть только точки. На что указывают эти точки?
Из источника:
(.) :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
Таким образом, (.)
является оператором компоновки.
Написать
В математике мы могли бы написать композицию функций f (x) и g (x), то есть f (g (x)), как
(f ∘ g)(x)
который может быть прочитан "F составлен с G".
Таким образом, в Haskell можно записать f ∘ g или f, составленные с помощью g:
f . g
Композиция является ассоциативной, что означает, что f (g (h (x))), записанный с помощью оператора композиции, может оставить скобки без какой-либо двусмысленности.
То есть, поскольку (f ∘ g) ∘ h эквивалентно f ∘ (g ∘ h), мы можем просто написать f ∘ g ∘ h.
По кругу назад
Возвращаясь к нашему более раннему упрощению, это:
sumEuler = sum . map_euler . mkList
просто означает, что sumEuler
является неприменимой композицией этих функций:
sumEuler = \x -> sum (map_euler (mkList x))