3. Structuration des règles et modification. PrologIA HERITAGE II+
A!ociation
Prolog
HERITAGE
3. Structuration des règles et modification
3.1. Introduction
3.2. Terminologie
3.3. Syntaxe des identificateurs
3.4. Contexte de Lecture et Ecriture
3.5. Modules
3.6. Résumé ou Approche simplifiée des identificateurs, contextes et modules
3.7. Ajout, suppression et recherche de règles
3.8. Manipulation des modules compilés
Les notions de module, de famille d'identificateurs, et de contexte de lecture/écriture ont été introduites pour permettre:
• La modularité et l'indépendance des programmes, en permettant de partitionner l'ensemble des identificateurs manipulés.
• La gestion de programmes Prolog comportant un très grand nombre de règles.
Ceci est fait en partageant l'ensemble des règles en différents modules.
3.1. Introduction
La notion de module vient de la nécessité de regrouper les règles d'un gros programme en ensembles fonctionnels que l'on appelle modules.
3.1.1. Qualification des prédicats
Afin de permettre une certaine indépendance entre l'élaboration des différents modules, il est souhaitable de pouvoir automatiquement rendre indépendants les noms des règles des différents modules.
La méthode la plus simple que l'on puisse imaginer est d'étendre les noms de règle en les préfixant systématiquement avec le nom du module : on a ainsi implicitement construit un partitionnement des règles calqué sur celui des noms de règle, la sémantique restant rigoureusement identique à celle d'un Prolog sans module. Le préfixe est alors appelé qualificateur.
<nom de règle> ::= <prefixe> : <suffixe>
<prefixe> ::= <lettre> <alpha num>
<suffixe> ::= <lettre> <alpha num>
©PrologIA
R 3 - 2
Manuel de Référence
Exemples : lexicon:data(1) ->; lexicon:data(2) ->; lexicon:pn(X) -> ...
...
grammar:sentence(X) -> lexicon:pn(X1) grammar:sv(X2)...
grammar:error_message(M) -> sys:write(M); grammar:data(8) ->;
...
Les noms de règles de modules différents sont par définition différents, puisqu'ils ont des qualificateurs différents (Il faut insister sur le fait que la suite prefixe+suffixe n'est pas une structure, mais constitue un identificateur insécable).
La notion de base est donc celle de partition des symboles de règle (ou noms des règles), qui induit une partition dans les règles correspondantes. Chaque partie est appelée un module. Cette notion est parfaitement dynamique: créer une règle avec un nom de règle appartenant à une partition non encore utilisée, correspond à créer un nouveau module.
Tous les modules sont situés sur le même plan, et il n'existe pas de notion de modules imbriqués.
Note
Pour compatibilité avec l'interpréteur Prolog II, la syntaxe des préfixes a été étendue de manière à pouvoir simuler une inclusion en permettant l'utilisation du séparateur
":" à l'intérieur du préfixe. Par exemple Base:Normal:data est un identificateur ayant pour préfixe "Base:Normal", et pour suffixe "data".
3.1.2. Qualification des foncteurs
Pour garder la symétrie entre les données et les programmes, le même partitionnement est utilisé pour les symboles de règles et les symboles de foncteurs.
La qualification des foncteurs permet également de garantir que certains noms n'interféreront pas avec d'autres modules (désignation d'un fichier temporaire, type de donnée opaque, ...).
Pour clarifier les notations, il a été créé une notation spécifique pour certains foncteurs dits génériques. Ces foncteurs peuvent être utilisés comme noms symboliques "globaux" (au sens de identiques dans tous les modules).
<nom de foncteur> ::= <nom de règle> |
<nom générique>
<nom générique> ::= : <suffixe>
Exemples de noms génériques :
:john, :data, :sentence, :singular
Les identificateurs génériques correspondent à ceux qui sont utilisés par défaut pour les noms utilisateurs lorsque l'on ne fait pas de déclaration de module.
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 3
3.1.3. Simplification des notations
La spécification systématique des qualificateurs est quelque chose de relativement lourd qui peut facilement être évité dans les textes sources en indiquant le début et la fin des règles d'un module. Les qualifications peuvent alors être réalisées automatiquement lors de la lecture du module.
L'exemple précédent peut ainsi se noter dans le texte source : module("lexicon"); data(1) ->; data(2) ->; pn(X) -> ...
end_module("lexicon"); module("grammar"); sentence(X) -> lexicon:pn(X1) sv(X2)...
error_message(M) -> sys:write(M); data(8)...
end_module("grammar");
Tout identificateur noté sans le caractère ":" est dit écrit en notation simplifiée (ou non qualifiée). Il est alors représenté, dans le texte source, par son seul suffixe.
Avec cette seule convention, si le qualificateur est différent de celui du module, la forme complète (i.e. qualifiée) de l'identificateur de règle doit être spécifiée. Nous verrons dans les sections suivantes d'autres déclarations permettant de simplifier encore les notations.
La règle de qualification est la même pour les noms des règles et pour les noms des foncteurs.
Exemple en syntaxe Edinburgh: module("test").
myprint(X) :- ...
data(1).
data(:john).
do_print1(L) :-
C =.. [myprint|L], call(C).
do_print2(L) :-
C =.. [write|L], call(C).
do_list_data :listing(data/1).
end_module("test").
/* -- programme défini -- */ test:myprint(X) :- ...
test:data(1).
test:data(:john).
test:do_print1(L) :-
C =.. [test:myprint|L], sys:call(C).
test:do_print2(L) :-
C =.. [sys:write|L], sys:call(C).
test:do_list_data :sys:listing(test:data/1).
3.1.4. Quelques règles simples d'utilisation
Une manière très simple d'utiliser les modules consiste à noter systématiquement avec des noms génériques les données que l'on veut noter dans plusieurs modules, et de préfixer systématiquement tous les noms d'appels de règles dans d'autres modules que le module courant et le module système. Exemple:
> insert;
module("lexicon");
©PrologIA
R 3 - 4
Manuel de Référence np(:john) ->; np(:marie) ->;
....
end_module("lexicon"); module("grammar"); nom_propre(X) -> lexicon:np(X);
...
end_module("grammar");
;
> grammar:nom_propre(X);
{X=john}
....
Lorsque l'on a un doute sur la qualification qui a été créée pour un identificateur, utiliser la règle prédéfinie string_ident/3 vous permet toujours de voir quel est le préfixe associé.
Pour éviter d'avoir à qualifier des noms externes, on peut les spécifier dans l'en-tête du module, l'effet étant alors similaire à une déclaration d'importation dans un langage classique. Cela n'exclut cependant pas d'utiliser d'autres noms, pourvu qu'ils soient explicitement qualifiés.
L'exemple ci-dessus peut ainsi s'écrire: module("grammar",["lexicon",["np"]]); nom_propre(X) -> np(X);
...
end_module("grammar");
3.1.5. Modules et préfixes standard
Quand Prolog est lancé sans spécifier d'état initial, l'état initial standard de la mémoire de travail (fichier initial.po) est chargé.
L'état initial comprend ‘trois’ modules:
- Le module qui contient les règles prédéfinies, correspondant au préfixe "sys".
- Les modules qui forment le superviseur, dont les préfixes sont de la forme réservée "sys: …".
- Le module par défaut pour les programmes de l'utilisateur, correspondant au préfixe vide "".
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 5
3.2. Terminologie
De nombreuses incompréhensions ayant résulté de l'emploi des mêmes mots pour désigner à la fois une entité Prolog et la chaîne de caractères la représentant, nous essayerons de donner ici une terminologie précise. Si en Prolog normal l'ambiguïté n'est pas très gênante, elle amène cependant une extrême confusion dans le cas des modules: une séquence de caractères alphanumériques située dans un module M1, peut ne pas représenter la même entité que celle désignée par la même séquence de caractères située dans un module M2 (c'est à dire les entités correspondantes ne sont pas unifiables).
Identificateur
Un élément de l'ensemble des entités non structurées, dont la représentation externe répond à la syntaxe des identificateurs.
Représentation complète d'un identificateur
La séquence de caractères qui représente de manière unique et non ambiguë une entité de type identificateur, indépendamment des conventions de lecture / écriture. Par extension on parlera d'identificateur complet pour désigner cette séquence de caractères. Un identificateur complet est formé par la concaténation des caractères d'un préfixe, du caractère ":", et d'une abréviation d'identificateur appelée identificateur abrégé.
Famille d'identificateurs
On appellera famille "p" l'ensemble de tous les identificateurs possédant le préfixe "p". Une famille pourra être désignée par la chaîne correspondant au préfixe des identificateurs qu'elle contient.
Ainsi sys:out, sys:in appartiennent à la famille "sys".
De même, sys:env:files appartient à la famille "sys:env", :peter appartient à la famille "".
Opération de lecture/écriture
Opération associant une entité Prolog (représentation interne) à une séquence de caractères (représentation externe) ou réciproquement.
Contexte de lecture et d'écriture
Règles de passage d'un identificateur abrégé à un identificateur complet (et viceversa) lors d'une opération de lecture/écriture.
Représentation abrégée d'un identificateur
C'est une représentation externe sans préfixe d'un identificateur. La représentation complète peut être déterminée de manière non ambiguë à partir des conventions de lecture / écriture (ou contexte de lecture / écriture). Deux représentations abrégées identiques peuvent représenter des entités différentes lorsqu'elles sont prises dans des contextes différents, mais représentent toujours la même entité lorsqu'elles sont prises dans le même contexte de lecture / écriture.
©PrologIA
R 3 - 6
Manuel de Référence
Module "p"
Dans un programme, c'est à tout moment l'ensemble des règles et des faits dont l'identificateur d'accès a le préfixe "p" (les tableaux Prolog sont ici assimilés à des faits). Il s'agit donc d'une notion dynamique: les modules évoluent avec l'état du programme. On peut voir la notion de module ainsi définie, comme un regroupement des règles par famille des identificateurs d'accès.
A!ociation
Prolog
HERITAGE
3.3. Syntaxe des identificateurs
La syntaxe des identificateurs est étendue de manière à pouvoir les regrouper par leur préfixe. La syntaxe d'un identificateur complet est donc: full_identifier = prefix_limit = prefix , prefix_limit , abbreviated_identifier ;
":"; prefix = [ name, { prefix_limit, name } ]; abbreviated_identifier = name ; name = letter, { letter | digit | "_" } ;
Ainsi les identificateurs complets suivants sont corrects et représentent tous des identificateurs différents:
:peter data:peter sys:write
Base:Normal:toto grammar:plural grammar:name lexicon:name
L'identificateur suivant n'est pas un identificateur complet: peter
Note: il est important de pouvoir distinguer syntaxiquement les identificateurs complets des identificateurs abrégés. La détermination du nom complet à associer à un nom abrégé est réalisée à partir du contexte de lecture et d'écriture
(cette notion est développée plus loin).
Il faut également souligner que le ":" n'est PAS un opérateur: le nom ne représente pas un objet sécable, seule la chaîne de caractères qui le représente est décomposable. Lorsqu'on parle de nom abrégé, on parle donc de la chaîne de caractères correspondante, seul le contexte de lecture permet de déterminer de quel objet Prolog il s'agit:
E/S avec conventions
de nommage (contexte) identificateur abrégé ——> identificateur complet ——> identificateur
chaîne de caractères chaîne de caractères
analyse
objet Prolog
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 7
Les Prolog classiques peuvent facilement être intégrés dans ce schéma: la syntaxe des identificateurs correspond à la seule notation abrégée, et la convention d'extension est de les préfixer avec "". C'est la convention par défaut lorsqu'on démarre Prolog II+.
3.3.1. Paramétrage de l'écriture d'un identificateur complet
Jusqu'à présent et dans la suite de ce manuel, le caractère qui délimite le préfixe du suffixe, dans la représentation d'un identificateur complet, a toujours été présenté comme étant le caractère ":". C'est le caractère qui est choisi par défaut, pour tenir ce rôle.
Il peut toutefois être remplacé par un caractère graphique, en effaçant le prédicat prédéfini
set_prefix_limit/1
.
set_prefix_limit(s)
s doit être une chaîne d'un seul caractère, ce caractère doit être un caractère graphique. Il devient alors le caractère utilisé pour délimiter le préfixe du suffixe dans l'écriture d'un identificateur complet. Exemple :
> set_prefix_limit("$");
{}
> ident(sys$ident);
{}
> string_ident(x,y,a$a$ident);
{x="a$a",y="ident"}
prefix_limit(s)
s est unifié avec le caractère qui sert à délimiter le préfixe du suffixe dans l'écriture d'un identificateur complet. Exemple :
> prefix_limit(x) set_prefix_limit("$") prefix_limit(y);
{x=":", y="$"}
Le changement de l'écriture des identificateurs complets doit se faire en connaissance de cause et avec des précautions. En effet, si la définition du caractère de délimitation se fait dynamiquement, il faut prêter attention aux représentations des identificateurs qui apparaissent de manière statique, dans les données des programmes Prolog ou C. D'autre part, l'utilisation de la notation de préfixes emboîtés ("Base:Normal:Data") pour un module, peut faire changer le nom du module après utilisation de la primitive.
Une fonction C est fournie pour connaître le caractère en vigueur. Elle est décrite dans le chapitre 7 de ce manuel.
©PrologIA
R 3 - 8
Manuel de Référence
3.4. Contexte de Lecture et Ecriture
Un contexte de lecture/écriture définit les conventions de passage d'un identificateur abrégé à un identificateur complet lors d'une opération de lecture (et vice-versa lors d'une opération d'écriture). A un moment donné, un et un seul contexte est utilisé: c'est le contexte courant de lecture/écriture.
Un contexte est défini de manière abstraite par un couple:
( suite d'identificateurs complets, préfixe par défaut )
3.4.1. Lecture
Lors d'une opération de lecture d'un identificateur abrégé, on l'identifie avec le premier identificateur de la suite ayant la même représentation abrégée. S'il n'y en a aucun, le préfixe par défaut est appliqué.
Soit par exemple le contexte 1 :
( "sys:out" "sys:outm" "m:out", "m1" ) la lecture des séquences suivantes donne: identificateur abrégé out mastic outm
-->
-->
--> identificateur complet sys:out m1:mastic sys:outm
A!ociation
Prolog
HERITAGE
3.4.2. Ecriture
On écrit les noms avec la forme la plus abrégée que permet le contexte. Lors d'une opération d'écriture d'un identificateur de préfixe p et d'abréviation a, on écrit la représentation abrégée a
1. si l'identificateur figure dans la suite et est le premier de la suite à avoir l'abréviation a :
2. si le préfixe par défaut est p et aucun identificateur de la suite ne possède l'abréviation a.
Dans tous les autres cas on écrit la représentation complète.
Ces deux conditions assurent la réversibilité des opérations de lecture/écriture réalisées avec le même contexte. Par exemple, avec le contexte précédent, l'écriture des séquences suivantes donne:
1
La manière précise de noter ces spécifications en Prolog II+ sera donnée plus loin : cf.
paragraphe 3.4.4.
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 9 identificateur complet sys:out m:out m1:mastic sys:outm b:c:toto
-->
-->
-->
-->
--> représentation externe out m:out mastic outm b:c:toto
3.4.3. Notation simplifiée de la suite d'identificateurs d'un contexte
La plupart des programmes manipulent des règles prédéfinies, et nécessitent donc, pour pouvoir utiliser la représentation abrégée de leurs noms, que tous ceux-ci soient listés explicitement lorsqu'on définit un contexte les utilisant. C'est pour pouvoir simplifier la définition d'un contexte, qu'ont été introduites les notions de
suite explicite et de suite implicite. La notation de la suite d'identificateurs du contexte est allégée en permettant de désigner implicitement un ensemble d'identificateurs, en spécifiant leur préfixe.
La suite d'identificateurs complets d'un contexte peut alors être elle-même représentée par un couple:
( suite explicite d'identificateurs, suite implicite d'identificateurs) où la suite implicite représente des familles d'identificateurs du programme, désignées par leur préfixe. Un contexte est maintenant décrit abstraitement par une structure de la forme:
( (suite explicite, suite implicite), préfixe par défaut )
Voici un exemple d'une telle description, pour la suite suivante: "m1:out",
"m2:peter", tous les identificateurs de la famille "m4", tous les identificateurs de la
famille "sys" :
(("m1:out" "m2:peter", "m4" "sys"), "a:b")
Il y a cependant un danger à confondre la notion de suite d'identificateurs pour un contexte de lecture/écriture, et celle de famille d'identificateurs d'un programme: l'ensemble des identificateurs ayant été utilisés dans un programme augmente avec le temps, et donc les opérations de lecture/écriture peuvent devenir non réversibles.
Pour résoudre ce problème, on introduit la notion de famille d'identificateurs fermée
pour le contexte . C'est un sous-ensemble d'une famille de noms donnée, clos par une directive spéciale (close_context_dictionary/1), et qui ne peut être modifié que par des primitives ad hoc (add_implicit/2, remove_implicit/2).
©PrologIA
R 3 - 10
Manuel de Référence
Famille "sys" in out..
others
... sous-ensemble clos utilisé pour les définitions implicites des contextes
Dans toute opération de contexte concernant une suite d'identificateurs désignés implicitement par leur préfixe, l'ensemble concerné est:
- le sous-ensemble clos, si la famille est fermée pour le contexte.
- l'ensemble de tous les identificateurs de la famille sinon. Cet ensemble est constitué de tous les identificateurs ayant apparu dans l'historique du programme avec le préfixe en question.
Un exemple de famille d'identificateurs fermée pour le contexte est la famille "sys".
Le sous-ensemble clos contient l'ensemble des identificateurs des règles et des fonctions prédéfinies de Prolog II.
Nous appellerons contexte sain, un contexte où toutes les suites implicites sont fermées pour le contexte. Un contexte sain garantit que les opérations de lecture et d'écriture sont répétables et réversibles.
Un exemple permettra de mieux comprendre ces notions. Soit le contexte abstrait suivant:
(("m1:out" "m2:peter", "sys"), "a:b")
La lecture de l'identificateur abrégé out donnera m1:out puisque le premier identificateur de la suite (explicite) a la même abréviation.
La lecture de l'identificateur abrégé paul donnera a:b:paul puisque cette abréviation ne correspond à aucun élément de la suite explicite, ni à aucun identificateur abrégé du sous-ensemble clos de la famille "sys".
La lecture de l'identificateur abrégé nil donnera sys:nil puisque cet identificateur fait partie du sous-ensemble clos de la famille fermée pour le contexte "sys".
L'écriture de sys:out donnera sys:out puisque le premier identificateur de la suite ayant l'abréviation out possède un préfixe différent.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 11
3.4.4. Notation en Prolog
La suite explicite d'identificateurs d'un contexte est représentée en Prolog sous forme d'une liste alternée permettant de regrouper des noms de même préfixe:
Préfixe1.ListeDeNomsAbrégés1 …
Préfixen.ListeDeNomsAbrégésn.nil
Ainsi la suite explicite "m1:out" "m1:outm" "jf:init" "m2:toto" "m2:peter" sera représentée dans la notation infixée:
"m1".("out"."outm".nil)
."jf".("init".nil)
."m2".("toto"."peter".nil)
.nil
en notation avec des crochets:
["m1",["out","outm"],
"jf",["init"],
"m2",["toto","peter"] ]
3.4.5. Primitives pour les contextes et les familles d'identificateurs
set_context(Identification, SuiteExplicite, SuiteImplicite, Défaut)
Cette commande permet à la fois de décrire un contexte, et de l'activer comme le contexte courant de lecture/écriture. Lors de l'effacement de cette commande, le contexte décrit est mémorisé dans le module d'environnement sous la forme: sys:env:context(identification,
suite_explicite,
suite_implicite,
préfixe_par_défaut) ->;
Lorsqu'un nouveau contexte est activé, l'ancien contexte n'est plus pris en compte.
L'argument identification doit être un identificateur ou une chaîne. Le deuxième argument doit être une liste alternée telle que décrite au paragraphe précédent. Le troisième argument est une liste de chaînes représentant des préfixes, et le quatrième argument est une chaîne définissant le préfixe par défaut.
Lorsque Prolog est lancé, le contexte courant de lecture écriture est le contexte sain défini par la commande: set_context("user", [], ["sys"], "")
Pour un autre exemple, le deuxième contexte abstrait défini au paragraphe précédent, sera défini et activé par la commande:
> set_context(No2, ["m1",["out"],"m2",["peter"]],
["sys"], "a:b"); set_context(Identification)
Permet de réactiver un contexte en le désignant par son identification, une fois qu'il a déjà été défini.
©PrologIA
R 3 - 12
Manuel de Référence
Note 1:
Lorsqu'on définit des contextes ne contenant pas la famille "sys" dans la
suite implicite, il faut faire attention au fait que nil ne représente pas en
général la marque de fin de liste sys:nil : cela dépend du préfixe par
défaut et de la suite explicite. Des erreurs peuvent en résulter lors de l'utilisation de règles prédéfinies auxquelles on passe une telle valeur en
argument. Il est conseillé dans ce cas d'utiliser le nom complet sys:nil, ou
la notation équivalente [ ].
Note 2:
Lorsqu'une règle contient une déclaration de contexte, celui-ci devient effectif au moment où la règle est exécutée (et non au moment ou la règle est lue). Dans l'exemple suivant, le contexte n'est pas modifié, et
l'identificateur abrégé "peche" est identifié à ":peche", puisque c'est le
contexte "user" qui est le contexte de lecture au moment de la lecture de la
règle.
Prolog II+, ...
>insert;
pomme -> set_context("new",["other",["peche"]],[],"def"); peche; peche ->;;
{}
>
current_context(t0)
current_context(t0,t1,t2,t3)
Unifie t0 avec le terme identifiant le contexte courant de lecture écriture, et
t1,t2,t3 avec, respectivement, le terme définissant la suite explicite, le terme définissant la suite implicite, et le préfixe par défaut de ce contexte.
close_context_dictionary(s)
Ferme la famille d'identificateurs s pour les opérations de contexte, dans son
état au moment où la commande est exécutée. C'est à dire que tout nouvel identificateur de cette famille ne sera pas pris en compte dans les opérations de contexte.
add_implicit(s1,s2)
Rajoute au sous-ensemble clos de la famille fermée pour le contexte s1, l'identificateur de nom abrégé s2. Si s1 n'est pas le préfixe d'une famille fermée pour le contexte, on a une erreur.
remove_implicit(s1,s2)
Opération inverse: enlève de l'ensemble d'identificateurs pris en compte pour le contexte de la famille fermée s1, l'identificateur de nom abrégé s2.
Lorsqu'on applique cette commande à la famille "sys", elle a le même effet, pour les identificateurs abrégés, que celui de banaliser des noms réservés.
Supposons que quelqu'un désire supprimer l'identificateur sys:dictionary de l'ensemble des identificateurs pris en compte pour les opérations de contexte concernant la famille "sys". L'exemple suivant montre le résultat obtenu :
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 13
> string_ident(p,a,dictionary)
outl(sys:dictionary.dictionary);
dictionary.dictionary
{p="sys", a="dictionary"}
> remove_implicit("sys","dictionary");
{}
> string_ident(p,a,dictionary)
outl(sys:dictionary.dictionary);
sys:dictionary.dictionary
{p="", a="dictionary"}
dictionary
Ecrit sur la sortie courante la forme abrégée des accès de la famille ayant le même préfixe que le préfixe par défaut du contexte de lecture/écriture.
dictionary(s1)
Unifie s1 avec la liste des préfixes des modules utilisateur présents en mémoire.
dictionary(s1, t0)
Unifie t0 avec la liste des formes identificateur_d'accès/arité des règles du module s1. Exemple :
> insert;
aa:bb(1) ->; aa:cc(1,2) ->;;
> dictionary("aa",L);
{L=aa:bb/1.aa:cc/2.nil}
file_dictionary(f, l_pref)
Unifie l_pref avec la liste des préfixes des modules contenant des règles, présents dans le fichier f. Ce fichier doit contenir du code prolog compilé, f est donc un module objet ou un état binaire de démarrage, il est obtenu grâce à l'un des prédicats de sauvegarde exit, save_state, save.
3.5. Modules
La définition de modules en Prolog nécessite de pouvoir traiter commodément des cas aussi différents que ceux que l'on peut rencontrer, par exemple, dans un système de langue naturelle. On y trouve essentiellement deux modules: le premier est le lexique qui contient beaucoup de noms de la base de données et de la grammaire, et peu de noms de règles. Le deuxième est la grammaire qui contient essentiellement des noms qui lui sont propres et des appels au lexique. Considérons l'exemple suivant: lexicon:pn(data:Peter, grammar:singular ) -> ; lexicon:pn(data:John, grammar:singular ) -> ;
...
lexicon:verbø(data:smiles, grammar:singular ) -> ;
...
lexicon:adj(data:plural, grammar:plural ) -> ;
...
©PrologIA
R 3 - 14
Manuel de Référence
grammar:sentence(x,z) -> grammar:sn(x,y,g) grammar:vp
(y,z,g); grammar:sn(x.l,l,g) -> lexicon:pn (x,g)
...
Définir des conventions qui permettent de simplifier l'écriture des identificateurs dans des cas aussi extrêmes est la raison profonde de l'élaboration du système de contexte de lecture décrit dans les chapitres précédents.
Un contexte permettant d'écrire le module lexicon avec seulement des identificateurs abrégés est facile à construire, il suffit de prendre comme préfixe par défaut celui couvrant le plus grand nombre d'identificateurs, et de représenter les quelques noms de règles dans la suite explicite: set_context( :lex
, ["grammar", ["singular", "plural"],
"lexicon", ["pn", "verbø", "adj"] ]
, []
, "data" )
3.5.1. Définition d'un module
Nous appellerons module "p", dans un programme donné, et à un moment donné, le regroupement de tous les faits et règles, les fonctions évaluables, les identificateurs affectés (par assign), et les tableaux dont l'identificateur d'accès appartient à la famille "p" (c'est-à-dire possède le préfixe "p"). Les identificateurs de la famille "p" peuvent apparaître dans d'autres modules, mais pas en position identificateur d'accès
(c'est à dire nom de prédicat de tête). La notion de module est dynamique: lorsqu'une nouvelle règle est créée ou supprimée, le module correspondant à son identificateur d'accès est modifié en conséquence.
On peut dire plus simplement, qu'un module est un regroupement de toutes les règles ou les faits dont l'identificateur d'accès a le même préfixe.
Un module peut exister sous forme de texte (avant lecture), ou sous forme interne
(après lecture) on parlera de module source et de module objet pour désigner l'un et l'autre cas.
3.5.2. Module source
La définition d'un module source consiste à définir :
- un contexte de lecture/écriture pour le module,
- une règle, facultative, d'initialisation du module,
- les règles du module proprement dites.
Le contexte sert pour la lecture du module source, lorsqu'il est compilé. Il sert
également pour l'écriture, lors de la décompilation du module (par les primitives list ou editm).
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 15
La règle d'initialisation du module, si elle existe 2 , est exécutée automatiquement dès la fin de compilation du module avec le contexte courant d'exécution et non celui de lecture du module. Cette règle, ou ce paquet de règles, est sans argument et a pour identificateur d'accès : l'identificateur ini_module préfixé par le préfixe du module.
Un module source se présente donc comme une suite de règles comprises entre les deux déclarations: sys:module( préfixe_des_accès, ... );
....
sys:end_module( préfixe_des_accès );
Remarque : si le module source n'a pas été construit de cette façon (les règles ont
été compilées par des insert ou assert, dans un ordre quelconque par exemple), la primitive editm permet de le reconstruire sous cette forme.
Les déclarations module et end_module sont destinées au mode insertion de règles de Prolog (cf. insert paragraphe 3.6). Par conséquent, les éléments Prolog compris entre ces deux déclarations doivent être des règles (ou des commentaires, qui sont ignorés). La déclaration module annonce la définition d'un module et la déclaration
end_module termine cette définition.
L'argument (chaîne Prolog) désignant le préfixe des accès dans la déclaration
module doit être le même que celui figurant dans la déclaration end_module, et toutes les règles comprises entre ces deux déclarations doivent avoir un identificateur d'accès avec ce préfixe; cette contrainte permet de contrôler des erreurs de nommage à la lecture.
Naïvement, on peut dire que la déclaration module signifie « les règles qui suivent,
sont à insérer avec le contexte de lecture défini ici, et en vérifiant que les règles lues possèdent (ou acquièrent) bien le préfixe voulu »
La forme complète de la déclaration module inclut la définition du contexte de lecture du module (voir la description des contextes §3.4.): sys:module( préfixe_des_accès, suite_explicite, suite_implicite, préfixe_par_défaut)
Lorsque tous les arguments ne sont pas donnés, on a les défauts suivants (on supposera dans ce qui suit que le contexte de lecture de la déclaration sys:module permet de l'abréger): module(p) module(p,s)
<=> module( p, [], ["sys"], p )
<=> module( p, s, ["sys"], p )
2
Dans le cas où des modifications sont apportées au module, c'est à dire une partie du module est recompilée (par reinsert ou insertz) ou rechargée (par reload), la règle d'initialisation n'est exécutée que si elle fait partie des règles modifiées.
©PrologIA
R 3 - 16
Manuel de Référence
module(p,s,i) <=> module( p, s, i, p )
La directive sys:omodule(context) en lieu et place d'une directive module utilise comme contexte de lecture du module, le contexte défini par la chaîne de caractères
context . Ce contexte a été défini au préalable par un appel à la primitive set_context ou bien par une précédente directive module. Si ce contexte n'existe pas, la directive
omodule(context) est équivalente à la directive module(context), où les règles de défauts s'appliquent.
Le contexte de lecture défini par l'en-tête du module est utilisé pour la lecture de tout le reste du module, y compris la déclaration sys:end_module. Le contexte existant avant la lecture est ensuite rétabli.
Si au moment de la lecture du module, une règle ou un fait est créé avec un identificateur d'accès dont le préfixe est différent de celui du module, une erreur est générée.
Les deux modules de l'exemple listé en tête du chapitre peuvent ainsi s'écrire de différentes manières:
1er exemple module("grammar"); sentence(x,z) -> sn(x,y,g) vp(y,z,g); sn(x.l,l,g) -> lexicon:pn(x,g);
...
end_module("grammar");
2ème exemple module("grammar", ["lexicon",["pn","verbø", ...]]); sentence(x,z) -> sn(x,y,g) vp(y,z,g); sn(x.l,l,g) -> pn(x,g);
...
end_module("grammar");
3ème exemple module( "lexicon"
, ["lexicon", ["pn", "verbø", "adj"],
"grammar", ["singular", "plural" ]]
, [ ]
, "data"); pn(Peter,singular) ->; pn(John,singular) ->;
...
verbø(smiles,singular ) ->
...
adj(data:plural,plural) ->;
...
end_module("lexicon");
Les mots entrés par l'utilisateur seront lus avec le contexte défini par: set_context( :my_entry, [ ], [ ], "data" ) ;
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 17
On peut, avec la notion de contexte de lecture du module, redéfinir de manière claire certaines des primitives utilisées à l'intérieur d'un module: module("example", ["example", ["out"]] ) ; out(x) -> nicer(x,y) sys:out(y);
...
explain(x) -> out(x) TellSomethingAbout(x); end_module("example");
Dans l'exemple suivant, la partie initialisation du module permet de rajouter la règle pertinente du module, en tant que règle prédéfinie Prolog, et permettra de l'appeler en utilisant l'identificateur abrégé, si le contexte courant contient bien le module sys des règles prédéfinies : module("parser"); ini_module -> assert(sys:parse(x),parse(x,nil).nil) add_implicit("sys","parse"); parse(l1,l2) -> sentence(l1,l2);
...
end_module("parser");
3.5.3. Module Objet
La manipulation de modules objets concerne des représentations internes, la notion de contexte n'intervient donc pas dans ces opérations.
Le chargement et la sauvegarde d'un ou plusieurs modules objets se font par les primitives load et save.
Si un état binaire de démarrage ou bien un fichier de modules objets contient un module "m" qui possède une règle avec le nom m:ini_module, alors celle-ci est automatiquement exécutée après le chargement de l'état binaire ou du fichier objet.
Lorsqu'un module objet est sauvé sur un fichier, il est sauvé avec un dictionnaire permettant de renommer les préfixes au chargement. Il est ainsi possible de résoudre les problèmes de conflits de noms quels qu'ils soient, et de construire des programmes n'interférant pas avec les données qu'ils manipulent, ou au contraire de définir des modules de type base de données et lexique, dont les noms peuvent être fusionnés au chargement.
3.6. Résumé ou Approche simplifiée des identificateurs, contextes et modules
3.6.1. Identificateurs
Les identificateurs sont des objets Prolog qui peuvent servir à la construction d'autres objets Prolog. On distingue deux modes d'utilisation des identificateurs :
- comme prédicat : sert à définir une règle (§3.1.1.)
- comme foncteur : sert de donnée (§3.1.2.)
©PrologIA
R 3 - 18
Manuel de Référence
Dans tous les cas, la syntaxe d'un identificateur Prolog (§3.3.) est : prefixe:suffixe prefixe et suffixe sont des suites de caractères en accord avec la syntaxe choisie (Prolog II ou
Edinburgh), : peut être remplacé par un caractère graphique (cf. set_prefix_limit).
Cette syntaxe a l'avantage de pouvoir désigner un gros ensemble d'identificateurs simplement d'après une particularité de leur nom.
On appellera famille d'identificateurs de préfixe "untel", tous les identificateurs
(prédicats ou foncteurs) dont le nom s'écrit untel:suffixe (§3.2.).
On appellera module de préfixe "untel", ou par extension module "untel", toutes les règles et/ou tableaux et/ou variables statiques dont l'identificateur d'accès s'écrit
untel:suffixe (§3.2.).
Astuce : si vous voulez un identificateur invariant pour tous les programmes, simple
à écrire et facile à retenir, choisissez pour cet identificateur le préfixe vide. Il est appelé dans les paragraphes précédents (§3.1.2.): foncteur générique.
3.6.2. Notation abrégée et contextes
On rencontre très fréquemment des situations où la grande majorité des identificateurs que l'on doit traiter, s'écrit toujours avec le même préfixe. On voudrait donc se passer de répéter ce préfixe et avoir une représentation d'identificateur sous forme abrégée.
Dans cette optique, pour simplifier la mise en œuvre et la lisibilité des programmes, il existe des conventions pour une représentation simplifiée des identificateurs, qui déterminent de manière unique la donnée Prolog. Ces conventions sont appelées contextes de lecture/écriture (§3.4.).
La conversion entre le codage interne de la donnée et sa représentation externe est réalisée uniquement au cours des opérations de lecture et d'écriture.
Un contexte de lecture/écriture permet de dire : sauf contre ordre, toutes les représentations d'identificateurs abrégées lues doivent prendre le préfixe par défaut pref, exceptées les représentations abrégées suivantes id1, id2, id3, … qui prennent respectivement les préfixes pref1, pref2,
pref3, … . Et pour tout ce qui doit être écrit, c'est à dire lorsque l'on doit construire la représentation la plus concise possible, faire en sorte qu'après relecture, on obtienne l'identificateur Prolog qu'on voulait écrire.
Un contexte désigne donc quel préfixe doit être attribué à la représentation simplifiée d'un identificateur pour obtenir sa représentation complète. D'où la définition du contexte par :
- une liste d'attributions explicites de préfixes,
- un préfixe par défaut pour les autres cas.
Ce contexte est complètement défini, on dira que c'est un contexte sain.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 19
Propriétés:
• La liste des attributions explicites est traitée de gauche à droite.
• Si une représentation abrégée apparaît plusieurs fois dans la liste on lui attribuera le premier préfixe associé (c'est à dire le plus à gauche).
• Toutes les chaînes déclarées dans le contexte n'ont pas besoin de correspondre
à des identificateurs existants. Si Prolog doit créer ou lire un identificateur, il consultera cette liste de représentations potentielles.
Pour abréger d'avantage l'écriture (même dans la définition du contexte), si on veut faire apparaître dans la liste explicite toute une famille d'identificateurs, au lieu de la mentionner de manière exhaustive, il suffit simplement de spécifier son nom. C'est alors considéré comme une attribution implicite, équivalant à calculer la liste explicite qui en découle et l'insérer dans la liste existante, au moment de chaque opération d'attribution de préfixe.
Propriétés:
• La liste des attributions implicites est traitée après la liste explicite.
• La liste des attributions implicites est traitée de gauche à droite.
D'où la définition du contexte par :
- une liste d'attributions explicites de préfixes,
- une liste d'attributions implicites de préfixes,
- un préfixe par défaut pour les autres cas.
Et sa définition en Prolog (§3.4.4. et §3.4.5.): set_context(Id, ListeExplicite, ListeImplicite, Defaut);
Ce contexte devient alors dépendant du temps, dans la mesure où les familles désignées pour une attribution implicite peuvent grandir avec le temps.
Pour se ramener à un contexte sain, et supprimer cette partie inconnue dans le cas d'attributions implicites, il est nécessaire de 'fermer' la famille. Cela signifie : au moment de la fermeture de la famille, la liste explicite qu'elle représente est calculée et mémorisée une fois pour toute. Le contexte est à nouveau complètement défini et invariant dans le temps.
3.6.3. Modules
Prolog étant incrémental, sans type de données, il n'y a aucune contrainte sur l'ordre d'insertion des règles.
Vous pouvez toutefois grouper toutes les règles d'un même module et les définir en même temps. Vous pouvez également définir des conventions de lecture de ce groupe de règles différentes de celles du contexte d'exécution. Ceci permet de rendre la définition du module autonome si le contexte est sain.
La définition d'un module peut donc se faire par l'énoncé de:
- un contexte de lecture/écriture du module,
- un ensemble de paquets de règles,
©PrologIA
R 3 - 20
Manuel de Référence
- éventuellement une règle d'initialisation du module, à exécuter juste après compilation ou chargement des règles.
La définition d'un module peut se faire en mode insertion, en utilisant la déclaration
module ou omodule (§3.5.2):
…
; insert; module(p,e,i,d); équivaut à regles… end_module(p); insert;
->set_context(p,e,i,d);
->mode_verif_prefixe(p); regles…
…
;
->exec(p:ini_module);
->restaure_context;
Attention : si vous utilisez dans le contexte d'un module, des conventions implicites, même pour un contexte sain, il faut que la famille soit déjà définie pour que les attributions implicites se fassent. Dans la même optique, si vous faites appel, dans la règle d'initialisation d'un module, à des règles externes au module, il faut que ces règles soient déjà définies pour que l'initialisation se termine.
Cela signifie que, bien qu'il n'y ait pas de contraintes sur l'ordre des paquets de règles, il peut y avoir des contraintes sur l'ordre de compilation ou de chargement des modules.
Il est également important de noter que les notions de fichier et de module sont complètement indépendantes. Un même fichier source peut contenir plusieurs modules, et un même nom de module peut apparaître dans plusieurs fichiers chargés dans la même session.
A!ociation
Prolog
HERITAGE
3.7. Ajout, suppression et recherche de règles
Le système Prolog II+ comporte un compilateur qui traduit vos programmes sources écrits en Prolog dans un langage objet dont le «niveau» (i.e. : le degré de proximité avec la machine hôte) varie d'une machine à une autre, mais qui est dans tous les cas bien plus efficace que la simple interprétation du code source Prolog.
En contre partie, il apparaît une certaine difficulté à restituer exactement le texte source de certaines règles, à cause de la disparition des noms des variables.
Le compilateur Prolog II+ est incrémental (la traduction se fait règle par règle) et
transparent (chaque règle est compilée et indexée dès sa saisie, sans qu'il y ait besoin de composer une quelconque commande particulière).
Par défaut Prolog II+ réalise des optimisations à la compilation de certaines règles : les expressions arithmétiques, les tests de type, ou encore les prédicats prédéfinis
val, assign et block. Ces optimisations permettent une exécution plus efficace de ces règles mais ont des conséquences sur le fonctionnement de certains autres prédicats prédéfinis (tels que rule, debug ou freeze). Il existe alors une option d'activation de
Prolog II+ qui permet de supprimer ces optimisations de compilation (voir à ce sujet le Manuel d'Utilisation).
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 21
Pour minimiser le temps mis pour choisir une règle qui servirait à effacer un but particulier, Prolog II+ au moment de la compilation d'un paquet de règles, crée une
'image' du paquet dans laquelle il regroupe toutes les règles dont le premier argument est de type et/ou de valeur identique. Il réalise ainsi plusieurs groupes de règles disjoints et pour un même but à effacer, les règles d'un seul des groupes seront choisies, ou bien aucunes. Ce traitement est appelé l'indexation. Supprimer l'indexation revient à laisser toutes les règles du paquet valides pour un choix.
Par convention on utilisera, dans un programme et dans le texte qui suit, le terme
identificateur/entier pour désigner un paquet de règles dont la tête a pour identificateur d'accès identificateur, et entier comme nombre d'arguments.
Avant toute chose, il est nécessaire de citer deux conséquences très importantes de la modification de paquets de règles, qui vont influer sur le comportement du programme à l'exécution.
1. La modification (ajout ou suppression de règle(s)) d'un paquet de règles compilées supprime son indexation. Pour l'indexer à nouveau, utiliser la primitive
index.
2. La modification d'un paquet de règles en cours d'exécution d'une de ses règles
(choix encore en attente) entraîne un comportement d'exécution (pour le backtracking ou pour un prochain appel) extrêmement dépendant de la configuration des règles (dépendant de la structure du paquet, de la règle courante, de la règle modifiée).
Dans la mesure du possible, il est préférable d'épuiser les choix avant de modifier les règles ou, si ce n'est pas le cas, de supprimer l'indexation avant l'exécution et la modification des règles compilées.
Toujours par souci d'améliorer les performances, il a été introduit dans Prolog II+, un nouveau 'type' de règles, appelées 'faits non compilés'. Ceci dans le but d'optimiser les problèmes de gestion d'informations par manipulation dynamique de faits. En effet pour vérifier un fait, il est inutile d'optimiser l'exécution du corps de la règle puisqu'il est inexistant, par contre il est important d'améliorer les accès aux arguments.
Prolog II+ va installer pour les 'faits non compilés' un processus d'indexation à partir de tous les arguments du fait, pour permettre au moment de l'exécution un accès quasi-direct sur les faits adéquats. Il n'est pas possible de supprimer l'indexation installée sur des 'faits non compilés'.
assert(t, q) asserta(t, q)
Compiler et ajouter une règle, au début de son paquet.
t doit être un terme pouvant être une tête de règle, c'est-à-dire un identificateur ou un n-uplet dont le premier argument est un identificateur (i.e.: <ident,
arg1, … argn> ou ident(arg1, … argn), ces deux notations étant
équivalentes). q doit être une liste de termes.
©PrologIA
R 3 - 22
Manuel de Référence
L'effacement de assert(t, q) compile la règle t -> q et l'ajoute au-dessus du groupe de règles ayant le même «nom» que t, c'est-à-dire au début du paquet correspondant à t. Par exemple, les deux commandes suivantes
assert(conc(e.x, y, e.z), conc(x, y, z).nil); assert(conc(nil, y, y), nil);
tapées dans cet ordre, ont pour effet l'ajout du programme
conc(nil, y, y) ->; conc(e.x, y, e.z) -> conc(x, y, z);
Ne permettent pas d'ajouter des règles qui contiennent des arbres infinis; cependant cela peut être fait avec la règle prédéfinie equations (Voir § 2.3).
assert''(t, q) assertz(t, q)
Compiler et ajouter une règle, à la fin de son paquet.
Même fonctionnement que assert, mais l'ajout se fait au-dessous, et non audessus, du paquet correspondant à t. Exemple: les deux commandes suivantes, dans l'ordre indiqué, produisent l'insertion du même programme conc que cidessus :
assert''(conc(nil, y, y), nil); assert''(conc(e.x, y, e.z), conc(x, y, z).nil);
Ne permettent pas d'ajouter des règles qui contiennent des arbres infinis; cependant cela peut être fait avec la règle prédéfinie equations (Voir § 2.3).
assertn(t, q, n)
Compiler et ajouter une règle, à la n-ième position dans le paquet.
Même fonctionnement que assert, mais l'ajout se fait à la n-ième position dans le paquet correspondant à t. Exemple:
assert(myrule(2),nil); assertn(myrule(0),nil,1); assertn(myrule(1),nil,2);
tapées dans cet ordre, ont pour effet l'ajout du programme
myrule(0) ->; myrule(1) ->; myrule(2) ->;
Ne permet pas d'ajouter des règles qui contiennent des arbres infinis; cependant cela peut être fait avec la règle prédéfinie equations (Voir § 2.3).
current_predicate(i/a)
Tester la présence d'une règle.
S'efface s'il existe une règle d'identificateur d'accès i et d'arité a. Si a est une variable, et i est connu, énumère successivement toutes les valeurs de a correspondant à une règle d'accès i. Si i n'est pas connu (i.e. est une variable), unifie l'argument successivement avec toutes les formes i/a des règles du module déterminé par le préfixe par défaut du contexte courant. i et a ne doivent pas être libre en même temps.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 23
discontiguous(i/a)
Permettre la non continuité d'un paquet de règles.
Lorsqu'une règle d'identificateur d'accès i et d'arité a sera compilée au moyen du prédicat insert, elle sera ajoutée à la fin de son paquet. Cette directive permet de scinder les paquets de règles au sein d'un même ou de plusieurs fichiers. Noter que cette directive n'a aucun effet sur une compilation au moyen du prédicat reinsert. qui écrasera le paquet de règles existant par le nouveau rencontré. Ceci est une directive de compilation et non pas un prédicat, c'est à dire qu'elle peut figurer au milieu d'un source que l'on compile mais ne peut se trouver dans une queue de règle.
dynamic(i/a)
Déclarer un paquet de règles dynamiques.
Tous les paquets de règles Prolog II+ sont dynamiques, c'est à dire qu'ils peuvent subir des modifications. Par conséquent, cette directive n'a aucun effet et n'existe que par compatibilité avec la norme Prolog. Ceci est une directive de compilation et non pas un prédicat, c'est à dire qu'elle peut figurer au milieu d'un source que l'on compile mais ne peut se trouver dans une queue de règle.
ensure_loaded(f)
S'assurer d'une seule inclusion de la compilation d'un fichier.
Réalise la compilation du fichier f à l'endroit où se trouve la directive, si ce fichier n'a pas déjà été compilé au cours de cette même phase de compilation, sinon ne fait rien. En fin de compilation de plus haut niveau d'imbrication, et donc pour une nouvelle phase de compilation, le fichier n'est plus considéré comme ayant été compilé. La compilation du fichier f s'effectue suivant le même mode que celui piloté par le prédicat de compilation de niveau supérieur
(insert, reinsert, insertz).
Ceci est une directive de compilation et non pas un prédicat, c'est à dire qu'elle peut figurer au milieu d'un source que l'on compile mais ne peut se trouver dans une queue de règle.
fasserta(t) fassertz(t)
Ajouter un fait non compilé.
Ajoute un fait non compilé en début (fasserta) ou en fin (fassertz) de son paquet. L'assertion est extrêmement rapide et permet de gérer de très grosses bases de faits. De plus, les indexations installées sur ces faits, par un appel préalable obligatoire à la primitive init_fassert permettent un accès très performant. Le terme t doit avoir la forme d'une tête de règle, c'est à dire un nuplet dont le premier élément est un identificateur. Pour un même paquet, on ne peut mélanger des règles compilées (assert, insert) et des faits non compilés (fassert). Exemple:
> fasserta(myrule(1,2,3,4.nil,<44,"abc">,6,1.2.2e0.nil));
©PrologIA
R 3 - 24
Manuel de Référence freplace(t, n_arg, t1)
Remplacer un argument non indexé dans un fait non compilé.
Remplace dans les faits, créés préalablement par fasserta ou fassertz, s'unifiant avec t l'argument de rang n_arg par le terme t1. Cet argument ne doit pas intervenir dans une combinaison d'indexation (cf. prédicat
init_fassert). Exemple:
> freplace(myrule(1,2,3,x,y,z,t), 7, 345);
freplace(i/a, n_reg, n_arg, t1)
Remplacer un argument non indexé dans un fait non compilé.
Remplace dans le fait de rang n_reg du paquet d'identificateur i et d'arité a, l'argument de rang n_arg par le terme t1. Ce fait aura dû être créé par l'un des prédicats fasserta ou fassertz. Cet argument ne doit pas intervenir dans une combinaison d'indexation (cf. prédicat init_fassert). Exemple:
> freplace(myrule/7, 2, 7, 345);
fretract(t)
Supprimer rapidement un fait non compilé correspondant à un modèle.
S'efface autant de fois qu'il existe de faits s'unifiant avec t. La recherche de tels faits est très rapide et profite des optimisations mises en place pour l'accès aux faits non compilés. Ces faits auront dû être créés par l'un des prédicats
fasserta ou fassertz. Dès qu'un fait s'unifiant avec t est trouvé, il est supprimé.
Si un fait qui convient, est invisible à la décompilation, il est supprimé, et la primitive s'efface sans unifier les variables libres de t.
Exemple:
> fretract(myrule(1,2,3,x,y,z,t));
fretractall(t)
Supprimer rapidement des faits non compilés correspondant à un modèle.
Supprime, en une fois, de manière très efficace tous les faits s'unifiant avec t.
Ces faits doivent être des faits non compilés créés par l'un des prédicats
fasserta ou fassertz. t doit permettre d'identifier un index pour cette base de fait, de la manière suivante: tous les arguments non libres du but doivent être atomiques, de plus ils désignent une combinaison d'arguments qui doit correspondre à une combinaison d'indexation définie pour cette base de faits.
Dans le cas contraire une erreur est générée. Exemple:
> init_fassert(bb/3,(1.2.3).(1.2).nil);
{}
> fasserta(bb(1,2,3));
{}
> fasserta(bb(1,2,x));
{}
> fasserta(bb(1,2,1.2));
{}
> fasserta(bb(1,1,1.1));
{}
> fretractall(bb(1,x,3));
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 25
-> <bb(1,v36,3)> : LA COMBINAISON CORRESPONDANT AUX ARGUMENTS
LIES N'EXISTE PAS
> fretractall(bb(1.2,2,x));
-> <bb(1.2,2,v59)> : UN ARGUMENT INDEXE N'EST PAS ATOMIQUE
> fretractall(bb(1,2,x));
{}
> list(bb/3); bb(1,1,1.1) -> ;
{}
hidden_rule(x)
Masquer une règle ou un module pour la décompilation.
Rend 'non visible pour rule'. Si x est de la forme i/a, avec i identificateur et a entier, concerne le paquet de règles d'accès i et d'arité a. Si x est une chaîne, concerne toutes les règles du module de nom x. Par 'non visible pour rule', on entend non décompilable, c'est à dire qui ne peut être reconstruit sous forme de source Prolog. Par conséquent toutes les primitives de décompilation ou d'impression de ces règles n'auront aucun effet.
hidden_debug(x)
Masquer les accès à une règle ou tous les accès d'un module.
Rend 'non visible pour debug'. Si x est de la forme i/a, avec i identificateur et a entier, concerne le paquet de règles d'accès i et d'arité a. Si x est une chaîne, concerne toutes les règles du module de nom x. Par 'non visible pour debug', on entend dont l'accès est caché. Le debugger ne montrera ni les appels à ce paquet de règles, ni les appels réalisés dans la queue des règles de ce paquet.
Si dans une queue de règle non visible on appelle une règle visible, seule la queue de cette dernière sera visualisée. Les règles prédéfinies qui retournent des accès de règles (dictionary par exemple), ignoreront ces règles 'cachées pour debug'. On notera que pour décompiler ces règles, il faut utiliser des règles prédéfinies de décompilation nominatives (qui attendent l'identificateur d'accès et l'arité) et non globales (qui attendent un nom de module par exemple).
hidden(x)
Masquer une règle ou un module.
Si x est de la forme i/a, avec i identificateur et a entier, concerne le paquet de règles d'accès i et d'arité a. Si x est une chaîne, concerne toutes les règles du module de nom x. Rend les règles non visibles à la décompilation et non visibles au debugger ou aux primitives qui montrent les accès. Est équivalent à la suite de buts hidden_rule(x) hidden_debug(x).
include(f)
Inclure la compilation d'un fichier.
©PrologIA
R 3 - 26
Manuel de Référence
Réalise la compilation du fichier f à l'endroit où se trouve la directive. Tout se passe donc comme si la directive était remplacée par le contenu du fichier f. La compilation du fichier f s'effectue suivant le même mode que celui piloté par le prédicat de compilation de niveau supérieur (insert, reinsert, insertz).
Ceci est une directive de compilation et non pas un prédicat, c'est à dire qu'elle peut figurer au milieu d'un source que l'on compile mais ne peut se trouver dans une queue de règle.
init_fassert(i/a, l)
Décrire l'indexation d'un paquet de faits non compilés.
Initialisation d'un paquet de faits non compilés d'identificateur d'accès i et d'arité a. L'argument l est une liste (terminée par nil) indiquant les combinaisons d'indexation choisies pour les arguments de ces faits. Cette liste est ordonnée par priorité de combinaison d'indexation.
Chaque élément de cette liste indique une combinaison et doit avoir pour valeur:
- soit une liste d'entiers (terminée ou non par nil) qui indique la combinaison des arguments à indexer, ou bien un entier si la combinaison se réduit à un seul élément. La table de hash-code correspondante à cette combinaison aura alors une taille par défaut (512 entrées).
- soit un doublet formé :
- de la forme précédemment décrite en premier argument.
- d'un entier indiquant la taille de la table de hash-code
correspondante à cette combinaison en deuxième argument
Ce prédicat doit être appelé avant tout fasserta ou fassertz. Il pourra être appelé plusieurs fois pour le même paquet, à condition que l'argument l soit le même lors des différents appels.
Exemple:
> init_fassert(myrule/7, (1.2.3). (4.5.nil). 5. <6,200>.
<2.6,300>. nil);
où l'on choisit:
- une indexation multiple en priorité sur la combinaison des arguments 1, 2 et
3.
- une sur les arguments 4 et 5 combinés.
- une sur le cinquième argument seul.
- une sur le sixième argument seul avec une taille de 200 pour la table de hash-code associée à cette combinaison.
- une sur la combinaison des arguments 2 et 6 avec une taille de 300 pour sa table de hash-code.
Le choix de la combinaison d'index lors de l'exécution dépend du type des arguments d'appel: priorité aux entiers, aux réels, aux identificateurs et aux chaînes de caractères. Si parmi les arguments correspondant à la première combinaison, l'un d'entre eux n'a pas le bon type, c'est la deuxième combinaison qui est examinée, et ainsi de suite.
NB: - La suppression complète d'un paquet de faits non compilés implique un nouvel appel à la primitive init_fassert. (après suppress(i/a) par exemple)
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 27
- La suppression de toutes les règles d'un paquet ne supprime pas la définition de l'indexation. init_fassert ne doit pas être refait. (après plusieurs suppress(i/a,n) par exemple)
- L'appel aux prédicats edit et editm sur des faits non compilés les transforme en règles compilées (équivalent à suppress/insert).
initialization(B)
Initialiser un groupe de règles.
Inclut le but B dans la liste des buts qui seront exécutés dès la fin de la compilation du fichier dans lequel se trouve cette directive. Ceci est une directive de compilation et non pas un prédicat, c'est à dire qu'elle peut figurer au milieu d'un source que l'on compile mais ne peut se trouver dans une queue de règle. Cette liste de buts sera exécutée à la condition qu'aucune erreur n'ait
été détectée durant la compilation.
insert insertz reinsert
Compiler des règles.
Ces règles prédéfinies font basculer le système dans un mode dans lequel les
énoncés (règles) lus sur l'unité d'entrée courante sont ajoutés, dans l'ordre dans lequel ils sont lus. Les directives et les déclarations sont respectivement exécutées et prises en compte immédiatement lorsqu'elles sont rencontrées. Le mode insert se termine soit quand un énoncé vide est trouvé (rencontre de ";;" en syntaxe Prolog II), soit si l'on rencontre la fin du fichier d'entrée.
Exemple:
>insert;
conc(nil, y, y) ->; conc(e.x, y, e.z) -> conc(x, y, z); ;
{}
Si une erreur, de syntaxe est rencontrée, un avertissement est affiché, et un certain nombre de caractères (en principe tous les caractères jusqu'à un «;», ou jusqu'à la fin de la ligne sur l'unité console) sont ignorés, puis l'insertion reprend. A la fin de l'insertion insert génère alors l'erreur 86, et le complément d'erreur indique le nombre d'erreurs trouvées pendant l'insertion.
Les règles sont automatiquement indexées sur le premier argument au fur et à mesure de leur entrée.
Selon le mode de warning choisi au lancement de Prolog, à la lecture des règles un warning est affiché quand une variable singleton apparaît. Une variable singleton est une variable qui n'apparaît qu'une seule fois dans la règle, elle est donc à priori inutile. Pour la variable muette _ le warning n'est pas affiché.
insert sert à définir des paquets de règles, reinsert sert à redéfinir des paquets de règles et insertz sert à compléter des paquets de règles en ajoutant des alternatives en fin.
©PrologIA
R 3 - 28
Manuel de Référence
Par conséquent insert provoque une erreur lorsqu'un paquet lu existe déjà et qu'il n'a pas été déclaré non continu (directives discontiguous/1 ou multifile/1), alors que insertz ajoute le paquet lu en fin de paquet existant et alors que
reinsert remplace l'ancien paquet par la nouvelle définition. Attention ceci peut
être dangereux : une erreur sur le nombre d'arguments d'une règle à l'intérieur d'un paquet, provoquera avec reinsert l'écrasement des règles précédentes quand on continuera à lire le paquet. Dans le même esprit, l'utilisation de
insertz permettant de définir un paquet de règles en plusieurs morceaux (c'est
à dire mixé avec d'autres règles), des erreurs sur le nombre d'arguments ou des confusions sur le nom de prédicats très ressemblants ne seront pas visibles.
Lorsqu'un module m est lu, le contexte défini par l'en-tête du module est mémorisé dans le module sous forme d'une règle
m:module_context(A1,A2,A3,A4) où A1, A2, A3, A4 ont les valeurs définissant le contexte de lecture du module (cette règle est utilisée par les primitives d'édition de module telles que editm).
insert(f) insertz(f) reinsert(f)
Compiler des règles à partir d'un fichier.
Même fonctionnement que le mode insert, mais les énoncés sont lus sur le fichier indiqué. Selon que l'on a par ailleurs activé ou non le mode écho (cf.
règle echo) les règles sont affichées sur la console au fur et à mesure de leur lecture.
Lorsqu'un module m est lu, le nom du fichier source du module est mémorisé dans le module sous forme d'une règle m:module_file(s) où s est une chaîne représentant le nom du fichier (cette règle est utilisée par les primitives d'édition de module telles que editm).
is_uncompiled(i/a)
Fait un succès si les règles de nom i et d'arité a forment un paquet de faits non compilés, échoue sinon.
list
Liste toutes les règles du module déterminé par le préfixe par défaut du contexte courant.
list(t)
Lister un paquet de règles.
Liste sur la sortie courante le ou les paquet de règles indiqués par t. t peut être une séquence de un ou plusieurs termes de la forme suivante (i désigne un identificateur, a un entier, et v une variable):
i/a Toutes les règles composant la paquet de nom i et d'arité (nombre d'arguments) a.
i/v Toutes les règles dont l'accès est i, quelque soit leur arité.
v/a Toutes les règles dont l'arité est a.
i
équivalent de i/v.
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 29 exemple:
> list(num.bin/2);
list(s)
Lister un module.
s doit être une chaîne de caractères; toutes les règles ayant le préfixe s sont listées sur la sortie courante.
list(i/a,n)
Lister une règle.
i doit être un identificateur et a un entier; la règle numéro n du paquet correspondant au nom i et à l'arité (nombre d'arguments) a est listée sur la sortie courante.
multifile(i/a)
Permettre la non continuité d'un paquet de règles.
Directive de compilation identique à la directive discontiguous/1.
not_defined(s,l)
Trouver les accès non définis d'un module.
Unifie l avec la liste des accès appartenant au module s qui ne sont pas définis. Les accès sont notés i/a où i est l'identificateur d'accès et a son arité.
Remarque : cette règle prédéfinie active le récupérateur de mémoire sur le dictionnaire, avant de fournir le résultat.
Exemple:
> insert; module("agence"); voyage(v,d,h,s) -> transport(v,t)
sejour(v,h,n)
duree(d)
multiplier(n,d,p)
additionner(p,t,s); transport(Rome,1200) ->; transport(Londres,800) ->; transport(Tunis,2000) ->; end_module("agence");
;
{}
> dictionary("agence",l);
{l=agence:module_context / 4.agence:transport / 2.
agence:voyage / 4.nil}
> not_defined("agence",l);
{l=agence:additionner / 3.agence:duree / 1. agence:multiplier
/ 3. agence:sejour / 3.nil}
predefined(t)
Tester si un modèle correspond à une règle prédéfinie.
S'efface avec succès si t est un terme correspondant à l'appel d'une règle prédéfinie.
©PrologIA
R 3 - 30
Manuel de Référence rule(t, q)
Rechercher des règles correspondant à un modèle donné.
Cette primitive s'efface autant de fois qu'il existe de règles dont la tête s'unifie avec t et la queue avec q. Si une telle règle n'existe pas, alors rule(t,q) échoue. t doit être soit un identificateur, soit un n-uplet dont le premier argument est un identificateur connu.
La règle rule utilise l'indexation des règles lorsque c'est possible (voir
no_index). Exemple d'utilisation de rule (se rapportant toujours au programme conc donné plus haut) :
>rule(conc(x,y,z),q);
{x=nil,z=y,q=nil}
{x=v149.v150,z=v149.v151,q=conc(v150,y,v151).nil}
>
rule(n, t, q)
Rechercher des règles correspondant à un modèle donné.
Même fonctionnement que rule(t,q) mais, en plus, n est unifié avec le rang de la règle dans son paquet.
Exemple :
>rule(n,conc(x,y,z),q);
{n=1,x=nil,z=y,q=nil}
{n=2,x=v172.v173,z=v172.v174,q=conc(v173,y,v174).nil}
>rule(n,conc(x,y,z),conc(x',y',z').l);
{n=2,x=v263.x',z=v263.z',y'=y,l=nil}
>rule(1,conc(x,y,z),q);
{x=nil,z=y,q=nil}
>
rule(n, a, t, q)
Rechercher des règles ayant un nom donné.
a doit être un identificateur, ou un tuple dont le 1er argument est un identificateur. Pour chacune des règles ayant a pour identificateur d'accès, cette règle s'efface après unification de n avec le rang de la règle dans son paquet, de t avec la tête de la règle et de q avec la queue de celle-ci. Ainsi, cette forme de rule peut être utilisée pour chercher des règles dont on connaît le nom mais pas le modèle.
Exemple :
conc(nil,x2,x2) ->; conc(e.x1,x2,e.r) -> conc(x1,x2,r); conc(x1,x2,x3,r) -> conc(x1,x2,u) conc(u,x3,r);;
> rule(n,conc,t,q);
{n=1,t=conc(v124,v125,v126,v127),
q=conc(v124,v125,v128).conc(v128,v126,v127).nil}
{n=1,t=conc(nil,v125,v125),q=nil}
{n=2,t=conc(v127.v128,v125,v127.v129),
q=conc(v128,v125,v129).nil}
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 31
rule_nb(i/a, n) rule_nb(i, n)
Compter les règles d'un paquet.
Au moment de l'appel i doit être un identificateur et a un entier; n est alors unifié avec le nombre de règles composant le paquet dont le nom est i et l' arité
(nombre d'arguments) est a. Dans la deuxième forme, seul le paquet ayant l'arité la plus faible est considéré.
retract(t, q)
Rechercher et supprimer des règles correspondant à un modèle donné.
Cette primitive s'efface autant de fois qu'il existe de règles dont la tête s'unifie avec t et la queue avec q. Utilise l'indexation des règles si c'est possible, sinon essaie les règles les unes après les autres dans l'ordre. Si une telle règle n'existe pas, alors retract(t,q) échoue. t doit être soit un identificateur, soit un n-uplet dont le premier argument est un identificateur connu.
Si une règle qui convient, est invisible à la décompilation, la règle est supprimée, et la primitive s'efface sans unifier les variables libres de t et q.
suppress(i/a)
Supprimer tout un paquet de règles.
i doit être un identificateur et a un entier; toutes les règles composant le paquet de nom i et d'arité (nombre d'arguments) a sont supprimées. S'il n'y a pas de règle, le prédicat s'efface en imprimant éventuellement 3 un warning.
Exemple:
data(1) ->; data(2) ->; data(3) ->;;
> data(x);
{x=1}
{x=2}
{x=3}
> suppress(data/1);
{}
> data(x);
WARNING : APPEL A UNE REGLE NON DEFINIE
>
suppress(i/a, n)
Supprimer une règle.
i doit être un identificateur et a et n deux entiers; la règle numéro n du paquet correspondant au nom i et à l'arité (nombre d'arguments) a est supprimée. S'il n'y a pas une telle règle, le prédicat s'efface en imprimant éventuellement 4 un warning.
Exemple:
data(1) ->;
3
Cela dépend du niveau de warning choisi par une option au lancement de Prolog.
4
Cela dépend du niveau de warning choisi par une option au lancement de Prolog.
©PrologIA
R 3 - 32
Manuel de Référence data(2) ->; data(3) ->;;
>data(x);
{x=1}
{x=2}
{x=3}
> suppress(data/1, 2);
{}
> data(x);
{x=1}
{x=3}
>
3.8. Manipulation des modules compilés
index(i/a)
Indexation d'un paquet de règles compilées.
Provoque l'indexation sur le 1er argument du paquet de règles compilées dont l'identificateur d'accès est i et l'arité a. Ne fait rien si la règle est déjà indexée.
Cette règle peut être utilisée pour accélérer l'exécution des règles créées par
assert, qui ne sont pas indexées automatiquement contrairement à celles créées par insert. N'a pas d'effet sur les faits non compilés.
kill_module(s)
Suppression de module.
s doit être une chaîne ou une liste de chaînes. Pour chaque module désigné dans s, toutes les règles du module sont supprimées, les tableaux du module sont désalloués, et les assignations des identificateurs du module sont défaites. Un message est éventuellement 5 affiché quand un module n'existe pas, et le traitement se poursuit.
load(f, l) load(f)
Chargement de modules sauvés.
f est un nom de fichier (chaîne de caractères) et l est une liste de substitution de préfixes. Cette commande produit le chargement des modules sauvegardés dans le fichier indiqué; celui-ci doit avoir été produit par la commande save.
Les règles d'initialisation de module, si elle existent, sont exécutées.
l est une liste de la forme <pref1 . subs1> . … . <prefk . subsk> . nil qui spécifie le renommage des modules chargés : pref1 sera substitué par subs1,
pref2 par subs2, etc.… Si un module à renommer n'est pas présent, la substitution est ignorée pour ce module et un message peut 6 être affiché.
Exemple:
A!ociation
Prolog
HERITAGE
5
Cela dépend du niveau de warning choisi par une option au lancement de Prolog.
6
Cela dépend du niveau de warning choisi par une option au lancement de Prolog.
©PrologIA
A!ociation
Prolog
HERITAGE
Structuration et modification des règles
R 3 - 33
>load("myfile.mo", <"data","donnees">.nil);
{}
Une tentative de chargement d'un élément (règle ou tableau) déjà connu donne lieu à une erreur («définition multiple»).
La deuxième forme, load(f), équivaut à load(f, nil).
no_index(i/a)
Suppression de l'indexation d'un paquet de règles compilées.
Provoque la désindexation du paquet de règles compilées dont l'identificateur d'accès est i et l'arité a. Ne fait rien si la règle n'est pas indexée. On a intérêt à utiliser cette règle avant de manipuler dynamiquement des règles lues par
insert, de manière à ce que les modifications soient toujours prises en compte de la même manière. N'a pas d'effet sur les faits non compilés.
reload(f) reload(f, l)
Chargement de modules sauvés.
Même fonctionnement que load, sauf dans le cas de redéfinition d'un élément: la version rencontrée dans le fichier remplace celle qui se trouve en mémoire, sans produire d'erreur.
save(l, f)
Sauvegarde de modules.
f est un nom de fichier (chaîne de caractères) et l une liste de préfixes (chaînes de caractères). Cette commande produit la sauvegarde dans le fichier indiqué de tous les éléments (règles, variables statiques et tableaux) des modules correspondant aux préfixes donnés. Si un module n'existe pas, la sauvegarde se poursuit après affichage éventuel 7 d'un message.
Exemple:
> save(["","data","dict"], "myfile.mo");
{}
Le fichier produit est un fichier de code objet, ce code est exploitable uniquement par une machine Prolog. La mémorisation de programmes sous cette forme permet un rechargement plus rapide qu'à partir des fichiers sources.
save_state(s)
Sauvegarde d'un état de démarrage.
Sauvegarde tout le programme (règles, tableaux, identificateurs assignés, y compris le superviseur Prolog II+) dans un fichier de nom s (où s est une chaîne) qui peut être ensuite utilisé comme fichier de démarrage. Cette primitive a le même effet que exit(s) mais sans sortir de Prolog.
7
Cela dépend du niveau de warning choisi par une option au lancement de Prolog.
©PrologIA
A!ociation
Prolog
HERITAGE

公開リンクが更新されました
あなたのチャットの公開リンクが更新されました。