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