Punkt-Operator in Haskell: Notwendigkeit mehr Erklärung
-
08-07-2019 - |
Frage
Ich versuche zu verstehen, was der Punkt-Operator in diesem Haskell Code tun:
sumEuler = sum . (map euler) . mkList
Der gesamte Quellcode ist unten.
Mein Verständnis
Der Punkt-Operator nimmt die beiden Funktionen sum
und das Ergebnis map euler
und das Ergebnis des mkList
als Eingang.
Aber ist sum
keine Funktion es das Argument der Funktion ist, nicht wahr? Also, was ist hier los?
Auch was tut (map euler)
?
Code
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
Lösung
Einfach gesagt, .
ist Funktion Zusammensetzung, wie in der Mathematik:
f (g x) = (f . g) x
In Ihrem Fall, erstellen Sie eine neue Funktion, sumEuler
, die auch wie folgt definiert werden:
sumEuler x = sum (map euler (mkList x))
Der Stil in Ihrem Beispiel „Punkt-free“ Stil genannt wird - die Argumente der Funktion weggelassen. Dies sorgt für klareren Code in vielen Fällen. (Es kann schwierig sein, das erste Mal grok Sie es sehen, aber Sie werden nach einer Weile daran gewöhnen. Es ist ein verbreitetes Haskell Idiom.)
Wenn Sie immer noch verwirrt sind, kann es helfen .
zu so etwas wie eine UNIX-Pipe zu beziehen. Wenn f
der Ausgang g
der Eingang wird, dessen Ausgang wird h
der Eingang, dann würden Sie, dass auf der Kommandozeile wie f < x | g | h
schreiben. In Haskell, arbeitet .
wie die UNIX |
, aber "nach hinten" - h . g . f $ x
. Ich finde diese Notation als sehr hilfreich, wenn, sagen wir, eine Liste zu verarbeiten. Statt einiger unhandlichen Konstruktion wie map (\x -> x * 2 + 10) [1..10]
, könnten Sie einfach (+10) . (*2) <$> [1..10]
schreiben. (Und wenn Sie wollen nur, dass die Funktion auf einen einzigen Wert anzuwenden;.! Es ist (+10) . (*2) $ 10
Konsistente)
Das Haskell Wiki hat einen guten Artikel mit etwas mehr Detail: http://www.haskell.org/ haskellwiki / Pointfree
Andere Tipps
Die. Operator komponiert Funktionen. Zum Beispiel:
a . b
Wo a und b sind Funktionen ist eine neue Funktion , die b auf seine Argumente ausgeführt wird, dann wird ein auf diesen Ergebnissen. Ihr Code
sumEuler = sum . (map euler) . mkList
ist genau das gleiche wie:
sumEuler myArgument = sum (map euler (mkList myArgument))
aber hoffentlich leichter zu lesen. Der Grund gibt es Pars um Karte euler ist, weil es deutlicher macht, dass es 3 Funktionen zusammensetzt: Summe Karte euler und mkList -. Karte euler ist eine einzige Funktion
sum
ist eine Funktion in der Haskell Prelude, kein Argument sumEuler
. Es hat den Typ
Num a => [a] -> a
Die Funktion Zusammensetzung Operator .
hat Typen
(b -> c) -> (a -> b) -> a -> c
So haben wir
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
Beachten Sie, dass Int
eine Instanz von Num
ist.
Die. Operator für Funktions Zusammensetzung verwendet. Genau wie Mathematik, wenn Sie auf die Funktionen f (x) und g (x) f haben. g wird f (g (x)).
Karte ist eine eingebaute Funktion, die eine Funktion auf eine Liste gilt. Indem die Funktion in Klammern wird die Funktion als Argument behandelt. Ein Begriff hierfür ist currying . Sie sollten das nachzuschlagen.
Was ist tut, ist, dass sie eine Funktion nimmt mit sagen zwei Argumente, es das Argument euler gilt. (Karte euler) richtig? und das Ergebnis ist eine neue Funktion, die nur ein Argument.
Summe. (Karte euler). mkList ist im Grunde eine andere Art das alles zusammen zu stellen. Ich muss sagen, mein Haskell ein wenig eingerostet ist, aber vielleicht können Sie diese letzte Funktion zusammengestellt selbst?
Der Punkt-Operator wendet die Funktion auf der linken Seite (sum
) mit dem Ausgang der Funktion auf der rechten Seite. In Ihrem Fall sind verketten Sie mehrere Funktionen zusammen - das Ergebnis von mkList
vorbei ist, (map euler)
und dann vorbei das Ergebnis, das sum
.
Diese Seite hat eine gute Einführung in einige der Konzepte.
Dot Operator in Haskell
Ich versuche zu verstehen, was der Punkt-Operator in diesem Haskell Code tun:
sumEuler = sum . (map euler) . mkList
Kurze Antwort
Equivalent Code ohne Punkte, das ist nur
sumEuler = \x -> sum ((map euler) (mkList x))
oder ohne Lambda
sumEuler x = sum ((map euler) (mkList x))
, da der Punkt (.) Zeigt Funktion Zusammensetzung.
Längere Antwort
Lassen Sie uns zuerst die partielle Anwendung von euler
zu map
vereinfachen:
map_euler = map euler
sumEuler = sum . map_euler . mkList
Jetzt haben wir nur die Punkte. Was wird durch diese Punkte angedeutet?
Von die Quelle :
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g = \x -> f (g x)
So (.)
ist die Operator zu komponieren.
Compose
In der Mathematik, könnten wir die Zusammensetzung der Funktionen f (x) und g (x) schreiben, das heißt, f (g (x)), wie
(f ∘ g) (x)
, die gelesen werden kann „f mit g zusammengesetzt“.
So in Haskell, f ∘ g oder f mit g zusammengesetzt ist, kann geschrieben werden:
f . g
Zusammensetzung ist assoziativ, was bedeutet, daß f (g (h (x))), mit der Zusammensetzung Operator geschrieben werden, kann ohne Mehrdeutigkeit die Klammern wegzulassen.
Das heißt, da (f ∘ g) ∘ h entspricht f ∘ (g ∘ h), können wir einfach schreiben f ∘ g ∘ h.
Circling zurück
Circling zurück zu unserer früheren Vereinfachung folgt aus:
sumEuler = sum . map_euler . mkList
nur bedeutet, dass sumEuler
eine unangewendet Zusammensetzung dieser Funktionen:
sumEuler = \x -> sum (map_euler (mkList x))