FSharp et le pattern matching

Je m’intéresse un peu à la programmation fonctionnelle en ce moment et une des fonctionnalités que je trouve très intéressante est le pattern matching.

Pour faire comprendre l’intérêt, prenons un exemple classique : la fonction Factorielle.

Exemple de Pattern matching

En C # :

public class Factorielle
{
    public static int fact(int n)
    {
        if (n == 0)
        {
            return 1;
        }
        else
        {
            return n*fact(n - 1);
        }
    }
}

En F# :

let rec fact n =
    match n with
    | 0 -> 1
    | _ -> n * fact(n-1)

ou encore plus court :

let rec fact = function
    | 0 -> 1
    | x -> x * fact(x-1)

Pour commencer, un avantage de F# est de n’avoir besoin de définir que la fonction.
En C#, rien que pour la fonction factorielle, on doit :

  • créer une classe,
  • définir la déclaration de la fonction,
  • préciser qu’elle est public …

Si on regarde bien, on voit aussi qu’on ajoute beaucoup de parenthèses et d’accolades pour pas grand-chose. En théorie, si le code est bien écrit, les accolades ne devraient servir à rien. On devrait pouvoir facilement voir où sont les blocs de code.

En gros, on se retrouve avec un code plein de bruit.

Pourtant, tout ce qui est important dans la fonction factorielle, c’est qu’en lui transmettant 0, on veut comme résultat 1 et pour les autres cas, on veut n * fact(n-1).

En F#, on se concentre sur l’essentiel.

Pattern matching sur une liste

Pour continuer sur le pattern matching, on va créer une fonction qui va faire la somme des éléments d’une liste.

Pour implémenter ça, on a deux cas à prendre en compte : une liste vide et les autres cas. Pour la liste vide, on renvoi 0 et dans les autres cas, on retourne la somme de la tête de la liste et la somme du reste de la liste.

// xs est la liste à additionner.
let rec sum xs =
    match xs with
    | [] -> 0
    | head::tail -> head + sum tail

Dans ce cas, on créer un pattern head::tail et F# va de débrouiller pour extraire les données de la liste suivant la définition de la fonction cons : (::) -> a -> [a] -> [a]; ce qui ce traduirait par une fonction générique en C#, List<T> operator ::(this T el, List<T> xs).

Pour bien comprendre ça, on peut voir comment construire un liste de plusieurs façons :

[1..5]
= [1,2,3,4,5]
= 1 :: [2,3,4,5]
= 1 :: 2 :: [3,4,5]
...
1 :: 2 :: 3 :: 4 :: 5 :: []

Toutes les syntaxes ci-dessus sont équivalentes. Dans la ligne 6, on utilise l’opérateur cons (::) pour créer une liste en ajoutant des éléments à la liste vide. Si on donne la liste [1,2,3,4,5] à la fonction sum, celle-ci va décomposer la liste en suivant le principe inverse.

Pattern matching sur plusieurs arguments

C’est la partie du pattern matching que je trouve la plus intéressante. On peut facilement prendre en compte plusieurs arguments pour le pattern.

let mafonction a b =
    match a,b with
    | _,0 -> 0
    | 0,_ -> 0
    | _ -> a + b;;

Même si le code produit en fsharp est plus consit et expréssif qu’en C#, j’aurai aimé avoir une syntaxe comme en Haskell. En prenant pour exemple la fonction factorielle :

fact 0 = 1
fact n = n * fact(n-1)

Pour finir, je vous conseille le site Wikibooks > F Sharp Programming pour en apprendre plus sur le langage. Vous trouverez plus d’information sur F# en général mais aussi sur le pattern matching.