Оператор "точка" в 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))

стиль в вашем примере называется стилем" без точек " - аргументы функции опущены. Это делает код во многих случаях более четким. (Это может быть трудно понять в первый раз вы видите его, но вы привыкнете к нему через некоторое время. Это обычный Haskell идиома.)

если вы все еще смущены, это может помочь связать . к чему-то вроде трубы UNIX. Если 's становится 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. Последовательный!)

в Хаскеле Вики есть хорошая статья с более подробно: http://www.haskell.org/haskellwiki/Pointfree


sum является функцией в прелюдии Хаскелла, а не аргументом для 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.


The . оператор составляет функции. Например,

a . b

здесь a и b функции-новый функции это запускает b по его аргументам, а затем a по этим результатам. Ваш код

sumEuler = sum . (map euler) . mkList

- Это точно так же, как:

sumEuler myArgument = sum (map euler (mkList myArgument))

но, надеюсь, легче читать. Причина, по которой есть родители вокруг карта Эйлера потому что это делает его более ясным, что есть 3 функции составляются: sum, карта Эйлера и mkList - карта Эйлера Единая функция.


The . оператор используется для композиции функций. Как и математика, если вам нужны функции f(x) и g (x) f . g становится f(g (x)).

карта-это встроенная функция, которая применяет функцию к списку. Помещая функцию в круглые скобки, функция рассматривается как аргумент. Это называется карринг. Тебе стоит это проверить.

Что делает, так это то, что он принимает функцию с двумя аргументами, она применяет аргумент Эйлера. (карта Эйлера) верно? и результатом является новая функция, которая принимает только один аргумент.

сумма . (карта Эйлера) . mkList-это в основном причудливый способ собрать все это вместе. Я должен сказать, мой Haskell немного ржавый, но, может быть, вы можете собрать эту последнюю функцию самостоятельно?


оператор точки применяет функцию слева (sum) к выходу функции справа. В вашем случае вы связываете несколько функций вместе - вы передаете результат mkList to (map euler), а затем передать результат этого в sum. этот сайт имеет хорошее введение в несколько концепций.


оператор точки в Haskell

я пытаюсь понять, что делает оператор точки в этом коде Haskell:

sumEuler = sum . (map euler) . mkList

короткий ответ:

эквивалентный код без точек, то есть просто

sumEuler = \x -> sum ((map euler) (mkList x))

или без лямбда

sumEuler x = sum ((map euler) (mkList x))

потому что точка (.) показывает состав функции.

более длинный ответ

во-первых, давайте упростим частичное применение euler to map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

теперь у нас есть только точки. На что указывают эти точки?

С источник:

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)
(.) - это оператор compose.

Compose

в математике мы могли бы написать состав функций, f(x) и g(x), то есть f(g (x)), как

(f ∘ g) (x)

где можно прочитать "f составлено с g".

так в Haskell, f ∘ g или f, составленный с g, можно написать:

f . g

композиция ассоциативна, что означает, что f(g(h(x))), написанный с помощью оператора композиции, может оставить скобки без какой-либо двусмысленности.

то есть, с (ф ∘ г) ∘ ч приравнивается к F ∘ (г ∘ ч), можно просто писать F ∘ г ∘ з.

возвращается

возвращаясь к нашему предыдущему упрощению, это:

sumEuler = sum . map_euler . mkList

просто означает, что sumEuler является неприменимой композицией этих функций:

sumEuler = \x -> sum (map_euler (mkList x))