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

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

sumEuler = sum . (map euler) . mkList

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

Moje zrozumienie

Operator kropki przyjmuje dwie funkcje sum oraz wynik map euler i wynik mkList jako dane wejściowe.

Ale sum to nie jest funkcja, to jest argument funkcji, prawda? Co tu się dzieje?

Również, co robi (map euler)?

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
Author: Martin Thoma, 2009-03-10

6 answers

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

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

W Twoim przypadku tworzysz nową funkcję, sumEuler, która może być również zdefiniowana w następujący sposób:

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

Styl w twoim przykładzie nazywa się stylem" point-free " - argumenty funkcji są pomijane. To sprawia, że w wielu przypadkach kod jest jaśniejszy. (Może być ciężko grok za pierwszym razem, ale przyzwyczaisz się do tego po pewnym czasie. Jest to częsty idiom Haskella.)

Jeśli jesteś wciąż zdezorientowany, może pomóc w powiązaniu . z czymś w rodzaju uniksowej rury. Jeśli wyjście f staje się wejściem g, A wyjście staje się wejściem h, napiszesz to w wierszu 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, gdy, powiedzmy, przetwarzamy listę. Zamiast jakiejś nieporęcznej konstrukcji jak map (\x -> x * 2 + 10) [1..10], możesz po prostu napisać (+10) . (*2) <$> [1..10]. (I, jeśli chcesz zastosować tę funkcję tylko do pojedynczej wartości; (+10) . (*2) $ 10. Konsekwentnie!)

Haskell wiki ma dobry artykuł z bardziej szczegółowymi informacjami: http://www.haskell.org/haskellwiki/Pointfree

 117
Author: jrockway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-26 05:54:00

sum jest funkcją w preludium Haskella, a nie argumentem sumEuler. Posiada Typ

Num a => [a] -> a

Operator składu funkcji . mA typ

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

Więc mamy

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

Zauważ, że Int jest instancją Num.

 20
Author: Chris Conway,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-10 20:02:00

The . operator komponuje funkcje. Na przykład,

a . b

Gdzie a i b są funkcjami jest nową funkcją , 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 takie samo jak:

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

Ale mam nadzieję, że łatwiejsze do odczytania. Powodem, dla którego istnieją pareny wokół mapy Eulera jest to, że wyjaśnia to, że złożone są 3 Funkcje: suma, Mapa euler i mkList - Mapa Eulera jest funkcją pojedynczą.

 20
Author: Jesse Rusak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-28 04:08:09

The . operator jest używany do składu funkcji. Podobnie jak matematyka, jeśli masz funkcje f (x) i g (x) f . g staje się f (g (x)).

Map jest wbudowaną funkcją, która stosuje funkcję do listy. Poprzez umieszczenie funkcji w nawiasach funkcja jest traktowana jako argument. Termin na to to currying . Powinieneś to sprawdzić.

To, co robi, to to, że przyjmuje funkcję z dwoma argumentami, stosuje argument Eulera. / align = "left" / a rezultatem jest nowa funkcja, która przyjmuje tylko jeden argument.

Sum . (Mapa Eulera). mkList jest w zasadzie fantazyjnym sposobem na złożenie tego wszystkiego razem. Muszę powiedzieć, że mój Haskell jest trochę zardzewiały, ale może uda Ci się złożyć tę ostatnią funkcję samodzielnie?

 10
Author: John Leidegren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-10 17:08:08

Operator kropki stosuje funkcję po lewej (sum) do wyjścia funkcji po prawej. W Twoim przypadku łączysz ze sobą kilka funkcji-przekazujesz wynik mkList do (map euler), a następnie przekazujesz wynik tego do sum. ta strona ma dobre wprowadzenie do kilku pojęć.

 4
Author: Andy Mikula,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-10 17:09:09

Operator kropki w Haskell

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

sumEuler = sum . (map euler) . mkList

Krótka odpowiedź

Odpowiednik kodu bez kropek, czyli po prostu

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

Lub bez lambdy

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

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

Dłuższa odpowiedź

Najpierw uprośćmy częściowe zastosowanie euler do map:

map_euler = map euler
sumEuler = sum . map_euler . mkList
Teraz mamy tylko kropki. Na co wskazują te kropki?

From the source:

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

Zatem (.) jest operatorem składowym .

Skomponuj

W matematyce możemy zapisać skład funkcji, f (x) i g (x), czyli f(g(x)), jako

(f ∘ g) (x)

Które można odczytać jako "F złożone z g".

Więc w Haskell, f ∘ g, lub F złożone z g, można zapisać:

f . g

Skład jest asocjacyjny, co oznacza, że f (g (h (x))), zapisany operatorem kompozycji, może bez niejednoznaczności pominąć nawiasy.

Czyli z (f ∘ g) ∘ H jest równoważny do F ∘ (g ∘ h), możemy po prostu napisać F ∘ g ∘ s.

Krąży wstecz

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

sumEuler = sum . map_euler . mkList

Oznacza po prostu, że sumEuler jest nieujemnym składem tych funkcji:

sumEuler = \x -> sum (map_euler (mkList x))
 1
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-28 04:06:22