Punktoperator in Haskell: Weitere Erklärungen erforderlich

84

Ich versuche zu verstehen, was der Punktoperator in diesem Haskell-Code tut:

sumEuler = sum . (map euler) . mkList

Der gesamte Quellcode ist unten.

Mein Verständnis

Der Punktoperator übernimmt die beiden Funktionen sumund das Ergebnis map eulerund das Ergebnis von mkListals Eingabe.

Aber sumist eine Funktion nicht das Argument der Funktion, oder? Also, was ist hier los?

Und was (map euler)macht das?

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
cbrulak
quelle

Antworten:

137

Einfach ausgedrückt .ist die Funktionszusammensetzung genau wie in der Mathematik:

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

In Ihrem Fall erstellen Sie eine neue Funktion, sumEulerdie auch folgendermaßen definiert werden kann:

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

Der Stil in Ihrem Beispiel heißt "punktfreier" Stil - die Argumente für die Funktion werden weggelassen. Dies sorgt in vielen Fällen für klareren Code. (Es kann schwierig sein, beim ersten Mal zu groken, aber Sie werden sich nach einer Weile daran gewöhnen. Es ist eine verbreitete Haskell-Redewendung.)

Wenn Sie immer noch verwirrt sind, kann es hilfreich sein, sich .auf eine UNIX-Pipe zu beziehen . Wenn fdie Ausgabe zur gEingabe wird, deren Ausgabe zur hEingabe wird, schreiben Sie dies wie folgt in die Befehlszeile f < x | g | h. Funktioniert in Haskell .wie UNIX |, jedoch "rückwärts" - h . g . f $ x. Ich finde diese Notation sehr hilfreich, wenn ich beispielsweise eine Liste verarbeite. Anstelle einer unhandlichen Konstruktion map (\x -> x * 2 + 10) [1..10]könnte man einfach schreiben (+10) . (*2) <$> [1..10]. (Und wenn Sie diese Funktion nur auf einen einzelnen Wert anwenden möchten, ist dies (+10) . (*2) $ 10. Konsistent!)

Das Haskell-Wiki enthält einen guten Artikel mit weiteren Details: http://www.haskell.org/haskellwiki/Pointfree

Jrockway
quelle
1
Winziger Streit: Das erste Code-Snippet ist nicht wirklich gültig, Haskell.
SwiftsNamesake
2
@SwiftsNamesake Für diejenigen von uns, die Haskell nicht fließend sprechen, meinen Sie damit nur, dass das einzelne Gleichheitszeichen hier keine Bedeutung hat? (Also sollte das Snippet mit " f (g x)= (f . g) x" formatiert sein ?) Oder etwas anderes?
user234461
1
@ user234461 Genau, ja. Sie benötigen ==stattdessen, wenn Sie einen gültigen Standard-Haskell wünschen.
SwiftsNamesake
Das kleine Snippet oben ist nur Gold. Wie die anderen Antworten hier sind sie richtig, aber dieses Snippet hat direkt intuitiv in meinem Kopf geklickt, was es unnötig machte, den Rest Ihrer Antwort zu lesen.
Tarick Welling
24

Das . Operator komponiert Funktionen. Beispielsweise,

a . b

Wenn a und b Funktionen sind, handelt es sich um eine neue Funktion , die b für ihre Argumente und dann a für diese Ergebnisse ausführt. Dein 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, warum es Parens um Map Euler gibt, ist, dass dadurch klarer wird, dass drei Funktionen zusammengesetzt sind: Summe , Map Euler und mkList - Map Euler ist eine einzelne Funktion.

Jesse Rusak
quelle
23

sumist eine Funktion im Haskell-Präludium, kein Argument dafür sumEuler. Es hat den Typ

Num a => [a] -> a

Der Funktionskompositionsoperator . hat den Typ

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

Also haben wir

           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

Beachten Sie, dass dies Inttatsächlich eine Instanz der NumTypklasse ist.

Chris Conway
quelle
11

Das . Der Operator wird für die Funktionszusammensetzung verwendet. Genau wie Mathe, wenn Sie die Funktionen f (x) und g (x) f müssen. g wird zu f (g (x)).

map ist eine integrierte Funktion, die eine Funktion auf eine Liste anwendet. Wenn Sie die Funktion in Klammern setzen, wird die Funktion als Argument behandelt. Ein Begriff dafür ist Curry . Sie sollten das nachschlagen.

Was es tut, ist, dass es eine Funktion mit beispielsweise zwei Argumenten übernimmt, es wendet das Argument euler an. (Karten-Euler) richtig? und das Ergebnis ist eine neue Funktion, die nur ein Argument akzeptiert.

Summe . (Karten-Euler). mkList ist im Grunde eine ausgefallene Art, all das zusammenzustellen. Ich muss sagen, mein Haskell ist ein bisschen verrostet, aber vielleicht können Sie diese letzte Funktion selbst zusammenstellen?

John Leidegren
quelle
5

Punktoperator in Haskell

Ich versuche zu verstehen, was der Punktoperator in diesem Haskell-Code tut:

sumEuler = sum . (map euler) . mkList

Kurze Antwort

Äquivalenter Code ohne Punkte, das ist gerecht

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

oder ohne das Lambda

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

weil der Punkt (.) die Funktionszusammensetzung anzeigt.

Längere Antwort

Lassen Sie uns zuerst die partielle Anwendung vereinfachen eulerzu map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

Jetzt haben wir nur noch die Punkte. Was wird durch diese Punkte angezeigt?

Aus der Quelle :

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

Somit (.)ist der Compose-Operator .

Komponieren

In der Mathematik könnten wir die Zusammensetzung der Funktionen f (x) und g (x), dh f (g (x)), als schreiben

(f ∘ g) (x)

was gelesen werden kann "f zusammengesetzt mit g".

In Haskell kann also f ∘ g oder f mit g zusammengesetzt geschrieben werden:

f . g

Die Komposition ist assoziativ, was bedeutet, dass f (g (h (x))), geschrieben mit dem Kompositionsoperator, die Klammern ohne Mehrdeutigkeit weglassen kann.

Das heißt, da (f ∘ g) ∘ h äquivalent zu f ∘ (g ∘ h) ist, können wir einfach f ∘ g ∘ h schreiben.

Zurück kreisen

Zurück zu unserer früheren Vereinfachung:

sumEuler = sum . map_euler . mkList

bedeutet nur, dass sumEuleres sich um eine nicht angewendete Zusammensetzung dieser Funktionen handelt:

sumEuler = \x -> sum (map_euler (mkList x))
Aaron Hall
quelle
4

Der Punktoperator wendet die Funktion links ( sum) auf die Ausgabe der Funktion rechts an. In Ihrem Fall verketten Sie mehrere Funktionen miteinander - Sie übergeben das Ergebnis von mkListan (map euler)und dann das Ergebnis von an sum. Diese Seite bietet eine gute Einführung in einige der Konzepte.

Andy Mikula
quelle