Cours 3 : Types, filtrage, listes
Le "let" des types
type complexe = float*float;;
(3.2,4.8);;
      
On ne peut pas forcer caml à typer les couples de réels en complexes....
Types avec étiquettes
Principe
On peut voir ça comme une union d'ensemble un peu spéciale (cf le poly), où comme une façon de construire des valeurs avec des étiquettes. Reprenons la définition des complexes:
type complexe = Toto of float*float;;
(3.2,4.8);;
Toto (3.2,4.8);;
Attention, les étiquettes (le Toto ici) doivent commencer par des majuscules. On peut vouloir définir en caml un type Nombre, qui inclut les entiers (on choisira l'étiquette Z pour faire beau), les réels (étiquette R), les complexes (étiquette C), les erreurs numériques (du genre la division par 0) notées Nan pour "not a number".
type nombre =   Z of int
              | R of float
              | C of float*float
              | Nan;;
3;;
Z 3;;
Z 3.0;;
R 3.0;;
3.0;;
C (2.8,4.2);;
C (2.8,3);;
Nan;;
On peut aussi définir comme ça des types énumérés
type jour = Lundi | Mardi | Mercredi | Jeudi | Vendredi | Samedi | Dimanche;;
let week_end = (Samedi,Dimanche);;
Listes maison
Avec les étiquettes, on peut définir des types récursifs ?!?!
type int_liste =   Rien
                 | Et of int * int_liste;;
Rien;;
let a = Et (1,Rien);;
let b = Et (2,a);;
let c = Et (3,b);;
let d = Et (4,c);;
let e = Et ("toto",d);;
Et pour des listes de string, de float, de ...
type string_liste =   Rien
                    | Et of string * string_liste;;
type float_liste =    Rien
                    | Et of float * float_liste;;
...
... en plus, on ne peut plus réutiliser les étiquettes Rien et Et que l'on avait utilisées pour le type int_liste. Quittons caml, et faisons plutôt:
type 'a liste =   Rien
                | Et of 'a * 'a liste;;
Rien;;
let a = Et (1,Rien);;
let b = Et (2,a);;
let c = Et (3,b);;
let d = Et (4,c);;
let e = Et ("toto",d);;
let e = Et ("toto",Rien);;
let f = Et ("titi",e);;
let g = Et ("tutu",f);;
.. et encore plus fort...
Et (Rien, Rien);;
let x = Et (c, Et (d, Et (b, Rien)));;
let y = Et (f, Et (g, Rien));;
Et (3,y);;
Et (x, Rien);;
Filtrage
Pattern matching : le jeu enfin disponible
Mettez des valeurs à la place des ? pour que valeur soir égal à resultat.
let valeur   = C (2.3,5.8);;
let resultat = let a = ?
               and b = ?
               in
                  C (a,b);;
let resultat = let a = ?
               in
                  C a;;
Essayons avec les listes:
let valeur   = Et ("toto", Et ("titi", Et ("tutu", Rien)));;
let resultat = let a = ?
               and b = ?
               in
                  Et (a, b);;
let resultat = let a = ?
               in
                  Et (a, Rien);;
let resultat = let a = ?
               and b = ?
               in
                  Et (a, Et (b, Rien));;
let resultat = let a = ?
               and b = ?
	       and c = ?
               in
                  Et (a, Et (b, c));;
let resultat = let a = ?
               and b = ?
               in
                 (a, b);;
Laquelles de ces valeurs donnent une solution ?
let valeur1  = Et ("toto", Et ("toto", Et ("tutu", Rien)));;
let valeur2  = Et ("toto", Et ("tutu", Et ("toto", Rien)));;
let valeur3  = Et ("toto", Et ("toto", Et ("toto", Rien)));;
let valeur4  = Et (1, Et (2, Et (1, Et (3, Et (4, Et (5, Rien))))));;
let valeur5  = Et ("toto", Et ("toto", Rien));;
let resultat = let a = ?
               and b = ?
               and c = ?
               in
                 Et (a, Et (b, Et (a, c)));;
Caml sait jouer
Le jeux est de la forme
let valeur = ...;;
let resultat = let a = ?
               and b = ?
               and c = ?
               and ...
               in
                  pattern;;
"matcher le pattern avec la valeur" signifie définir les symboles a,b,c,... qui répondent au jeu. Caml sait le faire, et les symboles peuvent être utilisés pour calculer.
let valeur   = Et ("toto", Et ("titi", Et ("tutu", Rien)));;
let phrase = match valeur with
               Et (a, Et (b, c)) -> "Commence par "^a^" et "^b^".";;
phrase;;
On peut faire de phrase une fonction:
let phrase = 
  function v ->
    match v with
      Et (a, Et (b, c)) -> "Commence par "^a^" et "^b^".";;

let valeur1   = Et ("toto", Et ("titi", Et ("tutu", Rien)));;
let valeur2   = Et ("pi", Et ("po", Et ("pu", Rien)));;
(phrase valeur1, phrase valeur2);;
Quand on n'a pas besoin de v pour autre chose que le matching (ce qui est le cas ici), il existe une notation simplifiée qui permet de matcher directement l'argument de la fonction. Ce n'est pas un mécanisme nouveau, mais juste une simplification d'écriture:
let phrase = 
  function 
    Et (a, Et (b, c)) -> "Commence par "^a^" et "^b^".";;
Traitons tous les cas de liste
let phrase = 
  function 
      Rien              -> "C'est vide."
    | Et (a, Rien)      -> "Ne contient que "^a^"."
    | Et (a, Et (b, c)) -> "Commence par "^a^" et "^b^".";;
Encore une fois, la notation complète avec le match...with serait aussi correcte:
let phrase = 
  function v ->
    match v with 
        Rien              -> "C'est vide."
      | Et (a, Rien)      -> "Ne contient que "^a^"."
      | Et (a, Et (b, c)) -> "Commence par "^a^" et "^b^".";;
Comme c n'est pas utilisé après la ->, il n'était pas utile de lui donner un nom. Marquer qu'il doit y avoir un symbole ici suffit. C'est ce que fait _
let phrase = 
  function 
      Rien              -> "C'est vide."
    | Et (a, Rien)      -> "Ne contient que "^a^"."
    | Et (a, Et (b, _)) -> "Commence par "^a^" et "^b^".";;
Essayons ceci
let phrase = 
  function
      Rien              -> "C'est vide."
    | Et (a, b)         -> "Commence par "^a^"."
    | Et (a, Rien)      -> "Ne contient que "^a^"."
    | Et (a, Et (b, _)) -> "Commence par "^a^" et "^b^".";;
Et ceci
let phrase = 
  function
      Rien              -> "C'est vide."
    | Et (a, Rien)      -> "Ne contient que "^a^"."
    | Et (a, Et (a, _)) -> "Commence par deux "^a^"."
    | Et (a, Et (b, _)) -> "Commence par "^a^" et "^b^".";;
Dommage, l'idée était bonne, mais caml l'interdit pour des raisons théoriques liées à l'inférence de type. On peut toutefois écrire des contraintes qui font échouer un motif a posteriori et qui permettent en cas d'échec d'essayer le motif suivant. C'est le rôle de when.
let phrase = 
  function
      Rien                       -> "C'est vide."
    | Et (a, Rien)               -> "Ne contient que "^a^"."
    | Et (a, Et (b, _)) when a=b -> "Commence par deux "^a^"."
    | Et (a, Et (b, _))          -> "Commence par "^a^" et "^b^".";;

let valeur1   = Et ("toto", Et ("titi", Et ("tutu", Rien)));;
let valeur2   = Et ("po", Et ("po", Et ("pu", Rien)));;
let valeur3   = Et ("dalle", Rien);;
phrase Rien;;
phrase valeur1;;
phrase valeur2;;
phrase valeur3;;
Listes
Les 'a list déjà définies en caml sont identiques à nos 'a liste, si ce n'est que Et (a,b) se note a::b, et Rien se note []. Caml autorise une notation de listes avec [;;;], mais ce n'est qu'une notation.
1::(2::(3::(4::[])));;
1::2::3::4::[];;
[1;2;3;4];;
1::2::3::4;;
Reprenons notre fonction phrase
let phrase = 
  function
      []               -> "C'est vide."
    | a::[]            -> "Ne contient que "^a^"."
    | a::b::_ when a=b -> "Commence par deux "^a^"."
    | a::b::_          -> "Commence par "^a^" et "^b^".";;

let valeur1 = "toto"::"titi"::"tutu"::[];;
let valeur2 = ["po";"po";"pu"];;
let valeur3 = ["dalle"];;
phrase [];;
phrase valeur1;;
phrase valeur2;;
phrase valeur3;;