4. Structure de données sûre 65
4.5. Interactions avec Tom
4.5.4. Les invariants
En utilisant la définition de structure de données de la figure 4.12, on manipule des
objets, commepar(concPar()), qui ne correspondent pas nécessairement aux structures
que l’on veut décrire. Le problème posé par des objets commepar(concPar())s’ajoute
à celui qui est présenté dans la note A : cet objet ne correspond directement à aucun
objet dans le langage des structures du systèmeBV. On peut toutefois le considérer égal
au neutre pour l’opérateur [ ],◦.
La possibilité de représenter de différentes manières des structures équivalentes pour
le système BV pousse à choisir un de ces représentants comme représentant canonique.
Exemple 23. Les structures par(concPar(a())) et cop(concCop(a())) sont toutes
deux équivalentes à la structurea(), carhRi ≈[R]≈(R)≈R.
– [],hi et()sont réduits lorsqu’ils contiennent une seule sous-structure :
par(concPar(x))→x
cop(concCop(x))→x
seq(concSeq(x))→x
– Les structures imbriquées sont aplaties, en utilisant les règles :
par(concPar(a
1, . . . ,a
i,par(concPar(x
1, . . . ,x
n)),b
1, . . . ,b
j))
→par(concPar(a
1, . . . ,a
i,x
1, . . . ,x
n,b
1, . . . ,b
j))
cop(concCop(a
1, . . . ,a
i,cop(concCop(x
1, . . . ,x
n)),b
1, . . . ,b
j))
→cop(concCop(a
1, . . . ,a
i,x
1, . . . ,x
n,b
1, . . . ,b
j))
seq(concSeq(a
1, . . . ,a
i,seq(concSeq(x
1, . . . ,x
n)),b
1, . . . ,b
j))
→seq(concSeq(a
1, . . . ,a
i,x
1, . . . ,x
n,b
1, . . . ,b
j))
– Les sous-termes depar etcopar sont triés selon un ordre total< :
concPar(. . . ,x
i, . . . ,x
j, . . .)→concPar(. . . ,x
j, . . . ,x
i, . . .) ifx
j<x
iconcCop(. . . ,x
i, . . . ,x
j, . . .)→concCop(. . . ,x
j, . . . ,x
i, . . .) ifx
j<x
iCette notion de forme canonique nous permet de vérifier efficacement si deux objets
représentent la même structure modulo l’associativité, la commutativité, les éléments
neutres et les règles de réduction des différents connecteurs. Cette vérification est de
fait très efficace, car elle correspond à une comparaison de pointeurs, deux objets d’une
même classe d’équivalence étant représentés par le même représentant canonique, donc
le même objet en mémoire grâce au partage maximal.
Le premier invariant à maintenir est la réduction des singletons pour les structures
seq, par et copar. Lorsque l’utilisateur essaye de construire un seq, par ou cop avec
une liste vide de structures, alors la fonction de création doit retourner l’unité o().
Sinon, si la liste contient un seul élément, elle doit retourner l’élément lui même. Dans
les autres cas, cette fonction de création construira la structure demandée. Comme tous
les objets manipulés par l’utilisateur sont en forme normale (sont des représentants
canoniques), il n’est pas nécessaire dans le cas de cet invariant de traiter le cas d’une
liste de structures en argument contenant l’unité ou un seul élément, car cela est rendu
impossible par l’invariant sur la liste. Ce comportement est implanté comme un hook
pour les opérateursseq,par etcop.
Le premier hook de la figure 4.13 implémente la réduction des singletons pour
l’opé-rateurparen utilisant le filtrage de Tom. Le filtrage sur les opérateurs de liste de Tom
est utilisé pour examiner la liste passée en argument du constructeur depar. Si la liste
n’est pas un singleton, alors la fonction originale de construction est implicitement
ap-pelée. Cette fonction de création originale n’est accessible que depuis la définition du
hook correspondant à l’opérateur. Ainsi, l’utilisateur de la structure de données ne peut
l’utiliser, mais utilisera la fonction comportant le hook, qui met en place les invariants.
Les opérateurscopetseqsont munis de hooks similaires.
Les hooks permettant de normaliser les listes de structures sont plus complexes. Il
est tout d’abord nécessaire d’utiliser un ordre total sur les structures. Cet ordre peut
4.5. Interactions avecTom
...
par:make(l) {
%match(StrucPar l) {
concPar() -> { return ‘o(); }
concPar(x) -> { return ‘x; }
}
}
cop:make(l) {
%match(StrucCop l) {
concCop() -> { return ‘o(); }
concCop(x) -> { return ‘x; }
}
}
seq:make(l) {
%match(StrucSeq l) {
concSeq() -> { return ‘o(); }
concSeq(x) -> { return ‘x; }
}
}
...
Fig. 4.13: Leshooks pour par,copetseq.
être implanté par une fonction extérieure
3. Toutefois, les structures générées par Gom
comportent une implantation d’un ordre total très efficace, rendu disponible par
l’im-plantation de l’interfaceComparablepar toutes les classes correspondant aux opérateurs.
Il est donc raisonnable dans ce cas d’utiliser cet ordre, car aucune condition particulière
n’est requise sur l’ordre utilisé (il doit toutefois être total).
On peut donc définir les hooks pour les opérateurs variadiquesconcSeq, concPar et
concCop. Ceux-ci sont définis en surchargeant la fonction d’insertion en tête dans la liste
d’arguments de l’opérateur variadique, en utilisantmake_insert.
Lehook pour concSeqest le plus simple, car les structureshisont seulement
associa-tives, avec ◦leur élément neutre. Le hook correspondant doit donc supprimer les unités
et aplatir les seq imbriqués. L’implantation de ce hook en figure 4.14 utilise une fois
encore le filtrage de Tom pour examiner la forme de l’élément à insérer dans la liste
d’arguments d’un opérateur concSeq. Si celui-ci est une unité, la liste de départ est
retournée, et si c’est une structure seq, sa liste interne d’arguments est concaténée à la
liste d’arguments de l’opérateur. Les hooks pour concCop et concPar, présentés en
fi-gure 4.15 sont similaires, mais examinent aussi le second argument : la liste dans laquelle
on insère l’élément, afin d’implanter une insertion triée selon l’ordre interne à la
struc-3
La seule contrainte est que cette fonction doit être déclarée statique, et être accessible. Elle peut être écrite en utilisant le filtrage deTom.
StrucSeq = concSeq( Struc* )
concSeq:make_insert(e,l) {
%match(Struc e) {
o() -> { return l; }
seq(concSeq(subL*)) -> { return ‘concSeq(subL*,l*); }
}
}
Fig.4.14: Lehook pour l’opérateurconcSeq
ture. Cehook utilise‘concPar(L*,l*)pour effectuer un appel récursif à la fonction de
création (utilisant la construction ‘ de Tom, présentée en section 2.2.4). Il utilise aussi
l’appel à la fonctionrealMake, qui correspond à l’opération de construction originale.
Le calcul des structures vérifie aussi les lois de De Morgan pour la négation. On peut
donc aussi écrire unhook pour l’opérateur de négationneg, qui applique les lois de De
Morgan de manière similaire auhook de l’exemple 22. L’application de ce hook garantit
que dans les structures manipulées seuls les atomes peuvent être en position négative.
Cela simplifie l’application des règles de déduction, en supprimant le besoin de propager
les négations.
Dans le document
Réécriture et compilation de confiance
(Page 107-110)