Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
Publié parLiliane Billon Modifié depuis plus de 11 années
1
9 avril 2001 Listes et Suites Finies Représentation Propriété Accès aux éléments Récursivité Construction Sous-suites et arbres représentatifs Application : les analyseurs
2
9 avril 2001 Introduction Tout au long du chapitre, utilisation dun même exemple tiré du programme menu du TD1. –Avant : une règle associée à chaque élément du menu (entrée, plat, dessert) –Maintenant : on va regrouper ces données et considérer la suite des entrées, la suite des plats et la suite des desserts
3
9 avril 2001 Représentation (1) A une suite, ordonnée ou non, on associe la liste de ses éléments. –Utilisation du symbole fonctionnel binaire «. » suite {e1, e2, …} ==> liste (e1.(e2.(…))) Exemples : –suite des variables X et Y ==> (X.Y) –suite {gateau, fruit, glace} ==>(gateau.(fruit.glace))
4
9 avril 2001 Représentation (2) –La liste vide est notée « nil ». Elle sert souvent à marquer la fin de liste. Il est préférable de noter la liste précédente sous la forme : (gateau.(fruit.(glace.nil))) Cela définit le sens du parenthésage du symbole fonctionnel «. ». Les listes peuvent alors être représentées sous forme d arbres.
5
9 avril 2001 Représentation (3) Exemples :. XY... gateau fruit glace nil
6
9 avril 2001 Représentation (4) –Lorsque la liste comporte un trop grand nombre d'éléments, on admet le codage sans parenthèses Exemples : –X.Y –gateau.fruit.glace.nil
7
9 avril 2001 Propriété fondamentale (1) Jusquà présent : arbres particuliers avec des branches gauches sans ramification. –On utilise le terme de peigne pour les désigner. Exercice : –résoudre l'équation X.Y = gateau.fruit.glace.nil –par identification on a la solution : {X = gateau; Y = fruit.glace.nil}
8
9 avril 2001 Propriété fondamentale (2) La notation X.Y représente une liste dont la tête (le 1er élément) est X et la queue (le reste de la liste) est Y. Cela constitue la base de lutilisation des listes dans les programmes Prolog. Attention : considérez la liste X.Y. Si Y ne se termine pas par nil, on ne connaît pas le type de Y (élément si pas de nil à la fin, liste si nil à la fin), doù l'intérêt de nil pour sassurer que Y est une liste.
9
9 avril 2001 Propriété fondamentale (3) Exemple du programme menu : entrees(sardine.pate.melon.avocat.nil). viandes(poulet.roti.steack.nil). poissons(merlan.colin.loup.nil). desserts(gateau.fruit.glace.nil). Et si on pose la question : entrees(E). Leffacement de ce but (synonyme de question) provoque laffectation suivante : E=sardine.pate.melon.avocat.nil
10
9 avril 2001 Propriété fondamentale (4) –Nous savons donc affecter une liste à une variable. –Nous avons aussi besoin de pouvoir considérer les éléments dune liste de façon individuelle, par exemple pour récupérer une entrée particulière. Supposons que lon dispose du prédicat : element-de(X, L) capable dextraire un objet X dun peigne L.
11
9 avril 2001 Propriété fondamentale (5) –Nous avions : menu(X, Y, Z) :- entree(X), plat(Y), dessert(Z). plat(X) :- viande(X). plat(X) :- poisson(X). Avec la nouvelle déclaration commune des entrées comme liste, ce programme échouera. Il faut donc le modifier. Pour cela nous allons construire une nouvelle définition du prédicat entree (ne pas confondre avec le prédicat entrees).
12
9 avril 2001 Propriété fondamentale (6) On utilise la propriété suivante : si X est une entrée, alors X est un élément quelconque de la liste des entrées. On peut donc écrire : entree(X) :- entrees(E), element-de(X, E). Et de façon analogue : viande(X) :- viandes(V), element-de(X, V). poisson(X) :- poissons(P), element-de(X, P). dessert(X) :- desserts(D), element-de(X, D).
13
9 avril 2001 Accès aux éléments dune liste(1) Il sagit de rechercher les règles qui vont assurer le fonctionnement du prédicat element-de(X, L) qui signifie que X est lun des éléments de L. Méthode de base mais trop particulière : X est élément de L si X est le premier élément, ou le deuxième, … ou le dernier. Cela nécessite la connaissance de la longueur de la liste.
14
9 avril 2001 Accès aux éléments dune liste(2) Autre méthode indépendante de la taille de L : on utilise la remarque selon laquelle toute liste peut se décomposer simplement en deux parties, la tête et la queue de liste. Cela conduit à distinguer deux cas : *X est élément de L si X est la tête de L. *X est élément de L si X est élément de la queue de L. Ce qui se traduit directement en Prolog par : –R1 : element-de(X, L) :- est-tete(X, L). –R2 : element-de(X, L) :- element-de-la-queue(X,L). –R1 et R2 sont là en tant que repères et ne rentrent pas dans les règles.
15
9 avril 2001 Accès aux éléments dune liste(3) Mais on peut aller plus loin car L peut toujours être représentée sous la forme U.Y, ce qui nous donne : –Si X est en tête de U.Y alors X = U. –Si X est un élément de la queue de U.Y alors X est élément de Y. Doù la version finale : –R1 : element-de(X, X.Y). –R2 : element-de(X, U.Y) :- X\=U, element-de(X, Y).
16
9 avril 2001 Accès aux éléments dune liste(4) Commentaires : –On interprète ces deux règles de la façon suivante : *R1 : X est élément de toute liste qui commence par X. *R2 : X est élément de toute liste dont la queue est Y, si X est élément de Y. –Le second membre de R2 provoque un nouvel appel à R1 et R2. Il sagit donc dun appel récursif. La plupart des problèmes sur les listes ont des solutions mettant en jeu la récursivité.
17
9 avril 2001 Récursivité (1) On observe deux types de règles et deux types d'arrêt : Une ou plusieurs règles provoquent la récursivité, généralement sur des données assez simples, et assurent le déroulement de la boucle. Dans notre exemple, il sagit de la règle R2. Une ou plusieurs règles stoppent la boucle. Dans notre exemple, cest R1 qui sen charge. Sauf impératif contraire, les règles d'arrêt sont placées en tête.
18
9 avril 2001 Récursivité (2) Conseils pour lécriture de programmes récursifs : Chaque règle doit être, sautant que possible, conçue comme une déclaration ou une définition, indépendamment de toute forme d'exécution. Lordre des règles peut être déterminé par l'exécution du programme (et pas seulement pour les parties récursives du programme).
19
9 avril 2001 Récursivité (3) Il apparaît deux types d'arrêt : Un arrêt explicite. Par exemple, dans R1, l'identité entre l'élément cherché et la tête de la liste fournie, ce quexprime la notation X et X.Y. Un arrêt implicite. En particulier par la rencontre dun terme impossible à effacer. Il existe cependant une forme derreur pour laquelle ces deux blocages se révèlent insuffisants, cest la rencontre de listes infinies.
20
9 avril 2001 Construction dune liste (1) Nous avons vu comment parcourir une liste. Nous allons voir comment en construire une. –Examinons le problème suivant : L une liste contenant un nombre pair d'éléments. –Par exemple : L = 0.1.2.3.4.5.nil –Cherchons à construire une nouvelle liste W en prenant les éléments de rang impair dans L.
21
9 avril 2001 Construction dune liste (2) –Dans notre exemple cela donnerait : –W = 0.2.4.nil –Nous disposons de deux outils : le découpage dune liste et la récursivité. Méthode à suivre : –Création dun appel récursif. –Modification des règles pour obtenir le résultat désiré. elements-impairs(L) :- queue(L, Y), queue(Y, Y1), elements-impairs(Y1).
22
9 avril 2001 Construction dune liste (3) –On a L = X1.Y ce qui nous donne : elements-impairs(X1.Y) :- queue(X1.Y, Y), queue(Y, Y1), elements-impairs(Y1). –La première condition est alors inutile. De plus la décomposition de L peut etre repetee avec Y = X2.Y1. On a donc : elements-impairs(X1.X2.Y1) :- elements-impairs(Y1). –Nous avons donc notre règle récursive. Il reste à trouver la règle d'arrêt. Il faut s'arrêter à la liste vide : elements-impairs(nil).
23
9 avril 2001 Construction dune liste (4) –Doù le programme : R1 : elements-impairs(nil). R2 : elements-impairs(U.V.Y) :- elements-impairs(Y). –Il reste à modifier le programme de façon à construire la nouvelle liste à partir du parcours de lancienne. Doù le programme final : R1 : elements-impairs(nil, nil). R2 : elements-impairs(U.V.Y, U.M) :- elements- impairs(Y, M).
24
9 avril 2001 Construction dune liste (5) –Interprétation de ces deux règles : R1 : prendre un élément sur deux dans la liste vide donne la liste vide R2 : prendre un élément sur deux dans la liste U.V.Y donne la liste U.M si prendre un élément sur deux dans la liste Y donne la liste M. –Remarque : les éléments dans la liste résultat sont rangés dans le même ordre que dans la liste initiale.
25
9 avril 2001 Construction dune liste (6) Construction dune liste au moyen dune liste auxiliaire : –Problème : soit D une liste donnée, R est la liste D inversée. On est dans une situation différente la précédente à cause de lordre inverse des éléments. Nous allons donc utiliser une liste auxiliaire pour ranger les éléments au fur et à mesure de leur lecture en attendant de pouvoir les utiliser.
26
9 avril 2001 Construction dune liste (7) –Boucle de récursivité : renverser(D) :- tete(X, D), queue(Y, D), renverser(Y). Comme précédemment, on remplace D par X.Y ce qui donne : renverser(X.Y) :- renverser(Y). renverser(nil). –Nous allons maintenant ajouter deux arguments à ces règles, la liste auxiliaire et la liste résultat R.
27
9 avril 2001 Construction dune liste (8) –On obtient les règles suivantes : R2 : renverser(D, L, R) :- renverser(Y, X.L, R). R1 : renverser(nil, L, R) :- transformer(L, R). –Il faut déterminer la transformation à effectuer sur L pour obtenir R. Au lancement L est vide. Chaque fois quun élément apparaît en tête de D, il est retiré et placé en tête de L. Il y a donc inversion de lordre des éléments donc L et R sont identiques. Il ny a pas de transformation à effectuer.
28
9 avril 2001 Construction dune liste (9) –Le programme final est donc : R1 : renverser(nil, L, L). R2 : renverser(X.Y, L, R) :- renverser(Y, X.L, R). –Interprétation de ces deux règles : Renverser la liste vide, à partir de la liste auxiliaire L, donne la liste résultat L. Renverser la liste X.Y, à partir de la liste auxiliaire L, donne la liste résultat R, si renverser la liste Y, à partir de la liste auxiliaire X.L, donne la même liste R.
29
9 avril 2001 Construction dune liste (10) –Exercice classique : concaténation de deux listes Plusieurs possibilités : –récursivité –découpage de la liste –construction directe –utilisation d une liste auxiliaire –passage final de paramètre (méthode précédente)
30
9 avril 2001 Listes et sous-suites (1) Utilisation du programme menu : Volonté de poursuivre le regroupement des données. Liste unique qui regrouperait tous les mets du menu »L= sardine.melon…glace.nil Cette représentation nest pas adéquate car il ny a pas de distinction entre entrée, plat ou dessert. Il est donc nécessaire de découper la liste en sous- listes qui rassembleront les entrées, plats et desserts. La sous-liste des plats sera elle-même divisée en deux parties, les viandes et les poissons.
31
9 avril 2001 Listes et sous-suites (2) Cela nous donne la structure : L = (sardines.pate.melon.celeri.nil).((poulet.roti.steack.n il).(merlan.colin.loup.nil)).(gateau.fruit.glace.nil).nil L est de la forme : L = L1.L2.L3.nil Exercices : –Construire larbre associé à la liste L. –Résoudre les équations : L=X.Y, L=X.Y.Z, L=X.Y.Z.T, L=X.(U.V).Y, L=(X.Y).Z, L=X.((U.V).Y).Z, L=X.(U.V).Y.Z
32
9 avril 2001 Application : les analyseurs (1) Voici deux exemples de grammaires : G1 = {S --> c ; S --> aSb} G2 = {S --> AB ; A --> 0A ; A --> 0 ; B --> 1B ; B --> 1} Exercice : écrire des mots de ces deux grammaires. Exercice : G1 étant donné, construire un analyseur reconnaissant les mots de L(G1). On fournira au programme une chaîne sous forme de liste, par exemple : ``a ``.``a``.``c``.``b``.``b``.nil
33
9 avril 2001 Application : les analyseurs (2) Approche naturelle : Les règles du programme doivent définir la structure des mots de L(G1). Elles doivent donc rendre compte des règles de G1. S --> c s exprime sans difficulté : une chaîne est dérivable de S si cette chaîne se réduit à ``c``.nil, soit : –R1 : derivable-de-S(X) :- se-reduit-a(``c``.nil, X). se-reduit-a signifie simplement que X est la chaîne ``c``.nil
34
9 avril 2001 Application : les analyseurs (3) S --> aSb est plus difficile à traduire. –Il nous faut dire que la chaîne d'entrée X se décompose en trois parties U, V, W, avec les caractéristiques suivantes: U est le caractère ``a``, V est une chaîne qui dérive de S, et W est le caractère ``b``. La concaténation de U, V et W doit redonner X. –Problème : on ne sait que concaténer les chaînes et non les caractères seuls. Il faut donc modifier U et W en ``a``.nil et ``b``.nil. On peut alors écrire : »R2 : derivable-de-S(X) :- se-reduit-a(``a``.nil, U), derivable-de-S(V), se-reduit-a(``b``, W), concatener(U, V, Y), concatener(Y, W, X).
35
9 avril 2001 Application : les analyseurs (4) On peut alors remplacer U et W par leur valeur, ce qui donne : –R1 : derivable-de-S(``c``.nil). –R2 : derivable-de-S(X) :- derivable-de-S(V), concatener(``a``.nil, V, Y), concatener(Y,``b``.nil, X). Concaténer ``a``.nil à la liste V donne la liste ``a``.V, on peut donc substituer ``a``.V à Y. doù la version finale : –R1 : derivable-de-S(``c``.nil). –R2 : derivable-de-S(X) :- derivable-de-S(V), concatener(``a``.V,``b``.nil, X).
36
9 avril 2001 Application : les analyseurs (5) –Interprétation : R1 : la chaîne ``c``.nil est dérivable de laxiome R2 : la chaîne X est dérivable de laxiome, si elle est la concaténation de la chaîne ``a``.nil, d une chaîne V dérivable de laxiome, et de la chaîne ``b``.nil. Problème de la concaténation : peut être très coûteuse et cause de boucle infinie. Ici il y a un problème avec une boucle infinie. Il faut donc corriger le programme.
37
9 avril 2001 Application : les analyseurs (6) L'exécution doit s'arrêter lorsque lapplication de R1 conduit à une valeur correcte de X. On va donc modifier R1 : –R1 : derivable-de-S(``c``.nil) :- /. Mais dune part il vaut mieux éviter dutiliser un cut, et dautre part le programme obtenu ne fonctionne pas en synthèse. A retenir : la plupart des programmes qui demandent une concaténation dans une boucle de récursivité sont très lents à l'exécution.
38
9 avril 2001 Application : les analyseurs (7) Autre approche : approche par les graphes. On peut considérer que le mot aacbb se caractérise par cinq transitions successives entre six états. On a alors le schéma suivant (qui est un graphe orienté à six nœuds) : 123456 aacbb
39
9 avril 2001 Application : les analyseurs (8) La chaîne à analyser prend une forme complexe : une suite de flèches étiquetées, entre des numéros de nœuds du graphe. Le plus simple pour spécifier cette chaîne est dutiliser un ensemble dassertions : fleche(``a``, 1, 2). fleche(``a``, 2, 3). fleche(``c``, 3, 4). fleche(``b``, 4, 5). fleche(``b``, 5, 6).
40
9 avril 2001 Application : les analyseurs (9) Nous allons maintenant caractériser chacune des règles de la grammaire G1 par un graphe. Cela donne en Prolog : R1 : fleche(``S``, i, j) :- fleche(``c``, i, j). R2 : fleche(``S``, i, m) :- fleche(``a``, i, j), fleche(``S``, j, k), fleche(``b``, k, m). Amélioration du programme : –Pour un nœud du graphe, au lieu dun numéro, on peut utiliser la chaîne d'entrée elle-même. On désignera chaque nœud par la chaîne de caractères qui le suivent.
41
9 avril 2001 Application : les analyseurs (10) On a donc : 1 ``a``.``a``.``c``.``b``.``b``.nil 2 ``a``.``c``.``b``.``b``.nil 3 ``c``.``b``.``b``.nil 4 ``b``.``b``.nil 5 ``b``.nil 6 nil
42
9 avril 2001 Application : les analyseurs (11) On constate que deux nœuds successifs ne diffèrent que par un seul élément, et si le nœud numéro i X.U, alors le nœud numéro i+1 U. La différence entre les deux listes X.U et U est l'élément X qui étiquette la flèche entre les deux nœuds. On obtient donc : R1 : fleche(``S``, ``c``.U, U) :- fleche(``c``, ``c``.U, U). R2 : fleche(``S``, ``a``.U, V) :- fleche(``a``, ``a``.U, U), fleche(``S``, U, W), fleche(``b``, W, V).
43
9 avril 2001 Application : les analyseurs (12) On peut remarquer que W = ``b``.V ce qui donne : R2 : fleche(``S``, ``a``.U, V) :- fleche(``a``, ``a``.U, U), fleche(``S``, U, ``b``.V), fleche(``b``, ``b``.V, V). fleche(``a``, ``a``.U, U) signifie quil doit exister une flèche étiquetée ``a`` entre les nœuds ``a``.U et U, ce qui est toujours vrai. On a donc le programme suivant : R1 : fleche(``S``, ``c``.U, U). R2 : fleche(``S``, ``a``.U, V) :- fleche(``S``, U, ``b``.V).
44
9 avril 2001 Application : les analyseurs (13) Il ne reste que les termes relatifs aux non-terminaux, qui interviennent de façon explicite, et non sous- forme de variable. Cest-à-dire que toutes les règles relatives au non-terminal N contiendront le caractère ``N`` comme premier argument, qui sert en quelque sorte à identifier la règle. On peut donc écrire : R1 : fleche-S(``c``.U, U). R2 : fleche-S(``a``.U, V) :- fleche-S(U, ``b``.V).
45
9 avril 2001 Application : les analyseurs (14) Ou puisque nous devons reconnaître des mots : R1 : mot(``c``.U, U). R2 : mot(``a``.U, V) :- mot(U, ``b``.V). Interprétation 1: Au non-terminal S, nous avons fait correspondre lidentificateur mot dun terme à deux arguments. Le premier est la liste des caractères de la chaîne, avant la reconnaissance du non-terminal, le second est la liste des caractères de la chaîne restante lorsque le non-terminal a été identifié.
46
9 avril 2001 Application : les analyseurs (15) On appelle aussi ces deux arguments « liste d'entrée » et « liste de sortie ». On en tire l'interprétation suivante : –R1 : Avec la liste d'entrée ``c``.U on reconnaît un mot et il reste la liste de sortie U. –R2 : Avec la liste d'entrée ``a``.U on reconnaît un mot et il reste la liste V, si on reconnaît un mot avec la liste d'entrée U et la liste de sortie ``b``.V.
47
9 avril 2001 Application : les analyseurs (16) Interprétation 2 : on peut aussi considérer que le non-terminal reconnu représente la différence entre la liste d'entrée et la liste de sortie. Doù une nouvelle façon d'interpréter les règles : –R1 : on reconnaît un mot par différence entre les deux listes ``c``.U et U. –R2 : on reconnaît un mot par différence entre les deux listes ``a``.U et V, si on reconnaît un mot par différence entre les deux listes U et ``b``.V.
48
9 avril 2001 Application : les analyseurs (17) Déroulement du programme : (1) En analyse : la liste d'entrée est constituée de la chaîne entière, et, lorsque cette chaîne aura été reconnue, la liste de sortie sera vide. Il faut donc lancer le programme par : mot(``a``.``a``.``c``.``b``.``b``.nil, nil). –On obtient la liste deffacements : mot(``a``.``c``.``b``.``b``.nil, ``b``.nil). mot(``c``.``b``.``b``.nil, ``b``.``b``.nil). fin
49
9 avril 2001 Application : les analyseurs (18) (2) En synthèse : la chaîne d'entrée est inconnue, et on veut la liste vide en sortie. Il faut donc donner comme but : mot(X, nil). –(a) R1 sapplique et donne {x=``c``.U1 ; U1 = nil} doù la première réponse {X = ``c``.nil} –(b) Remontée en (a) R2 sapplique, donne {X = ``a``.U1 ; V1 = nil} et laisse à effacer mot(U1, ``b``.nil). –( c ) R1 sapplique, donne {U1 = ``c``.U2 ; U2 = ``b``.nil} doù la deuxième réponse {X = ``a``.``c``.``b``.nil} –Par remontée en ( c ) on construira la troisième réponse, etc.
50
9 avril 2001 Application : les analyseurs (19) Conclusion : La construction de lanalyseur est quasiment automatique. Une règle Prolog par règle de la grammaire. Un terme par non-terminal. Pour chaque terme, deux arguments de la forme : –X.U et U pour une règle qui dérive un terminal X. –X.Y.Z.U et V pour les terminaux X, Y, Z placés en tête du second membre. –U et M.N.P.V pour les terminaux placés entre deux non- terminaux (ou à la fin de la règle).
51
9 avril 2001 Application : les analyseurs (20) Exercice : écrire lanalyseur associé à G2. mot(U, V) :- nonterminal-A(U, W), nonterminal-B(W, V). nonterminal-A(0.U, U). nonterminal-A(0.U, V) :- nonterminal-A(U, V). nonterminal-B(1.U, U). nonterminal-B(1.U, V) :- nonterminal-B(U, V).
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.