Laboratoire 03: Filtrage par motif, types algébriques et listes
Note : ce laboratoire est prévu pour être fait durant deux scéances.
1. Définition de type et filtrage par motif
1.1. Cartes
-
Définir un type
cardqui représente les différentes cartes possibles pour un jeu de carte. Une carte peut soit être une valeur numérique, soit une figure parmi le valet, la dame, et le roi. Une carte a également une couleur (cœur, carreau, trèfle, ou pique). -
On souhaite comparer les cartes par valeur, où la valeur d’une carte numérique et égale à son nombre, et où le valet à une valeur inférieure à la dame, qui a une valeur inférieure au roi. Définir une fonction
card_comparequi prends deux cartes en arguments et renvoie :
0si les deux cartes ont la même valeur1si la première carte a une valeur strictement supérieure à la seconde-1si la première carte a une valeur strictement inférieure à la seconde.
1.2. Formes
- Définir un type
shapequi peut représenter les formes suivantes :
- un rectangle, défini pas sa longueur et sa largeur
- un cercle, défini par son rayon
- un losange, défini par la taille de ses deux diagonales
- Définir une fonction
surfacequi renvoie la surface d’une valeur de typeshape. Pour rappel :
- la surface d’un rectangle est égale à \(lw\) où l est sa longueur et w sa largeur
- la surface d’un cercle est égale à \(\pi r^2\) où r est son rayon
- la surface d’un losange est égale à \((pq)/2\) où p et q sont les longueurs de ses diagonales
2. Manipulation de listes
Rappel : une liste est un objet récursif défini comme étant
- soit vide (
[]), - soit un élément suivi d’une liste (
head :: tail).
Le premier élément d’une liste est obtenu
grâce à la fonction List.hd et le dernier élément d’une liste est donnée par List.tl. La
fonction qui ajoute un élément en tête de liste est l’opérateur infixe :: (cons). On peut
également concaténer des listes avec l’opérateur @.
List est un module. Il est possible d’utiliser ses fonctions en se passant
du préfixe List..
2.1. Une simple liste d’entiers
On veut fabriquer une liste contenant les entiers de 0 à n en ordre croissant.
- On écrit la fonction récursive non terminale suivante pour fabrique une telle liste. Quel est problème ? (Regarder le résultat en appelant la fonction aidera)
let rec integers_1 (n : int) : int list =
if n < 0 then
[]
else n :: integers_1 (n - 1)
-
Ré-essayer en utilisant
@à la place de::et nommer la fonction obtenueintegers_2. Quel est problème (tester la fonction avec n=100,000) ? -
Ré-essayer en utilisant
List.revet nommer la fonction obtenueintegers_3. -
Réécrire la fonction
integers_1de façon à ce qu’elle soit récursive terminale, en utilisant un accumulateur. Que peut-on dire sur la complexité de cette réimplémentation, comparé àintegers_3?
Résultat attendu :
# #use "tp3.ml";;
...
val integers_1 : int -> int list = <fun>
...
val integers_2: int -> int list = <fun>
# let l = integers_1 8;;
val l : int list = [8; 7; 6; 5; 4; 3; 2; 1; 0]
# integers_2 8;;
- : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8]
# List.rev l;;
- : int list = [0; 1; 2; 3; 4; 5; 6; 7; 8]
2.2. Traiter des listes de nombres
Écrire les fonctions suivantes (et observer leur type). Dans la mesure du possible, essayer de ne parcourir la liste qu’une seule fois.
Certaines fonctions demandent au préalable des conditions précises sur la liste (comme le fait que la liste ne soit pas vide). Pour écrire ces fonctions, on définira une exception par
exception EmptyList
et on la lèvera si besoin (donc, par exemple, lorsque la liste est vide alors qu’elle ne devrait pas l’être) par
raise EmptyList
(Conseil : demander à l’interpréteur le type de la fonction raise.)
Écrire les fonctions suivantes.
three_or_more lqui teste si la listela au moins trois éléments (utiliser la fonctionList.lengthn’est pas une bonne idée ! Pourquoi ?).size lqui renvoie la taille de la listel(toujours sans utiliserList.length).last lqui renvoie le dernier élément de la listelnon vide.is_increasing lqui teste si la listelest croissante.even_odd lqui teste si la listelest telle que ses 1er, 3ème, 5ème, etc. éléments sont impairs et les autres pairs.find e lqui teste si l’élémenteest dans la listel.member e lqui renvoie la portion de la listelcommençant à la première occurrence de l’élémente.member_last e lqui renvoie la portion de la listelcommençant à la dernière occurrence de l’élémente.nb_occ e lqui compte le nombre d’occurrences de l’élémentedans la listel.nth n lqui renvoie le nème élément de la listel.max_list lqui renvoie le maximum de la listelnon vide.nb_max lqui renvoie le nombre de maximums de la listel. Essayer de réaliser ce calcul en un seul parcours.average lqui renvoie la moyenne des nombres de la liste de flottantsl.size_in_range a b lqui teste si la longueur de la listelest dans l’intervalle[a, b](ou[b, a]).find_pattern p lqui teste si la listepest un motif de la listel, au sens où la listelcontient à un endroit donné la suite des éléments dep, dans le même ordre.
# three_or_more [], three_or_more [1; 1; 1; 1; 1];;
- : bool * bool = (false, true)
# size [], size [3; 1; 4; 5; 2];;
- : int * int = (0, 5)
# last [1], last [3; 1; 4; 5; 2];;
- : int * int = (1, 2)
# is_increasing [], is_increasing [3; 1; 4; 5; 2], is_increasing [1; 3; 5; 5; 7];;
- : bool * bool * bool = (true, false, true)
# even_odd [], even_odd [1; 4; 3; 6; 9; 2], even_odd [2; 3; 3];;
- : bool * bool * bool = (true, true, false)
# find 3 [], find 3 [1; 2; 3], find 3 [2; 4; 6];;
- : bool * bool * bool = (false, true, false)
# member 3 [], member 3 [1; 2; 3; 4; 3; 5], member 3 [2; 4; 6];;
- : int list * int list * int list = ([], [3; 4; 3; 5], [])
# member_last 3 [1; 2; 3; 4; 3; 5], member_last 3 [2; 4; 6];;
- : int list * int list = ([3; 5], [])
# nb_occ 3 [], nb_occ 3 [1; 2; 3; 4; 3; 5], nb_occ 3 [2; 4; 6];;
- : int * int * int = (0, 2, 0)
# nth 3 [1; 2; 3; 4; 3; 5], nth 3 [2; 4; 6];;
- : int * int = (3, 6)
# max_list [1; 2; 3; 0; 3; 0], max_list [2; 4; 6];;
- : int * int = (3, 6)
# nb_max [1; 2; 3; 0; 3; 0], nb_max [2; 4; 6];;
- : int * int = (2, 1)
# average [5.; 8.5; 11.5; 15.];;
- : float = 10.
# size_in_range 0 0 [], size_in_range 1 3 [0; 0], size_in_range 1 3 [0; 0; 0; 0];;
- : bool * bool * bool = (true, true, false)
# find_pattern [] [1; 2], find_pattern [1; 1] [1; 2; 1], find_pattern [1; 1] [1; 2; 1; 1];;
- : bool * bool * bool = (true, false, true)
2.3. Créer des listes de nombres
Écrire les fonctions suivantes (et observer leur type) :
list_copy lqui renvoie une copie de la listel.random_list n maxqui renvoie une liste denentiers aléatoires strictement inférieurs àmax.reverse lqui renvoie l’image miroir de la listel.flatten_list lqui aplatit la liste de listesl.fibo nqui crée la liste desnpremiers nombres de Fibonacci sans utiliser la fonctionfibdéfinie précédemment. Essayer de le faire sans avoir besoin de renverser la liste.without_duplicates lqui supprime les doublons dans la liste triéel.records lqui calcule la liste des records de la listel. Un record dans une liste est une valeur strictement plus grande que toutes les précédentes.frequences lqui calcule le nombre d’occurrences de chaque élément de la listel.look_and_say nqui calcule lesnpremiers termes de la suite https://oeis.org/A005150 qui commence par
1, 11, 21, 1211, 111221, 312211, 13112221.
Chaque nombre peut être représenté par la liste de ses chiffres, de gauche à droite.
Résultat attendu :
# list_copy [1; 2; 3];;
- : int list = [1; 2; 3]
# let l = random_list 10 2;;
val l : int list = [0; 0; 1; 1; 0; 1; 1; 0; 0; 0]
# reverse l;;
- : int list = [0; 0; 0; 1; 1; 0; 1; 1; 0; 0]
# flatten_list [[1; 2]; []; [3; 4; 5]; [6]];;
- : int list = [1; 2; 3; 4; 5; 6]
# fibo 10;;
- : int list = [0; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55ls]
# without_duplicates [0; 0;1; 2; 3; 3; 3; 3; 4; 5; 5; 6; 8; 8];;
- : int list = [0; 1; 2; 3; 4; 5; 6; 8]
# records [0; 2; 3; 2; 6; 3; 2; 7; 4; 8; 4];;
- : int list = [0; 2; 3; 6; 7; 8]
# look_and_say 6;;
- : int list list = [[3; 1; 2; 2; 1; 1]; [1; 1; 1; 2; 2; 1]; [1; 2; 1; 1]; [2; 1]; [1; 1]; [1]]
# frequences l;;
- : (int * int) list = [(0, 6); (1, 4)]
# frequences (random_list 10000 5);;
- : (int * int) list = [(3, 1977); (1, 2027); (0, 2044); (4, 1980); (2, 1972)]
2.4. Tris
On souhaite trier une liste d’entiers en utilisant la méthode « diviser pour régner ». L’idée est de diviser la liste en deux, de trier chacune des listes obtenues et de les recombiner.
- Le tri fusion.
- Écrire une fonction
f_splitqui sépare une liste quelconque en deux listes de tailles à peu près égales (à1près, suivant la parité de la longueur de la liste). - Écrire une fonction
f_mergequi fabrique une liste triée à partir de deux listes triées quelconques. - Écrire une fonction
fusion_sortqui trie une liste récursivement en utilisant les fonctions précédentes.
- Écrire une fonction
- Le tri rapide (quicksort).
- Écrire une fonction
q_splitqui sépare une liste quelconquelen trois listes en fonction d’un pivot (on peut choisir le premier élément del) :- les éléments strictement plus petits que le pivot,
- les éléments égaux au pivot,
- les éléments plus grands que le pivot.
- Écrire une fonction
q_mergequi fabrique une liste triée à partir de trois listes triées obtenues comme ci-dessus. - Écrire une fonction
quick_sortqui trie une liste récursivement en utilisant les fonctions précédentes.
- Écrire une fonction
Résultat attendu :
# let l = random_list 20 100;;
val l : int list = [58; 37; 58; 72; 19; 58; 18; 41; 58; 86; 94; 59; 92; 35; 40; 47; 92; 6; 42; 95]
# let l1, l2 = f_split l;;
val l1 : int list = [58; 58; 19; 18; 58; 94; 92; 40; 92; 42]
val l2 : int list = [37; 72; 58; 41; 86; 59; 35; 47; 6; 95]
# let l1, l2 = (List.sort compare l1), (List.sort compare l2);;
val l1 : int list = [18; 19; 40; 42; 58; 58; 58; 92; 92; 94]
val l2 : int list = [6; 35; 37; 41; 47; 58; 59; 72; 86; 95]
# f_merge l1 l2;;
- : int list = [6; 18; 19; 35; 37; 40; 41; 42; 47; 58; 58; 58; 58; 59; 72; 86; 92; 92; 94; 95]
# fusion_sort l;;
- : int list = [6; 18; 19; 35; 37; 40; 41; 42; 47; 58; 58; 58; 58; 59; 72; 86; 92; 92; 94; 95]
# let l1, l2, l3 = q_split l;;
val l1 : int list = [37; 19; 18; 41; 35; 40; 47; 6; 42]
val l2 : int list = [58; 58; 58; 58]
val l3 : int list = [72; 86; 94; 59; 92; 92; 95]
# let l1, l2, l3 = (List.sort compare l1), l2, (List.sort compare l3);;
val l1 : int list = [6; 18; 19; 35; 37; 40; 41; 42; 47]
val l2 : int list = [58; 58; 58; 58]
val l3 : int list = [59; 72; 86; 92; 92; 94; 95]
# q_merge l1 l2 l3;;
- : int list = [6; 18; 19; 35; 37; 40; 41; 42; 47; 58; 58; 58; 58; 59; 72; 86; 92; 92; 94; 95]
# quick_sort l;;
- : int list = [6; 18; 19; 35; 37; 40; 41; 42; 47; 58; 58; 58; 58; 59; 72; 86; 92; 92; 94; 95]