J'essaie de comprendre ce que fait l'opérateur point dans ce code Haskell:
sumEuler = sum . (map euler) . mkList
Le code source complet est ci-dessous.
L'opérateur point prend les deux fonctions sum
et le résultat de map euler
et le résultat de mkList
comme entrée.
Mais, sum
n'est pas une fonction, c'est l'argument de la fonction, non? Que se passe-t-il?
De plus, qu'est-ce que (map euler)
Faire?
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
En termes simples, .
Est la composition des fonctions, tout comme en mathématiques:
f (g x) = (f . g) x
Dans votre cas, vous créez une nouvelle fonction, sumEuler
qui pourrait également être définie comme ceci:
sumEuler x = sum (map euler (mkList x))
Le style de votre exemple est appelé style "sans point" - les arguments de la fonction sont omis. Cela rend le code plus clair dans de nombreux cas. (Il peut être difficile de grogner la première fois que vous le voyez, mais vous vous y habituerez après un certain temps. C'est un idiome Haskell commun.)
Si vous êtes toujours confus, il peut être utile de relier .
À quelque chose comme un canal UNIX. Si la sortie de f
devient l'entrée de g
, dont la sortie devient l'entrée de h
, vous l'écririez sur la ligne de commande comme f < x | g | h
. Dans Haskell, .
Fonctionne comme UNIX |
, Mais "en arrière" - h . g . f $ x
. Je trouve cette notation très utile lorsque, par exemple, le traitement d'une liste. Au lieu d'une construction lourde comme map (\x -> x * 2 + 10) [1..10]
, vous pouvez simplement écrire (+10) . (*2) <$> [1..10]
. (Et si vous ne souhaitez appliquer cette fonction qu'à une seule valeur, c'est (+10) . (*2) $ 10
. Cohérent!)
Le wiki Haskell contient un bon article avec plus de détails: http://www.haskell.org/haskellwiki/Pointfree
Le . L'opérateur compose les fonctions. Par exemple,
a . b
Où a et b sont des fonctions est une nouvelle fonction qui exécute b sur ses arguments, puis a sur ces résultats. Votre code
sumEuler = sum . (map euler) . mkList
est exactement le même que:
sumEuler myArgument = sum (map euler (mkList myArgument))
mais j'espère plus facile à lire. La raison pour laquelle il y a des parens autour map euler est parce que cela rend plus clair qu'il y a 3 fonctions en cours de composition: sum , map euler and mkList - map euler est une fonction unique.
sum
est une fonction du Haskell Prelude, pas un argument de sumEuler
. Il a le type
Num a => [a] -> a
L'opérateur de composition de fonction .
a un type
(b -> c) -> (a -> b) -> a -> c
Nous avons donc
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
Notez que Int
est une instance de Num
.
Le . L'opérateur est utilisé pour la composition des fonctions. Tout comme les mathématiques, si vous devez utiliser les fonctions f(x) et g(x) f. G devient f (g (x))).
map est une fonction intégrée qui applique une fonction à une liste. En mettant la fonction entre parenthèses, la fonction est traitée comme un argument. Un terme pour cela est currying . Vous devriez chercher cela.
Ce qui est, c'est qu'il prend une fonction avec disons deux arguments, il applique l'argument euler. (carte euler) non? et le résultat est une nouvelle fonction, qui ne prend qu'un seul argument.
somme . (carte euler). mkList est fondamentalement une manière sophistiquée de rassembler tout cela. Je dois dire que mon Haskell est un peu rouillé mais peut-être pouvez-vous assembler cette dernière fonction vous-même?
L'opérateur point applique la fonction à gauche (sum
) à la sortie de la fonction à droite. Dans votre cas, vous enchaînez plusieurs fonctions ensemble - vous passez le résultat de mkList
à (map euler)
, puis en transmettant le résultat à sum
. Ce site a une bonne introduction à plusieurs des concepts.
Opérateur de points à Haskell
J'essaie de comprendre ce que fait l'opérateur point dans ce code Haskell:
sumEuler = sum . (map euler) . mkList
Code équivalent sans points, c'est juste
sumEuler = \x -> sum ((map euler) (mkList x))
ou sans le lambda
sumEuler x = sum ((map euler) (mkList x))
car le point (.) indique la composition de la fonction.
Tout d'abord, simplifions l'application partielle de euler
à map
:
map_euler = map euler
sumEuler = sum . map_euler . mkList
Maintenant, nous avons juste les points. Qu'est-ce qui est indiqué par ces points?
De la source :
(.) :: (b -> c) -> (a -> b) -> a -> c (.) f g = \x -> f (g x)
Donc (.)
est l'opérateur de composition .
En mathématiques, nous pouvons écrire la composition des fonctions, f(x) et g (x), c'est-à-dire f (g (x)), comme
(f ∘ g) (x)
qui peut être lu "f composé avec g".
Donc en Haskell, f ∘ g, ou f composé de g, peut s'écrire:
f . g
La composition est associative, ce qui signifie que f (g (h (x))), écrit avec l'opérateur de composition, peut laisser de côté les parenthèses sans ambiguïté.
Autrement dit, puisque (f ∘ g) ∘ h est équivalent à f ∘ (g ∘ h), nous pouvons simplement écrire f ∘ g ∘ h.
En revenant à notre simplification précédente, ceci:
sumEuler = sum . map_euler . mkList
signifie simplement que sumEuler
est une composition non appliquée de ces fonctions:
sumEuler = \x -> sum (map_euler (mkList x))