• Aucun résultat trouvé

Op´ erations sur les polynˆ omes creu

Fonctionnelles et polymorphisme

5.7 Op´ erations sur les polynˆ omes creu

Addition des polynˆomes creux

L’addition des polynˆomes creux va analyser r´ecursivement ses deux arguments pour en construire la somme. Comme nous l’avons vu dans le cas des polynˆomes pleins, il arrive que certains termes d’un des deux polynˆomes arguments n’aient pas de corre- spondants dans l’autre, parce que les polynˆomes n’ont pas le mˆeme degr´e. Dans le cas des polynˆomes creux, l’une des listes de monˆomes sera ´epuis´ee avant l’autre. Si nous atteignons ainsi la fin de l’un des polynˆomes, l’autre constitue le r´esultat cherch´e : par exemple, si l’on ajoute un polynˆome P0 r´eduit `a une constante `a un autre polynˆome P ,

il faut ajouter les deux monˆomes de degr´e 0 de P et P0, mais le r´esultat comprend aussi

la liste des monˆomes restants de P . En effet, les coefficients manquants du polynˆome de plus bas degr´e correspondent `a des z´eros implicites. Dans le cas g´en´eral, nous ajoutons les termes de mˆeme degr´e ou recopions dans le r´esultat final les termes qui n’ont pas d’analogue dans l’autre polynˆome.

# let rec ajoute_polyn^omes_creux p1 p2 = match p1, p2 with

| _, [] -> p1 | [], _ -> p2

| (a1, degr´e1 as m1) :: reste1, (a2, degr´e2 as m2) :: reste2 -> if degr´e1 = degr´e2

then ((a1 + a2), degr´e1) :: ajoute_polyn^omes_creux reste1 reste2 else if degr´e1 < degr´e2

then m1 :: ajoute_polyn^omes_creux reste1 p2 else m2 :: ajoute_polyn^omes_creux p1 reste2;; ajoute_polyn^omes_creux :

(int * ’a) list -> (int * ’a) list -> (int * ’a) list = <fun>

Le filtre (a1,degr´e1 as m1) :: reste1, (a2,degr´e2 as m2) :: reste2 est com- plexe et n´ecessite une explication. Il est clairement constitu´e de deux filtres analogues

s´epar´es par une virgule, l’un pour filtrer p1 et l’autre pour filtrer p2. Examinons celui qui concerne p1. Le filtre (a1,degr´e1 as m1) :: reste1 signifie que :

• p1 est une liste non vide dont la tˆete est filtr´ee par (a1, degr´e1 as m1) et le reste est nomm´e reste1,

• la tˆete de p1 est donc un couple dont les composantes sont nomm´ees a1 et degr´e1, • le couple lui-mˆeme, (a1, degr´e1), est nomm´e m1 grˆace au filtre synonyme as

m1.

Admirons au passage la puissance et l’´el´egance du m´ecanisme de filtrage. Remarquez ´egalement que les filtres sont essay´es dans l’ordre de pr´esentation dans le filtrage. Par exemple, la valeur ([], []) sera filtr´ee par le premier filtre, bien qu’elle soit aussi filtrable par le second. `A titre d’exemple, nous calculons la somme des polynˆomes X2+ 3X4 et 3 + 2X2+ 5X10.

# imprime_polyn^ome_creux

(ajoute_polyn^omes_creux [(1,2); (3,4)] [(3,0); (2,2); (5,10)]);; 3 + 3x^2 + 3x^4 + 5x^10- : unit = ()

Multiplication des polynˆomes creux

La multiplication op`ere ´egalement par filtrage simultan´e de ses deux arguments. Dans le cas o`u l’un des polynˆomes est ´epuis´e, il n’y a plus de multiplication `a faire. En ef- fet, les monˆomes manquants ont implicitement des coefficients nuls, donc les multiplica- tions produiront toujours des coefficients nuls. En ce cas, le r´esultat est donc la liste vide. Sinon, on applique simplement la r`egle habituelle de distributivit´e de la multiplication par rapport `a l’addition. Voyons : soit m1 le premier monˆome de P1 et reste1 les autres

monˆomes de P1. On a P1 = m1+reste1, donc P1×P2= m1×P2+ reste1×P2. Si l’on ap-

pelle notre fonction multiplie_polyn^omes_creux, alors reste1×P2correspond `a l’appel

r´ecursif multiplie_polyn^omes_creux reste1 p2. Quant `a l’expression m1× P2, c’est

un cas plus simple o`u l’on multiplie un polynˆome par un monˆome. Nous le traiterons par la fonction auxiliaire multiplie_par_mon^ome_creux. L’expression m1×P2+reste1×P2

s’´ecrit donc :

ajoute_polyn^omes_creux

(multiplie_par_mon^ome_creux m1 p2) (multiplie_polyn^omes_creux reste1 p2)

Il reste `a d´efinir multiplie_par_mon^ome_creux. Si m1est le monˆome et P le polynˆome,

il suffit de multiplier chaque monˆome de P par le monˆome m1, ce qui se fait simplement

en multipliant les coefficients et en ajoutant les degr´es. En r´esum´e :

# let multiplie_par_mon^ome_creux (a1, degr´e1) p =

map (function (a, degr´e) -> (a * a1, degr´e1 + degr´e)) p;; multiplie_par_mon^ome_creux :

int * int -> (int * int) list -> (int * int) list = <fun> # let rec multiplie_polyn^omes_creux p1 p2 =

match p1, p2 with | (_, []) -> [] | ([], _) -> []

Op´erations sur les polynˆomes creux 87

ajoute_polyn^omes_creux

(multiplie_par_mon^ome_creux m1 p2) (multiplie_polyn^omes_creux reste1 p2);; multiplie_polyn^omes_creux :

(int * int) list -> (int * int) list -> (int * int) list = <fun>

Nous calculons (1 + X10000)2 `a titre d’exemple :

# let p = [(1, 0); (1, 10000)] in

imprime_polyn^ome_creux (multiplie_polyn^omes_creux p p);; 1 + 2x^10000 + x^20000- : unit = ()

En premi`ere lecture, vous en savez largement assez pour passer d`es maintenant au chapitre suivant.

Polymorphisme et r`egle η

Nous devons signaler ici une petite difficult´e qui apparaˆıt lorsqu’on utilise la r`egle η pour simplifier une d´efinition, lorsque le r´esultat doit ˆetre polymorphe. Supposons que nous d´efinissions le tri par ordre croissant ainsi :

# let tri_croissant l =

tri_par_insertion (function x -> function y -> x <= y) l;; tri_croissant : ’a list -> ’a list = <fun>

On peut esp´erer simplifier cette d´efinition `a l’aide de la r`egle η, en supprimant l’argument l :

# let tri_croissant =

tri_par_insertion (function x -> function y -> x <= y);; tri_croissant : ’_a list -> ’_a list = <fun>

On constate alors que la fonction tri_croissant n’a plus le mˆeme type, et qu’il ap- paraˆıt dans ce type d’´etranges param`etres de type ’_a.Au contraire des param`etres ’a qui signifient pour tout type a, et d´enotent donc des types polymorphes, les param`etres ’_asignifient pour un certain type a qui sera d´etermin´e par les utilisations ult´erieures de la fonction. La fonction tri_croissant est donc monomorphe :

# tri_croissant [3; 2; 1];; - : int list = [1; 2; 3] # tri_croissant;;

- : int list -> int list = <fun> # tri_croissant ["Bonjour"];; Entr´ee interactive:

>tri_croissant ["Bonjour"];; > ^^^^^^^^^^^

Cette expression est de type string list, mais est utilis´ee avec le type int list.

Le type inconnu ’_a est devenu le type int et la fonction tri_croissant est dor´enavant de type int -> int.

Ce ph´enom`ene est dˆu `a la coexistence en Caml du polymorphisme et des structures mutables. Il est expliqu´e en d´etails `a la fin de ce livre, page 363, lorsque nous aurons vu les m´ecanismes qui permettent de le comprendre. Retenons pour l’instant que seule les fonctions (et les constantes) sont susceptibles d’ˆetre polymorphes, les d´efinitions de

fonctions obtenues par application partielle d’une fonction plus g´en´erale sont monomor- phes. Nous avons d´ej`a constat´e (page 73) que l’application de la r`egle η peut modifier le type d’une fonction, le rendant plus g´en´eral ; ici, c’est l’inverse : on passe d’un type polymorphe `a un type monomorphe moins g´en´eral.