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