• Aucun résultat trouvé

Structures de donn´ ees et filtrage

Fonctionnelles et polymorphisme

6.7 Structures de donn´ ees et filtrage

Comme nous l’avons vu `a maintes reprises, le filtrage va de paire avec les d´efinitions de structures de donn´ees. Bien plus, la d´efinition d’un type sert de guide pour ´ecrire le squelette du filtrage des fonctions qui op`erent sur ce type. Nous voulons cependant attirer votre attention sur quelques traits avanc´es du filtrage et quelques ´ecueils qui guettent les d´ebutants qui ´ecrivent leurs premiers filtrages.

Filtrage de valeurs calcul´ees

En premier lieu, il faut conserver `a l’esprit que le filtrage en Caml est structurel : on ne peut utiliser dans les motifs que des constructeurs, des constantes et des variables, `

a l’exclusion des valeurs calcul´ees. C’est pourquoi les variables qui interviennent dans un motif ne servent jamais `a faire des tests, mais au contraire `a lier des parties de la valeur filtr´ee. Comparer par exemple, la d´efinition (erron´ee) de la fonction est_un avec celle (correcte) de la fonction test_`a_un:

# let un = 1;; un : int = 1

# let est_un = function | un -> true

| _ -> false;; Entr´ee interactive: > | _ -> false;; > ^

Attention: ce cas de filtrage est inutile. est_un : ’a -> bool = <fun>

# est_un 2;; - : bool = true

# let test_`a_un x = if x = un then true else false;; test_`a_un : int -> bool = <fun>

# test_`a_un 2;; - : bool = false

Le premier filtre de la fonction est_un comprend la variable un, qui est sans rapport avec l’identificateur un pr´ec´edemment d´efini `a la valeur 1. Autrement dit, le nom de la variable un est sans importance dans le filtrage de la fonction est_un : on peut le remplacer par x ou y, et le filtrage est ´equivalent `a x -> true | _ -> false. Contrairement `a ce que l’utilisateur voulait sans doute exprimer, la fonction est_un ne teste donc pas si son argument correspond `a la mˆeme valeur que l’identificateur un: en fait, la fonction est_un renvoie toujours true. Cela explique le r´esultat de est_un 2. Cela explique aussi le message du compilateur : « ce cas de filtrage est inutile ». Le compilateur s’est rendu compte que le cas _ -> ne servira jamais. C’est

Structures de donn´ees et filtrage 121 pour ´eviter ce genre de confusions qu’on utilise la convention de faire commencer les noms de constructeurs par une majuscule et d’´ecrire les variables dans les filtres en minuscules. Retenons que

Toute variable dans un filtre est une nouvelle variable.

Lin´earit´e du filtrage

Il faut ´egalement savoir que le filtrage en Caml est lin´eaire, ce qui signifie qu’un nom de variable ne peut apparaˆıtre qu’une seule fois dans un filtre. Cette contrainte est viol´ee le plus souvent lorsqu’on veut tester l’´egalit´e de deux morceaux d’une valeur. Voici une tentative (erron´ee) de d´efinir la fonction d’´egalit´e : si le couple argument comporte deux composantes identiques on renvoie vrai et sinon on renvoie faux.

# let ´egal = function | (x, x) -> true | _ -> false;; Entr´ee interactive: > | (x, x) -> true > ^

L’identificateur x est d´efini plusieurs fois dans ce motif.

Les tests d’´egalit´e op´er´es par le filtrage ne concernent que les constantes (les construc- teurs). Les tests d’´egalit´e plus g´en´eraux ne s’expriment pas par filtrage, ils doivent faire l’objet d’une alternative explicite (un if then else) dans l’expression d’une clause de filtrage ou bien d’une garde que nous ´etudions bri`evement dans le prochain paragraphe. Une d´efinition acceptable de ´egal serait donc :

# let ´egal = function (x, y) -> if x = y then true else false;; ´

egal : ’a * ’a -> bool = <fun>

Remarque : comme nous l’avons vu au paragraphe 2.1, l’alternative du corps de ´egal est inutile ; on la remplace donc simplement par sa partie condition pour obtenir

# let ´egal (x, y) = x = y;; ´

egal : ’a * ’a -> bool = <fun>

Ce qui nous permet de constater que la fonction ´egalne d´efinit pas la fonction d’´egalit´e mais est en fait un synonyme de l’op´erateur = (plus pr´ecis´ement ´egalest la version non curryfi´ee de l’op´erateur =).

Combiner filtrage et tests : les gardes

Vous remarquerez sans doute que le filtrage permet une programmation partic- uli`erement claire, et autorise l’´ecriture compacte de multiples conditions. Cependant le simple filtrage structurel (et lin´eaire) ne permet pas de m´elanger la s´election sur la forme de la valeur filtr´ee et les tests sur les valeurs effectives des composants du filtre. C’est pourquoi le filtrage de Caml propose une construction suppl´ementaire, les gardes, pour effectuer des tests arbitraires pendant le filtrage. La clause

o`u condition est une expression bool´eenne quelconque, filtre les mˆemes valeurs que filtre, mais elle n’est s´electionn´ee que dans le cas o`u condition est vraie ; dans le cas contraire le filtrage continue normalement en s´equence.

`

A l’aide d’une garde, on ´ecrit facilement une version correcte de la fonction est_un :

# let est_un = function | x when x = un -> true | _ -> false;;

est_un : int -> bool = <fun> # est_un 2;;

- : bool = false

On ´ecrit aussi la fonction valeur_d’une_carte encore plus ´el´egamment :

# let valeur_d’une_carte couleur_d’atout = function | As _ -> 11

| Roi _ -> 4 | Dame _ -> 3

| Valet c when c = couleur_d’atout -> 20 | Valet _ -> 2

| Petite_carte (9, c) when c = couleur_d’atout -> 14 | Petite_carte (10, _) -> 10

| _ -> 0;;

valeur_d’une_carte : couleur -> carte -> int = <fun>

Filtrage exhaustif, filtrage partiel

Enfin, il faut se m´efier des filtrages non exhaustifs, c’est-`a-dire des filtrages qui oublient des cas. C’est une des forces du filtrage de servir de guide pour facilement envisager tous les cas concernant une structure de donn´ees, il faut donc en profiter pleinement. Si vous oubliez d’envisager certains cas, le compilateur ´emet un message d’avertissement et il faut en tenir compte. Voici un exemple caricatural de filtrage non exhaustif :

# let vide = function [] -> true;; Entr´ee interactive:

>let vide = function [] -> true;; > ^^^^^^^^^^^^^^^^^^^

Attention: ce filtrage n’est pas exhaustif. vide : ’a list -> bool = <fun>

Dans cette situation, il faut vous efforcer de « boucher les trous » de votre filtrage. Laisser des filtrages non exhaustifs dans un programme est g´en´eralement consid´er´e comme un laisser-aller de mauvais aloi.