• Aucun résultat trouvé

6.3 Systématiser les échanges pour corriger les erreurs

6.3.2 Garantir une progression de l’interprétation : valider questions et

L’objectif de cette sous-section est double :

1. montrer qu’il est possible de modifier facilement la mise en œuvre des opérateurs proposés pour améliorer le programme d’interprétation généré, sans changer le sens de la description de la page ;

2. régler, au moins partiellement, les problèmes de progression mentionnés dans la sec- tion précédente, en permettant une validation des questions et des réponses.

Nous allons donc montrer comment garantir qu’une question ne sera pas posée à nou- veau si une réponse satisfaisante existe en mémoire. Nous allons également montrer com- ment générer automatiquement des contraintes de positionnement des réponses, afin de gui- der les opérateurs humains dans le positionnement de ces dernières et garantir leur utilité. 6.3.2.1 Syntaxe

La syntaxe des opérateurs reste identique, la façon de décrire une page ne change pas. 6.3.2.2 Structures de données nécessaires

Afin de permettre de gérer le risque de boucle identifié précédemment, nous distinguons deux types de questions :

– les questions valides, pour lesquelles un bloc get_answer_or_try recherchant un élé- ment du bon type T englobe l’appel à l’opérateur raise_question,

– et les questions invalides, pour lesquelles ce n’est pas le cas.

Pour savoir, lors de la génération d’une question, si celle-ci est correcte ou non, il est né- cessaire de connaître l’arbre d’appel dynamique des fonctions menant à l’invocation de raise_question Une certaine détection statique (à la compilation de la description) serait possible, mais elle ne pourrait pas tenir compte des éventuelles constructions contextuelles

que le langage peut décrire, car il faudrait explorer toutes les dérivations syntaxiques pos- sibles, ce qui n’est pas envisageable. Une détection à l’exécution nous a semblé, en pratique, un bon compromis.

Nouveau paramètre pour les instructions L’intégration d’un nouveau paramètre V (pour « validité ») pour les instructions du programme est alors nécessaire. Ce paramètre circule classiquement, c’est à dire comme ε, sauf au niveau de l’opérateur get_answer_or_try qui le modifie, et au niveau de l’opérateur raise_question qui l’utilise. Il contient l’ensemble des types pour lesquels il est possible de poser une question.

Afin de faciliter la production d’une réponse utile par l’environnement, il est intéressant de stocker pour chaque type T la zone R servant à rechercher une éventuelle réponse en mé- moire au niveau de l’opérateur get_answer_or_try. Ceci permet de guider l’environnement du module d’interprétation de page, et plus particulièrement les interfaces graphiques, dans le contrôle des réponses qui peuvent être acceptées.

Par conséquent, le nouveau paramètre V contient un ensemble (initialement vide) de couples du type

TypeDonnée × Zone. (6.39)

Nouvelles zones Cette connaissance de la zone dans laquelle sera recherchée la réponse peut être intégrée à la question pour guider la réponse externe. Les questions contiennent alors une nouvelle information zone_acceptation qui correspond à la zone dans laquelle une réponse peut être acceptée. Pour chaque couple question–réponse, trois zones sont alors nécessaires :

La zone de la question notée zoneQuestion ci-après, elle localise le problème.

La zone de recherche de la réponse dite zone d’acceptation et notée zoneAcceptation ci- après, elle correspond à la zone de recherche au début du bloc défini par get_answer_or_try englobant l’appel à raise_question.

La zone de la réponse notée zoneReponse ci-après, elle localise la réponse précisément. Lors de la création d’une réponse, le module ou l’interface graphique utilisé doit garantir que zoneReponse ∩ zoneAcceptation 6= /0. Par ailleurs, on peut faciliter la saisie d’une ré- ponse par un opérateur humain en initialisant zoneReponse avec la valeur de zoneQuestion. Représentation des questions et des réponses Si la question est valide, c’est à dire qu’il existe un bloc get_answer_or_try recherchant un élément du bon type T et englobant l’ap- pel à l’opérateur raise_question, alors elle présente de la forme suivante :

(zoneQuestion, question, { txt : Q,

type_attendu: T,

zone_acceptation: zoneAcceptation, . . . })

(6.40)

et la forme de la réponse associée est alors :

(zoneReponse, T, { valeur : Val, . . . }) (6.41) Si, au contraire, la question est invalide, c’est à dire qu’aucun bloc get_answer_or_try ne permet d’éviter d’appeler indéfiniment l’opérateur raise_question, alors elle a la forme

suivante :

(zoneQuestion, question_invalide, { txt : Q,

type_attendu: T, . . . })

(6.42)

L’intérêt de l’export de contraintes pour les réponses vers les autres modules est d’éviter de dupliquer la connaissance à propos du contenu des pages lors de la conception, et de transmettre automatiquement les informations nécessaires lors de l’exécution.

6.3.2.3 Compilation et sémantique

La séparation stricte entre le langage de description de la page et l’implémentation des opérateurs autorise à changer l’implémentation de ces derniers sans avoir d’impact sur la description, et donc sur le travail du concepteur.

La gestion du nouveau paramètre V et la distinction des questions valides et invalides nécessite une nouvelle version des règles de réécriture associées aux opérateurs.

T

[[ get_answer_or_try(T, Rauto)]]

≡ λ εRIMEVκζ.(

T

[[ conserver_resultat(T, Rauto)]]

ε R I M E (cons( (T, R), V) )

λ r ε′R′I′M′E′V′.(κ r ε′ R′ I′ M′ E′ V) ζ)

(6.43)

L’opérateur get_answer_or_try est responsable de la création d’un couple (type, zone) et lui donne une portée en supprimant cet élément en sortie de bloc (en rétablissant la valeur de V , reçue à l’entrée du bloc, lors de l’appel à κ).

T

[[ catch_question(T, Rauto)]] ≡ λ εRIMEVκζ.(

T

[[Rauto]] ε R I M (cons( (T, λ ε′R′I′M′E′V′.(κ question ε′ R′ I′ M′ E V)), E)) λ r ε′R′I′M′E′V′.(κ auto(r) ε′ R′ I′ M′ E V′) ζ) (6.44) Pour l’opérateur catch_question, la modification consiste simplement à rétablir la bonne valeur de V si une question est posée.

T

[[ raise_question(zone, type, info)]] ≡ λ εRIMEVκζ.                                        (

T

[[ a jouter_mem(zone, question, {type_attendu : type,

zone_acceptation: Rtry} ∪ info)]]

ε R I M E V chercher(type, E) ζ) si ∃(Ttry, Rtry) ∈ V ∧ type = Ttry

(

T

[[ a jouter_mem(zone, question_invalide,

{type_attendu : type} ∪ info)]] ε R I M E V chercher(_, E) ζ)

sinon

(6.45)

L’opérateur raise_question reprend la mise en œuvre précédente en ajoutant un test sup- plémentaire pour savoir si la question est valide ou non, et le cas échéant indiquer la zone d’acceptation de la réponse.

6.3.2.4 Exemple

Nous reprenons ici l’exemple, qui sert de cadre à cette section, visant à localiser puis à reconnaître des numéros de vente dans la colonne de gauche de tableaux (voirfigure 6.4).

FIGURE6.4 – Extrait d’un registre de ventes dont on souhaite extraire les numéros de vente

(colonne de gauche). Rappel de lafigure 6.3, page106.

Pour cet exemple, on ne considérera pas que la détection de la colonne puisse poser problème, et on s’intéressera simplement à la détection et à la reconnaissance des nombres. Cas avec une question valide Le fragment de description intéressant ne concerne alors que la règle de description localiser_lire_no dans l’exemple précédent, qu’on reprend dans l’équation 6.46suivante.

localiser_lire_no −→

var posNo posPreciseNo ResReco (posNo, _) ← localiser_no

@(posNo)

(posPreciseNo, ResReco) ← catch_question(no_vente,

get_answer_or_try(no_vente, reconnait_noint))

@(sous posNo) localiser_lire_no localiser_lire_no −→

passer

(6.46) En reprenant la même stratégie globale que précédemment (voir l’algorithme 2, page 112), l’évolution du contenu de la mémoire visuelle au moment intéressant du traitement se fera comme suit :

Contenu de la mémoire visuelle Itération 1, point A

{ (zoneNo1, no_vente, { valeur : 131 }),

(zoneNo2, question, { type_attendu : no_vente,

zone_acceptation: zoneColonne,

txt: "Quelle est la valeur de ce nombre ?" }), (zoneNo3, no_vente, { valeur : 133 }) }

Observations : Le module d’interprétation de page a localisé trois nombres, mais n’en a reconnu que deux. Il a posé une question pour connaître la valeur du nombre inconnu en indiquant la zone dans laquelle le résultat doit se trouver.

Contenu de la mémoire visuelle Itération 1, point B

{ (zoneColonne, colonne_no_ordre, { }), (zoneNo1, no_vente, { valeur : 131 }), (zoneNo2Precise, no_vente, { valeur : 132 }), (zoneNo3, no_vente, { valeur : 133 }) }

Observations : Un opérateur humain a indiqué la valeur du nombre inconnu en précisant la position du nombre. La question associée a été supprimée.

Cas avec une question invalide Un autre cas intéressant est celui où une erreur est pré- sente dans la description : aucun bloc get_answer_or_try n’est défini autour de l’appel à reconnait_nointqui peut poser une question.

localiser_lire_no −→

var posNo posPreciseNo ResReco (posNo, _) ← localiser_no

@(posNo)

(posPreciseNo, ResReco) ← catch_question(no_vente, reconnait_noint)

@(sous posNo) localiser_lire_no localiser_lire_no −→

passer

(6.47) Dans ce cas, la première question provoquera un arrêt de l’interprétation et la notifica- tion du problème.

Contenu de la mémoire visuelle Itération 1, point A

{ (zoneNo1, no_vente, { valeur : 131 }),

(zoneNo2, question_invalide, { type_attendu : no_vente, txt: "Quelle est la valeur [...]" }) }

Observations : Le module d’interprétation de page a localisé un nombre, et a tenté de poser une question pour réclamer la valeur du second nombre. Le risque de boucle infinie a été détecté et l’interprétation de la page courante arrêtée.

Au delà de l’intérêt pour limiter les erreurs de conception, cette amélioration de la mise en œuvre des opérateurs montre qu’il est facile de les adapter sans changer le sens de la description du contenu de la page. La formalisation des opérateurs permet de raisonner rigoureusement à propos du comportement du programme généré, et l’export de contraintes de position ou de domaine de réponse vers les autres modules permet d’éviter la duplication de connaissances relatives au contenu des pages lors de la conception pour laisser le système gérer automatiquement l’échange d’informations importantes lors de l’exécution.

6.3.3 Intégration homogène de l’interaction spontanée : gérer les problèmes