• Aucun résultat trouvé

V.2 Comparaison des arbres de types

V.2.2 Comparaison des empreintes

V.2.2.5 Algorithme de comparaison

L’automate de reconnaissance de massif est implémenté par l’algorithme donné dans la figure 42.

Cet algorithme prend en entrée deux chaînes bien parenthésées appelées respectivement u pour la chaîne représentant le type de l’élément source et v pour la chaîne représentant le type dans lequel l’élément doit être converti. L’algorithme utilise une pile pour mémoriser les parenthèses ouvrantes rencon-trées au cours du déroulement de l’algorithme. Chaque élément de la pile contient trois informations : pile.symb est le symbole représentant le constructeur du type de l’élément comparé (’{’, ’[’ ou ’(’) ; pile.état est l’état de retour sur er-reur correspondant à l’état courant quand le type à été empilé ; pile.compte est le nombre de parenthèses identiques à pile.symb rencontrées depuis que le type a été empilé.

L’algorithme utilise également les fonctions Inverse, AjoutCouple, SupprCouple et Alpha. Inverse renvoie la parenthèse fermante opposée à celle qui lui est passée en paramètre (c’est à dire ’)’ pour ’(’, ’]’ pour ’[’ et ’}’ pour ’{’). La fonction AjoutCouple(i,j) enregistre une relation entre le type d’indice i dans la source et le type d’indice j dans la cible. La fonction SupprCouple(i1, i2) efface les relations concernant l’ensemble des types dont les indices dans l’empreinte source sont compris entre i1 et i2. La fonction Alpha implémente α et α−1. Elle retourne l’indice de la parenthèse opposée au caractère dont l’indice est passé en paramètre.

Le résultat est un ensemble de couples (type source, type cible), appelé couplage de u dans v, qui sont utilisés pour la conversion de l’instance source u.

i := 1; /* indice source */ j := 1; /* indice cible */

empiler (pile, ε); /* initialisation de la pile */ tantque i <= |u| et j <= |v| faire /*boucle principale*/ si u[i] = v[j] alors

si v[j] = ’T’ alors

AjoutCouple (i,j); i := i + 1;

sinon /* v[j] != ’T’*/

si v[j] = Inverse (pile.symb) alors

/* v[j] est une parenthese fermante inverse du symbole en sommet de pile */

si pile.compte = 0 alors /* cas 2, transition M1 :

compte = 0 signifie que v(j) est la

parenthèse fermante associée a celle du sommet de pile :

le type u[i] est en relation avec v[j] transition M1 */

AjoutCouple (i, j); i := i + 1;

dépiler (pile);

sinon /* pile.compte != 0 */

/* cas 3, transition M2 : v[j] n’est pas au même niveau de parenthésage que le sommet de pile */ pile.compte := pile.compte − 1;

finsi

sinon /* v[j] != inverse (pile.symb) */

/* v[j] n’est pas l’inverse du somment de pile */ si v[j] ∈ (’{’,’[’,’(’) alors

/* cas 4, transition M1’ : v[j] est une parenthèse ouvrante,

on empile le type correspondant */ empiler (pile, (v[j],(i, j),0)); i := i + 1;

finsi

/* l’autre cas ne peut arriver car les chaînes sont bien parenthésées */

finsi finsi i := i + 1;

sinon /* u[i] != v[j] */

si v[j] = Inverse (pile.symb) alors

/* v[j] est une parenthese fermante inverse du symbole en sommet de pile */

si pile.compte > 0 alors /* cas 5, transition M2:

v[j] correspond à un autre niveau de

parenthésage que le type en sommet de pile */ pile.compte := pile.compte − 1;

sinon /* pile.compte = 0 */ /* cas 6 :

le compteur ne peut plus être décrémenté :

la correspondance de types echoue partiellement. les couplages de tous les types de l’instance source compris entre pile.ind et i sont effacées

*/

si (pile.état.j < j − 1) alors /* transition M3 */

i := pile.état.i + 1;

/* j est incrémenté en fin de boucle */ j := pile.état.j − 1;

pile.état.j := pile.état.j + 1; sinon /* transition M3’ */

SupprCouple (pile.état.i, i); j := Alpha ( pile.état.j + 1); i := pile.état.i; dépiler (pile); finsi finsi finsi si v[j] = pile.symb et u[i] ∉ (’}’,’]’,’)’) alors

/* cas 7, transition M4: v[j] est une parenthèse identique à la

derniere empilée */

pile.compte := pile.compte + 1; finsi

/* cas 8, transition M5:

v[j] n’est ni identique ni inverse au symbole de sommet de pile, on ne fait qu’avancer dans la chaîne cible */

finsi

j := j + 1; /* progression dans la chaîne cible */ fintantque si i > u alors retourner succes; sinon retourner echec; finsi

Figure 42 : Algorithme de comparaison d’empreintes Théorème 2

La complexité de l’algorithme est polynômiale en O(|v|2). Preuve

Si l’on ne considère que les états Mt ∪ M1 ∪ M1’ ∪ M2 ∪ M3 ∪ M4 ∪ M5, l’indice j est systématiquement incrémenté par ces état, ils peuvent donc engendrer au plus |v| itérations de la boucle de l’algorithme.

Considérons maintenant la transition M’3 cette transition ne peut entraîner le parcours supplémentaire que des fils du nœud de V correspondant à l’état (i, j) dans lequel est déclenchée la transition. Par conséquent, au plus |v| transitions M’3 sont déclenchées lors de la comparaison. La comparaison des

fils entraînant au plus ϕj − ϕj ≤ |v| transitions. Le surcoût dû à la transition M’3 est donc |v|2. Le nombre d’itérations de la boucle est donc limité par |v|+|v|2. Par conséquent la complexité de l’algorithme est O(|v|2).

Cet algorithme calcule un massif de l’arbre de types source U dans l’arbre de types cible V s’il en existe au moins un. Dans le cas de la comparaison de types de documents structurés, il peut exister un grand nombre de solutions − ceci est dû au nombre limité de constructeurs du système de types et à la forte compo-sante textuelle des documents traités, qui implique de nombreuses correspon-dances possibles entre les types de base intervenant dans les structures génériques des documents.

Les relations trouvées par cet algorithme sont des relations de massif entre types non définis récursivement. Aussi, pour prendre en compte les relations d’absorption et trouver des relations entre types récursifs, qui sont fréquents dans les structures considérées (les paragraphes, les sections d’articles, les arbres sont définis récursivement), nous étendons la méthode de comparaison de types effectuée par l’algorithme de recherche de massif.

Pour optimiser le processus de transformation, nous évitons certaines com-paraisons superflues. Lorsque la comparaison est effectuée entre deux types appartenant à une même structure générique, les éléments comparés peuvent être du même type. Il est, dans ce cas superflu de comparer les images de ces descendants dans les empreintes. Grâce à la table associative construite lors de la génération de l’empreinte, l’utilisation de la fonction φu−1 qui associe le nœud de l’arbre de type U à l’indice d’un symbole de l’empreinte appartenant à l’ensemble S est immédiate, on ajoute l’état M’’1 à l’automate de comparai-son :

M’’1={((i, j), vj, (y, h, k), ((φu o φu−1(i)) + 1, j + 1), (y, h, k))} avec la condition ui = vj, y ≠ vj−1

Cet état permet d’éviter la comparaison de la descendance de deux types égaux.