Dot Operator в Haskell: нужно больше объяснений - PullRequest
73 голосов
/ 10 марта 2009

Я пытаюсь понять, что делает оператор точки в этом коде на 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 ]

123 голосов
/ 10 марта 2009

Проще говоря, . - это композиция функций, как в математике:

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. Согласованно!)

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

22 голосов
/ 10 марта 2009

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

a . b

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

sumEuler = sum . (map euler) . mkList

точно такой же, как:

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

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

21 голосов
/ 10 марта 2009

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.

11 голосов
/ 10 марта 2009

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

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

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

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

4 голосов
/ 10 марта 2009

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

2 голосов
/ 28 января 2018

Точка оператора в Хаскеле

Я пытаюсь понять, что делает оператор точки в этом коде 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)

Таким образом (.) является оператором 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 ∘ g) ∘ h эквивалентно f ∘ (g ∘ h), мы можем просто написать f ∘ g ∘ h.

по кругу назад

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

sumEuler = sum . map_euler . mkList

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

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