Rappel¶
type somme : représenter plusieurs cas disjoints
opérateur de filtrage
match
I. Type somme¶
type nom = Cons1 of t1 | Cons2 of t2 | Cons3 of t3...
Les Constructeurs doivent tous être différents, (et commencer par une majuscule).
Les Constructeurs peuvent être Constants (pas de
of ...
dans ce cas)t1
,t2
... peuvent être des types quelconques (produit, enregistrement, tableau ...).. et même faire référence au type
nom
entrain d'être défini (type somme récursif)Possiblité de type polymorphe
type 'a nom = ...
Type somme avec constructeurs constants¶
Exercice 1 :¶
- Définir un type
nombre_etendu
avec les entiers, les réels, $+\infty$ et $-\infty$
type nombre_etendu = Reel of float | Entier of int | PlusInf | MoinsInf
type nombre_etendu = Reel of float | Entier of int | PlusInf | MoinsInf
- Définir un type
signe
pour représenter les informations Positif, Negatif, Nul
type signe = Positif | Negatif | Nul
type signe = Positif | Negatif | Nul
Exercice 1 - suite :¶
- Définir une fonction
signe_of_int:int->signe
let signe_of_int x =
if x = 0 then Nul
else if x > 0 then Positif
else Negatif
val signe_of_int : int -> signe = <fun>
- Définir une fonction
signe_of_float:float->signe
let signe_of_float x =
if x = 0. then Nul
else if x > 0. then Positif
else Negatif
val signe_of_float : float -> signe = <fun>
Exercice 1 - suite :¶
- Définir une fonction
signe_of_nombre_etendu:nombre_etendu->signe
let signe_of_nombre_etendu x = match x with
| Reel t -> signe_of_float t
| Entier n -> signe_of_int n
| PlusInf -> Positif
| MoinsInf -> Negatif
val signe_of_nombre_etendu : nombre_etendu -> signe = <fun>
Exercice 2 :¶
- Comment coder les cartes d'un jeu de 32 cartes ?
- Le plus "rustique" : une carte = un
int
entre 0 et 31
- carte mod 4 : la couleur de la carte (0 pour Pique, 1 pour Coeur, 2 pour Carreau, 3 pour Trèfle)
- carte / 4 : la hauteur de la carte (0 pour 7, 1 pour 8 ..., 7 pour Roi, 8 pour As)
ex : $13 = 3\times4 + 1$ : 10 de Coeur
- Un peu plus "structuré" : une carte = un couple d'
int
:
- première composante : entre 0 et 3 pour la couleur
- deuxième composante : entre 0 et 7 pour la hauteur
ex : (2,1) : 8 de Carreau
- En exploitant au maximum les possibilités des types OCaml
type couleur = Pique | Coeur | Carreau | Trefle (* plusieurs cas : type somme *)
type hauteur = As | Roi | Dame | Valet | Petite of int (* plusieurs cas : type somme *)
type carte = { h : hauteur; c : couleur } (* plusieurs informations : type enregistrement *)
type couleur = Pique | Coeur | Carreau | Trefle
type hauteur = As | Roi | Dame | Valet | Petite of int
type carte = { h : hauteur; c : couleur; }
- Définir le Roi de Trèfle et le 7 de Carreau
let c1 = {h = Roi; c = Trefle}
let c2 = {h = Petite(7); c = Carreau}
val c1 : carte = {h = Roi; c = Trefle}
val c2 : carte = {h = Petite 7; c = Carreau}
Exercice 2 - suite :¶
- Comment coder un jeu de Tarot ?
- L'excuse
- 21 atouts de 1 à 21
- 56 cartes (jeu classique + cavalier)
type couleur = Pique | Coeur | Carreau | Trefle (* plusieurs cas : type somme *)
type hauteur = Roi | Dame | Cavalier | Valet | Petite of int (* plusieurs cas : type somme *)
type carte_tarot = Classique of { h : hauteur; c : couleur } | Atout of int | Excuse
let c1 = Excuse
let c2 = Atout 21
let c3 = Classique { h = Roi; c = Trefle }
type couleur = Pique | Coeur | Carreau | Trefle
type hauteur = Roi | Dame | Cavalier | Valet | Petite of int
type carte_tarot = Classique of { h : hauteur; c : couleur; } | Atout of int | Excuse
val c1 : carte_tarot = Excuse
val c2 : carte_tarot = Atout 21
val c3 : carte_tarot = Classique {h = Roi; c = Trefle}
type iliste = Vide | Maillon of int * iliste
type iliste = Vide | Maillon of int * iliste
let l1 = Vide
let l2 = Maillon (3, Maillon (4, Vide))
val l1 : iliste = Vide
val l2 : iliste = Maillon (3, Maillon (4, Vide))
- Définir la fonction
affiche:iliste -> unit
let rec affiche l = match l with
| Vide -> ()
| Maillon (x, t) -> print_int x; print_string " "; affiche t
val affiche : iliste -> unit = <fun>
affiche l2; print_newline ()
3 4
- : unit = ()
- Affichage avec des crochets ?
let affiche l =
let rec affiche_aux l = match l with
| Vide -> ()
| Maillon (x, t) -> print_int x; print_string ";"; affiche_aux t
in print_string "["; affiche_aux l; print_string "]"
val affiche : iliste -> unit = <fun>
affiche l2; print_newline ()
[3;4;]
- : unit = ()
- Affichage sans
;
en trop ?
- Quel motif "capture" le dernier maillon de la liste ?
- Où le placer par rapport aux autres motifs ?
let affiche l =
let rec affiche_aux l = match l with
| Vide -> ()
| Maillon (x, Vide) -> print_int x
| Maillon (x, t) -> print_int x; print_string ";"; affiche_aux t
in print_string "["; affiche_aux l; print_string "]"
val affiche : iliste -> unit = <fun>
affiche l2; print_newline ()
[3;4]
- : unit = ()
type 'a option = None | Some of 'a
type 'a option = None | Some of 'a
- Définir une fonction
last_elt:'a array->'a option
qui renvoie le dernier élément d'un tableau s'il existe
None
si le tableau est de longueur 0Some x
avecx
dernier élément si le tableau est de longueur $> 0$
let last_elt tab =
if Array.length tab = 0 then None
else Some tab.(Array.length tab-1)
val last_elt : 'a array -> 'a option = <fun>
last_elt [||];;
last_elt [|5.2; 4.3; -2.1|];;
- : 'a option = None
- : float option = Some (-2.1)
Exercice 5¶
- Définir un type
'a liste
qui soit la liste vide, ou un maillon constitué d'un élément de type'a
et d'une'a liste
type 'a liste = Vide | Maillon of 'a * 'a liste
type 'a liste = Vide | Maillon of 'a * 'a liste
NB : c'est une type somme récursif polymorphe
- Définir une liste de 3 entiers, une liste de 2 chaînes de caractères
let l3 = Maillon (2, Maillon (5, Maillon (7, Vide)))
let l4 = Maillon ("écureuil", Maillon ("oiseau", Vide))
val l3 : int liste = Maillon (2, Maillon (5, Maillon (7, Vide)))
val l4 : string liste = Maillon ("écureuil", Maillon ("oiseau", Vide))
- Ecrire la fonction (récursive)
longueur:'a liste->int
- si la liste est vide : 0
- si la liste n'est pas vide : ...
let rec longueur l = match l with
| Vide -> 0
| Maillon (x, t) -> 1 + longueur t;;
longueur l3;;
longueur l4;;
val longueur : 'a liste -> int = <fun>
- : int = 3
- : int = 2
A SAVOIR : les Listes en OCaml¶
Le type 'a list
existe déjà en OCaml, et est très utilisé.
Il est défini comme un type somme :
[]
est le Constructeur constant correspondant à la liste vide::
est le Constructeur maillon sous forme d'opérateur infixe :x :: l
correspond à la liste dont l'élément de tête estx
, suivi de la listel
(cf TP à venir sur les listes OCaml)
II. L'opérateur de filtrage match
¶
match expr with | p1 -> v1 | p2 -> v2 | ...
expr
est l'expression à filtrerp1
,p2
sont des motifs (pattern) qui contraignent le type deexpr
v1
,v2
sont les valeurs calculées (elles doivent être de même type)
C'est le premier motif qui s'apparie à expr
(qui "matche") qui donne la valeur à renvoyer
NB
match
est un opérateur très général, qui peut s'appliquer à tout type de valeur OCaml :valeur construite, le plus souvent
mais aussi : entier, couple, tableau, liste, enregistrement ...
Les constantes, Constructeurs, crochets, accolades ... sont autant de contraintes qui définissent le motif
Les identifiants présents dans le motif définissent de nouvelles variables
Le symbole
_
désigne une valeur quelconque et ne définit pas de nouvelle variableOCaml est capable de détecter que le filtrage n'est pas exhaustif (cas oubliés)
Exemple : filtrage sur un int
et motif _
¶
let vaut_un x = match x with
| 1 -> true
| _ -> false;;
vaut_un 3;;
vaut_un 1;;
val vaut_un : int -> bool = <fun>
- : bool = false
- : bool = true
NB : ne pas abuser du filtrage ... surtout quand une expression conditionnelle ou booléenne suffit
let vaut_un x = x = 1
val vaut_un : int -> bool = <fun>
NB : Détection de cas oubliés
let vaut_un x = match x with
| 1 -> true
File "[70]", line 1, characters 16-40: 1 | ................match x with 2 | | 1 -> true Warning 8: this pattern-matching is not exhaustive. Here is an example of a case that is not matched: 0
val vaut_un : int -> bool = <fun>
Exercice 6¶
- Définir la fonction récursive
fibo:int->int
avec l'opérateurmatch
let rec fibo n = match n with
| 0 -> 0
| 1 -> 1
| _ -> fibo (n-1) + fibo (n-2);;
fibo 12;;
val fibo : int -> int = <fun>
- : int = 144
Attention à bien comprendre ce que font les motifs ...¶
- Que dire du motif
_
?
il "matche" sur n'importe quelle valeur et ne définit aucune variable
- Que dire du motif
x
?
il "matche" sur n'importe quelle valeur, et définit une variable x
liée à cette valeur
- Que dire du motif
y
?
il "matche" sur n'importe quelle valeur, et définit une variable y
liée à cette valeur
- Que dire du motif
(x,y)
oux,y
?
il "matche" sur n'importe quel couple, et définit une variable x
liée à la première valeur du couple, et une variable y
liée à la deuxième valeur du couple
- Que dire du motif
(x,x)
oux,x
?
ce motif est incorrect : il définit deux fois une variable de même nom
Que dire des choix de motifs suivants ?¶
let rec fibo n = match n with
| 0 -> 0
| 1 -> 1
| x -> fibo (n-1) + fibo (n-2);; (* bof...*)
fibo 12;;
val fibo : int -> int = <fun>
- : int = 144
La variable x
n'est pas utilisée
Que dire des choix de motifs suivants ?¶
let rec fibo n = match n with
| 0 -> 0
| 1 -> 1
| m -> fibo (m-1) + fibo (m-2);; (* bof...*)
fibo 12;;
val fibo : int -> int = <fun>
- : int = 144
La variable m
prend la valeur de n
Que dire des choix de motifs suivants ?¶
let rec fibo n = match n with
| 0 -> 0
| 1 -> 1
| n -> fibo (n-1) + fibo (n-2);; (* bof...*)
fibo 12;;
val fibo : int -> int = <fun>
- : int = 144
La nouvelle variable n
prend la valeur de l'ancienne variable n
La meilleure solution :
let rec fibo n = match n with
| 0 -> 0
| 1 -> 1
| _ -> fibo (n-1) + fibo (n-2);; (* bien ! *)
fibo 12;;
val fibo : int -> int = <fun>
- : int = 144
Motifs rassemblés¶
Lorsque c'est pertinent, plusieurs motifs peuvent être regroupés. Ils doivent définir les mêmes variables
| p1 | p2 -> v
type nombre = Entier of int | Reel of float
let somme n1 n2 = match (n1, n2) with
| (Entier n, Entier p) -> Entier (n+p)
| (Reel s, Reel t) -> Reel (s+.t)
| (Reel s, Entier p) | (Entier p, Reel s) -> Reel (s +. float_of_int p) (* motifs rassemblés *)
type nombre = Entier of int | Reel of float
val somme : nombre -> nombre -> nombre = <fun>
Exercice 7¶
- Définir la fonction
abs:nombre_etendu->nombre_etendu
en utilisant des motifs gardés`
let abs x = match x with
| Entier n when n < 0 -> Entier (-n) (* motif gardé *)
| Reel t when t < 0. -> Reel (-.t) (* motif gardé *)
| _ -> x
val abs : nombre -> nombre = <fun>
Opération de filtrage avec function
¶
Il est possible de noter plus succinctement une fonction qui filtre son dernier paramètre, avec le mot clé function
NB : uniquement lorsqu'on a une bonne maîtrise de tout le langage OCaml
let abs = function
| Entier n when n < 0 -> Entier (-n)
| Reel t when t < 0. -> Reel (-.t)
| x -> x
val abs : nombre -> nombre = <fun>