2009-09-25 17 views
21

Je lis this tutoriel sur Haskell. Ils définissent la composition de fonctions comme suit:Haskell fonction composition

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

Aucun exemple n'a été fourni, que je crois me éclairer sur ce qui est défini ici. Est-ce que quelqu'un peut fournir un exemple simple (avec explication) de la façon dont la composition de la fonction est utilisée?

Répondre

38

La composition de fonction est un moyen de "composer" deux fonctions ensemble en une seule fonction. Voici un exemple:

Supposons que vous avez ces fonctions:

even :: Int -> Bool 
not :: Bool -> Bool 

et que vous voulez définir votre propre fonction myOdd :: Int -> Bool en utilisant les deux ci-dessus.

La façon évidente de faire est la suivante:

myOdd :: Int -> Bool 
myOdd x = not (even x) 

Mais cela peut être fait composition plus en utilisant succinctement la fonction:

myOdd :: Int -> Bool 
myOdd = not . even 

Les myOdd fonctions se comportent exactement les mêmes, mais le second on est créé en "collant" deux fonctions ensemble.

Un scénario où cela est particulièrement utile est de supprimer le besoin d'un lambda explicite. Par exemple:

map (\x -> not (even x)) [1..9] 

peut être réécrite à:

map (not . even) [1..9] 

Un peu plus court, moins de place pour les erreurs.

+0

Pourquoi n'avez-vous pas besoin d'afficher le paramètre d'entrée dans la définition? Par exemple. comment se fait-il que vous n'écriviez pas 'myOdd x = non. même x'? – unclerojelio

+2

@unclerojelio C'est ce qu'on appelle le style sans point. Plutôt que de définir 'myOdd' en termes de résultat pour un argument donné (" Given 'x',' myOdd' renvoie la même valeur que '(pas. Pair) x'"), il est défini en termes de ce qu'il fait réellement est ("' myOdd' est la fonction qui résulte quand 'not' est composé avec' even' "). – chepner

13

La composition de f et g est une fonction qui applique la première g à son argument, puis f à la valeur renvoyée par g. Il renvoie ensuite la valeur de retour de f.

Cette identité peut être éclairante:

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

Si vous avez un Java/arrière-plan C, considérez cet exemple:

int f(int x); 
int g(int x); 
int theComposition(int x) { return f(g(x)); } 
+0

+1 pour l'équivalence – outis

4

De l'HaskellWiki page on function composition:

desort = (reverse . sort) 

maintenant desort est une fonction qui trie une liste à l'envers. Fondamentalement, desort nourrit ses arguments en sort, puis alimente la valeur de retour de sort en reverse, un retour que. Donc, il trie, puis il inverse la liste triée.

7

Cet exemple est contraint, mais supposons que nous avons

sqr x = x * x 
inc x = x + 1 

et nous voulons écrire une fonction qui calcule x^2 + 1. On peut écrire

xSquaredPlusOne = inc . sqr 

(ce qui signifie

xSquaredPlusOne x = (inc . sqr) x 

qui signifie

xSquaredPlusOne x = inc(sqr x) 

puisque f = inc et g = SQR).

26

Note amusante. La composition de la fonction est l'équivalent d'un syllogisme en logique:

Tous les hommes sont mortels. Socrate est un homme. Par conséquent, Socrate est mortel.

Un syllogisme compose de deux implications importantes en un seul:

(Man => Mortal), (Socrates => Man), therefore (Socrates => Mortal) 

... Par conséquent

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

... ce qui est le type de la fonction ..

3

La composition de fonction est un moyen d'enchaîner deux fonctions ou plus. Il est souvent assimilé à la tuyauterie en coquille. Par exemple, dans un style shell Unix, vous pouvez écrire quelque chose comme

cat foo.txt | sort -n | less 

Cela va cat, alimente sa sortie à sort, et alimente la sortie de celui less. Strictement, cela ressemble à l'opérateur $ de Haskell. Vous pourriez écrire quelque chose comme

sum $ sort $ filter (> 0) $ my_list 

Notez que, contrairement à l'exemple du shell, cela se lit de droite à gauche. Donc, nous commençons par my_list en entrée, puis nous exécutons filter dessus, puis nous sort, puis nous calculons le sum.

L'opérateur de composition de fonction, ., effectue une opération similaire. L'exemple ci-dessus produit un numéro; l'exemple ci-dessous produit une fonction :

sum . sort . filter (> 0) 

Notez que nous ne nourrissons pas en fait une liste dans ce. Au lieu de cela, nous venons de créer une nouvelle fonction, et nous pouvons alimenter plusieurs listes différentes pour cette fonction.Par exemple, vous pouvez nommer cette fonction:

my_function = sum . sort . filter (> 0) 

Ou vous pourriez passer comme argument à une autre fonction:

map (sum . sort . filter (> 0)) my_lists 

Vous pouvez essentiellement l'utiliser partout où vous pouvez utiliser toute autre sorte de fonction . C'est juste une manière rapide et lisible de dire "Je veux enchaîner ces fonctions".