web-dev-qa-db-fra.com

Opérateur de points à Haskell: besoin de plus d'explications

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.

Ma compréhension

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?

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

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

127
jrockway

Le . L'opérateur compose les fonctions. Par exemple,

a . b

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.

23
Jesse Rusak

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.

21
Chris Conway

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?

11
John Leidegren

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.

4
Andy Mikula

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

Réponse courte

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.

Réponse plus longue

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 .

Composer

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.

Retour en arrière

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))
2
Aaron Hall