Question

J'essaie de comprendre ce que fait l'opérateur point dans ce code Haskell:

sumEuler = sum . (map euler) . mkList

L'intégralité du code source 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 en entrée.

Mais (map euler) n'est pas une fonction, c'est l'argument de la fonction, non? Que se passe-t-il?

Aussi, que fait <=>?

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
Était-ce utile?

La solution

En termes simples, . est la composition de fonctions, 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 suit:

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

Le style de votre exemple s'appelle & "Point-Free &"; style - les arguments de la fonction sont omis. Cela rend le code plus clair dans de nombreux cas. (Il peut être difficile de parler la première fois que vous le voyez, mais vous vous y habituerez un peu plus tard. C’est un idiot commun de Haskell.)

Si vous êtes toujours confus, il peut être utile de relier f à quelque chose comme un tuyau UNIX. Si la sortie de g devient l'entrée de h, dont la sortie devient l'entrée de f < x | g | h, vous écrivez cela sur la ligne de commande, comme |. Dans Haskell, h . g . f $ x fonctionne comme le système UNIX map (\x -> x * 2 + 10) [1..10], mais & "À l'envers &"; - (+10) . (*2) <$> [1..10]. Je trouve cette notation très utile pour, par exemple, le traitement d’une liste. Au lieu d’une construction trop lourde comme (+10) . (*2) $ 10, vous pouvez simplement écrire <=>. (Et si vous ne souhaitez appliquer cette fonction qu'à une seule valeur, elle est <=>. Cohérente!)

Le wiki de Haskell contient un bon article plus détaillé: http://www.haskell.org/ haskellwiki / Pointfree

Autres conseils

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 groupes autour de map euler est qu’il est plus clair que 3 fonctions sont en cours de composition: sum , map euler et mkList - map euler est une fonction unique.

sum est une fonction du prélude Haskell, pas un argument pour sumEuler. Il a le type

Num a => [a] -> a

L'opérateur de composition de fonction . a le 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. opérateur est utilisé pour la composition de la fonction. Tout comme en 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 regarder ça.

Ce qu’il fait, c’est qu’il faut une fonction avec deux arguments, il applique l’argument euler. (carte euler) non? et le résultat est une nouvelle fonction, qui prend un seul argument.

somme. (carte euler). mkList est fondamentalement une manière élégante 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 de points applique la fonction à gauche (sum) à la sortie de la fonction à droite. Dans votre cas, vous enchaînez plusieurs fonctions. Vous transmettez le résultat de mkList à (map euler), puis le résultat à <=>. Ce site a une bonne introduction à plusieurs des concepts.

  

Opérateur de points dans 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-à-dire

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)

Ainsi (.) est l’opérateur composer .

Composer

En mathématiques, nous pourrions écrire la composition des fonctions, f (x) et g (x), c’est-à-dire f (g (x)), comme

  

(f & # 8728; g) (x)

qui peut être lu & "; composé avec g &";

Donc, dans Haskell, f & # 8728; g, ou f composé avec g, peut être écrit:

f . g

La composition est associative, ce qui signifie que f (g (h (x))), écrit avec l'opérateur de composition, peut omettre les parenthèses sans aucune ambiguïté.

C'est-à-dire puisque (f & # 8728; g) & # 8728; h est équivalent à f & # 8728; (g & # 8728; h), nous pouvons simplement écrire f & # 8728; g & # 8728; h.

Retour en arrière

Pour revenir à notre simplification antérieure, 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))
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top