octobre
2009
Motifs variable
Comme vous le savez sans doute le let ne permet pas seulement de déclarer une nouvelle variable par exemple ici la variable m :
let choose = function | None -> None | Some n -> let m = minimum n n.left in Some (m.key,m.item)
Motifs simples
Il permet aussi de déconstruire une valeur selon un motif comme ici la paire (m,l) :
let rec delete_min t = match t.left with | None -> t,t.right | Some n -> let m,l = delete_min n in m,Some {t with left = l}
Bien sûr le let n’accepte qu’un motif qui ne doit pas pouvoir échouer (on dit aussi exhaustif).
Dans le cas contraire OCaml génère un avertissement et vous seriez bien avisé d’utiliser un match afin de gérer tous les cas.
La déconstruction par une variable m réussi toujours puisqu’une variable est un motif universel (la variable _ n’a rien de plus universel, elle est seulement plus anonyme).
La déconstruction par un couple de variables (m,l) réussi toujours également puisqu’une paire ne peut pas être autre chose qu’un couple de valeurs.
Là où le let ne fait plus l’affaire c’est sur un type union comme celui-ci :
type ast_val = | Val of int (* offset *) | Array of int * int (* offset, size of item *)
Qui dit qu’une valeur est :
- ou bien une valeur positionnée à un certain offset
- ou bien un tableau de valeurs (de même taille) positionné à un certain offset
Dans le cas général, on utilise un match pour déconstruire les valeurs union.
Cependant, il arrive qu’un traitement ne dépende pas du constructeur mais d’une information disponible dans tous les cas.
Par exemple ici on génère du code qui va placer l’adresse d’une certaine valeur dans un certain registre cpu :
let rec eval_addr reg = function | Member(record,var) -> eval_addr reg record; ( match var with | Var offset -> emit (Addr(reg,Offset(reg,offset))) | Array(offset,_) -> emit (Addr(reg,Offset(reg,offset))) ) | ItemAt(vector,index,size) -> eval_addr reg vector; eval_val (reg+1) index; emit (Addr(reg,Scale(reg,reg+1,size)))
Le code émit ne dépend que de l’offset, il est le même que la valeur soit un tableau ou non.
Distinguer les deux cas devient alors une nuisance, il y a répétition et de plus il faut parenthéser le match pour qu’il ne capture pas le cas ItemAt(vector,index,size). On peut faire plus agréable à l’oeil.
Motifs union
L’union d’un motif a et d’un motif b est un motif noté a | b qui accepte aussi bien le motif a que le motif b. (¹)
Dans notre code on peut éviter la répétition en utilisant un motif union :
( match var with | Var offset | Array(offset,_) -> emit (Addr(reg,Offset(reg,offset))) )
En fait, puisqu’on n’a qu’un seul motif exhaustif qui sert essentiellement à déclarer la variable offset, on peut carrément remplacer match par un let :
let Var offset | Array(offset,_) = var in emit (Addr(reg,Offset(reg,offset)))
(¹) il y a quelques contraintes sur les motifs a et b, ils doivent être de même type et déclarer le même ensemble de variables