• Aucun résultat trouvé

Les op´erations ensemblistes sur les automates d´efinies `a la section 2.2.6 page 30 sont mises en œuvre grˆace `a des adaptateurs de curseur monodirectionnel unaires (le compl´ementaire dans Σ∗) ou binaires (intersection, union, diff´erence, diff´erence sym´etrique, concat´enation). La figure 5.2 d´ecrit les interactions de l’adaptateur unaire du compl´ementaire not_cursor et de l’adaptateur binaire d’intersection intersection_cursor avec les autres composants. Nous allons nous int´eresser `a l’intersection pour illustrer la d´emarche de cr´eation d’un tel curseur.

SoientA(Σ, Q, i, F,∆) etA(Σ, Q, i, F,∆) deux automates dont on veut calculer l’inter-section B(Σ, Q′′, i′′, F′′,∆′′) d´efinie par :

B= (Σ, Q×Q,(i, i), F ×F,∆′′)

5.2. LES OP ´ERATIONS ENSEMBLISTES 79

Q×Q et dont l’ensemble des transitions ∆′′ est d´efini par la fonction de transition :

δ1′′((q, q), σ) = (δ1(q, σ), δ1(q, σ)).

Les ´etats terminaux sont les couples dont les ´etats sont tous deux terminaux dans les auto-mates de d´epart.

Soit xun curseur d’intersection ´evoluant sur l’automateB et encapsulant deux curseurs mo-nodirectionnels c1 et c2 respectivement sur A et A. D’apr`es les d´efinitions pr´ec´edentes, x doit avoir le comportement suivant :

– L’´etat point´e par xest constitu´e d’une paire d’´etats des automates sous-jacents :

q′′= (q, q).

State src() const {

return make_pair(c1.src(), c2.src()); }

q′′ est final siq etq sont finaux : bool src_final() const {

return c1.src_final() && c2.src_final(); }

forwardimpl´emente la fonction de transitionδ1′′. Une transition ´etiquet´ee parasortant de l’´etat q′′ est d´efinie si et seulement si il en existe une ´etiquet´ee par la mˆeme lettre sortant des ´etatsq etq. Autrement dit,xpeut avancer sur une transition sic1etc2le peuvent :

bool forward(int a) {

return c1.forward(a) && c2.forward(a); }

– Une transition sortant deq′′´etiquet´ee par aest d´efinie dansB si elle est d´efinie pourq

etq′ :

bool exists(int a) const {

return c1.exists(a) && c2.exists(a); }

– L’´etat q′′ est un ´etat puits si au moins un des deux ´etatsq etq′ est un ´etat puits : bool sink() const {

return c1.sink() || c2.sink(); }

Cette interface concerne un mod`ele de curseur d’intersection simple. Elle est suffisante pour tester si un mot appartient `aB. Le niveau de fonctionnalit´es sup´erieur qu’offre le curseur d’in-tersection monodirectionnel doit permettre de parcourir les transitions d’un ´etat, δ2((q, q)),

80 CHAPITRE 5. LES ADAPTATEURS

grˆace aux m´ethodes first_transitionetnext_transition.

D’apr`es la sixi`eme propri´et´e des curseurs de la section 4.4.2 que nous g´en´eralisons `a l’inter-section, les transitions sortant d’un ´etat sont rang´ees en s´equence dont la transition puits ((q, q), ǫ,0) mat´erialise la position de fin :

δ2((q, q)) = ((σ1,(p1, p1)), ...,(σn,(pn, pn)),(ǫ,0))

Ici 0 repr´esente l’´etat puits de l’automate intersection, c’est-`a-dire un couple d’´etats dont au moins une des deux composantes est nulle. 0 peut donc prendre les valeurs (q,0), (0, q) ou (0,0). On construit cette s´equence en choisissant les transitions communes aux deux auto-mates :

i,(pi, pi))∈δ2((q, q))⇔(σi, pi)∈δ2(q) et (σi, pi)∈δ2(q)

Comme tout adaptateur, le curseur r´ealisera l’intersection des deux s´equences `a la vol´ee et de mani`ere incr´ementale en recherchant l’´el´ement commun suivant l’´el´ement courant lors de l’appel `anext_transition.

Pour des raisons d’efficacit´e, nous allons imposer que ces transitions soient tri´ees selon l’ordre croissant des lettres les ´etiquetant :

pour 1≤i, j≤n, i < j⇔σi < σj

Cette contrainte suppl´ementaire nous permet d’´ecrire des m´ethodes first_transition et next_transitionde complexit´e lin´eaire. Plus exactement, au cours d’une it´eration compl`ete de δ2((q, q)) le nombre de transitions compar´ees est born´e par la somme des cardinaux des contextes droits des deux ´etats sources : |~c(q)|+|~c(q)|. Sans cette propri´et´e, le temps de parcours de la s´equence intersection est quadratique.

– La m´ethode priv´ee ci-dessous factorise les parties communes de first_transition et next_transition. Son rˆole consiste `a trouver la transition suivante (σi+1,(pi+1, pi+1)) commune aux deux curseursc1etc2positionn´es sur (q, σi, pi) et (q, σi, pi). Elle renvoie faux si (σi+1,(pi+1, p′ i+1)) = (ǫ,0) : bool find_next() { while(1) { if (c1.letter() < c2.letter()) {

if (!c1.next_transition()) return false; }

else

if (c2.letter() < c1.letter()) {

if (!c2.next_transition()) return false; }

else // c1.letter() == c2.letter() return true;

}

return false; }

5.2. LES OP ´ERATIONS ENSEMBLISTES 81 – La m´ethode first_transition positionne le curseur x sur les deux premi`eres

transi-tions communes des curseurs c1etc2 : bool first_transition() {

return c1.first_transition() && c2.first_transition() && find_next(); }

– Partant de l’´el´ement courant (σi,(pi, p′

i)), la m´ethode next_transition it`ere sur les deux s´equences `a la fois jusqu’`a trouver la transition commune suivante (σi+1,(pi+1, pi+1)) : bool next_transition() {

return c1.next_transition() && c2.next_transition() && find_next(); }

– Enfin, les m´ethodesforward etfind compl`etent l’interface : void forward() { c1.forward(); c2.forward(); } bool find(int a) {

return c1.find(a) && c2.find(a); }

Remarque Nous avons impos´e le mˆeme alphabet Σ aux trois automates A, A′, B et ce dans un but de simplification de l’expos´e. En fait, cette limitation n’en est pas vraiment une car il est possible de modifier les propri´et´es des alphabets de mani`ere externe, soit en utilisant des adaptateurs de curseur filtrant les caract`eres en entr´ee ou en sortie de l’inter-face (voir la section 5.5.3 sur les automates isomorphes), soit en red´efinissant les relations d’ordre et d’´equivalence sur les ´el´ements de Σ. ´Evidemment, ces deux possibilit´es ne sont pas mutuellement exclusives. La seconde consiste `a fournir `a l’adaptateur de curseur deux nouveaux op´erateurs de comparaisons au sein de ce qu’on appelle un trait [46]. Un trait est une classe centralisant les m´ethodes impl´ementant les op´erations standards propres `a un type particulier. Le trait standardchar_traitsfournit entre autres une m´ethode de comparaison eq (equal) renvoyant vrai si les deux caract`eres pass´es en argument peuvent ˆetre consid´er´es comme ´egaux. La m´ethodelt(lower than) renvoie vrai si le premier argument est inf´erieur au deuxi`eme. Le comportement par d´efaut consiste `a utiliser les op´erateurs==et<sur les carac-t`eres mais la possibilit´e est laiss´ee `a l’utilisateur de l’adapter selon ses besoins. Par exemple, le trait suivant rend la comparaison des caract`eres insensible `a la casse («case-insensitive») :

struct insensitive_traits {

static bool eq(int x, int y) { return tolower(x) == tolower(y);

82 CHAPITRE 5. LES ADAPTATEURS

}

static bool lt(int x, int y) { return tolower(x) < tolower(y); }

};

La fonction C standard tolower convertit un caract`ere en son ´equivalent en minuscule si n´ecessaire induisant une ´equivalence entre a et A, b et B, etc. En munissant l’adaptateur d’intersection de ces op´erateurs :

intersection_cursor<fcursor1, fcursor2, insensitive_traits> c;

on rend le calcul plus«lˆache»`a condition bien sˆur que les comparaisons de lettres ´etiquetant les transitions passe par le trait. Voici le code r´eel de la m´ethode find_next introduite plus haut :

bool find_next() {

while(1) {

if (traits::lt(c1.letter(), c2.letter())) { if (!c1.next_transition()) return false; }

else

if (traits::lt(c2.letter(), c1.letter())) { if (!c2.next_transition()) return false; }

else // c1.letter() == c2.letter() return true;

}

return false; }

En lieu et place de l’op´erateur <des appels `a la m´ethode statiquelt sont effectu´es. Comme d’habitude, une utilisation usuelle d’un composant doit se traduire par du code simple `a ´ecrire et lisible, c’est pourquoi par d´efaut le type du trait utilisera le comportement standard de la classe char_traits<int> ce qui, dans ce cas pr´ecis, nous ram`ene `a la pr´ec´edente version de la fonction.

Le polymorphisme permet n’importe quelle combinaison de mani`ere imm´ediate comme d´ecrit `a la figure 5.3 o`u l’adaptateur impl´emente la diff´erence sym´etrique de deux automates

A1 etA2 d´efinie par (A1 ∪A2)\(A1∩A2). L’utilisation d’un tel objet en particulier et des adaptateurs en g´en´eral est d´ecrite `a la section 5.6 sur les algorithmes.