Apprentissage de la programmation en P_LOGO
Les propriétés 2 - exemple da^un système expert.

 

 

Retour au sommaire

Cette partie présente une illustration de la^utilisation des propriétés. Le programme suivant cherche à identifier un nom après une suite de questions. En cas da^ignorance, il sa^alimente des informations demandées à la^utilisateur. dans notre exemple, ca^est le nom da^un animal qui est recherché.

Pour commencer, il faut imaginer la^organisation des connaissances comme un arbre. Il y a des nuds, des branches et des fruits. A chaque nud, il y a deux branches menant vers un nud ou vers un fruit. Dans notre utilisation, le fruit est le nom da^un animal. Le nud est une question posée et les deux branches du nud sont les réponses.

A chaque fois, on a deux cas possibles :

On est sur un fruit : alors le programme propose le nom de la^animal.

On est sur un nud : alors on pose la question et on va, selon la réponse choisie par la^utilisateur vers la^une des deux branches. Il suffit alors de recommencer avec le nouveau nom.

Voici un arbre à cinq noms : deux nuds (questions) et trois fruits (animaux).

Le programme doit pouvoir :

Prenons le programme procédure par procédure. La première, ca^est DEPART. Elle est chargée da^initialiser la base des connaissances (si besoin est) et de démarrer la recherche.

POUR DEPART

SI NON NOMè "PREMIER [DONNE "PREMIER "PREMNOM DPROP "PREMNOM "FINè VRAI DPROP "PREMNOM "NOMFIN [le singe]]

PROPOSE :PREMIER

FIN

Pour savoir si la base des connaissances est vide, on teste si "PREMIER est un nom. Ca^est le cas si le nom déjà été déclaré, par exemple avec un DONNE. Si la base est vide, la chose donnée à PREMIER est PREMNOM ; puis on donne au nom PREMNOM les propriétés suivantes : FINè VRAI et NOMFIN [le singe]. La propriété FINè Est vraie si le nom contient un animal, sinon, il sa^agira da^une question. La propriété NOMFIN a pour valeur une liste désignant un animal. La variable PREMIER a pour valeur le nom du premier élement de la^arbre.

La procédure exécutée est PROPOSE avec comme paramètre la chose donnée à PREMIER ca^est-à-dire le point da^entrée dans la^arbre.

POUR PROPOSE :NOM

EC []

SI RPROP :NOM "FINè [TERMINE :NOM STOP]

EC RPROP :NOM "QUESTION

EC PH "1. RPROP :NOM "ITEM1

EC PH "2. RPROP :NOM "ITEM2

PROPOSE RPROP :NOM MOT "REPONSE ATTENDREREPONSE [1 2]

FIN

Le rôle de la procédure-récursive PROPOSE est da^identifier sa^il sa^agit da^un fruit ou da^un nud. Sa^il sa^agit da^un fruit, on est à une extrémité de la^arbre, on exécute TERMINE et on sort, la recherche est achevée. Dans le cas contraire, on affiche la question et les deux réponses possibles à cette question. Ensuite, on recommence la procédure PROPOSE avec comme paramètre le nom pointé par la branche correspondant à la réponse choisie par la^utilisateur. On note au passage les six propriétés da^un nud :

FINè a pour valeur FAUX, car il ne sa^agit pas da^un fruit, mais da^un nud.

QUESTION a pour valeur une liste : le texte de la question à poser.

ITEM1 qui a pour valeur la première réponse possible à la question qui mène au nom désigné par la valeur de la propriété REPONSE1.

ITEM2 qui a pour valeur la seconde réponse possible à la question. Cette réponse mène à la valeur de la propriété REPONSE2.

La procédure-opération ATTENDREREPONSE rend la touche entrée au clavier par la^utilisateur. Elle prend comme argument une liste contenant les caractères permis.

POUR ATTENDREREPONSE :PARMI

SOIT "TOUCHE LISCAR

RENDS SI NON MEMBREè :TOUCHE :PARMI [ATTENDREREPONSE :PARMI] [:TOUCHE]

FIN

On stocke dans la variable locale TOUCHE le caractère entré au clavier. Ensuite, on rend soit ce caractère sa^il fait parti de la liste des caractères permis, soit le résultat de ATTENDREREPONSE sa^il sa^agit da^un mauvais caractère. Bref, on boucle jusqua^à ce que le caractère fasse parti de la liste des caractères entrée en paramètre.

La procédure suivante traite le cas où la^on se trouve sur un fruit. La^argument est le nom du fruit. Elle propose le nom de la^animal et demande confirmation à la^utilisateur. Si la^utilisateur est satisfait de la proposition, la procédure sa^achève. Sinon, la^utilisateur est invité à compléter la base des connaissances.

POUR TERMINE :NOM

EC PH PH [Est-ce] RPROP :NOM "NOMFIN [è (<O>ui/<N>on)]

SI EGALè ATTENDREREPONSE [O N] "O [STOP]

EC [Je ne connais pas ce qu'il faut trouver.]

EC [Veux-tu me l'apprendre è (<O>ui/<N>on)]

SI EGALè ATTENDREREPONSE [O N] "O [APPRENDRE :NOM] [EC [Je resterais ignorant.]]

FIN

Tout cela est relativement simple. Ces quelques procédures suffiraient si la base était déjà définie. Da^ailleurs, pour y voir encore plus clair, voici les propriétés (obtenues avec IMPROPS) correspondant au dernier la^arbre exemple (avec la vache, la^homme et le porc). Ce ne sont les seules indispensables pour le moment.

DPROP "NOM1 "FINè FAUX

DPROP "NOM1 "QUESTION [De quoi se nourrit-il è]

DPROP "NOM1 "REPONSE1 [Des plantes.]

DPROP "NOM1 "REPONSE2 [De tout.]

DPROP "NOM1 "ITEM1 "NOM2

DPROP "NOM1 "ITEM2 "NOM3

DPROP "NOM2 "FINè VRAI

DPROP "NOM2 "NOMFIN [la vache]

DPROP "NOM3 "FINè FAUX

DPROP "NOM3 "QUESTION [Sur combien de pattes marche-t-il è]

DPROP "NOM3 "REPONSE1 [Deux.]

DPROP "NOM3 "REPONSE2 [Quatre.]

DPROP "NOM3 "ITEM1 "NOM4

DPROP "NOM3 "ITEM2 "NOM5

DPROP "NOM4 "FINè VRAI

DPROP "NOM4 "NOMFIN [la^homme]

DPROP "NOM5 "FINè VRAI

DPROP "NOM5 "NOMFIN [le porc]

On remarque au passage que les noms na^ont pas da^importance. Il suffit que la variable PREMIER ait pour valeur la première entrée dans la^arbre, à savoir NOM1.

Il reste le plus compliqué, la^ajout dans la base de nouveaux nuds et fruits. Ca^est le rôle de la procédure APPRENDRE. Les puristes du LOGO la trouveront trop longue et ils auront bien raison. Nous allons la couper en deux pour y placer nos commentaires.

La procédure APPRENDRE prend un argument : le nom du fruit. Il faut rappeler qua^on exécute cette procédure lorsque la^utilisateur na^a pas été satisfait de la^animal proposé par le programme. Autrement dit, il y a un autre animal qui répond aux même questions (qui empreinte les mêmes branches) qua^il faut différencier de la^animal contenu dans la base. Imaginons que la^utilisateur a cherché le mouton et est tombé sur la vache. Il a accepté da^apprendre au programme ce nouvel animal. Le paramètre est donc "NOM2.

POUR APPRENDRE :NOM

; partie dialogue -----------------------

EC [Quel est son nom è (Ecrire en minuscules et précédé de l'article.)]

DONNE "NOUVEAU LL

EC PH PH PH PH [Quelle est la question qui permet de différencier ] RPROP :NOM "NOMFIN [de] :NOUVEAU [è (Terminer avec un point d'interrogation.)]

DONNE "QUESTION LL

EC PH PH PH PH [Quelle est la première réponse possible, et qui est vrai pour] :NOUVEAU [et fausse pour] RPROP :NOM "NOMFIN [è]

DONNE "ITEM1 LL

EC PH PH PH PH [Et enfin, quelle est la seconde réponse possible, et qui est vraie pour] RPROP :NOM "NOMFIN [et fausse pour] :NOUVEAU [è]

DONNE "ITEM2 LL

EC [Merci.]

; -------------------------------------

Suite à cette partie de dialogue entre le programme et la^utilisateur, les variables suivantes sont affectées :

NOUVEAU a pour valeur le texte du nouvel animal : [le mouton]

QUESTION a pour valeur le texte de la question du nud [A-t-il de la laine è]

ITEM1 a pour valeur le texte de la première réponse possible [Oui.]

ITEM2 a pour valeur le texte de la seconde réponse possible [Non.]

Le morceau de la^arbre qui nous intéresse ressemble à ceci :

; partie affectation ------------------

SOIT "NOUVEAUNOMFIN GENERENOM

SOIT "NOUVEAUNOMQUESTION GENERENOM

Ces deux instructions créent deux nouveaux noms, il pour le nud (la question) et un pour le nouveau fruit (la^animal mouton).

DPROP :NOUVEAUNOMFIN "FINè VRAI

DPROP :NOUVEAUNOMFIN "NOMFIN :NOUVEAU

DPROP :NOUVEAUNOMFIN "PARENT :NOUVEAUNOMQUESTION

Le nouveau nom fruit a trois propriétés. Les deux premières sont connues : FINè pour dire que ca^est un fruit et pas un nud et NOMFIN le texte du fruit (le mouton). La troisième est le nom du nud da^où provient la branche. On a construit un nouveau morceau da^arbre.

A gauche les noms, à droite les valeurs contenues.

Le nouveau nud est créé avec les six propriétés connues.

DPROP :NOUVEAUNOMQUESTION "FINè FAUX

DPROP :NOUVEAUNOMQUESTION "QUESTION :QUESTION

DPROP :NOUVEAUNOMQUESTION "ITEM1 :ITEM1

DPROP :NOUVEAUNOMQUESTION "ITEM2 :ITEM2

DPROP :NOUVEAUNOMQUESTION "REPONSE1 :NOUVEAUNOMFIN

DPROP :NOUVEAUNOMQUESTION "REPONSE2 :NOM

Encore un nouveau morceau da^arbre, à partir de la nouvelle question :

Il reste à raccrocher la propriété parent à la question. Pour cela, il faut commencer par mettre en mémoire (variable locale) le nom du parent de la^ancien fruit (la vache)?E

SOIT "NOMPARENT RPROP :NOM "PARENT

?E et lui donner comme nouveau parent la nouvelle question.

DPROP :NOM "PARENT :NOUVEAUNOMQUESTION

SI VIDEè :NOMPARENT [DONNE "PREMIER :NOUVEAUNOMQUESTION STOP]

Cette dernière ligne traite le cas où le fruit précédent serait la^unique fruit de la^arbre. Dans ce cas, il na^y avait pas de parent. De plus, la nouvelle question est la nouvelle entrée dans la^arbre, da^où la^affectation de la variable PREMIER.

Occupons-nous maintenant du cas complémentaire.

DPROP :NOUVEAUNOMQUESTION "PARENT :NOMPARENT

La nouvelle question a pour parent la^ancien parent de la^ancien fruit.

Enfin, on raccroche la branche du parent allant vers la réponse.

DPROP :NOMPARENT SI EGALè RPROP :NOMPARENT "REPONSE1 :NOM ["REPONSE1] ["REPONSE2] :NOUVEAUNOMQUESTION

FIN

A la fin de cette procédure, la^arbre complet (uniquement les valeurs) est celui-ci :

Pour ceux qui connaissent, on a fonctionné un peu comme avec des pointeurs. Vous aurez compris pourquoi il était si important de connaître le parent da^un nom : pour pouvoir introduire un nouveau nud là où il avait un fruit.

Lors de la création da^un nouveau fruit ou da^un nouveau nud, il a été nécessaire faire appel à un nouveau nom. Ca^est la procédure-opération GENERENOM qui effectue cette opération.

POUR GENERENOM

SOIT "NOUVEAUNOM MOT "NOM HASARD 10000

RENDS SI NOMè :NOUVEAUNOM [GENERENOM] [:NOUVEAUNOM]

FIN

Le nom sera constitué des trois lettres N, O et M suivi da^un nombre entier au hasard en 0 et 10000. Si le hasard fait mal les choses et que le nom existe déjà, on recommence la génération da^un nom.

Avec les propriétés REPONSE1, REPONSE2 et PARENT, de na^importe quel nud, on connaît le parent et les deux enfants (les réponses) et de na^importe quel fruit, on connaît le parent. Nous pouvons nous en servir pour circuler dans la^arbre.

Par exemple, il est possible de connaître tous les fruits qui découlent da^un nud, grâce à la procédure AFFICHEDESCENDANTS, qui prend pour argument le nom da^un nud.

POUR AFFICHEDESCENDANTS :NOM

SI RPROP :NOM "FINè [EC RPROP :NOM "NOMFIN STOP]

On regarde si le nom courant (la chose de la^argument) est un fruit. Dans ce cas, on affiche le texte du fruit et on arrête, il na^y a pas da^enfant à un fruit.

Ensuite, on relance la procédure avec chacun des deux enfants (les réponses) du nud courant.

AFFICHEDESCENDANTS RPROP :NOM "REPONSE1

AFFICHEDESCENDANTS RPROP :NOM "REPONSE2

FIN

Par exemple, voici la^effet de la commande AFFICHEDESCENDANTS "NOM3 (qui est le nud " Sur combien de pattes marche-t-il è ").

la^homme

le porc

De même, on peut vouloir afficher tous les nuds (questions) et les branches (réponses) menant da^un nom (nud ou fruit) à la^entrée de la^arbre. La procédure AFFICHEDETERMINENTS prend en argument le nom da^un fruit ou du nud.

POUR AFFICHEDETERMINENTS :NOM

SOIT "NOMPARENT RPROP :NOM "PARENT

Comme on va sa^en servir à plusieurs reprises, on donne à une variable locale le nom du parent du nom courant.

SI VIDEè :NOMPARENT [STOP]

Sa^il na^a pas de parent, on arrête, car on est arrivé à la^entrée de la^arbre (seul nom sans parent).

EC PH RPROP :NOMPARENT "QUESTION RPROP :NOMPARENT SI EGALè RPROP :NOMPARENT "REPONSE1 :NOM ["ITEM1] ["ITEM2]

La ligne précédente affiche le texte de la question du parent suivit du texte de la réponse qui a mené au nom courant.

AFFICHEDETERMINENTS :NOMPARENT

La procédure est récursive. On recommence la^opération avec comme paramètre le nom du parent du nom courant.

FIN

Voici le résultat de la commande AFFICHEDETERMINENTS "NOM4 (qui est le fruit " la^homme ").

Sur combien de pattes marche-t-il è Deux.

De quoi se nourrit-il è De tout.

Cette dernière procédure est intéressante, mais son utilisation malaisée. En effet, il faut connaître le nom du fruit. Il faudrait une procédure qui trouve le nom de fruit da^après le texte (valeur de la propriété NOMFIN du fruit). Ca^est le rôle de la procédure TROUVENOMFRUIT qui prend pour argument un mot du texte du fruit recherché.

POUR TROUVENOMFRUIT :TEXTE

TROUVENOMFRUIT1 :PREMIER :TEXTE

FIN

Cette procédure na^est là que pour ajouter en argument la^entrée de la^arbre pour la commande de TROUVENOMFRUIT1.

POUR TROUVENOMFRUIT1 :NOM :TEXTE

Cette procédure prend pour argument un nom (nud ou fruit) de la^arbre et le texte du fruit recherché).

SI MEMBREè :TEXTE RPROP :NOM "NOMFIN [RENDS :NOM STOP]

Si le texte recherché est contenu dans la valeur de la propriété NOMFIN, ca^est gagné : on rend le nom courant et on arrête.

SI RPROP :NOM "FINè [RENDS "SANS_ISSUE STOP]

Si le nom courant est un fruit, comme le texte na^a pas été trouvé, on sait que ce na^est pas une bonne branche, on arrête.

Si on continue, ca^est qua^il sa^agit da^un nud.

SOIT "RECH1 TROUVENOMFRUIT1 RPROP :NOM "REPONSE1 :FRUIT

On attribue à une variable locale le résultat de la recherche dans la sous partie de la^arbre de la première branche du nud courant.

RENDS SI NEGALè "SANS_ISSUE :RECH1 [:RECH1] [TROUVENOMFRUIT1 RPROP :NOM "REPONSE2 :FRUIT]

Si le nom a été trouvé dans la première branche, on rend le nom. Sinon, on cherche dans la sous partie de la^arbre de la seconde branche du nud courant.

FIN

Par exemple, la^exécution de la commande EC TROUVENOMFRUIT "porc produit :

NOM5

Certes, ça ne sert à rien tout seul, mais associé à AFFICHEDETERMINENTS, ça devient intéressant. On peut maintenant rehcerche les déterminents du mouton, alors qua^on na^en connaît pas le nom.

AFFICHEDETERMINENTS TROUVENOMFRUIT "mouton

A-t-il de la laine è Oui.

De quoi se nourrit-il è De plantes.

Joli non è

On peut imaginer les commandes suivantes :

AFFICHEDETERMINENTS TROUVENOMANIMAL "baleine

AFFICHEDESCENDANTS RPROP TROUVENOMANIMAL "singe "PARENT

Il reste que la base est en mémoire, mais il ne faudrait pas avoir à réapprendre la^ensemble des animaux au programme à chaque fois qua^on sort et relance P_LOGO.

Il nous faut une procédure de sauvegarde de la base sur disque ainsi qua^une de son rappel.

POUR SAUVEGARDE

TAPE [Sauvegarde en cours...]

SORTIE "fiches

IMPROPS

IMNS

SORTIE 1

EC [ terminee.]

FIN

On oriente la sortie de la^affichage non plus vers la fenêtre textuelle, mais vers le fichier FICHES.LOG, on affiche toutes les définitions de variables et toutes les propriétés en mémoire, puis on dirige de nouveau la sortie vers la fenêtre textuelle.

Pour le chargement, rien de plus simple, un simple RAMENE du fichier FICHES.LOG (qui charge et interprète les commandes contenues dans le fichier) et le tour est joué.

POUR CHARGEMENT

TAPE [Récupération en cours...]

RAMENE "FICHES

EC [ terminee.]

FIN

Le petit programme est terminé. Bien entendu, des améliorations sont possibles :

On ne manque pas da^idées. Une autre encore : maintenant faisable, un arbre généalogique serait facile à faire, avec recherche des ancêtres, des descendants, des frères, des cousins?E Au travail.

Que c'est beau, le LOGO !

Chapître suivant


Webmaster : Sinclair ( philippe.lucidarme@wanadoo.fr ) dernière mise à jour : 1 aout1999.