Operator kropki w Haskell: potrzebuję więcej wyjaśnień

86

Próbuję zrozumieć, co robi operator kropki w tym kodzie Haskella:

sumEuler = sum . (map euler) . mkList

Cały kod źródłowy znajduje się poniżej.

Moje zrozumienie

Operator kropki przyjmuje jako dane wejściowe dwie funkcje sumoraz wynik map euleri wynik mkListfunkcji.

Ale sumczy funkcja nie jest argumentem funkcji, prawda? Więc co tu się dzieje?

Co też (map euler)robi?

Kod

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
cbrulak
źródło

Odpowiedzi:

138

Mówiąc prościej, .jest kompozycją funkcji, tak jak w matematyce:

f (g x) = (f . g) x

W twoim przypadku tworzysz nową funkcję, sumEulerktórą można również zdefiniować w ten sposób:

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

Styl w twoim przykładzie jest nazywany stylem „bez punktów” - argumenty funkcji są pomijane. W wielu przypadkach zapewnia to bardziej przejrzysty kod. (Może to być trudne, gdy zobaczysz go po raz pierwszy, ale po chwili się do tego przyzwyczaisz. To popularny idiom Haskella).

Jeśli nadal jesteś zdezorientowany, pomocne może być odniesienie się .do czegoś takiego jak potok UNIX. Jeśli fwyjście staje się gwejściem, którego wyjście staje się hwejściem, można to zapisać w linii poleceń, jak f < x | g | h. W Haskell .działa jak UNIX |, ale "wstecz" - h . g . f $ x. Uważam, że ta notacja jest bardzo pomocna podczas, powiedzmy, przetwarzania listy. Zamiast jakiejś nieporęcznej konstrukcji map (\x -> x * 2 + 10) [1..10], możesz po prostu pisać (+10) . (*2) <$> [1..10]. (A jeśli chcesz zastosować tę funkcję tylko do jednej wartości, to jest (+10) . (*2) $ 10. Spójne!)

Na wiki Haskell znajduje się dobry artykuł zawierający więcej szczegółów: http://www.haskell.org/haskellwiki/Pointfree

jrockway
źródło
1
Drobny spór: pierwszy fragment kodu w rzeczywistości nie jest prawidłowy, Haskell.
SwiftsNamesake
2
@SwiftsNamesake Dla tych z nas, którzy nie biegle posługują się językiem Haskell, czy masz na myśli, że pojedynczy znak równości nie ma tutaj znaczenia? (Więc fragment powinien być sformatowany jako „ f (g x)= (f . g) x”?) Czy coś innego?
user234461
1
@ user234461 Dokładnie, tak. Należałoby ==zamiast jeśli chcesz wzorca Haskell.
SwiftsNamesake
Ten mały fragment u góry to tylko złoto. Podobnie jak inne odpowiedzi tutaj są poprawne, ale ten fragment po prostu kliknął bezpośrednio intuicyjnie w mojej głowie, co sprawiło, że nie trzeba było czytać reszty odpowiedzi.
Tarick Welling
24

Plik. operator komponuje funkcje. Na przykład,

a . b

Gdzie a i b są funkcjami, to nowa funkcja, która uruchamia b na swoich argumentach, a następnie a na tych wynikach. Twój kod

sumEuler = sum . (map euler) . mkList

jest dokładnie taki sam jak:

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

ale miejmy nadzieję, że łatwiej je przeczytać. Powodem, dla którego wokół map eulerpareny, jest to, że wyjaśnia to, że składane są 3 funkcje: sum , map euler i mkList - map euler to pojedyncza funkcja.

Jesse Rusak
źródło
23

sumjest funkcją w Haskell Prelude, a nie argumentem do sumEuler. Ma swój typ

Num a => [a] -> a

Operator kompozycji funkcji . ma typ

(b -> c) -> (a -> b) -> a -> c

Więc mamy

           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

Zauważ, że Intrzeczywiście jest to instancja Numtypeklasy.

Chris Conway
źródło
11

Plik. operator jest używany do tworzenia funkcji. Podobnie jak matematyka, jeśli masz do funkcji f (x) i g (x) f. g staje się f (g (x)).

mapa to wbudowana funkcja, która stosuje funkcję do listy. Umieszczając funkcję w nawiasach, funkcja jest traktowana jako argument. Określeniem na to jest curry . Powinieneś to sprawdzić.

To, co robi, to to, że przyjmuje funkcję z, powiedzmy, dwoma argumentami, stosuje argument euler. (map euler) prawda? a wynikiem jest nowa funkcja, która przyjmuje tylko jeden argument.

suma . (mapa euler). mkList to po prostu fantazyjny sposób na złożenie tego wszystkiego w całość. Muszę powiedzieć, że mój Haskell jest trochę zardzewiały, ale może możesz sam połączyć tę ostatnią funkcję?

John Leidegren
źródło
5

Operator kropki w Haskell

Próbuję zrozumieć, co robi operator kropki w tym kodzie Haskella:

sumEuler = sum . (map euler) . mkList

Krótka odpowiedź

To znaczy równoważny kod bez kropek

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

lub bez lambda

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

ponieważ kropka (.) wskazuje skład funkcji.

Dłuższa odpowiedź

Najpierw uprośćmy częściowe zastosowanie funkcji eulerdo map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

Teraz mamy tylko kropki. Na co wskazują te kropki?

Ze źródła :

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

Tak (.)jest z operatorem tworzenia .

Komponować

W matematyce moglibyśmy zapisać skład funkcji, f (x) i g (x), czyli f (g (x)), jako

(f ∘ g) (x)

które można odczytać jako „f składa się z g”.

Zatem w Haskell f ∘ g lub f złożone z g można zapisać:

f . g

Kompozycja jest asocjacyjna, co oznacza, że ​​f (g (h (x))), napisane operatorem kompozycji, może pomijać nawiasy bez żadnej dwuznaczności.

To znaczy, ponieważ (f ∘ g) ∘ h jest równoważne f ∘ (g ∘ h), możemy po prostu napisać f ∘ g ∘ h.

Wracając

Wracając do naszego wcześniejszego uproszczenia, to:

sumEuler = sum . map_euler . mkList

oznacza po prostu, że sumEulerjest to niezastosowana kompozycja tych funkcji:

sumEuler = \x -> sum (map_euler (mkList x))
Aaron Hall
źródło
4

Operator kropkowy stosuje funkcję po lewej stronie ( sum) do wyjścia funkcji po prawej stronie. W twoim przypadku łączysz ze sobą kilka funkcji - przekazujesz wynik mkListdo (map euler), a następnie przekazujesz wynik do sum. Ta strona zawiera dobre wprowadzenie do kilku pojęć.

Andy Mikula
źródło