MANUEL DE REFERENCE. PrologIA HERITAGE II+
A!ociation
Prolog
HERITAGE
PROLOG II+
MANUEL DE REFERENCE
ii
Manuel de Référence
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Généralités
iii
Table des matières
Table des matières..........................................................................................iii
Introduction .....................................................................................................ix
Standardisation.................................................................................ix
Performances ...................................................................................ix
Modularité.........................................................................................x
Ouverture vers l'extérieur...................................................................x
Environnement de programmation.....................................................x
Interface graphique...........................................................................xi
Sur ce Manuel ...............................................................................................xiii
Garantie et responsabilités............................................................................xv
Droits d'auteur.......................................................................................... xv
0. Débuter avec Prolog..........................................................................R 0 - 1
0.1. Leçon 1 : Lancement de Prolog ...............................................R 0 - 1
0.2. Leçon 2 : Utilisation d'un programme d'exemple.....................R 0 - 2
0.3. Leçon 3 : L'Editeur ..................................................................R 0 - 4
0.4. Les erreurs les plus courantes...................................................R 0 - 5
0.4.1. Débordement de l'espace de code.................................R 0 - 5
0.4.2. Interruption. .................................................................R 0 - 5
0.4.3. Erreurs détectées en cours d'insertion...........................R 0 - 6
0.4.4. Erreurs concernant les fichiers. ....................................R 0 - 6
0.4.5. Erreurs de syntaxe........................................................R 0 - 6
0.4.6. Erreurs d'exécution.......................................................R 0 - 8
1. Eléments de base...............................................................................R 1 - 1
1.1. Notations utilisées.....................................................................R 1 - 1
1.2. Jeu de caractères .......................................................................R 1 - 2
1.3. Les variables .............................................................................R 1 - 3
1.4. Constantes ................................................................................R 1 - 5
1.5. Termes et arbres........................................................................R 1 - 9
1.6. Les opérateurs.........................................................................R 1 - 15
1.7. Les règles et les assertions......................................................R 1 - 16
1.8. Les mécanismes de base de Prolog.........................................R 1 - 18
1.9. La syntaxe complète de Prolog II+.........................................R 1 - 20
©PrologIA
iv
Manuel de Référence
1.9.1. Le niveau syntaxique ................................................. R 1 - 21
1.9.2. Les opérateurs ........................................................... R 1 - 22
1.9.3. Le niveau lexical ........................................................ R 1 - 24
1.9.4. Les caractères ............................................................ R 1 - 26
1.9.5. Les caractères accentués ............................................ R 1 - 27
1.10. Le macroprocesseur................................................................R 1 - 29
2. Le contrôle......................................................................................... R 2 - 1
2.1. Le contrôle ................................................................................R 2 - 1
2.2. Geler .........................................................................................R 2 - 9
2.3. A propos des arbres infinis .....................................................R 2 - 11
2.4. Quelques conseils pour la programmation récursive...............
R 2 - 12
2.5. Les meta-structures .................................................................R 2 - 14
2.5.1. Création..................................................................... R 2 - 14
2.5.2. Récupération.............................................................. R 2 - 15
2.5.3. Unification forcée...................................................... R 2 - 15
2.5.4. Sorties ....................................................................... R 2 - 16
2.5.5. Exemple..................................................................... R 2 - 16
3. Structuration des règles................................................................... R 3 - 1
3.1. Introduction...............................................................................R 3 - 1
3.1.1. Qualification des prédicats...........................................R 3 - 1
3.1.2. Qualification des foncteurs.......................................... R 3 - 2
3.1.3. Simplification des notations......................................... R 3 - 3
3.1.4. Quelques règles simples d'utilisation........................... R 3 - 3
3.1.5. Modules et préfixes standard....................................... R 3 - 4
3.2. Terminologie.............................................................................R 3 - 5
3.3. Syntaxe des identificateurs........................................................R 3 - 6
3.4. Contexte de Lecture et Ecriture..................................................R 3 - 8
3.4.1. Lecture......................................................................... R 3 - 8
3.4.2. Ecriture........................................................................ R 3 - 8
3.4.3. Notation simplifiée de la suite d'identificateurs ......................
d'un contexte................................................................ R 3 - 9
3.4.4. Notation en Prolog .................................................... R 3 - 11
3.4.5. Primitives pour les contextes et les familles ...........................
d'identificateurs.......................................................... R 3 - 11
3.5. Modules..................................................................................R 3 - 13
3.5.1. Définition d'un module.............................................. R 3 - 14
3.5.2. Module source........................................................... R 3 - 14
3.5.3. Module Objet ............................................................ R 3 - 17
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Généralités
3.6. Résumé ou Approche simplifiée des identificateurs, ...........................
contextes et modules...............................................................R 3 - 17
3.6.1. Identificateurs.............................................................R 3 - 17
3.6.2. Notation abrégée et contextes.....................................R 3 - 18
3.6.3. Modules.....................................................................R 3 - 19
3.7. Ajout, suppression et recherche de règles................................R 3 - 20
3.8. Manipulation des modules compilés.......................................R 3 - 32
4. Opérations prédéfinies.....................................................................R 4 - 1
4.1. Les tests de type........................................................................R 4 - 1
4.2. Les opérations arithmétiques.....................................................R 4 - 2
4.3. Affectation ................................................................................R 4 - 7
4.4. Opérations sur les chaînes........................................................R 4 - 9
4.5. Composition et décomposition d'objets...................................R 4 - 10
4.6. Comparaison de termes quelconques......................................R 4 - 14
5. Les entrées / sorties ...........................................................................R 5 - 1
5.0. Généralités................................................................................R 5 - 1
5.1. Entrées......................................................................................R 5 - 2
5.1.1. Règles pour l'entrée......................................................R 5 - 3
5.1.2. Modification de l'unité d'entrée.....................................R 5 - 8
5.2. Sorties.......................................................................................R 5 - 9
5.2.1. Règles pour la sortie.....................................................R 5 - 9
5.2.2. Modification de l'unité de sortie .................................R 5 - 12
5.3. Chargement et adaptation du module de dessin.......................R 5 - 14
5.4. Déclaration d'opérateurs..........................................................R 5 - 15
6. L'environnement...............................................................................R 6 - 1
6.1. Comment sortir de Prolog.........................................................R 6 - 1
6.2. Démarrage automatique d'un programme Prolog......................R 6 - 1
6.3. Edition de programmes.............................................................R 6 - 2
6.3.1. Modifier des paquets de règles.....................................R 6 - 2
6.3.2. Editer et recompiler un module source.........................R 6 - 3
6.3.3. Editer un fichier de texte quelconque............................R 6 - 4
6.4. Date, temps et mesures..............................................................R 6 - 4
6.5. Lien avec le système..................................................................R 6 - 5
6.6. Outil de mise au point de programmes......................................R 6 - 5
6.6.1. Mode trace ...................................................................R 6 - 6
6.6.2. Mode interactif.............................................................R 6 - 7
6.6.2.1. Points d'arrêt ...................................................R 6 - 7
6.6.2.2. Progression dans le code.................................R 6 - 8
v
©PrologIA
vi
Manuel de Référence
6.6.2.3. Terminer l'exécution ....................................... R 6 - 9
6.6.2.4. Affichage des informations............................. R 6 - 9
6.6.2.5. Informations annexes sur l'exécution............
R 6 - 14
6.6.2.6. Configuration ............................................... R 6 - 16
6.6.3. Activation d'un mode de mise au point....................... R 6 - 17
6.6.4. Gestion des points d'arrêt .......................................... R 6 - 18
6.6.5. Commandes du mode interactif ................................. R 6 - 18
6.6.6. Exemple..................................................................... R 6 - 20
6.7. Modification et visualisation de l'état courant..........................R 6 - 23
6.8. Gestion automatique des espaces et des piles..........................R 6 - 25
7. Extensions de Prolog avec des langages externes. ........................... R 7 - 1
7.1. Principes des fonctions de communication de données.............
R 7 - 2
7.2. Fonctions de communication de données simples.....................R 7 - 3
7.2.1. Test du type d'un argument.......................................... R 7 - 3
7.2.2. Transfert de données simples de Prolog vers un ....................
autre langage................................................................ R 7 - 4
7.2.3. Transfert de données simples d'un langage externe ...............
vers Prolog .................................................................. R 7 - 6
7.3. Fonctions de communication de termes quelconques................
R 7 - 8
7.3.1. Au moyen de chaînes de caractères.............................. R 7 - 8
7.3.2. Au moyen de structures de tableaux .......................... R 7 - 10
7.3.2.1. Description du codage d'un terme.................
R 7 - 11
7.3.2.2. Identificateurs............................................... R 7 - 14
7.3.2.3. Description des fonctions de communication R 7 - 16
7.4. Principe de la méthode des descripteurs..................................R 7 - 17
7.4.1. Eléments descriptifs................................................... R 7 - 17
7.4.2. Déclaration statique ................................................... R 7 - 18
7.4.3. Déclaration dynamique.............................................. R 7 - 19
7.5. Données partagées ..................................................................R 7 - 20
7.5.1. Exemple de zone commune de données..................... R 7 - 22
7.6. Ajout de fonctions externes.....................................................R 7 - 22
7.6.1. Exemple de déclaration de règle prédéfinie................
R 7 - 24
7.7. Ajout de fonctions externes à appel direct...............................R 7 - 26
7.7.1. Primitive CallC .......................................................... R 7 - 26
7.7.2. Conventions Prolog pour la communication ..........................
des données............................................................... R 7 - 27
7.7.2.1. Convention pour des paramètres sans .......................
valeur de retour ............................................ R 7 - 28
7.7.2.2. Convention pour le retour de la fonction.......
R 7 - 29
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Généralités
7.7.2.3. Convention pour des paramètres avec ........................
valeur de retour.............................................R 7 - 29
7.7.3. Exemple : méthode simple pour appeler une ..........................
fonction C ..................................................................R 7 - 32
8. Lancement d'un but Prolog par un programme C ..........................R 8 - 1
8.1. Principes de base ......................................................................R 8 - 1
8.2. Initialisation et terminaison de Prolog.......................................R 8 - 4
8.3. Empilement d'un but Prolog......................................................R 8 - 6
8.4. Programmation .........................................................................R 8 - 7
8.5. Méthode simple d'appel d'un but Prolog...................................R 8 - 8
8.5.1. Description...................................................................R 8 - 8
8.5.2. Exemple .......................................................................R 8 - 9
8.6 Autres fonctions................
......................................................R 8 - 10
9. Interruptions .....................................................................................R 9 - 1
9.1. Concepts...................................................................................R 9 - 1
9.2. Description des interfaces.........................................................R 9 - 3
9.3. Exemple complet.......................................................................R 9 - 3
10. Extensions Edinburgh....................................................................R 10 - 1
10.1. Syntaxe .................................................................................R 10 - 1
10.1.1. Généralités ................................................................R 10 - 1
10.1.2. Les opérateurs...........................................................R 10 - 2
10.2. Le contrôle ............................................................................R 10 - 3
10.3. Manipulation des règles ........................................................R 10 - 5
10.4. Opérations prédéfinies sur les données.................................R 10 - 6
10.4.1. Les tests de type........................................................R 10 - 6
10.4.2. Les opérations arithmétiques.....................................R 10 - 7
10.4.3. Composition et décomposition d'objets.....................R 10 - 8
10.4.3. Comparaison de termes quelconques......................R 10 - 11
10.5. Les entrées / sorties.............................................................R 10 - 12
10.5.1. Généralités ..............................................................R 10 - 12
10.5.2. Entrées ....................................................................R 10 - 14
10.5.2.2. Prédicats....................................................R 10 - 15
10.5.3. Sorties.....................................................................R 10 - 17
10.6. L'environnement..................................................................R 10 - 19
10.7. Traduction de DCG.............................................................R 10 - 20
vii
©PrologIA
viii
Manuel de Référence
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Généralités
Introduction
ix
Les besoins des utilisateurs de l'Intelligence Artificielle évoluent. En devenant industriels, les programmes sont de plus en plus gros, manipulent d'importantes quantités de données, communiquent avec d'autres logiciels.
Pour répondre à cette demande, PrologIA propose un nouveau système PrologII+.
Tout en conservant les caractéristiques qui ont fait le succès de Prolog II, les plus offerts par ce produit sont:
• performances
• modularité
• standardisation
• ouverture vers l'extérieur
• environnement de programmation
• interface graphique
Standardisation
• Choix d'un Mode standard s'inspirant fortement des dernières décisions des groupes de normalisation, pour la norme ISO-Prolog.
Performances
• Compilation incrémentale parfaitement transparente, permettant de bénéficier de la vitesse d'un langage compilé sans avoir à renoncer à la souplesse d'un langage interpreté.
• Optimisation de la récursivité terminale, permettant de programmer récursivement, c'est à dire de la manière la plus naturelle en Prolog, les processus itératifs, et cela sans débordement de la mémoire.
• Récupération dynamique de mémoire (garbage collector) dans les espaces de travail de Prolog (piles, code, dictionnaire). Une technique nouvelle nous permet une récupération plus performante.
• Réallocation dynamique des espaces de travail de Prolog.
• Compilation très optimisée de certaines règles prédéfinies et notamment des règles arithmétiques.
• Entiers en précision infinie.
©PrologIA
x
Manuel de Référence
Modularité
• Modularité: nouvelle notion, proposée pour le standard.
• L'écriture de gros programmes est rendue possible grâce aux modules, compilés séparément, chargeables et déchargeables à volonté.
• Run-time permettant de diffuser des applications autonomes.
Ouverture vers l'extérieur
• Liens bidirectionnels avec les autres langages : possibilité d'appeler en
Prolog des sous-programmes écrits dans d'autres langages. Possibilité d'appeler, depuis un programme quelconque, un programme écrit en Prolog (y compris avec l'obtention successive de différentes solutions). Tous les cas de figure d'appels croisés sont désormais pris en charge.
• Communication avec d'autres applications.
• Structures de données entièrement ouvertes, avec l'interface requis pour la communication inter-langage de tous les types de données, sans restriction sur la nature des termes (qui, par exemple, peuvent comporter y compris des variables et des identificateurs). Possibilité de partager des zones communes (tableaux) avec d'autres programmes.
• Interface SQL entre Prolog et les SGBD.
• Manipulation de bases de faits conséquentes.
• Données numériques (entiers et réels) homogènes avec les autres langages supportés par le système hôte, y compris pour leurs représentations étendues.
Environnement de programmation
• Manipulation de règles (assert, clause/rule, list etc…) intégrée au compilateur et fonctionnant sur les règles compilées.
• Récupération d'interruptions asynchrones en Prolog II+ autorisant, par exemple, la gestion d'environnements fortement interactifs (souris, fenêtres, etc…).
• Debugger de haut niveau permettant une mise au point rapide des gros programmes.
• Editeur intégré couplé avec la compilation incrémentale.
Interface graphique
• Bibliothèque portable entre divers environnements.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
• Définition d'objets: fenêtres, menus, boutons, ….
• Composition de dessins.
• Gestion de dialogues.
Généralités
xi
©PrologIA
xii
Manuel de Référence
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Généralités
xiii
Sur ce Manuel
La documentation concernant PROLOG II+ a été regroupée en deux manuels. Le premier, le Manuel de référence, décrit de manière précise le langage et son utilisation. Ce manuel est valable (en principe) pour toutes les implantations. Le second, le Manuel d'utilisation, décrit tout ce qui est dépendant d'un ordinateur et d'un système d'exploitation spécifique. Il y aura donc un manuel d'utilisation par matériel.
Pour installer le logiciel Prolog II+ et l'utiliser, voir les chapitres 1 et 2 du manuel utilisateur.
Le manuel de Référence commence par une introduction élémentaire à Prolog et son utilisation pratique. Dans le second chapitre, on présente de manière un peu plus théorique les bases de Prolog. Les chapitres suivants examinent les différentes règles prédéfinies concernant : le contrôle, la manipulation des règles, les opérations sur les données, les entrées-sorties, l'environnement, le debugger et l'éditeur. Les chapitres suivants concernent l'ouverture vers les autres langages : on décrit comment ajouter des règles prédéfinies écrites par l'utilisateur, faire des appels croisés ou déclarer des zones de données communes entre Prolog et d'autres langages, et intercepter des interruptions et déclencher un traitement Prolog. Enfin le dernier chapitre décrit la bibliothèque de primitives spécifiques à la syntaxe
Edinburgh.
En plus de ce manuel, il y a une partie Annexe qui est valable pour toutes les implantations également. Elle détaille quelques points secondaires mentionnés dans le manuel de Référence. En particulier vous y trouverez les différences entre l'interpréteur Prolog II et le compilateur Prolog II+, ou bien quelques exemples de programmes pour illustrer l'utilisation de Prolog.
Le manuel d'Utilisation précise comment se réalisent de manière pratique sur votre ordinateur, certaines fonctions décrites dans le manuel de Référence. Cela concerne la structure des noms de fichiers, la gestion des états sauvés, l'utilisation de la mémoire et comment lancer Prolog. La seconde partie de ce manuel décrit ce qui est spécifique à cette version : ce qu'il y a en plus et en moins par rapport à la version de base, ainsi que les valeurs extrêmes des constantes.
Nous vous conseillons également de consulter les documents suivants :
Prolog
F. Giannnesini, H. Kanoui, R. Pasero, M. Van Caneghem,
InterEditions, Mars 1985.
©PrologIA
xiv
Manuel de Référence
Prolog, bases théoriques et développements actuels,
A. Colmerauer, H. Kanoui, M. Van Caneghem,
TSI vol 2, numéro 4, 1983.
Pour la programmation en syntaxe Edinburgh:
Prolog Programming for Artificial Intelligence.
Ivan Bratko, Addison-Wesley, International Computer Science Series, 1986.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Généralités
xv
Garantie et responsabilités
PrologIA n'offre aucune garantie, expresse ou tacite, concernant ce manuel ou le logiciel qui y est décrit, ses qualités, ses performances ou sa capacité à satisfaire à quelque application que ce soit.
PrologIA ne pourra être tenue responsable des préjudices directs ou indirects, de quelque nature que ce soit, résultant d'une imperfection dans le programme ou le manuel, même si elle a été avisée de la possibilité que de tels préjudices se produisent. En particulier, elle ne pourra encourir aucune responsabilité du fait des données mémorisées ou exploitées, y compris pour les coûts de récupération ou de reproduction de ces données.
L'acheteur a toutefois droit à la garantie légale dans les cas et dans la mesure seulement où la garantie légale est applicable nonobstant toute exclusion ou limitation.
Droits d'auteur
Ce manuel et le logiciel qu'il décrit sont protégés par les droits d'auteur. Au terme de la législation traitant de ces droits, ce manuel et ce logiciel ne peuvent être copiés ou adaptés, en tout ou en partie, sans le consentement écrit de PrologIA, sauf dans le cadre d'une utilisation normale ou pour faire une copie de sauvegarde. Ces exceptions n'autorisent cependant pas la confection de copies à l'intention d'un tiers, que ce soit ou non pour les vendre.
Prolog II est une marque déposée de PrologIA.
©PrologIA
xvi
Manuel de Référence
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
0. Débuter avec Prolog
0.1. Leçon 1 : Lancement de Prolog
0.2. Leçon 2 : Utilisation d'un programme d'exemple
0.3. Leçon 3 : L'Editeur
0.4. Les erreurs les plus courantes
Dans les exemples de ce manuel, ce qui est écrit par l'utilisateur apparait en
caractère penché
, et tout ce qui est écrit par l'ordinateur apparait en caractère Télétype droit
.
0.1. Leçon 1 : Lancement de Prolog
Lancez Prolog, ceci le fait démarrer à partir d'un état sauvé initial nommé initial.po.
Le caractère ">" représente le prompt de Prolog. Les différentes possibilités de
: démarrage sont décrites dans le manuel d'Utilisation. Prolog affiche alors sur l'écran
Prolog II+ Version...
Code : ... PrologIA
>
Le caractère ">" indique à l'utilisateur qu'il est sous Prolog et que le système est en attente de commande. Pour commencer, essayons une commande simple, imprimons le message "Hello World":
> out("Hello World") line;
"Hello World"
{}
>
Entrons maintenant un premier petit programme; pour cela on se met en mode insertion de règles (commande insert). Le système est alors prêt à lire des règles (ou des commentaires) et les enregistre en mémoire au fur et à mesure. Par exemple, on entre trois règles qui expriment que Pierre, Jean et Marie habitent respectivement à
Paris, Marseille et Paris :
> insert;
habite_a(Pierre, Paris)->; habite_a(Jean, Marseille)->; habite_a(Marie, Paris)->;;
©PrologIA
R 0 - 2
Manuel de Référence
{}
>
Remarquez que chaque règle est terminée par le caractère ";" et que l'insertion se termine par un ";" supplémentaire. Les règles sont insérées dans l'ordre où on les a tapées. On s'en assure en tapant la commande list (qui affiche les règles du module courant).
>list; habite_a(Pierre, Paris) -> ; habite_a(Jean, Marseille) -> ; habite_a(Marie, Paris) -> ;
{}
>
Faisons quelques essais avec ce programme : Où habite Pierre ?
>habite_a(Pierre, x);
{x=Paris}
>
Qui habite à Paris ?
>habite_a(x, Paris);
{x=Pierre}
{x=Marie}
>
Qui habite où ?
>habite_a(x, y);
{x=Pierre, y=Paris}
{x=Jean, y=Marseille}
{x=Marie, y=Paris}
>
A chaque fois, la réponse du système est l'ensemble des valeurs à donner aux variables figurant dans la question pour que la relation correspondante soit satisfaite. Pour terminer la session, on tape la commande :
>quit;
Bye......
et on se retrouve sous l'interpréteur de commandes du système d'exploitation.
A!ociation
Prolog
HERITAGE
0.2. Leçon 2 : Utilisation d'un programme d'exemple
Dans ce qui suit, on utilisera le programme menu.p2 qui se trouve décrit dans de nombreuses publications. Ce programme se trouve dans le répertoire d'exemples du
Kit Prolog II+. Pour cette leçon, il vous faut au préalable recopier le fichier
menu.p2 dans votre répertoire courant. Il suffit alors de lancer Prolog, puis insérer le fichier menu.p2 en tapant :
PROLOG II+ ...
> echo insert("menu.p2");
©PrologIA
A!ociation
Prolog
HERITAGE
Débuter avec Prolog
R 0 - 3
insert lit des règles sur le fichier spécifié et les insère dans le module courant.
Lorsque le fichier d'entrée est épuisé, l'entrée courante bascule sur le clavier qui est l'unité d'entrée par défaut. La primitive echo réalise l'affichage à la console au fur et
à mesure de la lecture du fichier:
" la carte " hors_d_oeuvre(Artichauts_Mélanie) -> ; hors_d_oeuvre(Truffes_sous_le_sel) -> ; hors_d_oeuvre(Cresson_oeuf_poche) -> ; viande(Grillade_de_boeuf) -> ; viande(Poulet_au_tilleul) -> ; poisson(Bar_aux_algues) -> ; poisson(Chapon_farci) -> ; dessert(Sorbet_aux_poires) -> ; dessert(Fraises_chantilly) -> ; dessert(Melon_en_surprise) -> ;
" plat de résistance" plat(p) -> viande(p) ; plat(p) -> poisson(p) ;
" composition d'un repas " repas(e,p,d) -> hors_d_oeuvre(e) plat(p) dessert(d) ;
" valeur calorique pour une portion " calories(Artichauts_Mélanie,150) -> ; calories(Cresson_oeuf_poche,202) -> ; calories(Truffes_sous_le_sel,212) -> ; calories(Grillade_de_boeuf,532) -> ; calories(Poulet_au_tilleul,400) -> ; calories(Bar_aux_algues,292) -> ; calories(Chapon_farci,254) -> ; calories(Sorbet_aux_poires,223) -> ; calories(Fraises_chantilly,289) -> ; calories(Melon_en_surprise,122) -> ;
" valeur calorique d'un repas " valeur(e,p,d,v) ->
calories(e,x)
calories(p,y)
calories(d,z)
ajouter(x,y,z,v) ;
" repas équilibré " repas_equilibre(e,p,d) ->
repas(e,p,d)
valeur(e,p,d,v)
inferieur(v,800) ;
" divers" ajouter(a,b,c,d) ->
val(add(a,add(b,c)),d) ; inferieur(x,y) -> val(inf(x,y),1) ;
{}
>
©PrologIA
R 0 - 4
Manuel de Référence
Faisons quelques essais avec ce programme. La question : «Quels sont les plats ?» se traduit par :
>plat(p);
{p=Grillade_de_boeuf}
{p=Poulet_au_tilleul}
{p=Bar_aux_algues}
{p=Chapon_farci}
Quels sont les hors-d'oeuvres ?
>hors_d_oeuvre(x);
{x=Artichauts_Mélanie}
{x=Truffes_sous_le_sel}
{x=Cresson_oeuf_poche}
Cette leçon s'arrête là et on sauve le module courant "" (module avec le préfixe ""):
> save([""],"menu.mo") quit;
Bye........
Note: On peut utiliser également la commande exit: en sortant Prolog sauve l'état courant dans un fichier nommé par défaut prolog.po.
Le module sauvé nous servira dans la leçon suivante.
A!ociation
Prolog
HERITAGE
0.3. Leçon 3 : L'Editeur
Le but de cette leçon est de montrer une façon très simple de travailler avec l'éditeur intégré à Prolog, au niveau d'un paquet de règles. Sur les machines possédant un environnement fenêtrage et souris, l'utilisation de copier/coller peut être plus commode.
On commence par lancer Prolog et charger le module sauvé contenant le programme
menu.p2 si l'on a sauvé avec save:
...
> load("menu.mo");
{}
>
Si l'on est sorti avec exit, on commence par lancer Prolog avec l'état sauvé prolog.po contenant le programme menu.p2 tel que nous l'avons laissé à la fin de la leçon précédente:
....
>
Supposons que l'on veuille changer notre menu, et remplacer les règles :
viande(Grillade_de_boeuf) -> ; viande(Poulet_au_tilleul) -> ;
par :
viande(Veau_marengo) -> ;
©PrologIA
A!ociation
Prolog
HERITAGE
Débuter avec Prolog
R 0 - 5
viande(Poulet_roti) -> ;
Appeller l'éditeur par la commande :
> edit(viande/1); où la notation viande/1 désigne le paquet de règles concernant la relation viande avec 1 argument.
L'éditeur est activé et montre les règles définissant la relation viande. Réaliser les modifications voulues, et sortir de l'éditeur normalement. Vérifiez que Prolog a bien relu les nouvelles définitions:
> list(viande/1); viande(Veau_marengo) -> ; viande(Poulet_roti) -> ;
{}
On exécute le programme modifié et on finit par quit puisqu'on ne désire pas conserver l'état courant.
> viande(p) ;
{p=Veau_Marengo}
{p=Poulet_roti}
> plat(p) ;
{p=Veau_Marengo}
{p=Poulet_roti}
{p=Bar_aux_algues}
{p=Chapon_farci}
> quit ;
Bye......
0.4. Les erreurs les plus courantes
Voici quelques explications sur les erreurs les plus courantes qui peuvent se produire lors de l'utilisation de Prolog II+.
0.4.1. Débordement de l'espace de code.
•PAS ASSEZ DE PLACE POUR LE CODE
Il faut redémarrer Prolog avec un espace plus grand pour le code (option -c de la ligne de commande). Si le système de réallocation automatique n'est pas désactivé au démarrage de Prolog, un certain nombre de réallocations se produiront avant le débordement. Le code occupera alors toute la place disponible de la machine. Dans ce cas il faut, si c'est possible, augmenter la mémoire totale de Prolog ou bien vérifier le fonctionnement du programme, sans doute anormal.
0.4.2. Interruption.
Un programme en cours d'exécution peut être interrompu à tout instant par un caractère d'interruption (<Control-C> <return> souvent).
> insert;
©PrologIA
R 0 - 6
Manuel de Référence rr -> rr;;
{}
> rr;
<Ctrl-C>
INTERRUPTION UTILISATEUR
{}
>
0.4.3. Erreurs détectées en cours d'insertion.
•REGLE DEJA DEFINIE
Cette erreur provient du fait que l'utilisateur n'a pas respecté le principe suivant :
Toutes les règles de même nom et de même arité doivent être consécutives (deux règles de même nom/arité ne peuvent être séparées ni par un commentaire, ni par une règle de nom/arité différent). Les configurations suivantes sont donc illégales ;
qq(x) -> ; rr(y) -> ; qq(z) -> ;
car la règle rr est mal placée.
ATTENTION: cette erreur ne se produira pas si vous utilisez les primitives reinsert
ou insertz. En effet reinsert écrase le paquet de règles existant par sa nouvelle définition et insertz complète le paquet de règles. En particulier, en utilisant reinsert dans le cas décrit ci-dessus, il n'y aura aucune erreur et le paquet de règles d'accés
qq contiendra une seule règle, la dernière.
0.4.4. Erreurs concernant les fichiers.
•FIN DE FICHIER
Ce message signale que la lecture du fichier d'entrée est terminée. C'est plutôt un avertissement qu'un message d'erreur. L'entrée bascule alors automatiquement sur le clavier.
•ERREUR D'OUVERTURE DU FICHIER D'ENTREE
Ce message signale que le fichier n'existe pas, ou qu'il est inaccessible. Il suffit de refaire la commande après avoir apporté les corrections nécessaires.
0.4.5. Erreurs de syntaxe.
La plupart des erreurs de syntaxe sont suffisamment explicites pour qu'on voit tout de suite de quoi il s'agit. Il suffit de se reporter à la syntaxe (Chapitre 1) pour avoir des explications complémentaires. Cependant, certaines d'entre elles méritent un commentaire :
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Débuter avec Prolog
R 0 - 7
•UNITE MAL PLACEE
> insert; plat(x) -> viande(, boeuf) ;;
-> "," : UNITE MAL PLACEE
Cette erreur montre que la syntaxe n'a pas été respectée. Dans cet exemple, il manque un argument.
•IL MANQUE " A LA FIN DE LA CHAINE
> insert;
viande("boeuf)
-> MANQUE " A LA FIN DE LA CHAINE.
Une chaîne doit tenir sur une ligne, si la fin de ligne n'est pas masquée. On rappelle qu'une chaîne commence et finit sur le caractère("). Si le caractère fait partie de la chaîne, il doit être doublé.
•CE TERME NE PEUT PAS ETRE UNE TETE DE REGLE
> insert;
p(x) -> ;;
-> v41(v60) : CE TERME NE PEUT PAS ETRE UNE TETE DE REGLE
Le seul accès légal à une règle est un identificateur (il commençe par deux lettres en syntaxe Prolog II). En particulier, l'accès ne peut être une variable (dont le nom commence par une seule lettre en syntaxe Prolog II), ni un nombre, ni une chaîne.
On rappelle, pour la syntaxe Prolog II, qu'un nom de variable, de relation, ou de constante est une suite de mots séparés par un "_" et éventuellement suivie d'apostrophes. Un mot est constitué d'une ou plusieurs lettres éventuellement suivies de chiffres. Si le premier mot du nom commence par au moins deux lettres, on a affaire à un nom de relation ou de constante (ce qu'on appelle un identificateur), ce qui constitue un accès légal à une règle. Dans le cas contraire on a affaire à une
variable. Par exemple :
x, x12, x_toto_a25, a', a12'', b_titi'' sont des noms de variables.
bonjour, xx'', qq_toto_25, comment_allez_vous, 'avec des blancs' sont des noms de relations ou de constantes.
•LITTERAL INCORRECT
> insert;
humain(x) ->
homme(x) humain(x) -> femme(x);
-> LITTERAL INCORRECT skipping: femme(x);
©PrologIA
R 0 - 8
Manuel de Référence
Lorsqu'une telle erreur se produit au niveau de la flèche Prolog, il s'agit très souvent de l'oubli du ";" à la fin de la règle précédente.
0.4.6. Erreurs d'exécution.
•CE TERME NE PEUT ETRE UN BUT
Voici un exemple qui utilise la règle : exec(p) -> p ;
> exec(outm("bonjour")); bonjour{}
>
La variable p de la règle exec est remplacée par outm("bonjour") qui est effacé sans problème. En revanche :
> exec(x);
-> CE TERME NE PEUT ETRE UN BUT
> provoque une erreur car la variable p reste libre au moment où on tente de l'effacer.
•ARGUMENT DE MAUVAIS TYPE
Cette erreur survient lorsque l'on essaie d'effacer une règle prédéfinie avec un des arguments qui n'est pas du type attendu. Généralement l'argument qui est en cause est affiché avec le message.
•APPEL A UNE REGLE NON DEFINIE
On essaye d'effacer une règle qui n'est pas définie alors qu'on est en mode erreur
(flag uE). Notez que pour faire échouer volontairement l'exécution, il faut appeler la règle prédéfinie fail.
Il existe plusieurs comportement possibles, pilotés par un flag dans la commande de lancement de Prolog, pour l'effacement d'un prédicat non défini: uW : affiche un warning et continue l'exécution du programme, uF : uE :
échoue, génère l'erreur.
A!ociation
Prolog
HERITAGE
Les autres erreurs d'exécution concernent principalement l'utilisation des primitives
block, block_exit et de val. En général les diagnostics sont suffisamment explicites.
Il est conseillé de lancer Prolog avec l'option -f uE qui permet d'avoir un diagnostic précis lors de l'appel erroné d'une règle n'existant pas (à cause d'une faute de frappe par exemple). Lorsque l'on veut appeler une règle et avoir un échec lorsque celle-ci n'existe pas, tester d'abord sa présence avec current_predicate.
©PrologIA
A!ociation
Prolog
HERITAGE
1 .
Eléments de base
1.1. Notations utilisées
1.2. Jeu de caractères
1.3. Les variables
1.4. Constantes
1.5. Termes et arbres
1.6. Les opérateurs
1.7. Les règles et les assertions
1.8. Les mécanismes de base de Prolog
1.9. La syntaxe complète de Prolog II+
1.10.
Le macroprocesseur
1 . 1 .
Notations utilisées
Dans ce manuel nous utiliserons des règles hors contexte pour décrire la syntaxe de toutes les formules intervenant dans Prolog. La notation utilisée ici est la même que celle utilisée par la commission de normalisation de Prolog :
• Les symboles non terminaux sont représentés par des identificateurs et les terminaux sont entre doubles quotes "…".
• Le symbole "=" est choisi comme symbole de réécriture et une règle se termine par un ";". Les éléments d'un membre droit de règle sont séparés par des ",".
• Les accolades {…} représentent un nombre quelconque d'apparitions,
éventuellement aucune, des éléments qu'elles encadrent.
• Les crochets […] signalent le caractère optionnel des éléments qu'ils encadrent.
• La barre verticale ...|... exprime l'alternative entre les éléments qu'elle sépare. Des parenthèses (...|...) sont utilisées lorsque le résultat de cette alternative apparaît dans une liste de symboles.
• Le signe "-" est utilisé pour représenter des exceptions.
• Certaines règles de réécriture dépendent des options de syntaxe sélectionnées : les règles dont le membre gauche est annoté par P ne sont actives que lorsque la syntaxe Prolog II est activée (par exemple separator
P
). Les règles dont le membre gauche est annoté par E ne sont actives que lorsque la syntaxe
Edinburgh est activée (par exemple graphic_char
E
).
©PrologIA
R 1 - 2
Manuel de Référence
• D'autres règles, annotées par une lettre, dépendent des options choisies au lancement de Prolog et ne sont valides que dans certains cas.
La totalité de la syntaxe de Prolog est donnée à la fin de ce chapitre. Des extraits plus ou moins simplifiés de cette syntaxe sont commentés dans les paragraphes suivants.
A!ociation
Prolog
HERITAGE
1 . 2 .
Jeu de caractères
L'utilisateur peut choisir parmi deux jeux de caractères : celui défini par le code ISO
8859-1 (cf. Annexe), ou celui disponible sur la machine hôte (cf. Manuel
Utilisateur). Dans ce manuel nous décrirons uniquement le jeu ISO. Le choix du jeu hôte aura pour effet de diminuer ou de grossir les sous-ensembles letter et
graphic_char avec des caractères n'existant pas dans le jeu ISO (cf. U3-2).
Voici une description simplifiée (la description complète se trouve au paragraphe
1.9) du jeu de caractères nécessaires à Prolog : letter = letter I = digit = alpha = separator = separator P = special_char= special_char E = graphic_char =
":" graphic_char E = character =
"A" | … | "Z" | "a" | … | "z"
| "À" … "ß" - "!" | "à" … "ÿ" - "÷"
"\", accent_escape ;
"0" | … | "9" ; letter | digit | "_" ;
"(" | ")" | "[" | "]" | "{" | "}" |
"|" | "," ;
";" | "." | "<" | ">" ;
"%" | "'" | """ | "_" | "!" | "`" ;
";" ;
"#" | "$" | "&" | "*" | "+" | "-" | "/" |
| "=" | "?" | "\" | "@" | "^" | "~"
| NBSP 1 … "¿" | "!" | "÷" ;
"." | "<" | ">" ; letter | digit | separator | graphic_char | special_char ;
Sans changer le sens de ce qui est écrit, on peut insérer des espaces n'importe où, sauf dans les constantes et les variables. On peut enlever des espaces n'importe où sauf dans les chaînes et sauf si cette suppression provoque la création de nouvelles constantes ou variables, par juxtaposition d'anciennes.
1
NBSP est l’espace non sécable.
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 3
Il existe diverses manières d'introduire des commentaires dans les programmes. Du point de vue de la syntaxe, un commentaire équivaut à un blanc et peut figurer partout où un blanc est permis :
• Le caractère "%" indique le début d'un commentaire qui s'étend depuis le "%" jusqu'à la fin de la ligne où ce commentaire apparaît.
• Les symboles "|*", "*|" et "/*", "*/" servent aussi à définir des commentaires, constitués par ces symboles et la suite des caractères qu'ils encadrent.
On ne doit pas mélanger ces symboles : un commentaire commençant par "|*" doit se terminer par "*|" et un commentaire commençant par "/*" doit se terminer par
"*/". D'autre part, on peut imbriquer de tels commentaires; par exemple, le texte suivant sera vu comme un unique commentaire :
|* second com- |* premier commentaire *| mentaire *|
• Une chaîne de caractères écrite au niveau supérieur, c'est-à-dire là où une règle est attendue, possède aussi la valeur d'un commentaire.
1 . 3 .
Les variables
Les variables servent aussi bien à désigner des constantes que des entités plus complexes. En voici la syntaxe : variable = variable = extended_var P = extended_var E =
"_" , {alpha} ; extended_var ; letter, [ (digit | "_"), {alpha} ] , { "'" } ; big_letter, [ {alpha} ] ;
Il y a donc, pour les variables, une syntaxe de base et une syntaxe étendue à choisir parmi deux : la syntaxe Prolog II et la syntaxe anglaise. On doit indiquer si l'on désire une syntaxe différente de la syntaxe étendue Prolog II, au moment du lancement de Prolog II+; de plus, puisque ces deux syntaxes de variables sont incompatibles 2 , on ne peut pas avoir les deux extensions en même temps.
2
En effet : l'expression x est une variable en Prolog II et ne l'est pas dans la syntaxe anglaise. De même, le nom Pierre n'est pas une variable en Prolog II, mais en est une en syntaxe anglaise !
©PrologIA
R 1 - 4
Manuel de Référence
Voici quelques exemples de variables correctes :
Syntaxe Prolog II Syntaxe Edinburgh
x x' X_12_plus x' ' x12 p_rix y33' y_en_a'
_prix
_123
X
Prix
_prix
X1Y2
_33
et quelques exemples d'expressions qui ne sont pas des variables correctes :
Syntaxe Prolog II Syntaxe Edinburgh
ph xx prix
1er_x
X' x12 prix y_en_a
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 5
1 . 4 .
C o n s t a n t e s
Les données les plus simples sont des constantes. Elles peuvent être de quatre types : les identificateurs, les nombres entiers, les nombres réels, et les chaînes de caractères.
identifier = identifier = identifierE = identifierE = abbreviated_id = abbreviated_id = prefix_limit = prefix = name = integer_number = integer_number = integer_number = integer_number = integer_number = real_number = real_numberS =
]; binary_number = octal_number = hex_number = digits = string = quoted_char = quoted_chari0 = quoted_chari1 = format_escape = prefix , prefix_limit , abbreviated_id ; abbreviated_id ; prefix , prefix_limit ,
"'", graphic_symbol, "'" ; graphic_symbol ; name - extended_var ;
"'", { (character - "'") | "'""'" }, "'";
":" ;
[ name , { prefix_limit , name } ] ; letter , { alpha } ; digits ;
"0b", binary_number ;
"0o", octal_number ;
"0x", hex_number ;
"0'", character ; digits, ".", digits, ("E"|"e"|"D"|"d"),
[ ["+"|"-"], digits]; digits, ".", digits,
[ ("E"|"e"|"D"|"d"), [ ["+"|"-"],digits] binary_digit , { binary_digit } ; octal_digit , { octal_digit } ; hex_digit , { hex_digit } ; digit , { digit } ;
""" , { quoted_char } , """ ; character - ( """ | "\" | newline) | """" ;
"\" ;
"\", format_escape ;
"b" | "f" | "n" | "r" | "t" | "\" | newline
| octal_digit, octal_digit, octal_digit
| ("x" | "X"), hex_digit, hex_digit ;
©PrologIA
R 1 - 6
Manuel de Référence
Identificateurs
Les identificateurs ont deux représentations externes : une représentation complète et une représentation abrégée. La première comporte un préfixe qui spécifie la famille dont l'identificateur fait partie; dans la représentation abrégée, le préfixe n'apparaît pas et il est supposé que certaines conventions de lecture-écriture précisent de manière non ambiguë quel est le préfixe de l'identificateur. Ces notions sont expliquées en détail au chapitre 3.
C'est la présence ou l'absence du caractère ":" qui distingue la représentation complète de la représentation abrégée d'un identificateur. Ce caractère peut être redéfini et remplacé par un caractère graphique, tel que le décrit le chapitre 3.
Les identificateurs complets suivants sont corrects et représentent tous des identificateurs différents:
data:peter x:peter sys:write
:peter grammar:singular lexicon:name sys:env:files grammar:plural
Note: La chaîne vide est un préfixe légal, l'identificateur :peter est donc correct syntaxiquement.
L'identificateur suivant n'est pas complet
peter
La syntaxe de la représentation abrégée des identificateurs et celle des variables sont très voisines et, ensemble, définissent ce que dans beaucoup d'autres langages de programmation on appelle «identificateur». Nous retiendrons que, dans la syntaxe
Prolog II, les variables commencent par une seule lettre ou par le caractère "_", tandis que les représentations abrégées d'identificateurs commencent par au moins
deux lettres. Dans la syntaxe Edinburgh, la différenciation se fait sur le premier caractère, qui doit être une lettre majuscule ou le caractère "_" pour représenter une variable ou bien une lettre minuscule pour représenter un identificateur en notation abrégée. Dans cette syntaxe, les représentations abrégées d'identificateurs peuvent
également être des suites de caractères graphiques.
Voici des exemples d'identificateurs abrégés corrects :
Syntaxe Prolog II Syntaxe Edinburgh
pomme pomme' pomme12 i10 pomme
***
§
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 7 des exemples d'identificateurs incorrects :
Syntaxe Prolog II Syntaxe Edinburgh
x
1er_lapin y_en_a l'herbe
Pomme
1er_lapin pomme'
Nombres
La syntaxe des nombres en Prolog II+ est sensiblement la même que dans la plupart des langages de programmation.
Les nombres entiers sont signés et permettent de représenter, a priori, des valeurs quelconques. Plusieurs syntaxes d'entiers sont acceptées.
Un entier peut être exprimé dans les bases suivantes : 2, 8, 10, 16. Pour cela, la mantisse de l'entier sera préfixée respectivement par : 0b, 0o, 0, 0x. Par défaut une mantisse non préfixée sera considérée en base décimale. Les petits entiers, inférieurs
à 256, pourront également être exprimés à l'aide du préfixe 0' suivi d'un caractère, la valeur de l'entier sera alors le code du caractère 3 . Par exemple, les expressions suivantes représentent des entiers :
Expression:
0b110
0o110
0110
0x110
0'A
Valeur:
6
72
110
272
65
On notera que, contrairement à d'autres langages, les nombres réels doivent comporter un exposant explicite : l'expression -12.34 ne définit pas un nombre réel, mais une paire pointée formée des deux entiers -12 et 34. En revanche,
-12.34e0 est un réel correct. C'est le choix par défaut en syntaxe Prolog II, il est néanmoins possible, pour se ramener à une syntaxe standard, de le modifier par une option sur la ligne de commande au lancement de Prolog (cf. § 2.3 du manuel d'utilisation).
Les nombres réels sont codés en double précision, cela correspond au type double du standard IEEE 64 bits. La lettre introduisant l'exposant peut être une des suivantes : e, E, d ou D. On préférera toutefois e et E.
3
Attention aux caractères étendus, leur valeur dépend du mode choisi : ISO 8859-1 ou code de la machine hôte.
©PrologIA
R 1 - 8
Manuel de Référence
Chaînes de caractères
Les chaînes de caractères sont encadrées par des doubles quotes """. Tous les caractères imprimables peuvent figurer dans une chaîne sans précaution particulière, sauf le caractère """ qui doit être doublé et éventuellement le caractère "\" qui doit
être doublé si l'interprétation de la lecture du "\" est active (cf. les options de comportement § U 2.3.); par exemple, la chaîne: est correcte.
"il faut traiter "" et \\ avec précaution"
De façon générale le caractère "\" ne doit pas forcément être doublé: lorsque l'interprétation de lecture du "\" est active (cf. § 2.3 du manuel d'utilisation), si avec les caractères qui le suivent, il forme une expression (séquence escape) représentant un autre caractère, il faut le doubler; dans tous les autres cas, il n'est pas utile de le doubler:
"Utiliser \ suivi de RETURN pour ignorer une fin de ligne"
"Les codes hexadécimaux doivent commencer par \\x"
Quand l'interprétation du "\" est active (règles annotées par i1), plusieurs expressions commençant par "\" permettent de représenter un caractère.
Les expressions suivantes permettent de spécifier certains caractères non imprimables dans la définition d'une chaîne :
\b espace arrière (backspace)
\f saut de page (form feed)
\n saut à la ligne (newline). Lors de son écriture, ce caractère est, le cas
échéant, remplacé par le(s) caractère(s) requis pour obtenir une nouvelle ligne sur l'unité de sortie.
\r retour en début de ligne (carriage return)
\t tabulation
D'autres expressions permettent de représenter des caractères qui n'existent pas sur la machine hôte (voir le paragraphe 1.9.5).
Au moyen du nombre qui est son code interne, tout caractère peut être spécifié dans une chaîne. Par exemple, les expressions "\033" et "\x1B" définissent toutes deux une chaîne composée de l'unique caractère dont le code est 27. Le caractère nul (i.e.: celui dont le codage interne est le nombre 0) ne doit pas figurer dans les chaînes de caractères.
Une chaîne de caractères peut s'étendre sur plusieurs lignes. Pour cela, le dernier caractère de chacune des lignes en question sauf la dernière doit être "\" ; le "\" et le caractère de fin de ligne seront ignorés. Par exemple, l'expression
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 9
"ceci est une chaî\ ne sur deux lignes" définit la même chaîne que "ceci est une chaîne sur deux lignes".
En Prolog, le type chaîne de caractère existe et une chaîne de caractères représente donc un objet de ce type. En syntaxe Edinburgh, une option de démarrage permet de définir l'interprétation syntaxique de cette unité lexicale, qui peut représenter un des termes suivants: un identificateur, une liste d'identificateurs d'une lettre, une liste d'entiers, une chaîne Prolog.
N.B. : En écrivant des programmes Prolog, il arrive qu'une donnée symbolique puisse être représentée soit par une chaîne de caractères, soit par un identificateur.
Les identificateurs étant eux-mêmes codés sous forme d'entités atomiques, on admet généralement qu'ils constituent une représentation des objets symboliques plus efficace que les chaînes.
1 . 5 .
Termes et arbres
Toutes les données manipulées en Prolog sont des arbres éventuellement infinis dont nous allons tout d'abord donner une description informelle. Ces arbres sont formés de nœuds étiquetés :
• soit par une constante et, dans ce cas, ils n'ont aucun fils,
• soit par le caractère "point" et, dans ce cas ils ont deux fils,
• soit par "<>" ou"<->" ou "<-->" ou "<--->" ou… et, dans ce cas, le nombre de traits d'union correspond au nombre de leurs fils.
La figure suivante présente deux exemples d'arbres finis :
• <--->
/ \ / | \
"a" • ___/ | \___
/ \ / | \
"b" • plus <---> <--->
/ \ / | \ / | \
"c" nil fois 5 8 fois 5 8
Figure 1.1
©PrologIA
R 1 - 10
Manuel de Référence
La figure 1.2 est un exemple d'arbre infini :
<--->
/ | \ ou "c" <--->
/ | \
et "a" <--->
/ | \
et <---> "b"
/ | \
ou "c" <--->
/ | \
et "a" <--->
/ | \
et <---> "b"
/ | \
ou "c" <--->
/ | \
et "a" <--->
/ | \
et <---> "b"
/ | \
Figure 1.2
Remarquons que le dessin des arbres peut être allégé comme le montre la figure 1.3.
identificateur au lieu de <--- . . . --->
/ | | \ / | | | \ x1 x2 ... xn identificateur x1 x2 ... xn
Figure 1.3
Bien entendu cette simplification présuppose que n ne soit pas nul. Les deux derniers arbres peuvent alors être représentés sous la forme classique de :
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 11
plus ou
/ \ / \
fois fois "c" et
/ \ / \ / \
5 8 5 8 "a" et
/ \
ou "b"
/ \
"c" et
/ \
"a" et
/ \
ou "b"
/ \
"c" et
/ \
"a" et
/ \
ou "b"
/ \
Figure 1.4
La notion d'arbre infini est suffisamment inhabituelle pour que nous nous étendions un peu dessus. Intuitivement un arbre est infini s'il possède une branche infinie.
Nous nous intéresserons plus spécialement à la fraction des arbres infinis qui ensemble avec les arbres finis forme les arbres dits rationnels : c'est à dire les arbres qui ont un nombre fini de sous-arbres. Si nous reprenons les deux derniers exemples d'arbres l'ensemble de leurs sous-arbres est décrit dans la figure 1.5.
©PrologIA
R 1 - 12
Manuel de Référence
{ <---> , <---> , plus , fois , 5 , 8 }
/ | \_____ / | \
/ | \ fois 5 8 plus <---> <--->
/ | \ / | \
fois 5 8 fois 5 8
{ <--->, <--->, <--->, ou, et, "a","b","c"}
/ | \ / | \ / | \
ou "c" <---> et "a" <---> t <---> "b"
/ | \ / | \ / | \
et "a" <---> et <---> "b" ou "c" <--->
/ | \ / | \ / | \
et <---> "b" ou "c" <---> et "a" <--->
/ | \ / | \ / | \
ou "c" <---> et "a" <---> et <---> "b"
Figure 1.5
Ces ensembles étant finis, il s'agit donc d'arbres rationnels. Le fait qu'un arbre rationnel contienne un ensemble fini de sous-arbres donne un moyen immédiat de le représenter par un diagramme fini : il suffit de fusionner tous les nœuds d'où partent les mêmes sous-arbres (figure 1.6).
A!ociation
Prolog
HERITAGE
Figure 1.6
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 13
Si on ne fait pas toutes les fusions on obtient la figure 1.7.
Figure 1.7
Il faut donc se méfier du fait que des diagrammes différents puissent représenter le même arbre.
Pour représenter les arbres nous utiliserons des formules appelées termes. Nous introduirons tout d'abord la notion de terme strict : strict_term = strict_term P = strict_term E = strict_term P = strict_term E = variable | identifier | constant
| "[ ]"
| "< >" ;
"(", strict_term , "." , strict_term , ")" ;
"[", strict_term , "|" , strict_term , "]" ;
"<", strict_term , { "," , strict_term } , ">" ;
"<>(", strict_term , { "," , strict_term } , ")" ;
Les termes stricts sont les "vrais" termes. Cependant pour des raisons de commodité on étend la syntaxe des termes stricts (sans en altérer le sens) en permettant :
• P d'ajouter et d'enlever des parenthèses mais en convenant que:
t1.t2. … .tn représente (t1.(t2.( … .tn) … ))) ;
• E d'ajouter et d'enlever des crochets mais en convenant que:
[t1,t2,…,tn] représente [t1 | [t2 | …[tn | [] ] ]] et
[t1,t2,…,tn| t] représente [t1 | [t2 | …[tn | t] ]];
• d'écrire id(t1, t2, … , tn) au lieu de <id, t1, t2, … , tn> à condition que id soit un identificateur et que n soit différent de 0.
©PrologIA
R 1 - 14
Manuel de Référence
Ceci conduit à une notion plus générale de terme :
aiment(Pierre.Paul.Jean.nil, pere_de(x))
et le terme strict correspondant est :
<aiment, (Pierre.(Paul.(Jean.nil))), <pere_de, x>>
Pour transformer un terme en un arbre il faut affecter ses variables par des arbres, d'où la notion d'affectation sylvestre, qui est un ensemble X de la forme :
X = { x1 := r1, x2 := r2, … } où les xi sont des variables distinctes et les ri des arbres. Nous introduirons aussi la notation suivante : si r1, r2 sont des arbres et si r1, r2, … , rn est une suite de n arbres alors (r1.r2) et <r1, r2, … , rn> représentent respectivement les arbres suivants :
• <-- ... ->
/ \ / | \ r1 r2 r1 r2 rn
Figure 1.8
Si t est un terme strict faisant intervenir un sous-ensemble de l'ensemble des variables de l'affectation sylvestre X = { x1 := r1, x2 := r2, … } alors l'expression
t/X désignera l'arbre obtenu en remplaçant les variables xi par les arbres correspondants ri. Plus précisément :
- t/X = ri si t = xi ;
- t/X = valeur de k si t est la constante k ;
- t/X = (t1/X . t2/X) si t = (t1 . t2) ;
- t/X = [t1/X | t2/X)] si t = [t1 | t2] ;
- t/X =<t1/X, … , tn/X> si t = <t1, … , tn>.
- t/X =<>(t1/X, … , tn/X) si t = <>(t1, … , tn).
Si t1 et t2 sont des termes alors les formules t1 = t2 et t1 ! t2 sont respectivement une équation et une inéquation. Un ensemble S de telles formules est un système
(d'équations et d'inéquations).
L'affectation sylvestre X est appelée solution du système :
S = {p1 := q1, p2 := q2, … } " {s1 ! t1, s2 ! t2, . . . }
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 15 si X contient les mêmes variables que S et si X est telle que les arbres pi/X sont respectivement égaux aux arbres qi/X et que les arbres si/X sont respectivement différents des arbres ti/X.
En utilisant ces notions il est possible de représenter le premier arbre de ce paragraphe par :
("a"."b"."c".nil) / {} ou ["a","b","c"] / {} suivant la syntaxe.
Le second arbre par :
plus(fois(l2, l1), fois(l2, l1)) / {} ou
plus(x, x) / X, avec X solution de {x = fois(l2, l1)} et le troisième par :
x/X, avec X solution de { x = ou("c", et("a", et(x, "b"))) }
1 . 6 .
Les opérateurs
Les opérateurs ont été introduits dans le compilateur Prolog II+, comme un moyen souple et clair d'exprimer certains arbres.
Par exemple, la représentation interne du terme mul(sub(5, 3),add(5, 3)) étant la même que celle du terme (5 - 3) * (5 + 3), la deuxième expression est certainement plus agréable à lire que la première.
La syntaxe des expressions écrites avec des opérateurs est donnée par: expr expr n n expr expr expr
0 n n
=
=
=
=
= prefix_op n,d postfix_op infix_op n,g n,g,d
=
=
= prefix_op expr expr expr g g pterm ;
; n,d
, expr
, postfix_op
, infix_op d n,g n,g,d
;
;
, expr d
; identifier | graphic_symbol ; identifier | graphic_symbol ; identifier | graphic_symbol ;
Voir la description complète au paragraphe 1.9.
©PrologIA
R 1 - 16
Manuel de Référence
1 . 7 .
Les règles et les assertions
D'un point de vue théorique, un programme Prolog sert à définir un sous-ensemble
A dans l'ensemble R de nos arbres. Les éléments de A sont appelés assertions et l'on peut généralement associer une phrase déclarative à chacun d'eux. La figure 1.9
montre quelques exemples de telles associations. L'ensemble A des assertions est généralement infini et constitue en quelque sorte une immense banque de données.
Nous verrons plus loin que l'exécution d'un programme peut être vue comme une consultation d'une fraction de cette banque. Bien entendu cette banque ne peut être enregistrée sous une forme explicite. Elle doit être représentée à partir d'une information finie mais suffisante pour pouvoir déduire la totalité de l'information contenue dans la banque. Dans ce but, la définition de l'ensemble A des assertions est faite au moyen d'un ensemble fini de règles, chacune étant de la forme :
t0 -> t1 … tn où n peut être nul et où les ti sont des termes.
est_fils_de
/ \
Jacques Marie
pour « Jacques est le fils de Marie »
plus
pour « 2 et 2 font 4 »
/ | \ suc suc suc
| | | suc suc suc
| | |
0 0 suc
|
suc
|
0 suite_infinie
|
•
/ \
1 •
/ \
1 •
/ \
1 ...
pour « 1 1 1 ... est une suite infinie »
Figure 1.9
Une syntaxe simplifiée 4 des règles est la suivante :
A!ociation
Prolog
HERITAGE
4
On trouvera au dernier paragraphe de ce chapitre la syntaxe complète des règles.
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 17
3.1
rule E =
3.2
rule E =
3.3
rule P = term , "." ; term , ":-" , term { "," , term },
"." ; term , "->", { term }, ";" ;
Avec la contrainte fondamentale suivante :
Le terme qui est le membre gauche d'une règle doit être:
soit un identificateur
soit un tuple dont le premier argument est un identificateur
Par exemple, les termes : go, pere_de(_x,_y) ou <pere_de, _x, _y> peuvent être des têtes de règle correctes, tandis que -contrairement à ce qui se passe pour Prolog IIdes termes comme <<pere_de, _x>, _y> ou <masc.sing, x, y> ne peuvent pas l'être.
Pour le moment nous ferons abstraction de la notion de parasite, qui comme nous le verrons plus tard, est un moyen ad hoc d'appeler des sous-programmes non écrits en Prolog.
Les règles de la forme :
t0 -> t1 … tn induisent un ensemble, généralement infini, de règles particulières portant sur des arbres :
t0 / X ! t1 / X … tn / X obtenues en considérant, pour chaque règle, toutes les affectations sylvestres possibles :
X = { x1 : = s1, … , xm : = sm } qui font intervenir les variables de la règle en question.
Chacune de ces règles particulières :
r0 ! r1 … rn peut s'interpréter de deux façons :
(1) Comme une règle de réécriture :
r0 se réécrit dans la suite r1 … rn, et donc, lorsque n=0, comme :
r0 s'efface.
©PrologIA
R 1 - 18
Manuel de Référence
(2) Comme une implication logique portant sur le sous-ensemble A d'arbres:
r1, r2, … , rn éléments de A, entraîne r0 élément de A.
Dans ce cas, lorsque n = 0, l'implication se résume à :
r0 élément de A.
Suivant que l'on prend l'une ou l'autre des interprétations, les assertions se définissent par :
Définition 1 : les assertions sont les arbres que l'on peut effacer, en une ou en plusieurs étapes au moyen des règles de réécriture.
Définition 2 : les assertions forment le plus petit ensemble A d'arbres qui satisfait les implications logiques.
On peut démontrer l'existence du plus petit ensemble de la deuxième définition et l'équivalence des deux définitions.
A!ociation
Prolog
HERITAGE
1 . 8 .
Les mécanismes de base de Prolog
Nous venons de montrer quelle est l'information implicite contenue dans un programme Prolog, mais nous n'avons pas montré ce qu'est l'exécution d'un programme Prolog. Cette exécution vise à résoudre le problème suivant :
Etant donné un programme qui est une définition récursive d'un ensemble A d'assertions.
Etant donné une suite de termes T0 = t1 … tn et l'ensemble de ses variables
{ x1, . . ., xm }.
Trouver toutes les affectations sylvestres X = { x1 := r1, … , xm = rm} qui sont telles que les arbres t1/X, . . ., tn/X soient des assertions.
Pour résoudre ce problème l'ordinateur doit produire toutes les dérivations de la forme :
(T0, S0) -> (T1, S1) -> (T2, S2) -> …
Les Ti étant des suites de termes appelés buts et les Si étant des systèmes d'équations et d'inéquations ayant au moins une solution. S0 est le système vide :
{ }. On peut expliquer simplement la dérivation (Ti, Si) -> (Ti+1, Si+1) au moyen des trois lignes suivantes :
(1) (q0 q1 … qn , S)
(2) p0 -> p1 … pm
(3) ( p1 … pm q1 … qn , S ! { q0 = p0 } )
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 19
La première ligne représente la forme que doit avoir le couple (Ti , Si), la seconde la règle utilisée et la troisième le résultat (Ti+1, Si+1). Avant d'utiliser une règle, il est nécessaire de renommer ses variables pour qu'aucune d'entre elles n'apparaisse dans
(Ti ,Si). Il est aussi nécessaire de vérifier que le nouveau système Si+1 qui est obtenu en ajoutant l'équation {q0 = p0} à Si possède au moins une solution.
Traditionnellement cette vérification est appelée unification de q0 avec p0.
Le but des dérivations précédentes est de trouver un couple (Tj, Sj) pour lequel la suite Tj est vide ; ce couple est donc dérivable de (T0, { }). On peut montrer que les affectations sylvestres X qui sont solution de Sj sont les réponses à notre problème.
Le résultat imprimé par l'ordinateur est alors une forme simplifiée du système Sj dans laquelle les inéquations sont omises.
On peut également montrer que Prolog II+ vérifie parfaitement si un système d'équations et d'inéquations a au moins une solution.
D'un point de vue plus pratique, quand on lance Prolog, on se trouve dans une boucle qui lit une suite de buts T0 = t1 … tn, cherche à les effacer de toutes les manières possibles ((T0, { }) -> … -> (Tj, Sj) avec Tj vide), puis imprime les systèmes Sj correspondants. L'ordinateur écrit le caractère ">" quand il attend une suite de buts. De la même manière que nous avions une contrainte sur les règles, nous avons la contrainte suivante sur les buts :
A chaque étape, les arbres représentant le but qui va être effacé doivent avoir leur branche de gauche représentée par un identificateur.
Les parasites permettent l'utilisation de sous-programmes externes. Pour expliquer comment ces sous-programmes sont appelés, il faut se référer à la première des trois lignes qui décrit le mécanisme de base de Prolog : si q0 est un parasite, alors au lieu d'essayer d'utiliser une règle, Prolog exécute le sous-programme correspondant.
Certains parasites apparaissent dans les règles prédéfinies qui font l'interface entre des sous-programmes externes et des règles Prolog. Cet ensemble de règles prédéfinies constitue un environnement de programmation très complet permettant notamment :
• de contrôler et de modifier le déroulement d'un programme (chapitre 2 : “Le contrôle de l'effacement des buts”) ;
• de structurer et modifier l'ensemble de règles qui constituent le programme
Prolog courant (chapitre 3 : “Structuration et modification des règles”) ;
• d'avoir accès aux fonctions classiques d'arithmétique et de traitement de chaînes
(chapitre 4 : “Opérations prédéfinies sur les données”) ;
• de gérer les entrées-sorties (chapitre 5 : “Les entrées / sorties”);
• de communiquer avec le système hôte (chapitre 6 : “L'environnement”).
©PrologIA
R 1 - 20
Manuel de Référence
Il est également possible au programmeur averti d'introduire de nouvelles règles prédéfinies qui font référence à de nouveaux parasites qui seront écrits dans un autre langage que Prolog (C, F
ORTRAN
, Pascal…). ( Voir chapitre 7 : "Extensions avec des langages externes" ou Annexe D : "Ajout de règles externes (méthode des parasites)").
Deux règles prédéfinies ont un lien étroit avec le mécanisme de base de Prolog II+:
dif et eq.
La règle dif est la plus importante. C'est elle qui permet d'introduire les inéquations dans les systèmes d'équations et d'inéquations. Plus précisément, l'exécution de
dif(x,y) ajoute l'inéquation x
Si+1 a au moins une solution.
!
y au système Si et vérifie que le nouveau système
De la même manière la règle eq introduit les équations.
eq(x, x) -> ;
Les règles suivantes ne sont pas exactement des règles prédéfinies, mais tout se comporte comme si elles existaient. Elles sont utilisées pour transformer une liste de buts en un seul but.
p.q -> p q ; nil -> ;
A!ociation
Prolog
HERITAGE
1 . 9 .
La syntaxe complète de Prolog II+
Nous rassemblons ici la syntaxe complète de Prolog II+ ainsi qu'un certain nombre d'exemples et de remarques additionnelles. Les notations utilisées sont celles de la commission de normalisation de Prolog (voir le premier paragraphe de ce chapitre).
©PrologIA
A!ociation
Prolog
HERITAGE
1 . 9 . 1 .
Eléments de base
R 1 - 21
Le niveau syntaxique
1 program =
2.10 directive P =7
2.21 directive E =7
3.10 rule P =
3.21 rule E =
4.1
term P =
4.2
term E =
5 termlist =
6.12 expr n
=
6.2
expr n
6.3
expr n
6.42 expr n
=
=
6.5
expr
0
=
=
7.1
pterm =
7.23 pterm =
7.3
pterm P =
7.44 pterm =
7.55 pterm =
7.66 pterm =
7.7
pterm =
7.8
pterm =
8.1
listexpr =
8.2
listexpr =
!
rule | directive " ;
"->", ! pterm" , ";" ;
":-", expr term, "." ; expr
1199
, "." ; term , "->", ! pterm ", ";" ; expr expr
1000
, [ "." , term ] ;
1200
999
;
, ! "," , expr prefix_op expr expr expr g n,d
, expr g
, infix_op
; n,g,d
999
"
; d
; n,g
;
, expr d
; pterm ;
( identifier | variable ) , [ "(" , termlist , ")"];
( identifier | variable ) , "[" , term , "]";
"<", termlist , ">" ;
"< >", [ "(", termlist ,")" ];
"!", termlist , """ ;
"[" , listexpr , "]" | "[ ]" ; constant | "!" | "/?", integer_number ;
"(" , term , ")" ; expr expr
999
999
, [ "," , listexpr ] ;
, "|" , expr
999
;
Quand Prolog est lancé, la machine est prête à exécuter un programme, elle attend un term. Quand Prolog est passé dans un mode de compilation, c'est à dire un mode d'insertion de programme (cf. chapitre 3 de ce manuel), Prolog attend alors un
program.
Notes:
0. Définit la syntaxe en mode Prolog II.
1. Définit la syntaxe en mode Edinburgh.
2. expr expr
1
n
représente la suite de règles expr
1
,...,expr
1200 en syntaxe Edinburgh.
,...,expr
1000 en syntaxe Prolog II, et
3. La règle 7.2 exprime une syntaxe alternative pour les références aux composantes des tableaux, qui permet un accès optimisé. Par exemple, si table est le nom d'un tableau (défini par l'emploi de la règle prédéfinie def_array) alors les deux expressions table(10) et table[10] sont équivalentes. Cependant, le compilateur traduit la deuxième forme de manière plus efficace.
©PrologIA
R 1 - 22
Manuel de Référence
4. Les règles 7.3 et 7.4 définissent deux syntaxes alternatives pour les tuples. En syntaxe Edinburgh seule la deuxième alternative est possible. On a donc les
équivalences:
< x , y >
!
< > ( x , y )
5. La règle 7.5 permet de décrire des grammaires non contextuelles (en vue d'écrire en Prolog des analyseurs syntaxiques).
6. En syntaxe Prolog II, les règles 4 d'une part, et 7.6, 8.1 et 8.2 d'autre part définissent deux syntaxes alternatives pour la notation des listes. On a les équivalences suivantes :
[ a | b ]
[ a , b ]
[ a , b , c , d ]
[ a , b , c | d ]
[ ]
!
!
!
!
!
a . b a . b . nil a . b . c . d . nil a . b . c . d nil
En syntaxe Prolog II, les deux syntaxes de liste peuvent toujours être mélangées dans les programmes :
[ a , b | c.d.nil ]
!
a . [b , c , d]
7. Certains termes admis au titre de directives, ne sont pas des règles prédéfinies mais simplement des déclarations (par exemple module, end_module). Ils ne doivent pas dans ce cas être précédés de "->" ou ":-" comme il est dit dans les règles 2.1 et 2.2.
1 . 9 . 2 .
Les opérateurs
Les opérateurs permettent d'étendre dynamiquement la syntaxe des termes. On distingue les opérateurs préfixés, postfixés, et infixés.
10
11
12 prefix_op postfix_op infix_op n,d n,g n,g,d
= identifier | graphic_symbol ;
= identifier | graphic_symbol ;
= identifier | graphic_symbol ;
On notera que dans la syntaxe Prolog II, les opérateurs ne sont pas autorisés au premier niveau des termes de la queue de règle, il faut parenthéser l'expression dans ce cas. En syntaxe Edinburgh, il n'y a pas de restriction.
Le type d'opérateur est indiqué par une convention permettant de définir la précédence des opérandes en fonction de la précédence n de l'opérateur:
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 23 type précédence type opérateur préc.opérande(s) exemple fx n fy n xf n yf n xfx n xfy n yfx n op n,d op n,d op op op op op n,g n,g n,g ,d n,g ,d n,g ,d d:= n-1 d:= n g:= n-1 g:= n g:= n-1, d:= n-1 g:= n-1, d:= n g:= n, d:= n-1
- (- 1) val(1'<'2,x) a,b,c E val(1/2/3,x)
Le tableau suivant décrit les opérateurs en syntaxe prolog II+. Le tableau indique pour chaque opérateur le terme syntaxique construit.
opérateur
'<'
'\/'
*
/
mod
rem
'<<'
'>>'
^
'=<'
'>'
'>='
=\=
=:=
+
-
'/\'
**
+
1
-
1 précédence
700
700
700
700
700
700
500
500
500
500
400
400
400
400
400
400
200
200
200
200 yfx yfx yfx yfx yfx yfx yfx yfx yfx xfy xfx fx fx xfx xfx xfx yfx type xfx xfx xfx terme construit sys:inf(t1,t2) sys:infe(t1,t2) sys:sup(t1,t2) sys:supe(t1,t2) sys:'=\='(t1,t2) sys:eql(t1,t2) sys:add(t1,t2) sys:sub(t1,t2) sys:'/\'(t1,t2) sys:'\/'(t1,t2) sys:mul(t1,t2) sys:div(t1,t2) sys:mod(t1,t2) sys:rem(t1,t2) sys:'<<'(t1,t2) sys:'>>'(t1,t2) sys:'^'(t1,t2) sys:'**'(t1,t2) sys:add(t1) sys:sub(t1)
Note 1 : Les arbres correspondant aux opérateurs unaires + et - sont évalués au moment de l'analyse si leur argument est une constante entière.
Note 2: Les opérateurs peuvent être écrits avec des quotes simples. Celles-ci n'ont donc pas d'autre fonction, en Prolog II+, que d'étendre la syntaxe des identificateurs. Il n'est donc pas possible d'utiliser une notation fonctionnelle autre que celles des tuples pour un foncteur déclaré en opérateur. Lorsque l'on a un doute sur le terme construit, on peut toujours le tester en décomposant le terme:
> eq(F(X,Y),1'<'2);
{F=inf, X=1, Y=2}
©PrologIA
R 1 - 24
Manuel de Référence
1 . 9 . 3 .
Le niveau lexical
Cette syntaxe définit les mêmes unités que celles reconnues par la primitive read_unit(x,y)
.
Notes :
1. La règle L4 définit la syntaxe de base des variables; deux extensions de cette syntaxe sont données par les règles L5.
P (syntaxe Prolog II) et L5.
E (syntaxe
Edinburgh). Dans les deux cas, le principe de l'extension est le même : un certain sous-ensemble des noms qui auraient été des représentations abrégées d'identificateurs pour la syntaxe de base, est ajouté à l'ensemble des variables.
Ces deux extensions sont facultatives et incompatibles entre elles. C'est l'utilisateur qui, au démarrage de la session Prolog, choisit la syntaxe qu'il souhaite employer: se référer au Manuel d'Utilisation.
2. Certaines combinaisons sont interdites comme "/*", "*/", "|*", "*|".
3. Une option sur la ligne de commande permet de rendre l'exposant facultatif, au prix d'une ambiguïté avec les listes en syntaxe Prolog II: 1.2 est lu comme un réel en entrée. Cette règle n'est valide que si cette option est choisie. Voir le manuel d'utilisation, paragraphe 2.3.
4. La règles L2.1 et L2.2 donnent la syntaxe de base des identificateurs. Une extension pour la syntaxe Edinburgh est donnée par les règles L2.3 et L2.4.
5. La règle L2.3 est nécessaire pour exprimer le passage de l'identificateur abrégé représenté par un graphic_symbol à sa représentation complète. En effet le caractère qui délimite le préfixe de l'identificateur abrégé, étant lui-même un caractère graphique, une ambiguïté apparaîtrait si les quotes n'étaient pas ajoutées. Par exemple, sys::- est vu par l'analyseur d'unités lexicales comme: l'identificateur sys, immédiatement suivi de l'identificateur ::- et sys:':-' est vu comme: l'identificateur prédéfini :- de la famille sys.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 25
L1 unit =
L2.1 identifier =
L2.2 identifier =
L2.3
E identifier 5 =
L2.4
E identifier 4 =
L3.1 abbreviated_id =
L3.2 abbreviated_id =
L4 variable 1 =
L5.
P extended_var =
L5.
E extended_var =
L6 prefix =
L7 constant =
L8.1 integer_number =
L8.2 integer_number =
L8.3 integer_number =
L8.4 integer_number =
L8.5 integer_number =
L9 real_number =
L9.1
S real_number 3 =
];
L10 string =
L11 name =
L12 digits =
L13.
P graphic_symbol 2 =
L13.
E graphic_symbol 2 =
L14.1 comment =
L14.2 comment =
L14.3 comment = identifier | separator | variable
| constant | graphic_symbol ; prefix , prefix_limit , abbreviated_id ; abbreviated_id ; prefix , prefix_limit ,
"'", graphic_symbol, "'" ; graphic_symbol ; name - extended_var ;
"'", " (character - "'") | "' '" #, "'" ;
"_" , "alpha# | extended_var ; letter, [ (digit | "_"), "alpha# ], " "'" #!; big_letter, [ {alpha} ] ;
[ name , " prefix_limit , name # ] ; integer_number | real_number | string ; digits ;
"0b", binary_number ;
"0o", octal_number ;
"0x", hex_number ;
"0'", character ; digits, ".", digits,
("E"|"e"|"D"|"d"), [ ["+"|"-"],digits]; digits, ".", digits,
[ ("E"|"e"|"D"|"d"), [ ["+"|"-"],digits]
""" , " string_char # , """ ; letter , " alpha # ; digit , " digit # ;
"->" | graphic_c, " graphic_c | "." #;
"
graphic_c #;
"|*", " character # , "*|" ;
"/*", " character # , "*/" ;
"%", " character # , newline ;
©PrologIA
R 1 - 26
Manuel de Référence
1 . 9 . 4 .
Les caractères
Les règles dont le membre gauche est annoté par I ne sont actives que lorsque le mode d'exécution de Prolog II+ est le mode ISO(cf. § U2.3). Les règles dont le membre gauche est annoté par H ne sont actives que lorsque le mode d'exécution de
Prolog II+ est le mode hôte(cf. § U2.3).
Est désigné par host_letter, tout caractère du système hôte communément admis comme lettre et qui n'appartient pas au jeu ISO 8859-1; de la même façon, est désigné par host_graphic_char, tout caractère imprimable du système hôte n'étant ni un alpha, ni un separator, ni un special_char et inconnu du jeu ISO 8859-1.
Une description adaptée au jeu de caractères de la machine hôte sera donnée dans le manuel d'utilisation au paragraphe 3.2.
La règle C6.2 et les règles C7.2 et C8.1 représentent le même ensemble de caractères, mais elles ne sont pas valides simultanément. Celle annotée par P est valide en syntaxe Marseille, celles annotées par E sont valides en syntaxe
Edinburgh. En changeant de syntaxe, ces caractères ne jouent plus le même rôle.
La règle C5 est valide, si le caractère qui délimite dans les identificateurs complets, le préfixe et le suffixe, n'a pas été redéfini. Il peut valoir alors, un des caractères graphiques.
A!ociation
Prolog
HERITAGE
C1 big_letter =
C2 letter =
C2.1 letter H =
C2.21 letter I, i1 =
C3.1 binary_digit =
C3.2 octal_digit =
C3.3 digit =
C3.4 hex_digit =
C4 alpha =
C5 prefix_limit =
C6.1 separator =
C6.2 separator P =
C7.1 special_char=
C7.2 special_char E =
C8 graphic_c =
"A" | … | "Z" ; big_letter | "a" | … | "z"
| "À" … "ß" - "$" | "à" … "ÿ" - "÷"; host_letter;
"\", accent_escape ;
"0" | "1" ;
"0" | … | "7" ;
"0" | … | "9" ; digit | "a" | "b" | "c" | "d" | "e" | "f"
| "A" | "B" | "C" | "D" | "E" | "F"; letter | digit | "_";
":" ;
"(" | ")" | "[" | "]" | "{" | "}" |
"|" | "," ;
";" | "." | "<" | ">" ;
"%" | "'" | """ | "_" | "!" | "`" ;
";" ; graphic_char ;
©PrologIA
A!ociation
Prolog
HERITAGE
C8.1 graphic_char E =
C8.2 graphic_char =
C8.3 graphic_char H =
C9 character =
C10 string_char =
C10.1 string_char i0 =
C10.2 string_char i1 =
C10.31string_char
I, i1 =
Eléments de base
R 1 - 27
"." | "<" | ">" ;
"#" | "$" | "&" | "*" | "+" | "-" | "/" | ":"
| "=" | "?" | "\" | "@" | "^" | "~"
| NBSP … "¿" | "!" | "÷" ; host_graphic_char ; letter | digit | separator | graphic_char | special_char ; character - ( """ | "\") | """";
"\";
"\", format_escape ;
"\", accent_escape ;
C111 accent_escape = accent , accent_letter | "~a"
| "~A" | "~n" | "~N" | "~o"
| "~O" | "cc"| "CC" | "ae" | "AE"
| "BB" | "/o" | "/O" | ""y"| "'y"| "'Y"
| "-d"|"-D" | "pp" | "PP" | "oa"| "oA" ;
C123 accent =
C13 accent_letter =
C142 format_escape =
"`" | "'" | "^" | ":" ;
"a" | "e" | "i" | "o" | "u"
| "A" | "E" | "I" | "O" | "U" ;
"b" | "f" | "n" | "r" | "t" | "\"
| newline
| octal_digit, octal_digit, octal_digit
| ("x" |"X"), hex_digit, hex_digit ;
1 . 9 . 5 .
Les caractères accentués
Une option de comportement (cf. §2.3. du manuel d'utilisation) définit le mode de lecture du caractère "\". Lorsque l'interprétation du "\" est active, les règles sont annotées par i1, lorsqu'elle ne l'est pas les règles sont annotées par i0. Les règles ainsi annotées sont exclusives. Leur validité dépend de l'option choisie.
Les notes ci-après sont valides uniquement lorsque l'interprétation du "\" est active.
Notes :
1. Il existe un mode d'exécution Prolog (cf. § U2.3.) dans lequel les accent_escape ne sont pas permis et sont remplacés par les format_escape; dans ce mode, ces règles(C2.2, C10.3, C11) ne sont pas valides. Sinon les accent_escape peuvent toujours être utilisés en entrée pour spécifier des caractères accentués. En sortie
©PrologIA
R 1 - 28
Manuel de Référence
Prolog utilise un accent_escape pour les caractères accentués du jeu ISO 8859-1 n'existant pas dans le jeu de caractère du système hôte.
2. De la même manière que pour les accent_escape, les format_escape peuvent toujours être utilisés en entrée pour spécifier un caractère. En sortie Prolog utilise un format_escape pour les caractères n'existant pas dans le jeu de caractère du système hôte, et ne pouvant être représentés par un accent_escape (en mode hôte par exemple).
3. Le caractère ":" dans un accent_escape représente le diacritique tréma: cano\:e <=> canoë
6
7
4
5
2
3
0
1
8
9
10
11
12
13
14
15
128 144 160 176 192 208 224 240
À
-,D
à
Á Ñ á
Â
Ã
Ä
Å
Æ
Ç
È
É
Ê
Ë
ì Ü ì ü
í I, o í I, o
ï
î
´
Ó
,
Ò
Ô
Õ
Ö
!
Ø
Ù
Ú
Û
Y
æ
, î
ß ï
â
ã
ä
å
ç
è
é
ê
ë
´
ú
û
ø
ù
ö
÷
ô
õ
ò
ó
!
ñ
ÿ
,y
Table 1 : les caractères accentués dans le code ISO 8859-1
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Eléments de base
R 1 - 29
La table suivante donne la correspondance entre les caractères accentués et leur expression sous la forme de accent_escape . On y apprend, par exemple, que les deux chaînes de caractères : sont équivalentes.
"tel maître, tel élève" et "tel ma\^itre, tel \'el\`eve"
8
9
10
6
7
4
5
2
3
0
1
11
12
13
14
15
128 144 160 176 192 208 224 240
`A -D `a -d
'A ~N 'a ~n
^A `O ^a `o
~A 'O ~a 'o
:A ^O :a ^o oA ~O oa ~o
AE :O ae :o
CC cc
`E /O `e /o
'E `U 'e `u
^E 'U ^e 'u
:E ^U :e ^u
`I :U `i :u
'I 'Y 'i 'y
^I PP ^i pp
:I BB :i :y
Table 2 : «accent_escape» utilisés par
Prolog II+
1 . 1 0 .
Le macroprocesseur
Il est possible d'écrire en Prolog II+ quelques macros (ou alias) très simples afin d'augmenter la lisibilité des programmes. Cette fonctionnalité est réalisée au moyen d'une directive de compilation (et non un prédicat):
set_alias(i,t)
Définition d'un alias.
A partir du lieu et moment où cette directive est rencontrée, le lecteur remplace certaines (détails ci-dessous) occurrences de l'identificateur i (appelé alias) par le terme t (appelé valeur). Cette définition est désactivée en fin de compilation de plus haut niveau d'imbrication.
La valeur t doit être de type entier, réel ou chaîne de caractères.
L'alias i doit être de type identificateur.
©PrologIA
R 1 - 30
Manuel de Référence
Seuls les identificateurs en position d'argument (pas les têtes de règle) et non préfixés explicitement dans le texte sont remplacés par la valeur de l'alias. Il est ainsi possible de conserver (en le préfixant explicitement dans le texte) un identificateur de même nom abrégé qu'un alias. Il est donc aussi logique que le préfixe de l'alias soit ignoré dans la directive. Néanmoins, dans le cas où celui-ci est déjà défini, la lecture d'une directive avec un alias explicitement préfixé dans le texte évitera sa substitution, et permettra donc une redéfinition
(accompagnée d'un message d'avertissement). Le prédicat val/1 permet la définition d'un alias au moyen d'un autre alias.
Exemples commentés:
> insert;
set_alias(foo,44); set_alias(foo,55);
Ici redéfinition de 44 en 55
-> set_alias(44,55) : ARGUMENT DE MAUVAIS TYPE
> insert;
set_alias(macro1,22); set_alias(aa:macro1,44);
Ici le préfixage est un moyen de redéfinition
WARNING: macro1 DEJA DEFINIE, NOUVELLE VALEUR PRISE EN COMPTE
rg1(macro1)->; rg1(aa:macro1)->;
-> insert;
Ici, 2ème niveau d'imbrication des compilations
set_alias(macro2,val(2 * macro1));
définition à partir d'une autre macro
rg2(macro2)->; rg2(macro1)->;
; macro1(macro2)->; macro1(macro1)->;
;
{}
> rg1(I);
{I=44}
{I=aa:macro1}
> rg2(I);
{I=88}
{I=44}
Ici, on revient au 1er niveau de compilation
Ici protection automatique des têtes de règle
Ici la nouvelle valeur est bien prise en compte
Ici pas de substitution de l'identificateur préfixé
La macro définie au 1er niveau de compilation a
été prise en compte dans le second niveau
> macro1(I);
{I=88}
La macro définie au 2ème niveau de compilation a été prise en compte dans le premier niveau
{I=44}
> insert;
rg(macro1)->; rg(macro2)->;
;
{}
> rg(I);
{I=macro1}
{I=macro2}
Ici on démarre une nouvelle compilation
Les définitions ont bien été annulées
NB: Une macro apparaissant comme opérande gauche d'un opérateur infixé doit être parenthésée. Exemple:
?- insert.
set_alias(foo,44).
oper(X) :- foo >= X.
oper(X) :- (foo) >= X.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
.
list.
oper(_345) :val(foo,_348),
_348 >= _345 .
oper(_345) :-
44 >= _345 .
Eléments de base
R 1 - 31
©PrologIA
R 1 - 32
Manuel de Référence
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
2. Le contrôle de l'effacement des buts
2.1. Le contrôle
2.2. Geler
2.3. A propos des arbres infinis
2.4. Quelques conseils pour la programmation récursive
2.5. Les méta-structures
2.1. Le contrôle
A chaque étape la machine Prolog doit faire deux choix :
(1) L'un pour choisir un but dans une suite de buts à effacer. C'est le premier
élément de la suite qui est toujours choisi. C'est à dire que pour effacer une suite de buts q0 q1 … qn, on efface d'abord q0 et ensuite q1 … qn.
(2) L'autre pour choisir la règle qui sert à effacer un but b. C'est la première règle dont la tête s'unifie avec le but b qui est choisie.
Cela peut se résumer par le programme Prolog suivant :
effacer(nil) -> ; effacer(t.l) -> rule(t, q) effacer(q) effacer(l) ;
rule(t,q) est une règle prédéfinie (voir le chapitre 3) qui donne par énumération toutes les règles dont la tête s'unifie avec t et la queue avec q.
Le moyen de contrôle consiste à modifier ou à restreindre les deux choix précédents. La manière dont Prolog fait ces choix peut amener certains programmes
à boucler et par conséquent à ne pas se comporter comme on pouvait l'espérer. Les deux exemples suivants illustrent ce phénomène.
Exemple 1 : Un cas typique est celui de la transitivité. Quand on cherche à effacer
plus_grand(Jo,x) en utilisant le programme suivant, on retrouve une instance de ce même but à effacer. Le programme se met alors à boucler et se termine par un débordement 1 de pile (ou par une interruption utilisateur!). La manière correcte d'écrire ce programme consiste à enlever la récursivité à gauche.
plus_grand(Jo, Max) -> ; plus_grand(Max, Fred) -> ; plus_grand(x, y) -> plus_grand(x, z) plus_grand(z, y);
1
Si le système de réallocation automatique n'est pas désactivé au démarrage de Prolog, un certain nombre de réallocations se produiront avant le débordement.
©PrologIA
R 2 - 2
Manuel de Référence
> plus_grand(Jo, x);
{x=Max}
{x=Fred}
DEBORDEMENT
"Une bonne solution" plus_grand'(Jo, Max) -> ; plus_grand'(Max, Fred) -> ; plus_grand(x, z) -> plus_grand'(x, y) plus_grand_ou_egal(y, z); plus_grand_ou_egal(x, x) ->; plus_grand_ou_egal(x, y) -> plus_grand(x, y);
Exemple 2 : Cet exemple énumère toutes les listes construites avec 1. Avec le
(mauvais) programme ci-dessous, c'est d'abord la liste infinie qui devrait être produite; bien entendu, la bonne solution s'obtient en permutant l'ordre des deux règles.
liste_de_un(1.x) -> liste_de_un(x) ; liste_de_un(nil) -> ;
>liste_de_un(x);
DEBORDEMENT
1
La coupure “!”
Normalement Prolog essaye d'effacer une suite de buts de toutes les manières possibles. Mais si on utilise une règle contenant un «!» (ou coupure) pour effacer un but q, l'effacement de ce «!» supprimera tous les choix de règles restant à faire pour effacer ce but q. Cela restreint la taille de l'espace de recherche : on peut dire que «!» fait «oublier» les autres manières possibles d'effacer q.
Le «!» ne peut apparaître que parmi les termes qui constituent le membre droit d'une règle. Les choix qui restent à examiner et que l'effacement du «!» fait
«oublier» sont :
- les autres règles ayant la même tête que celle où le «!» figure
- les autres règles qui auraient pu être utilisées pour effacer les termes compris entre le début de la queue et le «!»
Cette question est illustrée par les exemples suivants : couleur(rouge) ->; couleur(bleu) ->; taille(grand) ->; taille(petit) ->; choix1(x.y) -> couleur(x) taille(y);
A!ociation
Prolog
HERITAGE
1
Si le système de réallocation automatique n'est pas désactivé au démarrage de Prolog, un certain nombre de réallocations se produiront avant le débordement.
©PrologIA
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 3 choix1("c'est tout") ->; choix2(x.y) -> ! couleur(x) taille(y); choix2("c'est tout") ->; choix3(x.y) -> couleur(x) ! taille(y); choix3("c'est tout") ->; choix4(x.y) -> couleur(x) taille(y) !; choix4("c'est tout") ->;
>choix1(u);
{u=rouge.grand}
{u=rouge.petit}
{u=bleu.grand}
{u=bleu.petit}
{u="c'est tout"}
>choix2(u);
{u=rouge.grand}
{u=rouge.petit}
{u=bleu.grand}
{u=bleu.petit}
>choix3(u);
{u=rouge.grand}
{u=rouge.petit}
>choix4(u);
{u=rouge.grand}
>choix1(u) !;
{u=rouge.grand}
On peut considérer le «!» comme une annotation que l'on fait à un programme pour le rendre plus efficace; bien entendu, cela ne se justifie que si on ne s'intéresse qu'à la première solution fournie par ce programme.
Des utilisations classiques du «!» sont :
" Première solution uniquement " premiere_solution_uniquement(b) -> b !;
" Si Alors Sinon " si_alors_sinon(p,a,b) -> p ! a; si_alors_sinon(p,a,b) -> b;
" non " non(p) -> p ! fail; non(p) -> ;
Dans le cas de non montré ci-dessus, il faut remarquer que l'on peut avoir des résultats inattendus si p contient des variables libres. C'est ce que montre le petit exemple suivant :
homme(Abélard) -> ; femme(x) -> non(homme(x));
>femme(Eloïse);
{}
>femme(Abélard);
>femme(x) eq(x, Eloïse);
>
©PrologIA
R 2 - 4
Manuel de Référence
^(X,Y)
X doit être une variable et Y un terme quelconque.
^(X, Y) signifie: il existe X tel que Y soit vrai, et est équivalent à un appel de Y.
L'utilisation de ce prédicat (qui est aussi un opérateur) n'a de sens que dans les prédicats bagof/3 et setof/3 , pour indiquer les variables existentielles et les retirer de l'ensemble des variables libres.
bagof(x, p, l)
Pour chaque instantiation différente de l'ensemble des variables libres du but
p, non existentielles et n'apparaissant pas dans le terme x, unifie l avec la liste de toutes les solutions x lorsqu'on efface p. Chaque liste l est construite suivant l'ordre des solutions trouvées. Exemple:
aa(2,1) -> ; aa(1,2) -> ; aa(1,1) -> ; aa(2,2) -> ; aa(2,1) -> ;
>bagof(X, aa(X,Y),L);
{Y=1, L= 2.1.2.nil}
{Y=2, L= 1.2.nil}
> bagof(X,Y^aa(X,Y),L);
{L=2.1.1.2.2.nil}
> block(e, b), block_exit(e)
block est une règle prédéfinie qui permet de terminer brutalement l'effacement d'un but b. Cette primitive est faite essentiellement pour la récupération des erreurs. On peut considérer que :
- Pour effacer block(e, b) on efface b en ayant auparavant créé une paire de parenthèses fictives, étiquetées par e, autour du but b.
- block_exit(e) provoque l'abandon immédiat de l'effacement de tous les buts inclus entre les parenthèses étiquetées par e et supprime tous les choix éventuels en attente pour ces buts. L'effacement continue ensuite normalement, après les parenthèses étiquetées par e.
De plus :
- Une étiquette est un terme Prolog quelconque, et on considère que deux parenthèses étiquetées sont identiques si leurs étiquettes respectives sont unifiables.
- S'il y a plusieurs parenthèses étiquetées par e, block_exit(e) s'arrête au couple de parenthèses le plus interne.
- Si block_exit(e) ne rencontre pas de parenthèses e, alors on revient au niveau de commande avec le message d'erreur correspondant à e, si e est un entier, ou le message 'block_exit' SANS 'block' CORRESPONDANT, si e n'est pas un entier.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 5
C'est ce même mécanisme qui est utilisé pour la gestion des erreurs dans
Prolog. Une erreur rencontrée dans l'exécution de Prolog provoque l'effacement du but block_exit(i) où i est un entier correspondant au numéro de l'erreur. De cette manière l'utilisateur peut récupérer toutes les erreurs de
Prolog, pour pouvoir les traiter dans son application.
Lorsque les optimisations de compilation sont actives (option -f o1 au lancement de Prolog), la compilation de block est optimisée et la décompilation d'un tel but ne sera pas identique mais équivalente au but d'origine. Le but block(e,b) apparaissant dans une queue de règles, sera décompilé en block(e',_,b').
La règle prédéfinie quit génère un block_exit(14). Elle est définie ainsi : quit -> block_exit(14); quit(i) -> block_exit(14,i);
A titre d'illustration voici un programme qui lit une suite de commandes :
executer_commande ->
block(fin_commande, toujours(lire_et_exec)); lire_et_exec -> outm("?") in_char'(k) exec(k); exec("q") -> block_exit(fin_commande) ; exec("f") -> block_exit(16) ; /* Interruption */ exec("n") -> ; toujours(p) -> repeter p fail ; repeter -> ; repeter -> repeter ;
Dans cet exemple, la commande "q" utilise block_exit pour retourner au niveau supérieur de executer_commande. La commande "f" simule une erreur
Prolog et rend le contrôle au block qui traite les erreurs de Prolog (puisque 16 ne peut s'unifier avec fin_commande). S'il n'y a pas de block englobant, on revient au niveau supérieur de Prolog.
block(e, c, b), block_exit(e, c)
Fonctionnement analogue aux formes précédentes, avec deux «étiquettes» e et
c à la place d'une seule. b est le but à effacer. block(e,c,b) lance l'effacement de
b; l'effacement ultérieur de block_exit à deux arguments produira la recherche d'un block dont les deux premiers arguments s'unifient avec les deux arguments de block_exit.
Lorsque les optimisations de compilation sont actives (option -f o1 au lancement de Prolog), la compilation de block est optimisée et dans certains cas la décompilation d'un tel but ne sera pas identique mais équivalente au but d'origine. Le but block(e,c,b) apparaissant dans une queue de règles, sera décompilé en block(e',c',b').
Ensemble, ces deux règles prédéfinies constituent une sorte de raffinement du mécanisme de récupération des erreurs; en pratique, e correspond au type d'erreur guetté; c correspond à un «complément d'information» rendu par le mécanisme de détection.
©PrologIA
R 2 - 6
Manuel de Référence
Un grand nombre de règles prédéfinies utilisent cette deuxième étiquette comme complément d'erreur. C'est souvent l'argument, cause de l'erreur, qui y est transmis. En phase de mise au point de programmes, cela peut paraître encore insuffisant. Dans le kit, des modules objets : dbgbase.mo,
dbggraph.mo et dbgedin.mo, sont fournis. Ils permettent, après rechargement, en cas d'erreur, d'avoir des messages encore plus complets. Le deuxième argument de block sera unifié avec un terme de la forme : prédicat d'appel ou < prédicat d'appel, complément d'erreur>
Lorsque block_exit(e,c) est effacé dans un environnement «parenthésé» par
block(e',b), alors block_exit(e,c) se comporte comme block_exit(e).
Inversement, lorsque block_exit(e) est effacé dans un environnement
«parenthésé» par block(e',c',b) alors block_exit(e) se comporte comme
block_exit(e,nil)
Erreurs et sous-sessions Prolog
Tout ce qui est dit ci-dessus concernant le mécanisme d'erreur block /
block_exit, est vrai à l'intérieur d'une même session Prolog. Lorsqu'il s'agit de transmettre une erreur à travers une ou plusieurs sous-sessions (par exemple lorsqu'un but est lancé par menu), deux restrictions sont à mentionner :
- le deuxième argument de block_exit (souvent utilisé comme complément d'erreur) n'est pas transmis. Il vaudra toujours nil.
- le premier argument de block_exit est transmis uniquement s'il est de type entier. Dans le cas contraire c'est l'entier 317, pour l'erreur :
'block_exit' SANS 'block' CORRESPONDANT, qui est propagé.
Interruption
A tout instant un programme Prolog peut être interrompu au moyen d'une touche déterminée, dépendant du système utilisé (par exemple : <Ctrl-C>).
Cette interruption est prise en compte par le mécanisme général des erreurs de
Prolog et correspond à l'erreur 16. Cette erreur peut donc être récupérée par l'utilisateur. Sinon on revient au niveau de commande avec le message d'erreur suivant : INTERRUPTION UTILISATEUR.
bound(x)
bound(x) s'efface si x est lié. Une variable est considérée comme liée si elle a
été unifiée contre :
- une constante (entier, réel, identificateur, chaîne),
- un terme de la forme t1.t2,
- un terme de la forme <t1, t2, … , tn> ou ff(t1, t2, … , tn).
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 7
dif(t1, t2)
La règle prédéfinie dif(t1, t2) est utilisée pour contraindre t1 et t2 à représenter toujours des objets différents. Dès que cette contrainte ne peut plus être satisfaite, on revient en arrière (backtracking). L'implantation de dif utilise un mécanisme similaire à geler.
Voici deux primitives intéressantes que l'on peut écrire avec dif : hors_de(x, l) qui vérifie que x n'appartiendra jamais à la liste l; et tous_differents(l) qui vérifie que les composants de la liste l seront toujours différents deux à deux.
hors_de(x, nil) -> ; hors_de(x, y.l) -> dif(x, y) hors_de(x, l); tous_differents(nil) -> ; tous_differents(x.l) -> hors_de(x, l) tous_differents(l);
Avec ces deux primitives on peut par exemple écrire un programme qui calcule des permutations : il suffit de dire que l'on a une liste d'entiers et que tous les éléments de cette liste sont différents deux à deux :
permutation(x1,x2,x3,x4) -> tous_differents(x1.x2.x3.x4.nil) chiffre(x1) chiffre(x2) chiffre(x3) chiffre(x4); chiffre(1) -> ; chiffre(2) -> ; chiffre(3) -> ; chiffre(4) -> ;
Remarquons que dans cet exemple, Prolog met d'abord en place toutes les inégalités avant d'exécuter le programme non-déterministe classique. Ceci donne une programmation plus claire et plus efficace.
L'utilisation de dif permet également l' écriture de programmes qui auraient un comportement incorrect si l'on utilisait une définition utilisant la coupure. Par exemple la définition de la relation "x est élément de l à la valeur v" peut s'écrire :
élément(x,nil,false) ->;
élément(x,x.l,true) ->;
élément(x,y.l,v) -> dif(x,y) élément(x,l,v);
> élément(x,1.2.nil,v);
{x=1, v=true}
{x=2, v=true}
{x#1, x#2, v=false}
Si l'on définissait une telle relation en utilisant la primitive !, on introduirait de nombreux comportements anormaux :
élément(x,nil,false) ->;
élément(x,x.l,true) -> ! ;
élément(x,y.l,v) -> élément(x,l,v);
> élément(x,1.2.nil,v);
{x=1, v=true}
> élément(2,4.2.3.nil,false);
{}
% une seule solution !!
% succès !!
©PrologIA
R 2 - 8
Manuel de Référence
default(t1, t2)
La règle prédéfinie default permet de réaliser le contrôle suivant: Si on peut effacer t1, alors on l'efface de toutes les manières possibles, sinon on efface
t2. Il faut remarquer que contrairement à ce que l'on pourrait penser à première vue, cette primitive ne peut pas être réalisée avec '!'. Voyons sur un exemple une utilisation de cette règle: répondre(p) -> default(p,outml("personne")); homme(jean) ->; homme(pierre) ->;
> répondre(homme(x));
{x=jean}
{x=pierre}
> répondre(femme(x)); personne
{}
eq(t1, t2)
Unification de t1 et t2 : correspond simplement à la règle :
eq(x,x) -> ; .
fail
Règle prédéfinie provoquant toujours un échec (backtracking).
findall(x, p, l)
Unifie l avec la liste de toutes les solutions x lorsqu'on efface p.
free(x)
S'efface uniquement si x n'est pas lié.
list_of(x, y, p, l)
Fournit la liste triée, sans répétition, de tous les individus qui satisfont une certaine propriété.
x est une variable.
y est une liste de variables.
p est un terme Prolog contenant au moins toutes ces variables.
Pour chaque ensemble de valeurs de y, unifie l avec la liste des valeurs de x pour que p soit vraie (c'est à dire pour que p s'efface).
L'ordre de la liste l est identique à celui défini sur les termes (cf. term_cmp/3)
Exemple : Prolog contenant la base de règles suivante : homme(grand, michel, 184) ->; homme(grand, alain, 183) ->; homme(grand, henry, 192) ->; homme(petit, nicolas, 175) ->; homme(petit, julien, 176) ->; homme(petit, gilles, 120) ->;
> list_of(x,t.nil,homme(t,x,h),l);
{t=grand, l=alain.henry.michel.nil}
{t=petit, l=gilles.julien.nicolas.nil}
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 9
not(X)
Est décrit par les règles: not(X) :- X,!,fail.
not(X).
repeat
Est décrit par les règles : repeat ->; repeat -> repeat;
setof(x, p, l)
Pour chaque instantiation différente de l'ensemble des variables libres du but
p, non existentielles et n'apparaissant pas dans le terme x, unifie l avec la liste de toutes les solutions x lorsqu'on efface p. Chaque liste l est triée et sans répétition. L'ordre de la liste l est identique à celui défini sur les termes (cf.
term_cmp/3).
Exemple : Prolog contenant la base de règles suivante : aa(2,1) -> ; aa(1,2) -> ; aa(1,1) -> ; aa(2,2) -> ; aa(2,1) -> ;
> setof(X, aa(X,Y),L);
{Y=1, L= 1.2.nil}
{Y=2, L= 1.2.nil}
> setof(X,Y^aa(X,Y),L);
{L=1.2.nil}
>
or(x,y)
Définit un ou logique transparent à la coupure. Exemple:
> op(900,xfy,or);
{}
> enum(i,4) (eq(i,1) or eq(i,2));
{i=1}
{i=2}
> enum(i,4) ([eq(i,3),'!'] or [outl(no),fail]); no no
{i=3}
>
2.2. Geler
Il s'agit de résoudre de manière efficace un problème qui se pose souvent en
Prolog : retarder le plus possible certaines décisions. Ce qui revient à dire qu'il faut avoir suffisamment d'information avant de poursuivre l'exécution d'un programme, ou que certaines variables doivent avoir reçu une valeur pour pouvoir continuer. On profite alors des avantages de la programmation déclarative, tout en gardant une exécution efficace.
©PrologIA
R 2 - 10
Manuel de Référence freeze(x, q)
Le but de cette règle prédéfinie est de retarder l'effacement de q tant que x est inconnu. Plus précisément :
(1) Si x est libre, alors freeze(x, q) s'efface et l'effacement de q est mis en attente (on dit que q est gelé). L'effacement effectif de q sera déclenché au moment où x deviendra liée.
(2) Si x est liée alors freeze lance l'effacement de q normalement.
C'est au programmeur de s'assurer qu'une variable gelée sera toujours dégelée dans le futur, pour que le but associé ne reste pas indéfiniment en attente. Ceci n'est pas vérifié par Prolog, et peut conduire à des solutions trop générales.
Remarque : Les impressions des variables d'une question sur lesquelles il reste des buts gelés se fait d'une manière spéciale :
> freeze(x,foo);
{x~foo.nil}
Voyons maintenant quelques exemples d'utilisation de freeze.
Exemple 1 : Evaluation de somme(x, y, z). On veut résoudre l'équation
z = x + y seulement si les deux variables x et y sont connues.
somme(x, y, z) -> freeze(x, freeze(y, somme1(x, y, z))) ; somme1(x, y, z) -> val(x+y, z);
Exemple 2 : Mais on peut faire mieux : On veut résoudre la même équation si deux au moins des variables x, y, z sont connues :
somme (x, y, z) -> freeze(x, freeze(y, ou_bien(u, somme1(x, y, z)))) freeze(y, freeze(z, ou_bien(u, somme2(x, y, z)))) freeze(x, freeze(z, ou_bien(u, somme3(x, y, z)))); ou_bien(u, p) -> free(u) ! eq(u, Cst) p; ou_bien(u, p) ->; somme1(x, y, z) -> val(x+y, z); somme2(x, y, z) -> val(z-y, x); somme3(x, y, z) -> val(z-x, y);
Ci-dessus, la variable u, qui devient liée dès que l'une des additions est effectuée, sert à empêcher que les autres additions, redondantes, soient calculées.
Exemple 3 : Cet exemple montre comment utiliser une variable pour contrôler un programme de l'extérieur. La règle liste_de_un qui a été donnée dans un exemple précédent, bouclait. Pour empêcher cette boucle on utilise simplement une condition externe : longueur(l, n) qui vérifie que la longueur d'une liste l est inférieure ou égale à n.
longueur(l,n) -> freeze(l,longueur'(l,n)); longueur'(nil, n) ->;
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 11
longueur'(e.l, n) ->
dif(n,0) val(n-1, n') longueur(l, n'); liste_de_un(1.x) -> liste_de_un(x); liste_de_un(nil) -> ;
> longueur(l,5) liste_de_un(l);
{l=1.1.1.1.1.nil}
{l=1.1.1.1.nil}
{l=1.1.1.nil}
{l=1.nil}
{l=nil}
Quand tous les buts ont été effacés, il se peut qu'il reste encore certains buts gelés sur des variables qui sont encore libres.
Remarque : Les optimisations de compilation ne permettent pas de déclencher les buts gelés dès l'unification de la tête de règle mais seulement sur le premier but de la queue non optimisé. En particulier, l'exemple suivant
échouera :
> insert;
nombre(<i>) -> integer(i); lier(<1>) ->;;
{}
> freeze(x, lier(x)) nombre(x);
>
En effet, les prédicats de test de type étant optimisés, le dégel de lier(<i>) ne se fera pas puisqu'il n'y a pas d'autres buts dans la queue de nombre/1.
2.3. A propos des arbres infinis
Ce paragraphe donne quelques informations pratiques sur l'utilisation dans
Prolog II des arbres infinis. Tout d'abord, il faut remarquer qu'un certain nombre de règles prédéfinies ne travaillent que sur des arbres finis. En particulier, in ne peut pas lire d'arbres infinis, et assert ne peut pas ajouter de règles contenant des arbres infinis.
infinite no_infinite
Ces règles servent à définir le type d'impression de résultat utilisé: il faut avoir activé l'option infinite si l'on désire imprimer des solutions contenant des arbres infinis.
Voici un exemple qui utilise freeze et qui vérifie qu'un arbre est toujours fini.
Ceci est un moyen indirect de faire le test dit d'occur_check. Ce programme vérifie que l'on n'a pas deux fois le même sous-arbre dans chaque branche de l'arbre.
arbre_fini(x) -> branche_finie(x, nil); branche_finie(x, l) -> freeze(x, branche_finie'(x, l)); branche_finie'(x, l) -> hors_de(x, l) domine (x, l') branches_finies(l', x.l);
©PrologIA
R 2 - 12
Manuel de Référence branches_finies(nil, l) -> ; branches_finies(x.l', l) -> branche_finie (x, l) branches_finies (l', l) ; domine(x1.x2, x1.x2.nil) -> ! ; domine(x, x') -> tuple(x) ! split(x, x') ; domine(x, nil) -> ; infinite_flag
Symbole dont la valeur (0 ou 1) indique si infinite ou no_infinite est actif.
Cette valeur peut être testée par l'intermédiaire de la règle prédéfinie val.
equations(t, t', l)
Cette règle prédéfinie sert à transformer un arbre fini ou infini t en une liste d'équations l qui a comme solution t. t' indique la variable de l représentant la racine de l'arbre t. C'est cette primitive qui est utilisée pour imprimer les solutions lorsque l'option infinite est active. Regardons une utilisation sur l'exemple suivant :
> infinite eq(x,ff(ff(x))) equations(x,t',l) out(t')
outm(" : ") outl(l) ;
v131 : eq(v131,ff(v131)).nil
{x=v131, v131=ff(v131), l=eq(t',ff(t')).nil}
> eq(x,ff(ff(x)));
{x=v120,v120=ff(v120)}
Mais une des utilisations principales de cette primitive est le fait de pouvoir ajouter des arbres infinis. Nous utilisons la primitive assert (chapitre suivant) qui permet d'ajouter une règle.
ajout_infini(t) -> equations(t,t',l) assert(arbre(t'),l) ;
> infinite eq(x, ff(ff(x))) ajout_infini(x) ;
{x=v131, v131=ff(v131)}
> list(arbre/1) ;
arbre(x11) -> eq(x11, ff(x11)) ;
{}
> arbre(x) ;
{x=v131, v131=ff(v131)}
2.4. Quelques conseils pour la programmation récursive
Le système Prolog II+ comporte un récupérateur de mémoire (garbage collector) automatique. Ce "ramasse-miettes" est très performant et est capable de ne garder dans les piles que les données dont vous avez effectivement besoin; ceci permet d'utiliser à fond les définitions récursives de programmes. Malgré tout, certaines techniques de programmation peuvent vous aider à récupérer encore plus d'espace.
Pratiquement, il s'agit d'utiliser adroitement la coupure des points de choix ainsi que de tirer un maximum de profit de l'optimisation de récursion et d'appel terminal que fait le compilateur Prolog II+. L'utilisation de ces techniques permet l'écriture de programmes qui se rappellent eux-mêmes indéfiniment, sans jamais déborder.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 13
Ceci sera plus clair à partir d'un exemple:
> insert;
répéter -> out(1) répéter ;;
{}
> répéter ;
11111111111111111111111...
est un programme tournant indéfiniment sans déborder, car la récursion se fait sur le
dernier littéral de la règle. Par contre le programme suivant débordera, car la récursion accumule les littéraux à effacer (récursion non terminale):
répéter -> out(1) répéter out(2);
> répéter ;
11111111111111111111111...
-> DEBORDEMENT
1
PILE DE RECURSIVITE
> de même le fait qu'il reste des choix à la règle exécutée accumule de l'information à garder. L'effacement du but avec la définition qui suit provoquera un débordement par accumulation de points de choix:
> insert;
répéter -> répéter ; répéter ->;;
{}
> répéter fail;
-> DEBORDEMENT PILE DE RECURSIVITE
>
En revanche le programme suivant tournera indéfiniment (heureusement on peut l'interrompre avec Control-C!):
répéter -> ;
répéter -> répéter ;
Le coupe-choix "!" permet au ramasse-miettes de récupérer davantage d'espace, en supprimant des points de choix. Il faut cependant remarquer que si on le place en fin de règle, on fait disparaître la propriété de récursion terminale:
répéter -> out(1) répéter !; répéter ->;
Le programme ci-dessus provoquera un débordement (le "!" est un littéral à effacer), alors que le programme ci-dessous tournera indéfiniment:
répéter -> out(1) ! répéter ; répéter ->;
1
Si le système de réallocation automatique n'est pas désactivé au démarrage de Prolog, un certain nombre de réallocations se produiront avant le débordement.
©PrologIA
R 2 - 14
Manuel de Référence
2.5. Les meta-structures
2.5.1. Création
Il est possible en PrologII+ d'attacher à une variable une liste de termes. On parlera alors de méta-structure ou variable attribuée et de termes attribués.
La particularité de ces variables est que l'unification avec un terme connu ou bien avec une autre variable attribuée est remplacée par des appels à des prédicats utilisateurs.
En effet, une tentative d'unification d'une variable attribuée avec un terme connu est remplacée par autant d'appels que de termes dans la liste attachée à cette variable. De la même manière, une tentative d'unification de deux variables attribuées se traduira par des appels successifs à tous les prédicats contenus dans chacune des deux listes.
Les paramètres de chacun de ces prédicats sont imposés: le premier paramètre sera la variable attribuée elle même, le second sera le terme avec lequel on essaie de l'unifier, et le troisième sera le terme attribué correspondant dans la liste.
Le lien d'un terme avec une variable peut s'effectuer au moyen de deux prédicats prédéfinis: new_tlv/3 et add_tlv/3 (TLV: Term Linked to Variable).
new_tlv(v, t, i)
Attache à la variable v le terme t. La liste de termes attribués à la variable v sera donc réduite à un seul élément: le terme t. Si v était déja une variable attribuée, l'ancienne liste est perdue. Si v n'est pas une variable, une erreur est générée.
Le troisième argument i doit être un identificateur: il s'agit du nom de la règle utilisateur décrite plus haut (d'arité 3) qui sera appelée lors d'une tentative d'unification de la variable v avec un terme connu ou bien avec une autre variable attribuée.
add_tlv(v, t, i)
Ajoute à la variable v le terme t. La liste de termes attribués à la variable v sera donc augmentée d'un élément: le terme t. Si v n'était pas déja une variable attribuée, ce prédicat a le même effet que le précédent. Si v n'est pas une variable, une erreur est générée. Le troisième argument i a la même signification que précédemment.
L'ordre dans lequel les prédicats utilisateurs seront appelés n'est pas spécifié.
Exemple:
> insert; rule1(V,T,A) -> outm("Rule1 ") out(T) outm(" ") outl(A); rule2(V,T,A) -> outm("Rule2 ") out(T) outm(" ") outl(A); rule3(V,T,A) -> outm("Rule3 ") out(T) outm(" ") outl(A);
;
{}
> new_tlv(V,1.2.nil, rule1) add_tlv(V,3.4.nil,rule2)
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 15
add_tlv(V,5.6.nil,rule3) eq(V,foo);
Rule1 foo 1.2.nil
Rule2 foo 3.4.nil
Rule3 foo 5.6.nil
{V@rule1(1.2.nil).rule2(3.4.nil).rule3(5.6.nil).nil}
Une combinaison de ces deux prédicats peut être simplement obtenue par un appel au prédicat :
set_tlv(v, l)
où l doit être une liste d'éléments de la forme i(t), i et t ayant la même signification que dans les prédicats précédents. Si la liste l comporte N
éléments, l'appel à ce prédicat est équivalent à un premier appel à new_tlv/3, suivi de N-1 appels à add_tlv/3.
Exemple:
> set_tlv(X,foo(1).aa(2).nil);
{X@foo(1).aa(2).nil}
2.5.2. Récupération
La liste des termes attribués à une variable peut être obtenue au moyen du prédicat
get_tlv(v, l)
Unifie l avec la liste formée des termes attribués à la variable v, chacun de ces termes étant encapsulé par le prédicat utilisateur appelé lors d'une tentative d'unification de cette variable avec une autre variable attribuée ou un terme connu. Cette liste l est donc de la forme: id1(t1).id2(t2)....nil avec l'association de chaque identificateur id (prédicat utilisateur) au terme correspondant. Si v n'est pas une variable, l est unifiée avec nil.
Exemple:
> new_tlv(V,1.2.nil, rule1) add_tlv(V,3.4.nil,rule2)
add_tlv(V,5.6.nil,rule3) get_tlv(V,T);
{V@rule1(1.2.nil).rule2(3.4.nil).rule3(5.6.nil).nil,
T=rule1(1.2.nil).rule2(3.4.nil).rule3(5.6.nil).nil}
2.5.3. Unification forcée
Une unification classique (sans appel aux prédicats utilisateurs) peut être forcée entre une variable attribuée et un terme connu ou bien une autre variable attribuée.
Cette unification s'effectue au moyen d'un prédicat spécial:
unify_tlv(t1, t2)
Réalise une unification ordinaire entre les deux termes t1 et t2. Voyons tous les cas possibles:
- Si ni t1 ni t2 ne sont des variables attribuées, est équivalent à eq/2.
- Si t1 est une variable attribuée:
©PrologIA
R 2 - 16
Manuel de Référence
- Si t2 est une variable ordinaire, t2, unifié avec t1, deviendra donc une variable attribuée.
- Si t2 est un terme connu, t1, unifié avec t2 deviendra donc ce terme connu (la liste de termes attribués sera alors perdue).
- Si t2 est aussi une variable attribuée, après unification t1 et t2 désigneront une variable attribuée ayant pour liste de termes attribués la concaténation des deux listes initiales (celle de t1 et celle de t2).
Exemple:
> new_tlv(V,1.2.nil,foo) unify_tlv(V,3);
{V=3}
> new_tlv(V,1.2.nil,foo) unify_tlv(Z,V);
{V=Z,V@foo(1.2.nil).nil,Z@foo(1.2.nil).nil}
> new_tlv(V,1.2.nil,foo) new_tlv(X,3.4.nil,faa)
unify_tlv(V,X);
{V=X,V@foo(1.2.nil).faa(3.4.nil).nil,
X@foo(1.2.nil).faa(3.4.nil).nil}
2.5.4. Sorties
Les impressions des variables attribuées d'une question se fait d'une manière spéciale : <nom de variable>@<terme>
Le terme qui suit @ est la liste des termes attribués à la variable, présentée de manière identique au prédicat get_tlv/2.
Exemple:
> new_tlv(x,zz,foo);
{x@foo(zz).nil}
2.5.5. Exemple
Ce programme attache à une variable l'ensemble de ses valeurs possibles. S'il ne reste plus qu'une seule valeur, elle est affectée à la variable.
insert;
% Si X est une nouvelle variable, lui attache l'ensemble
% de valeurs L, sinon lui attache l'intersection de L avec
% l'ensemble déja attaché be_in(X,L) -> get_tlv(X,L1) be_in(L1, X, L); be_in(nil,X,L) -> ! new_set(X,L); be_in(L1,X,L) -> get_values(L1, L2) inters(L,L2,L3) attach_or_affect(X, L3);
% attache à X la liste L de ses valeurs possibles new_set(X,L) -> new_tlv(X,L,verify_in);
% Récupère la liste des valeurs possibles get_values([],[])->!; get_values(t_ag(L1).L2, L)-> get_values(L2,L3) conc(L1,L3,L);
% concatenation de 2 listes conc(nil,L,L)->;
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Le contrôle de l'effacement des buts
R 2 - 17
conc(X.L1,L2,X.L3) -> conc(L1,L2,L3); attach_or_affect(X,[F]) -> !
unify_tlv(X,F);% une seule valeur possible -> on affecte attach_or_affect(X,L) -> dif(L,nil) new_set(X,L); % nouvel ensemble possible
% Le 3ème argument est l'intersection des 2 listes inters([],L,[])->; inters(x.l,l1,x.l2)-> member(x,l1) ! inters(l,l1,l2); inters(x.l,l1,l2)-> inters(l,l1,l2);
% Predicat utilisateur verify_in(V_arlibc, T_unifie, T_att) -> member(T_unifie,T_att) unify_tlv(V_arlibc, T_unifie);
% End of insertion ;
{}
> % quelques exécutions
be_in(x,[1,2,3,4]) be_in(x,[1,3,7]) be_in(x,[1,7,8]);
{x=1}
> be_in(x,[4,5,6]) be_in(x,[7,8,9]) ; % Echec
> be_in(x,[1,2,3,4,5]) be_in(y,[1,6,7,8]) eq(x,y);
{x=1,y=1}
> be_in(x,[1,2,3,4,5]) be_in(y,[1,2,6,7,8]) eq(x,y);
{x=1,y=1}
{x=2,y=2}
©PrologIA
R 2 - 18
Manuel de Référence
A!ociation
Prolog
HERITAGE
©PrologIA
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
A!ociation
Prolog
HERITAGE
4. Opérations prédéfinies sur les données
4.1. Les tests de type
4.2. Les opérations arithmétiques
4.3. Affectation
4.4. Opérations sur les chaînes
4.5. Composition et décomposition d'objets
4.6. Comparaison de termes quelconques
4.1. Les tests de type
Ces règles permettent de connaître le type d'un objet. Si l'argument est une variable libre, ces règles n'ont pas de sens et échouent.
ident(t)
Vérifie que t est un identificateur.
integer(t)
Vérifie que t est un entier.
real(t)
Vérifie que t est un réel.
double(t)
Identique à real(t).
dot(t)
Vérifie que t est de la forme t1.t2 ou [ t1 | t2 ].
string(t)
Vérifie que t est une chaîne.
tuple(t)
Vérifie que t est un terme de la forme f(t1 … tn) ou <t1 … tn>.
Les primitives suivantes concernent l'«état» des variables et ont donc un esprit voisin de celui des tests de type :
bound(x)
bound(x) s'efface si x est lié. Une variable est considérée comme liée si elle a
été unifiée contre :
©PrologIA
R 4 - 2
Manuel de Référence
- une constante (entier, réel, identificateur, chaîne),
- un terme de la forme t1.t2,
- un terme de la forme <t1, t2, … , tn> ou ff(t1, t2, … , tn).
free(x)
S'efface uniquement si x n'est pas lié.
var_time(x,n)
Date de création d'une variable.
x doit être une variable libre. Cette règle prédéfinie unifie n avec la «date de création» de la variable x : il s'agit d'un nombre entier qui identifie de manière unique la variable; deux variables ont la même date si et seulement si elles sont liées entre elles (c'est ce même nombre qui est utilisé par le compilateur pour l'impression des variables libres -- voir l'exemple ci-dessous).
Exemple:
>eq(x,y) var_time(x,_tx) var_time(y,_ty) var_time(z,_tz)
out(x.y.z) line;
v35.v35.v172
{y=x, _tx=35, _ty=35, _tz=172}
>
La date de création d'une variable correspond à une notion du «temps» qui est sensiblement la même que celle de l'horloge Prolog : le temps avance lors de l'effacement des buts et recule avec le backtracking. Dans l'exemple suivant, la variable x est créée trois fois; on constate qu'elle possède bien la même date de création:
>enum(i,3) var_time(x,t);
{i=1,t=35}
{i=2,t=35}
{i=3,t=35}
>
Attention, la valeur de la date de création des variables n'est pas invariante lors des tassages des piles par le garbage collector.
A!ociation
Prolog
HERITAGE
4.2. Les opérations arithmétiques
L'évaluation d'une expression est faite au moyen de la règle prédéfinie val. Le résultat d'une évaluation est soit un entier, soit un réel, soit un identificateur, soit une chaîne. Les booléens sont représentés par les entiers 0 et 1.
©PrologIA
A!ociation
Prolog
HERITAGE
Opérations prédéfinies sur les données
R 4 - 3
val(t1, t2) tval(t1, t2)
Evaluent l'expression t1 et unifient le résultat avec t2. L'expression à évaluer est construite récursivement à partir des constantes, des identificateurs, des
éléments de tableau et des fonctions évaluables. Les optimisations (si elles n'ont pas été désactivées au lancement de Prolog) du code généré pour l'appel au prédicat val ne permettent pas, pour la décompilation ou la mise au point, de restituer le terme d'origine mais un terme équivalent. De plus elles suspendent momentanément le mécanisme de gestion automatique des espaces et des piles. Le prédicat tval lui, n'est pas optimisé. On pourra le préférer à val dans les cas de programmes consommateurs d'espace où val manipule fréquemment des termes complexes. On évitera ainsi d'avoir à faire une gestion "manuelle" de la mémoire.
Exemples:
>val(add(mul(2, add(3, 4)), 1000), x);
{x=1014}
>val(add(mul(2e0, add(3, 4e0)), 1000), x);
{x=1.014000e+03}
>val(2 * (3 + 4) + 1000, x);
{x=1014}
Les fonctions arithmétiques peuvent avoir des arguments de types différents, dans ce cas il y a une conversion automatique des types en type le plus général (les types sont, par ordre décroissant: réel, entier). Quand une fonction
à évaluer a un nombre incorrect d'arguments ou quand certains de ses arguments sont du mauvais type, val produit une erreur. Cette erreur est récupérable par la règle prédéfinie block.
• La valeur d'un nombre ou d'une chaîne est égale à ce nombre ou à cette chaîne.
• La valeur d'un tableau (externe ou interne) indicé est égale à la valeur de l'élément correspondant de ce tableau. Exemple:
>def_array(tab,100) assign(tab[50],3);
{}
>val(tab[50],_x);
{_x=3}
• La valeur d'un identificateur i est définie comme suit :
(1) si un terme t a été affecté à i (au moyen de la règle assign : voir le paragraphe suivant) alors la valeur de i est t.
(2) si i n'a pas fait l'objet d'une affectation préalable, alors la valeur de i est i lui-même.
Exemple :
>assign(un, 1);
{}
>val(un, x) val(deux, y);
{x=1,y=deux}
©PrologIA
R 4 - 4
Manuel de Référence
Certaines fonctions évaluables peuvent aussi être exprimées à l'aide d'opérateurs.
Les fonctions évaluables sont :
add(t1, t2) ou t1 + t2 valeur(add(t1, t2)) = valeur(t1) + valeur(t2).
Les valeurs de t1 et t2 doivent être de type entier ou réel.
sub(t1, t2) ou t1 - t2 valeur(sub(t1, t2)) = valeur(t1) - valeur(t2).
Les valeurs de t1 et t2 doivent être de type entier ou réel.
mul(t1, t2) ou t1 * t2 valeur(mul(t1, t2)) = valeur(t1) * valeur(t2).
Les valeurs de t1 et t2 doivent être de type entier ou réel.
div(t1, t2) ou t1 / t2 valeur(div(t1, t2)) = valeur(t1) / valeur(t2).
Les valeurs de t1 et t2 doivent être de type entier ou réel.
mod(t1, t2) ou t1 mod t2 valeur(mod(t1, t2)) = valeur(t1) modulo valeur(t2).
mod (modulo) se différencie de rem (remainder) dans le cas où les opérandes
t1 et t2 sont de signes contraires. En effet dans ce cas, le second opérande (t2) est ajouté au reste de la division entière de t1 par t2 (remainder).
Les valeurs de t1 et t2 doivent être de type entier.
rem(t1, t2) ou t1 rem t2 valeur(rem(t1, t2)) = reste de la division entière de t1 par t2.
Les valeurs de t1 et t2 doivent être de type entier.
eql(t1, t2) ou t1 =:= t2 ou =:=(t1, t2) valeur(eql(t1, t2)) = si valeur(t1) = valeur(t2) alors 1 sinon 0.
Si t1 et t2 sont de type arithmétique, la conversion automatique des types est appliquée avant l'évaluation.
'=\='(t1, t2) ou t1 =\= t2 valeur(=\=(t1, t2)) = si valeur(t1) ! valeur(t2) alors 1 sinon 0.
Si t1 et t2 sont de type arithmétique, la conversion automatique des types est appliquée avant l'évaluation.
inf(t1, t2) ou t1 '<' t2 valeur(inf(t1, t2)) = si valeur(t1) < valeur (t2) alors 1 sinon 0.
Pour les entiers et les réels, on prend la relation "<" entre les nombres. Pour les chaînes, on prend l'ordre alphabétique et pour les identificateurs, on prend l'ordre alphabétique sur les chaînes associées.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Opérations prédéfinies sur les données
R 4 - 5
infe(t1, t2) ou t1 '=<' t2 valeur(infe(t1, t2)) = si valeur(t1) " valeur (t2) alors 1 sinon 0. Cf. inf.
sup(t1, t2) ou t1 '>' t2 valeur(sup(t1, t2)) = si valeur(t1) > valeur (t2) alors 1 sinon 0. Cf. inf.
supe(t1, t2) ou t1 '>=' t2 valeur(supe(t1, t2)) = si valeur(t1) # valeur (t2) alors 1 sinon 0. Cf. inf.
if(t, t1, t2) valeur(if(t, t1, t2)) = si (valeur(t)!0) alors valeur(t1) sinon valeur(t2).
sign(t)
valeur (sign(t))= si valeur(t) = 0 alors 0, si valeur(t) < 0 alors -1, sinon 1.
La valeur de t doit être de type entier ou réel.
ceiling(t)
valeur (ceiling(t))= - (partie entière(-valeur(t)))
La valeur de t doit être de type entier ou réel. Le résultat est de type entier.
floor(t)
valeur (floor(t)) = partie entière(valeur(t)) .
La valeur de t doit être de type entier ou réel. Le résultat est de type entier.
round(t)
valeur(round(t))= partie entière(valeur(t)+0.5).
La valeur de t doit être de type entier ou réel. Le résultat est de type entier.
trunc(t)
valeur(trunc(t)) = conversion en entier de la valeur de t.
La valeur de t doit être de type entier ou réel.
float(t)
valeur(float(t)) = conversion en réel de la valeur de t.
La valeur de t doit être de type entier ou réel.
double(t)
valeur(double(t)) = conversion en réel de la valeur de t.
La valeur de t doit être de type entier ou réel.
abs(t)
valeur(abs(t)) = valeur absolue de la valeur de t.
La valeur de t doit être de type entier ou réel.
Les fonctions suivantes doivent être appliquées à des arguments de type entier. Elles donnent un résultat de type entier.
©PrologIA
R 4 - 6
Manuel de Référence
'/\'(t1,t2)
valeur('/\'(t1,t2)) = 'et' bit à bit entre t1 et t2.
'\/'(t1,t2)
valeur('\/'(t1,t2)) = 'ou' bit à bit entre t1 et t2.
'<<'(t1,t2)
valeur('<<'(t1,t2)) = t1 décalé de t2 bits vers la gauche.
'>>'(t1,t2)
valeur('>>'(t1,t2)) = t1 décalé de t2 bits vers la droite.
'~'(t)
valeur('~'(t)) = complément bit à bit de t.
Les fonctions suivantes doivent être appliquées à des entiers ou des réels et donnent un résultat de type réel. Les fonctions trigonométriques travaillent avec des angles exprimés en radians.
atan(t)
valeur (atan(t)) = arc tangente(valeur(t)).
cos(t)
valeur (cos(t)) = cosinus(valeur(t)).
exp(t)
valeur (exp(t)) = exponentielle(valeur(t)).
ln(t)
valeur(ln(t)) = logarithme népérien(valeur(t)).
rad(t)
valeur(rad(t)) = conversion en radian(valeur(t)).
sin(t)
valeur(sin(t)) = sinus(valeur(t)).
sqrt(t)
valeur(sqrt(t)) = racine carrée(valeur(t)).
tan(t)
valeur(tan(t)) = tangente(valeur(t)).
t1 ** t2 valeur(t1 ** t2) = valeur(t1) à la puissance valeur(t2).
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Opérations prédéfinies sur les données
R 4 - 7
4.3. Affectation
assign(i, t) tassign(i, t) cassign(i, t)
Affectent le terme t à l'identificateur i, t peut être un terme Prolog quelconque.
Tout se passe comme si i devenait le nom d'une variable «globale»
(ultérieurement accessible pendant l'effacement de n'importe quel but) et
«statique» (résistante au backtracking) possédant t pour valeur. Il s'agit donc bien de l'affectation classique, comme elle se pratique dans F
ORTRAN
, Pascal, etc…. Les optimisations (si elles n'ont pas été désactivées au lancement de
Prolog) du code généré pour l'appel au prédicat assign suspendent momentanément le mécanisme de gestion automatique des espaces et des piles. Le prédicat tassign lui, n'est pas optimisé. On pourra le préférer à
assign dans les cas de programmes consommateurs d'espace où assign manipule fréquemment des termes complexes. On évitera ainsi d'avoir à faire une gestion "manuelle" de la mémoire. Le prédicat cassign est similaire au prédicat tassign mais permet de conserver les contraintes (dif, freeze) et les attributs (variables attribuées) associés aux variables du terme t.
Exemple :
>assign(nom_fichier, "monfichier.txt");
{}
>val(nom_fichier,x)
{x="monfichier.txt"}
>
En Prolog, ces variables statiques peuvent être vues comme une manière particulièrement efficace de réaliser des assertions (ou règles sans queue). Du point de vue de l'utilisateur, on peut considérer que l'emploi de assign et val fait ci-dessus est conceptuellement équivalent à:
>retract(nom_fichier(x), nil);
>assert(nom_fichier("monfichier.txt"), nil);
{}
>nom_fichier(x);
{x="monfichier.txt"}
>
Exemple : possibilité d'affecter un terme quelconque, et non uniquement des constantes.
>assign(myterm, jean.<34,"rue blanche">.A_utres_infos);
{}
>val(myterm,x) val(myterm,y);
{x=jean.<34,"rue blanche">.v64,y=jean.<34,"rue
blanche">.v65}
>
assign(tab(i), t) ou assign(tab[i], t)
tassign(tab(i), t) ou tassign(tab[i], t)
Affectent le terme t à l'élément de rang i du tableau tab. Ce tableau doit avoir
été défini au préalable avec def_array.
©PrologIA
R 4 - 8
Manuel de Référence
Pour l'accès à des éléments de tableaux (dans val) les notations tab(i) et tab[i] sont équivalentes; cependant, elles ne sont pas représentées par les mêmes termes Prolog, et dans le cas de l'utilisation de tab[i] le compilateur optimise l'accès au tableau. Pratiquement, on réservera la notation tab[i] pour la désignation d'éléments de tableau uniquement. Nous ferons également la même remarque que précédemment concernant les optimisations : assign est optimisé, tassign ne l'est pas.
backtrack_term
Tableau prédéfini du module sys, de longueur 100 (indices possibles entre 1 et 100) qui peut être manipulé à l'aide des prédicats prédéfinis val ou tval et
assign ou tassign. Les valeurs qui lui sont assignées sont perdues au backtracking. Chaque élément du tableau est initialisé à 0. Exemple:
> insert;
test(i) -> assign(backtrack_term[1],111) val(backtrack_term[1],i); test(i) -> val(backtrack_term[1],i);
;
{}
> test(i);
{i=111}
{i=0}
def_array(i, n)
Définit dynamiquement un tableau de termes Prolog de nom i et de taille n. Ce tableau se comportera comme une variable «globale» (ultérieurement accessible pendant l'effacement de n'importe quel but) et «statique» (résistante au backtracking). Chaque élément du tableau est initialisé avec l'entier 0. Les valeurs légales de l'indice sont incluses dans 1..n.
Si un tableau de même nom existe déjà :
- s'il s'agit d'un tableau de même taille, il ne se passe rien
- si les tailles diffèrent, il se produit une erreur
L'accès et l'affectation sont analogues à ceux des tableaux des autres langages de programmation : voir ci-dessus val pour l'accès et assign pour l'affectation.
Le tableau est désalloué lorsqu'on tue le module auquel il appartient (c.à.d. les règles ayant le même préfixe) ou bien lorsqu'on exécute le prédicat kill_array.
"gestion d'une pile" inc(i) -> val(i + 1, x) assign(i, x); dec(i) -> val(i - 1, x) assign(i, x); initialise -> assign(pointeur,0) def_array(pile,100); empile(v) ->
inc(pointeur)
val(inf(pointeur,100),1)
!
val(pointeur,p)
assign(pile[p], v); empile(v) -> outml("débordement de la pile") fail;
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Opérations prédéfinies sur les données
R 4 - 9
depile(v) ->
val(eql(pointeur,0),0)
!
val(pile[pointeur],v)
dec(pointeur); depile(v) -> outm("pile vide") line fail;;
>initialise;
{}
>empile(12345);
{}
>empile(23456);
{}
>depile(x) depile(y);
{x=23456, y=12345}
>depile(x);
redef_array(i, n)
Analogue à def_array mais, si le tableau de nom i existe déjà, redéfinit sa taille: n, conserve les affectations existantes pour les indices valides, et initialise s'il y a lieu les nouveaux éléments.
is_array(i,t)
Vérifie que l'identificateur i désigne bien un tableau et unifie t avec sa taille.
kill_array(i)
Détruit le tableau de nom i. Si le tableau n'est pas défini, le prédicat s'efface en imprimant éventuellement 1 un warning.
4.4. Opérations sur les chaînes
char_code(c, n)
Fait correspondre au caractère c son code interne n et vice-versa. Ce code peut
être un code ISO 2 ou le code de la machine hôte. Si c est un identificateur dont la représentation (hormis les quotes) est limitée à un caractère, le code correspondant est unifié avec n.
Exemple :
>char_code("A",n) char_code('A',n);
{n=65}
>char_code(c,65);
{c="A"}
1
Cela dépend du niveau de warning choisi par une option au lancement de Prolog.
2Il s'agit du code ISO 8859-1 (voir la table complète en annexe F). Ce jeu est le jeu ASCII américain étendu sur 8 bits avec des caractères accentués.
©PrologIA
R 4 - 10
Manuel de Référence
conc_string(s1, s2, s3)
Enumère tous les triplets s1, s2, s3 de chaînes telles que s3 est la concaténation de s1 et s2. s1, s2, s3 doivent être suffisamment connus pour produire des ensembles finis de triplets, c'est à dire soit le troisième argument connu, soit deux des trois chaînes connues.
Exemple :
>conc_string("ab", "cd", s);
{s="abcd"}
>conc_string(s1, s2, "abcd");
{s1="",s2="abcd"}
{s1="a",s2="bcd"}
{s1="ab",s2="cd"}
{s1="abc",s2="d"}
{s1="abcd",s2=""}
substring(s1, i, j, s2)
Extrait de la chaîne s1 une sous-chaîne commençant à la position i, de longueur j et essaie de l'unifier avec s2.
Exemple :
>substring("1234567890", 3, 4, x);
{x="3456"}
find_pattern(s1, s2, n)
Unifie n avec la position du début de la chaîne s2 dans la chaîne s1. Si s2 n'est pas trouvé, alors l'effacement de find_pattern échoue.
Exemple :
>find_pattern("1234567890", "3456", p);
{p=3}
>find_pattern("1234567890", "abcd", p);
>
Note:
Cette primitive est définie dans le module externe prouser.c, la modification inconsidérée du source correspondant peut être responsable de son malfonctionnement. Dans le source livré, la longueur des chaînes est limitée à 256 caractères.
A!ociation
Prolog
HERITAGE
4.5. Composition et décomposition d'objets
arg2(n, t1, t2)
Unifie t2 avec la longueur ou l'élément de rang n d'un n-uplet, d'une liste ou d'une chaîne.
(1) Si t1 est une chaîne alors si
n=0, t2 = longueur(t1)
n!0, t2 = nième caractère de la chaîne t1.
©PrologIA
A!ociation
Prolog
HERITAGE
Opérations prédéfinies sur les données
R 4 - 11
(2) Si t1 est une liste alors si
n=0, t2 = nombre d'éléments de la liste
n!0, t2 = nième élément de la liste t1.
(3) Si t1 est un n-uplet alors si
n=0, t2 = nombre d'arguments du n-uplet
n!0, t2 = nième argument du n-uplet t1.
Exemples :
>arg2(0,"abcdef",x);
{x=6}
>arg2(3,"abcdef",x);
{x="c"}
>arg2(0,aa.bb.cc.dd.ee.ff.nil,x);
{x=6}
>arg2(3,aa.bb.cc.dd.ee.ff.nil,x);
{x=cc}
>arg2(0,<aa,bb,cc,dd,ee,ff>,x);
{x=6}
>arg2(3,<aa,bb,cc,dd,ee,ff>,x);
{x=cc}
arg(n, t1, t2)
La primitive correspondante de l'interpréteur Prolog II a été renommée arg2 dans le compilateur. arg/3 est une règle prédéfinie du mode Edinburgh.
conc_list_string(l, s)
Concatène les chaînes de la liste l pour créer la chaîne s.
Exemple :
>conc_list_string("How "."are "."you "."?".nil, x);
{x="How are you ?"}
copy_term(t1, t2)
Fait une copie du terme t1 en remplaçant chaque variable libre (contrainte ou pas) par une nouvelle variable sans contrainte et unifie cette copie avec t2.
C'est à dire que les deux termes sont représentés par le même arbre, aux noms de variables près. t1 est un terme prolog quelconque, voire infini.
Exemple :
>copy_term(one(2.x,y), t);
{t=one(2.v33,v31)}
copy_term_with_constraints(t1, t2)
Analogue au prédicat copy_term/2 mais copie les contraintes sur les variables.
©PrologIA
R 4 - 12
Manuel de Référence
enum(i, k1, k2)
enum(i, k2)
k1 et k2 doivent être des entiers connus. Si k1 > k2 alors on a un échec. Par définition, cette règle s'efface de k2-k1+1 manières : la première consiste à unifier i et k1; la deuxième consiste à unifier i et k1+1, etc…; la dernière consiste à unifier i et k2.
Exemple :
>enum(i,5,9);
{i=5}
{i=6}
{i=7}
{i=8}
{i=9}
La deuxième forme, enum(i,k), équivaut à enum(i,1,k).
gensymbol(i)
Crée un nouvel identificateur de la forme idn, où n est un entier, et l'unifie avec
i.
list_string(l, s)
Compose une chaîne s à partir d'une liste l de caractères.
Exemple :
>list_string("H"."e"."l"."l"."o".nil, x);
{x="Hello"}
list_tuple(l, t)
Compose un n-uplet t à partir de la liste d'arguments l.
Exemple :
>list_tuple(111.aaa.222."Hello".nil,x);
{x=<111,aaa,222,"Hello">}
member(x,l)
Unifie successivement x avec chaque élément de la liste l. Est décrit par les règles : member(x, x.l) ->; member(x, _.l) -> member(x, l);
setarg(n, t1, t2)
Remplace l'élément de rang n du terme t1 par le terme t2. t1 doit être une liste ou bien un n-uplet. Le terme initial est restauré sur un backtracking.
Exemple:
> reinsert;
newarg(55) ->; newarg(66) ->;;
{}
> eq(x, 1.2.3.nil) newarg(y) arg2(2,x,e1) setarg(2,x,y)
arg2(2,x,e2);
{x=1.55.3.nil, y=55, e1=2, e2=55}
{x=1.66.3.nil, y=66, e1=2, e2=66}
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Opérations prédéfinies sur les données
R 4 - 13
split(t, l)
Décompose une chaîne ou un n-uplet en la liste de ses composants. Au moment de l'effacement de split(t, l), t doit être connu; on a alors :
Si t est une chaîne, l est unifié avec la liste de ses caractères.
Si t est un n-uplet, l est unifié avec la liste des arguments de t.
Sinon échec.
Exemple :
>split("abcde", x);
{x="a"."b"."c"."d"."e".nil}
>split(plusgrand(Jean, Pierre.Marie), x));
{x=plusgrand.Jean.(Pierre.Marie).nil}
string_ident(s1, s2, i)
string_ident(s, i)
Etablit la correspondance entre un identificateur et sa représentation sous forme de chaîne. Si l'identificateur n'existe pas, il est créé. Dans la forme à trois arguments, fait correspondre aux chaînes s1 et s2 l'identificateur dont le préfixe est s1 et la représentation abrégée s2, et vice-versa. Dans la forme à deux arguments, utilise le contexte courant de lecture/écriture pour établir la relation entre l'identificateur i et sa représentation s. s peut être une représentation complète ou une représentation abrégée.
Exemple :
>string_ident(p, i, invites:Jean);
{p="invites",i="Jean"}
>string_ident("invites","Jean",x);
{x=invites:Jean}
Exemple dans le contexte standard :
>string_ident(s,sys:outm);
{s="outm"}
string_integer(s, n)
Fait correspondre la chaîne s à l'entier n et vice-versa.
>string_integer(s,123);
{s="123"}
>string_integer("-0123",n);
{n=-123}
string_real(s, r)
Fait correspondre la chaîne s au réel r et vice-versa.
>string_real(s,12.34e5) string_real(s,x);
{s="1.234000000000000e+06",x=1.234000000000000e+06}
>string_real(s,1d110) string_real(s,x);
{s="1.000000000000000e+110",x=1.000000000000000e+110}
string_double(s, d)
Identique à string_real(s,d).
©PrologIA
R 4 - 14
Manuel de Référence string_term(s, t, s',n) string_term(s, t, s') string_term(s, t)
Essaie de lire dans la chaîne de caractères s un terme Prolog qui sera unifié avec t. s' est la partie de la chaîne s qui n'a pas pu être utilisée. Si une variable apparaît dans s, une variable libre Prolog est créée.
Inversement (si s n'est pas connue) écrit, à la façon de out(t), le terme t dans une chaîne de taille maximale de n caractères (400<n < 32768) et l'unifie avec
s, s' n'est pas utilisée dans ce cas. t est un terme tout à fait quelconque, pouvant même contenir des variables et des structures complexes. Si l'écriture de t ne tient pas sur n caractères une erreur est générée. Si n n'est pas spécifié,
400 est choisi.
>string_term(s,myterm(x,1.2e0),s') string_term(s,x,s');
{s="myterm(v14,1.200000000000000e+00)",x=myterm(v15,1.2000
00000000000e+00), s'=""}
>string_term("1.2,3)",t,s') string_term(s,t,s');
{t=1.2,s'=",3)",s="1.2"}
term_vars(t, l)
Unifie l avec la liste des variables libres, éventuellement contraintes, de t. t est un terme prolog quelconque, voire infini.
Exemple :
>term_vars(one(2.x,y,y), t);
{t=x.y.nil}
A!ociation
Prolog
HERITAGE
4.6. Comparaison de termes quelconques
Il est possible de comparer formellement deux termes Prolog quelconques. Pour cela, l'ordre croissant suivant sur les termes a été défini:
- les variables
- les réels
- les entiers
- les identificateurs
- les chaînes de caractères
- les n-uplets de longueur < 2.
- les listes
- les n-uplets de longueur >= 2.
©PrologIA
A!ociation
Prolog
HERITAGE
Opérations prédéfinies sur les données
R 4 - 15
La comparaison de deux variables est fonction de leur date de création. La comparaison de deux entiers ou deux réels suit les règles habituelles. La comparaison de deux chaînes de caractères ou deux atomes suit l'ordre alphanumérique. La comparaison de deux listes est celle de la comparaison des deux premiers éléments trouvés différents. La comparaison de deux n-uplets s'effectue d'abord sur leur longueur, et à longueur égale sur la comparaison des deux premiers éléments trouvés différents. Le prédicat de comparaison est le suivant:
term_cmp(X, Y, V)
Compare les termes X et Y, unifie V avec l'entier -1 si X précède Y, 1 si Y précède X, 0 si les termes X et Y sont formellement égaux. Quelques exemples:
> term_cmp(1e0,1,V);
{V=-1}
> term_cmp(foo,zebra,V);
{V=-1}
> term_cmp(short,short,V);
{V=0}
> term_cmp(shorter,short,V);
{V=1}
> term_cmp(foo(a),foo(b),V);
{V=-1}
> term_cmp(foo(aa),foo(bb),V);
{V=-1}
> term_cmp(X,X,V);
{V=0}
> term_cmp(Y,X,V);
{V=-1}
> term_cmp(_,_,V);
{V=-1}
> term_cmp("foo",foo,V);
{V=1}
> term_cmp(1.2.3.nil,1.1.3.4.nil,V);
{V=1}
> term_cmp(1.2.nil,<1,2>,V);
{V=-1}
> term_cmp(1.2.nil,<X>,V);
{V=1}
>
term_cmpv(X, Y, V)
Compare les termes X et Y à la manière de term_cmp, mais sans prendre en compte les variables apparaissant dans les termes X et Y. Pour le prédicat
term_cmpv, deux variables distinctes sont considérées comme identiques.
Exemple:
> term_cmpv(foo(V1),foo(V2),V);
{V=0}
©PrologIA
R 4 - 16
Manuel de Référence
Deux prédicats de tri d'une liste sont fournis : sort/2, keysort/2. Pour effectuer ce tri, ils utilisent de manière interne le prédicat term_cmp/3 décrit ci-dessus.
sort(L1, L2)
L2 est unifiée avec la liste des éléments de L1 triés dans l'ordre croissant.
Dans cette liste, les éléments multiples ne sont conservés qu'une seule fois.
Exemple:
> sort(4.7.foo.3.4.1.nil,L);
{L=1.3.4.7.foo.nil}
keysort(L1, L2)
L2 est unifiée avec la liste des éléments de L1 triés dans l'ordre croissant.
Dans cette liste, les éléments multiples sont conservés. Exemple:
> keysort(4.7.foo.3.4.1.nil,L);
{L=1.3.4.4.7.foo.nil}
Ces mêmes prédicats admettent un argument supplémentaire qui désignera le prédicat utilisateur servant pour la comparaison des termes. Ce prédicat utilisateur devra être d'arité égale à 3. Il recevra les termes à comparer dans les 2 premiers et devra rendre dans le troisième argument le résultat de la comparaison (-1, 0 ou 1).
sort(L1, L2, C_ompar)
L2 est unifiée avec la liste des éléments de L1 triés dans l'ordre spécifié par le prédicat C_ompar. Dans cette liste, les éléments multiples ne sont conservés qu'une seule fois. Le prédicat sort/2 décrit précédemment n'est qu'un cas particulier de celui-ci, en donnant à l'argument C_ompar la valeur term_cmp.
Exemple:
> insert; compar_tuple(P1(X), P2(Y), R) -> term_cmp(X,Y,R);
;
> sort(aa(4).bb(7).cc(foo).dd(3).aa(4).ff(1).nil,L, compar_tuple);
{L=ff(1).dd(3).aa(4).bb(7).cc(foo).nil}
keysort(L1, L2, C_ompar)
L2 est unifiée avec la liste des éléments de L1 triés dans l'ordre spécifié par le prédicat C_ompar. Dans cette liste, les éléments multiples sont conservés. Le prédicat keysort/2 décrit précédemment n'est qu'un cas particulier de celui-ci, en donnant à l'argument C_ompar la valeur term_cmp. Exemple:
> insert; compar_tuple(P1(X), P2(Y), R) -> term_cmp(X,Y,R);
;
> keysort(aa(4).bb(7).cc(foo).dd(3).aa(4).ff(1).nil,L, compar_tuple);
{L=ff(1).dd(3).aa(4).aa(4).bb(7).cc(foo).nil}
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
5. Les entrées / sorties
5.0. Généralités
5.1. Entrées
5.2. Sorties
5.3. Chargement et adaptation du module de dessin
5.4. Déclaration d'opérateurs
Tous les organes d'entrée / sortie (clavier, écran, fichiers, fenêtres, etc…) utilisés par un programme Prolog II+ y sont représentés par des entités appelées unités
d'entrée/sortie. La représentation écrite d'une telle unité est une chaîne de caractères 1 ; le cas échéant, cette chaîne représente aussi une information identifiant l'unité vis-à-vis du système d'exploitation : nom de fichier, etc…
A tout moment, le système Prolog II+ «connaît» un certain nombre d'unités d'entrée
/ sortie : ce sont les unités qui ont été précédemment ouvertes et qui n'ont pas encore
été fermées. Les descripteurs de ces unités sont rangés dans une sorte de pile : l'unité qui est au sommet s'appelle l'unité courante. En principe, toutes les opérations d'entrée / sortie s'effectuent sur l'unité courante correspondante. Au lancement, une unité d'entrée (appelée console) et une unité de sortie (appelée également console) sont ouvertes automatiquement par le système et sont les unités courantes; habituellement, ces unités sont associées au clavier et à l'écran de l'ordinateur ou du terminal depuis lequel Prolog a été lancé.
Changer d'unité (i.e.: effacer input(f) ou output(f)) ne ferme pas «physiquement» l'unité courante ni ne change son état, mais ouvre -si nécessaire- une nouvelle unité et la met au sommet de la pile, de façon à pouvoir restaurer l'unité précédente, à la fermeture de cette nouvelle unité. Le nombre d'unités pouvant être ouvertes simultanément est dépendant de la machine hôte.
5.0. Généralités
Les unités d'entrée / sortie créées ont un type bien défini, ainsi qu'un mode d'ouverture. Les différents types possibles sont :
- :console pour l'unité créée automatiquement par Prolog au démarrage.
- :text pour un fichier disque au format texte.
- :binary pour un fichier disque au format binaire.
- :memory_file pour un fichier mémoire décrit ci après.
- :unix_pipe pour un "tube" de communication inter-processus (UNIX).
- :graphic_area, :tty_area, :edit_area pour des unités graphiques.
1
Les unités fenêtres peuvent aussi être représentées par des entiers.
©PrologIA
R 5 - 2
Manuel de Référence
Les différents modes d'ouverture possibles sont :
:read pour la lecture.
:write pour l'écriture. Si l'objet physique (un fichier disque par exemple) du même nom existe déjà, son contenu sera effacé et remplacé par les sorties qui seront demandées.
:append pour l'écriture. Ce mode est spécifique aux fichiers. Si un objet physique associé existe déjà, il est ouvert et les futures écritures y seront ajoutées en fin. Ce mode d'ouverture peut être défini par une convention de nommage de l'unité, dont le nom doit dans ce cas se terminer par "+". Le fichier associé à l'unité est alors celui de nom donné, après avoir ôté le "+".
Par exemple output("file+") va provoquer l'ouverture du fichier de nom "file" en mode append.
Certaines unités peuvent être ouvertes à la fois en lecture et en écriture. C'est le cas des types :console, :tty_area , :edit_area, et :memory_file.
memory_file(s) memory_file(s,n)
Crée une unité de type "fichier mémoire" de nom s. Son tampon de caractères a une longueur de 400 caractères pour la première forme du prédicat. En utilisant la deuxième forme, le tampon sera de longueur n ou de la valeur la plus proche, sachant que la taille minimale allouée est de 400 caractères et la taille maximale allouée est de 32K caractères. Cette unité peut être utilisée en entrée et en sortie. Elle peut être en même temps unité courante d'entrée et unité courante de sortie. Si le tampon est trop petit pour les opérations d'écritures qui y sont faites, il est automatiquement doublé de taille, sans dépasser les 32K caractères. L'unité sera fermée physiquement quand elle aura été fermée à la fois en lecture et en écriture. Cette unité peut être utilisée au même titre que les fichiers.
Les types et les modes d'ouverture des unités peuvent être obtenus par le prédicat :
current_file(x) current_file(x,t,m)
Unifie successivement x avec les noms des unités ouvertes en lecture et/ou en
écriture, t avec le type de cette unité, et m avec le mode d'ouverture.
A!ociation
Prolog
HERITAGE
5.1. Entrées
Le tampon de caractères standard pour une unité d'entrée fourni par Prolog est limité à 400 caractères. S'il arrive qu'une ligne soit plus longue, Prolog se comporte comme s'il y avait un espace après le 400-ième caractère. Le reste de la ligne est lu après épuisement des 400 caractères précédents.
Il est conseillé de manipuler des lignes moins longues. Se rappeler que :
- le Retour Chariot sera lu au même titre que le caractère blanc par les primitives qui commencent par lire tous les espaces,
©PrologIA
A!ociation
Prolog
HERITAGE
Les entrées / sorties
R 5 - 3
- les espaces et les tabulations peuvent apparaître partout entre les unités lexicales,
- les chaînes, les identificateurs, les entiers et la mantisse des réels peuvent s'écrire sur plusieurs lignes, en masquant le Retour Chariot par le caractère '\', si l'option d'interprétation du '\' est active (cf. U2.3.).
5.1.1. Règles pour l'entrée.
eol
Test de fin de ligne en entrée.
Teste si une fin de ligne a été atteinte; plus précisément : si le prochain caractère qui sera lu est une marque de fin-de-ligne, alors eol s'efface; sinon, l'effacement de eol échoue.
Exemple :
> in_char'(c) eol;
a<CR>
{c="a"}
> in_char'(c) eol;
a<space><CR>
>
eof
Test de fin d'entrée.
Teste s'il existe encore des caractères en entrée sur l'unité courante. Autrement dit, teste si la prochaine lecture va provoquer l'erreur 104: FIN DE FICHIER.
Si ce n'est pas le cas, l'effacement de eof échoue. Ne peut évidemment jamais s'effacer sur la "console" ou dans une fenêtre TTY, puisque ce sont des unités en mode terminal, qui ont toujours des caractères, éventuellement après saisie.
Exemple :
> input("fichier_vide") eof input("console");
{}
>
in_char(t)
Lecture d'un caractère.
Lit le prochain caractère, le transforme en une chaîne de longueur 1 et essaie de l'unifier avec t.
Remarque: le caractère fin de ligne, s'il est assimilé à un espace pour d'autres primitives qui analysent les caractères lus, vaudra bien pour cette primitive
"\n".
Remarque: toute commande (suite de buts) se termine au ";". Lorsque in_char est lancé sur la ligne de commande, le premier caractère lu est le caractère qui suit immédiatement le ';'. Il s'agit souvent de : fin de ligne.
Exemple :
> in_char(c);
{c="\n"}
> clear_input in_char(c);
a<CR>
{c="a"}
©PrologIA
R 5 - 4
Manuel de Référence in_char'(t)
Lecture d'un caractère non blanc.
Lit tous les caractères blancs et se comporte comme in_char(t).
Exemple:
>in_char'(x) in_char(y) in_char'(z) in_char(t);
ab c d
{x="a",y="b",z="c",t=" "}
next_char(t)
Essaie d'unifier le prochain caractère à l'entrée avec t sans le lire vraiment.
next_char'(t)
Lit tous les caractères blancs s'il y en a et ensuite se comporte comme
next_char(t).
Exemple:
>next_char'(x) in_char(y); abc
{x="a",y="a"}
inl(s) inl(s, n)
Lecture d'une ligne.
Lit toute la fin de la ligne courante et l'unifie, en tant que chaîne de caractères, avec s. n est unifié avec le rang de la ligne lue, dans son unité. n est beaucoup plus significatif pour les fichiers, voire les fenêtres EDIT. Dans la "console" ou les fenêtres TTY, les lignes sont divisées en deux groupes : lignes d'entrée et lignes de sortie. n est alors le rang à l'intérieur d'un de ces groupes.
Exemple:
> clear_input inl(s);
Maitre corbeau, sur un arbre perche
{s="Maitre corbeau, sur un arbre perche"}
in(t, c) in(t, D, c)
Lecture d'un terme Prolog.
Lit la plus grande suite x de caractères qui constituent un terme ou le début d'un terme. Si x est l'expression d'un terme, on essaie d'unifier ce dernier avec
t. Si x n'est pas un terme, une erreur se produit. Tous les caractères blancs qui suivent x sont lus; c est unifié avec la première unité lexicale lue, ne faisant pas partie de x. D est unifié avec le dictionnaire des variables du terme, c'est-àdire une liste de doublets correspondant aux variables de t. Chaque doublet contient la chaîne de caractères qui est le nom originel de la variable et la variable en question.
Exemple:
>in(t, c);
ceci.est.un.terme;
{t=ceci.est.un.terme, c=";"}
>in(t, D, c);
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Les entrées / sorties
R 5 - 5
aa(x, bb(y_n1',x));
{t=aa(v129, bb(v163,v129)),
D=<"x", v129>.<"y_n1'",v163>.nil, c=";"}
Note: La lecture d'une unité en avance est ici indispensable à cause des opérateurs.
in_integer(t)
Lit d'abord tous les caractères blancs, puis essaie de lire un entier sur l'unité courante. Si l'objet lu n'a pas la syntaxe d'un entier, alors rien n'est lu et
in_integer échoue. Sinon t est unifié avec l'entier qui a été lu.
Exemple:
>in_integer(i) in_char(c) in_integer(j);
-123+456
{i=-123,c="+",j=456}
ATTENTION : l'écriture des entiers et réels pouvant se faire sur plusieurs lignes, à l'aide de "\<return>" (si l'option d'interprétation du '\' est active (cf.
U2.3.)), si l'objet en entrée n'est pas un entier, mais un réel écrit sur plusieurs lignes, Prolog ne peut pas restaurer les lignes précédant la dernière.
in_real(t)
Lit d'abord tous les caractères blancs, puis essaie de lire un réel sur l'unité courante. Si l'objet lu n'a pas la syntaxe (définie par l'option courante) d'un réel, alors rien n'est lu et in_real échoue. Sinon t est unifié avec le réel qui a
été lu.
> in_real(x) in_char(c) in_real(y);
-123e+4,54.63e0
{x=-1.230000000000000e+06, c=",", y=5.463000000000000e+01}
ATTENTION : l'écriture des entiers et réels pouvant se faire sur plusieurs lignes, à l'aide de "\<return>" (si l'option d'interprétation du '\' est active (cf.
U2.3.)), si l'objet en entrée n'est pas un réel, mais un entier écrit sur plusieurs lignes, Prolog ne peut pas restaurer les lignes précédant la dernière.
in_double(t)
Identique à in_real(t).
in_string(t)
Lit d'abord tous les caractères blancs, puis essaie de lire une chaîne Prolog. Si l'objet n'a pas la syntaxe d'une chaîne, alors rien n'est lu et in_string échoue.
Sinon t est unifié avec la chaîne lue. Attention : il s'agit bien d'un terme Prolog
de type chaîne (i.e.: avec les quotes) et non de «n'importe quoi, considéré comme une chaîne».
Exemple:
>in_string(s) in_char(c);
"Hello !";
{s="Hello !", c=";"}
©PrologIA
R 5 - 6
Manuel de Référence in_ident(t)
Lit d'abord tous les caractères blancs, puis essaie de lire un identificateur
Prolog. Si l'objet lu n'a pas la syntaxe (définie par l'option courante) d'un identificateur, alors rien n'est lu et in_ident échoue. Sinon t est unifié avec l'identificateur qui a été lu.
Exemple:
lire(entier(n)) -> in_integer(n) !; lire(ident(i)) -> in_ident(i) !; lire(chaine(s)) -> in_string(s) !; lire(caractere(c)) -> in_char(c);;
>lire(x1) lire(x2) lire(x3) lire(x4) lire(x5) lire(x6) …; aa := bb * 123 …
{x1=ident(aa),x2=caractere(":"),x3=caractere("="), x4=ident(bb),x5=caractere("*"),x6=entier(123), … }
in_word(t1, t2)
Lit tous les caractères blancs, puis lit le mot le plus long, le transforme en une chaîne et essaie d'unifier cette chaîne avec t1. Un mot est soit :
(1) une suite de lettres
(2) une suite de chiffres
(3) tout caractère qui n'est ni un blanc, ni une lettre, ni un chiffre.
Dans le cas (1), t2 est l'identificateur correspondant à t1 si cet identificateur est déjà connu, ou nil s'il s'agit de sa première apparition. Dans ce dernier cas, l'identificateur n'est pas créé dans le dictionnaire.
Dans le cas (2), t2 est l'entier correspondant à t1.
Dans le cas (3), t2 = t1.
>in_word(x, y);
toto
{x="toto", y=nil}
>in_word(x, y);
insert
{x="insert", y=insert}
>in_word(x, y);
012345
{x="012345", y=12345}
>in_word(x, y);
+
{x="+", y="+"}
in_sentence(t1, t2)
Lit une phrase qui se termine par ".", "?", "!" ou tout autre caractère qui a été défini comme terminateur et met la phrase sous la forme de deux listes. t1 est la liste des unités lexicales constituant la phrase (les mots au sens de in_word) et t2 est la liste des atomes correspondants, comme pour in_word.
La liste des terminateurs de phrases peut être modifiée par les prédicats
add_sentence_terminator/1 et remove_sentence_terminator/1.
>in_sentence(x, y);
ceci est un string.
{x="ceci"."est"."un"."string".".".nil,
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Les entrées / sorties
R 5 - 7
y=nil.nil.nil.string.".".nil}
>in_sentence(x, y);
somme := 052345;.
{x="somme".":"."="."052345".";".".".nil,
y=nil.":"."=".52345.";".".".nil}
add_sentence_terminator(C)
Ajoute le caractère C dans la liste des terminateurs de phrases (initialement dotée des caractères ".", "?", et "!").
remove_sentence_terminator(C)
Enlève le caractère C de la liste des terminateurs de phrases. Provoque un
échec si ce caractère n'est pas dans cette liste.
read_unit(t, u)
Lecture d'une unité lexicale.
Lit une unité lexicale et unifie t avec son type (identificateur, nombre, etc…) donné par un nombre conventionnel, et u avec sa valeur.
Les unités lexicales de Prolog II+ sont décrites par la grammaire donnée au paragraphe 1.9. Relativement aux notations utilisées dans cette grammaire, le type t utilisé par read_unit est défini comme suit :
4
5
2
3 valeur de t catégorie lexicale
1 identifier separator 1 variable constant graphic_symbol valeur de u l'identificateur la chaîne correspondante la chaîne correspondante la constante la chaîne correspondante
Exemple:
> read_unit(t1,u1) read_unit(t2,u2) read_unit(t3,u3)
read_unit(t4,u4); x12 + :pi ,
{t1=3,u1="x12",t2=5,u2="+",t3=1,u3=pi,t4=2,u4=","}
> read_unit(t,u);
123
{t=4,u=123}
read_rule(t1,t2)
Lit sur l'unité d'entrée courante une règle Prolog, et unifie t1 avec la tête, et t2 avec la liste des termes de la queue.
Exemple:
> read_rule(t1,q1) read_rule(t2,q2);
tt(1,x) -> outl(x) fail; tt(2,x) -> !;
{t1=tt(1,v118),q1=outl(v118).fail.nil,
t2=tt(1,v228),q2='!'.nil}
1
separator est un séparateur syntaxique: voir § 1.2 de ce manuel.
©PrologIA
R 5 - 8
Manuel de Référence
>
sscanf
La fonction C sscanf est accessible depuis Prolog avec la primitive callC (voir
§ 7.7. de ce manuel).
> callC(sscanf("123","%lf",<"R",y>));
{y=1.230000000000000e+02}
> eq(f,"%x %o") callC(sscanf("12 12",f,<"I",x>,<"I",y>));
{x=18, y=10}
5.1.2. Modification de l'unité d'entrée.
Le nom des unités d'entrée, peut être soit une chaîne Prolog, soit un identificateur
Prolog, soit un entier. Toutefois, les fichiers et les fenêtres prédéfinies sont toujours désignés par des chaînes.
input(u) input(u,n)
L'unité dont le nom est u, devient l'unité d'entrée courante. Si cette unité ne figure pas parmi les unités ouvertes, alors son descripteur est créé et ajouté dans la pile des unités d'entrée ouvertes. Si l'unité est nouvelle, un fichier est recherché et ouvert. Un fichier de commandes ouvert par input peut lui-même contenir une commande input, la nouvelle unité courante est empilée audessus de la précédente. Dans la forme à deux arguments, n précise la taille, en nombre de caractères, du tampon de l'unité. Elle doit être comprise entre
400 et 32K. Si n n'est pas dans cet intervalle, la taille du tampon sera de la valeur la plus proche.
input_is(u)
Unifie u avec le nom de l'unité d'entrée courante.
close_input
L'unité courante est fermée (sauf s'il s'agit de la console ou d'une fenêtre) et son descripteur est enlevé du sommet de la pile des unités: l'unité précédente redevient unité courante.
close_input(u)
L'unité de nom u, qui doit figurer dans la liste des unités d'entrée ouvertes, est fermée (sauf s'il s'agit de la console ou d'une fenêtre) et son descripteur est enlevé de cette liste.
clear_input
Saute les caractères qui restent non lus sur la ligne courante d'entrée.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Les entrées / sorties
R 5 - 9
5.2. Sorties
A travers les primitives out, outm et line, Prolog tient à jour un pointeur de ligne pour chaque unité de sortie. C'est l'effacement de la primitive line qui le réinitialise.
Ce pointeur permet de maintenir en interne, une représentation de ce qui se passe sur la ligne courante de l'unité. Il sert en particulier, à déterminer la place qui reste sur la ligne (la longueur de ligne est définie par set_line_width) et par conséquent sert à générer un passage à la ligne forcé.
L'utilisation de set_cursor n'agit pas sur ce pointeur de ligne, par conséquent la représentation de la ligne que se fait Prolog n'est plus exacte. Il peut arriver que des passages à la ligne forcés (\<return>) soient malvenus. Il est donc conseillé, pour
éviter cela, d'utiliser conjointement à set_cursor, les primitives outl et outml plutôt que les primitives out et outm. Ainsi le pointeur de ligne est constamment réinitialisé et la gestion de la présentation est totalement laissée à set_cursor. On notera que le comportement est le même, en faisant appel à des règles externes (C, Pascal, …) qui gèrent la présentation de l'écran.
5.2.1. Règles pour la sortie
beep
Génère une tonalité d'avertissement.
flush
Vide le tampon de caractères de l'unité courante de sortie, en envoyant tous les caractères en attente d'écriture sur l'unité physique associée.
out(t) outl(t)
Ecrit le terme t sur l'unité courante. Tout terme écrit avec out peut être relu avec in si l'option d'interprétation du caractère spécial «\» est active (cf. §2.3.
du manuel d'utilisation). Si le terme est plus long que la longueur restant sur la ligne courante, un retour chariot est inséré en respectant les principes suivants:
(1) Lorsqu'une chaîne ne peut être imprimée sur une seule ligne, elle est coupée à l'endroit requis par insertion d'un retour-chariot masqué (c'est-àdire précédé par le caractère «\»).
(2) Un nombre n'est pas coupé sauf si, tout seul, il est plus long que toute une ligne; dans ce cas il est coupé comme une chaîne.
(3) Un identificateur n'est pas coupé sauf si, tout seul, il est plus long que toute une ligne; dans ce cas il est coupé comme une chaîne.
L'écriture du terme se fait avec les conventions suivantes:
(1) Les listes sont imprimées en notation pointée pour la syntaxe Prolog II, en notation avec des crochets carrés pour la syntaxe Edinburgh ou si l'option "notation standard des réels" est activée.
©PrologIA
R 5 - 10
Manuel de Référence
(2) Si une chaîne contient un caractère non imprimable, celui-ci est écrit sous forme d'escape séquence imprimable (voir outm).
(3) Tout identificateur ne répondant pas à la syntaxe d'un identificateur est quoté.
(4) Le contexte courant d'exécution détermine les abréviations possibles pour les identificateurs.
outl(t) équivaut à la suite de buts out(t) line : lorsque le terme t a été écrit on produit le passage à la ligne suivante.
Exemple:
> out(1.Pierre."Salut!".nil);
1.Pierre."Salut!".nil{}
>
N.B. : Les accolades imprimées à la suite du terme indiquent la réussite de l'effacement du but out(1.Pierre."Salut!".nil). Une manière d'en empêcher l'impression consiste à faire échouer artificiellement cet effacement :
>out(1.Pierre."Salut!".nil) line fail;
1.Pierre."Salut!".nil
>
outm(s) outml(s)
Ecrit la chaîne s sur l'unité active de sortie, sans écrire les quotes et en interprétant correctement les caractères de formatage que s peut contenir (par exemple: \n). Si la chaîne est plus longue que la place restante sur la ligne en cours, alors elle est coupée par un retour chariot «masqué». Les caractères correspondant à un code ISO 8859-1 non imprimable sont envoyés sur l'unité courante de sortie sans transformation.
Exemple:
> out("\tBonjour!") line fail;
"\tBonjour!"
> outm("\tBonjour!") line fail;
Bonjour!
>
outml(s) équivaut à outm(s) line.
outm(s, n)
Ecrit n fois la chaîne s avec les mêmes conventions que ci-dessus.
Exemple :
>outm("-",40) line fail;
----------------------------------------
>
out_equ(t)
Ecrit le système minimal d'équations du terme t. Cette règle est beaucoup plus lente que out, mais factorise les sous-arbres et donne donc, entre accolades, une représentation plus compacte quand c'est possible.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Les entrées / sorties
R 5 - 11
line
Va à la ligne.
page
Va à la page. Sur l'unité "console", l'écran est effacé et le curseur positionné en haut à gauche de l'écran. N'a pas d'effet dans l'environnement graphique.
paper
Provoque la copie de ce qui se passe à la console dans un fichier journal
(prolog.log par défaut).
no_paper
Annule l'effet de paper. Le fichier journal est fermé uniquement en fin de session. Un appel ultérieur à paper viendra ajouter des informations à la fin du fichier.
set_cursor(n1, n2)
Le curseur est positionné en (n1,n2) sur l'écran, n1 étant la coordonnée de la colonne et n2 celle de la ligne. (1,1) correspond au coin en haut à gauche de l'écran. On doit avoir :
1 ! n1 ! largeur de la ligne et 1 ! n2 ! nombre de lignes de l'écran.
N'a pas d'effet dans l'environnement graphique.
set_line_cursor(n)
Positionne le pointeur courant de caractères à la position n sur la ligne courante. Cela fournit une sorte de tabulation. set_line_cursor ne revient pas en arrière à partir de la position courante du pointeur. La position du premier caractère est 1.
sprintf
La fonction C sprintf est accessible depuis Prolog avec la primitive callC (voir
§ 7.7. de ce manuel).
> callC(sprintf(<"",x,80>,"valeur: %ld",200));
{x="valeur: 200"}
L'utilisation des primitives suivantes nécessite le chargement préalable du module de dessin d'arbres (voir § 5.3).
draw_equ(t)
Dessine le système minimal d'équations représentant un arbre fini ou infini t .
Pour utiliser cette primitive, il faut que le module de dessin d'arbres soit chargé.
©PrologIA
R 5 - 12
Manuel de Référence
> ... draw_equ(aa(bb(cc,dd),bb(cc,dd))) ...
aa
+--^--+ v1026 v1026 v1026 = bb
+^-+
cc dd
{}
draw_tree(t)
Dessine l'arbre fini t sur l'écran (si le terminal a des possibilités graphiques, les symboles semi-graphiques sont utilisés). Pour utiliser cette primitive, il faut que le module de dessin d'arbres soit chargé.
> ... draw_tree(aa(bb(cc,dd),bb(cc,dd))) ...
aa
+--^---+
bb bb
+^-+ +^-+
cc dd cc dd
{}
draw_mode(x)
Cette primitive, ainsi que la suivante concernent la manière dont les arbres sont dessinés. En effet, suivant le type d'écran certains caractères semi-graphiques sont utilisés pour obtenir des dessins d'arbres plus jolis. draw_mode(x) fournit dans x une chaîne correspondant au type d'écran utilisé. Les valeurs possibles de x sont dépendantes de la machine: "VT100", "GRAPHICS",
"TTY". "TTY" qui correspond à un écran sans possibilité graphique, existe sur toutes les machines. Pour utiliser cette primitive, il faut que le module de dessin d'arbres soit chargé.
set_draw_mode(x)
Permet de choisir le type d'écran utilisé pour le dessin des arbres. Les valeurs de x sont les mêmes que précédemment. Si vous voulez faire imprimer des arbres sur une imprimante classique, il faut se mettre en mode "TTY". Pour utiliser cette primitive, il faut que le module de dessin d'arbres soit chargé.
5.2.2. Modification de l'unité de sortie
Le nom des unités de sortie, peut être soit une chaîne Prolog, soit un identificateur
Prolog, soit un entier. Toutefois, les fichiers et les fenêtres prédéfinies sont toujours désignés par des chaînes.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Les entrées / sorties
R 5 - 13
output(u) output(u,n)
L'unité de nom u devient l'unité courante de sortie. Si l'unité désignée par u ne figure pas déjà dans la liste des unités de sortie ouvertes, alors un descripteur pour l'unité est alloué et éventuellement un fichier est créé ayant le nom indiqué par u. Dans la forme à deux arguments, n précise la taille, en nombre de caractères, du tampon de l'unité. Elle doit être comprise entre 400 et 32K.
Si n n'est pas dans cet intervalle, la taille du tampon sera de la valeur la plus proche. Dans la forme à un seul argument, le tampon aura une taille de 400 caractères.
En aucun cas ces primitives ne créent une fenêtre ou un fichier mémoire, il faut utiliser pour cela les primitives spécifiques. Si u est une chaîne de caractères terminée par le signe "+", le fichier dont le nom est la chaîne privée de ce signe est alors ouvert en mode append, c'est à dire que si ce fichier existe déjà sur disque, les sorties qui suivront vont y être ajoutées en fin.
output_is(u)
Fournit dans u le nom de l'unité de sortie courante.
close_output
L'unité courante est enlevée du sommet de la pile des unités de sortie ouvertes.
Si elle correspond à un fichier, alors celui-ci est fermé.
close_output(u)
u est le nom d'une unité qui doit figurer dans la liste des unités de sortie ouvertes. S'il s'agit d'un fichier, il est fermé. Le descripteur de l'unité est enlevé de cette liste et détruit. En aucun cas cette primitive ne ferme une fenêtre, il faut utiliser pour cela les primitives spécifiques.
line_width(n)
Fournit dans n la longueur maximum actuelle des lignes de l'unité courante, définie par set_line_width.
set_line_width(n)
Permet de définir la longueur maximale des lignes de l'unité courante de sortie. n devient la nouvelle longueur de ligne de l'unité. Par défaut, la longueur maximale de la ligne est de 80 caractères. Dans tous les cas, elle est limitée à 400 caractères. Si aucun passage à la ligne suivante n'a été demandé, avant que la longueur maximum de la ligne ait été atteinte, Prolog le force, en insérant un <retour chariot> entre deux unités ou bien la séquence <\><retour chariot> à l'intérieur d'une unité.
echo
Active l'option qui provoque l'affichage sur l'unité "console" des caractères lus ou écrits sur une autre unité, de type fichier.
©PrologIA
R 5 - 14
Manuel de Référence no_echo
Annule l'effet de echo.
5.3. Chargement et adaptation du module de dessin
Le module Dessin ne figure pas dans l'état initial qui vous est livré. Vous pouvez l'inclure en chargeant le fichier binaire:
> load("dessin.mo");
Pour charger à partir du répertoire Prolog, taper la commande:
> getenv("PrologDir2",S)
conc_string(S,"dessin.mo",S1) load(S1);
Vous pouvez également recompiler le module source:
> insert("dessin.m2");
Ce fichier crée un nouveau module, Dessin, contenant le programme de tracé d'arbres et d'équations. Ceci rend disponible les règles suivantes (voir §5.2.1.):
draw_equ draw_tree draw_mode set_draw_mode
gr_tree_click si le mode graphique est activé.
S'agissant d'un module de dessin il faut, bien entendu, y incorporer certains renseignements sur les possibilités semi-graphiques des terminaux utilisés. A cette fin, un "mode de dessin" (draw_mode) est associé à chaque protocole de gestion de terminal.
Quelques configurations sont déjà définies : vt100, tty, …. Il vous appartient d'ajouter à dessin.m2 celles qui correspondent aux terminaux que vous utilisez.
Pour définir un nouveau mode de dessin, vous devez:
1.
Lui donner un nom interne, qui est un identificateur (tty, vt100, etc...) un nom externe, qui est une chaîne de caractères ("TTY", "VT100", etc...) et associer ces deux noms en ajoutant une assertion:
termi(nom_externe,nom_interne) ->;
Notez que l'utilisateur final n'est censé connaître que le nom externe.
2.
Ajouter deux nouvelles définitions des règles en_graphique et
hors_graphique qui commandent le passage du terminal du mode caractères au mode semi-graphique et réciproquement.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Les entrées / sorties
R 5 - 15
3.
Ajouter une nouvelle règle config définissant les chaînes de caractères qui, en mode graphique, provoquent l'affichage des graphismes indiqués.
Remarque: Dans tous les cas, le mode "TTY" permet d'obtenir des dessins approximatifs sans utiliser aucune possibilité graphique du terminal. Ceci peut s'avérer utile, par exemple, pour imprimer des dessins d'arbres sur une imprimante classique.
5.4. Déclaration d'opérateurs
Les opérateurs permettent une notation sans parenthèses des termes fonctionnels à un ou deux arguments. Le symbole fonctionnel est remplacé par un opérateur placé en position:
- infixé pour les termes fonctionnels à deux arguments.
- préfixé ou postfixé pour les termes fonctionnels à un argument.
Un opérateur est défini par un symbole, une précédence, et des règles de parenthésage. Pour lever l'ambiguïté des expressions contenant plusieurs opérateurs, on parenthèse d'abord les opérateurs de plus faible précédence. Lorsque des opérateurs ont même précédence, on utilise les règles de parenthésage. La syntaxe des expressions avec opérateurs et les opérateurs prédéfinis sont donnés section
1.9.2 de ce manuel.
Exemple:
/ opérateur binaire de précédence 400, parenthésage à gauche d'abord
- opérateur binaire de précédence 500, parenthésage à gauche d'abord expression: expression non ambiguë: ((3 - 2) - ((1 / 4) / 3))) arbre Prolog:
3 - 2 - 1 / 4 / 3 sub(sub(3,2),div(div(1,4),3))
Il faut remarquer que si la notation avec opérateur est plus légère, son abus peut mener à une réelle difficulté de compréhension des arbres effectivement représentés par des expressions contenant des opérateurs peu usités.
op(n,i1,s) op(n,i1,s,i2)
Déclarent l'opérateur s avec la précédence n (0 <= n <= 1200) et le type de parenthésage i1. s est un identificateur, ou une chaîne représentant un symbole graphique. Dans la forme à 3 arguments, s peut être une liste, la déclaration s'applique alors à chaque élément de la liste. La forme à 3 arguments est aussi une directive de compilation, c'est à dire que la déclaration d'un opérateur peut aussi se faire au milieu d'un source que l'on compile. Le symbole fonctionnel représenté par s est i2 si celui-ci est défini, sinon le symbole s quoté.
L'associativité i1 est précisée par un identificateur combinant deux ou trois des lettres f,x,y avec les conventions suivantes:
f
représente l'opérateur.
x
représente une expression de précédence inférieure à f.
y
représente une expression de précédence inférieure ou égale à f.
©PrologIA
R 5 - 16
Manuel de Référence
Les combinaisons possibles sont:
fx Opérateur préfixé avec opérande de précédence inférieure.
fy Opérateur préfixé avec opérande de précédence inférieure ou égale.
xf Opérateur postfixé avec opérande de précédence inférieure.
yf Opérateur postfixé avec opérande de précédence inférieure ou égale.
xfx Opérateur infixé avec opérandes de précédence inférieure.
yfx Opérateur infixé admettant à gauche un opérande de même précédence, avec parenthésage gauche droite.
xfy Opérateur infixé admettant à droite un opérande de même précédence, avec parenthésage droite gauche.
Lorsque n est égal à 0, op/3 a pour effet de supprimer la déclaration existante pour l'opérateur s.
Lorsque les arguments de op/3 sont des variables libres, ils sont unifiés successivement avec les définitions des opérateurs existants.
Exemple:
>
insert; op(900,fy,not) op(700,xfx,"=",eq); not x -> x ! fail; not x ->; ;
{}
> (not 1=2)1;
{}
> split(1=2,L);
{L=eq.1.2.nil}
A!ociation
Prolog
HERITAGE
1
Se rappeler qu'en syntaxe Prolog II les opérateurs doivent être parenthésés au premier niveau d'une queue de règle.
©PrologIA
A!ociation
Prolog
HERITAGE
6. L'environnement
6.1. Comment sortir de Prolog
6.2. Démarrage automatique d'un programme Prolog
6.3. Edition de programmes
6.4. Date, temps et mesures
6.5. Lien avec le système
6.6. Utilisation du debugger
6.7. Modification et visualisation de l'état courant
6.8. Gestion automatique des espaces et des piles
Parmi les règles prédéfinies qui permettent à l'utilisateur de communiquer avec l'extérieur de Prolog, ce chapitre décrit celles qui sont indépendantes du système particulier utilisé. Celles qui, au contraire, dépendent de l'implantation considérée sont décrites dans le manuel d'utilisation.
6.1. Comment sortir de Prolog
exit exit(s)
Fait sortir de Prolog en sauvant la totalité des programmes couramment connus, y compris ceux qui constituent le superviseur Prolog II+. La sauvegarde est faite dans le fichier de nom s, ou bien pour la forme sans argument dans le fichier de nom prolog.po.
Remarque : Il est possible de sauver uniquement les modules que vous avez créés ou modifiés pendant la session (commande save - cf. paragraphe 3.7) et de quitter Prolog en faisant la commande quit.
quit quit(n)
Fait quitter Prolog sans rien sauver. La valeur n est envoyée au système d'exploitation à titre de «statut de terminaison». Quelque soit ce système, si n vaut 0 alors la valeur envoyée est celle qui correspond à «pas d'erreur» (cette valeur peut être non nulle).
La première forme quit, équivaut à quit(0).
6.2. Démarrage automatique d'un programme Prolog
Si l'état binaire sur lequel Prolog est lancé, contient des modules possédant une règle ini_module, chacune de ces règles est effacée (dans un ordre défini par
Prolog) dès la fin de l'initialisation. Ensuite, Prolog tente d'effacer le but :to_begin.
©PrologIA
R 6 - 2
Manuel de Référence
La règle :to_begin n'est pas définie dans l'état initial. Si l'utilisateur veut faire démarrer automatiquement un programme, il doit ajouter la règle:
:to_begin -> xxxx ; où xxxx est le but initial à effacer de son programme d'application. Il doit ensuite sauvegarder cet état. Lorsque plus tard il appellera Prolog avec ce nouvel état, xxxx sera immédiatement exécuté.
A!ociation
Prolog
HERITAGE
6.3. Edition de programmes
-
-
-
-
Prolog II+ étant un compilateur incrémental, il vous permet d'éditer des règles sans quitter Prolog, en utilisant un éditeur que nous appellerons ici éditeur résident.
L'éditeur résident est activé par la commande Prolog edit
Il existe plusieurs variantes de cette commande pour:
-
-
modifier un ou plusieurs paquets de règles.
éditer et recompiler un module source.
éditer un fichier de texte quelconque.
A propos d'édition, il faut savoir que : les règles sont supprimées de l'espace du code de Prolog pour être écrites dans le fichier de texte qui sert pour l'édition. A la fin de l'édition, le fichier est réinséré.
l'édition compile et par conséquent l'édition de faits non compilés les transformera en règles compilées.
à tout moment, les règles n'existent qu'en un seul exemplaire : soit comme code Prolog, soit comme source Prolog.
si une interruption utilisateur survient, l'espace du code sera tout de même modifié, de façons différentes en fonction du moment de l'interruption.
l'édition de règles non visibles provoque la suppression de ces règles, mais ne les fournit pas sous forme de source!
6.3.1. Modifier des paquets de règles
Cette commande provoque l'édition des paquets de règles listés en argument. A la fin de l'édition, les paquets édités sont réinsérés à la place des paquets originaux.
Les paquets peuvent appartenir à plusieurs modules différents, c'est alors le contexte de lecture/écriture du module du premier paquet qui est utilisé.
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 3
edit(i/a) edit(l)
i/a représente le paquet de règles dont l'accès est l'identificateur i, et le nombre d'arguments est a.
l est une liste (terminée ou non par nil) d'éléments i/a.
Au retour d'un appel de l'éditeur résident, Prolog peut déceler une erreur de syntaxe dans les règles que vous venez d'éditer. Il affiche alors un message avec le numéro et le contenu de la ligne contenant l'erreur, le diagnostic correspondant et vous demande si vous désirez recommencer le cycle d'édition et compilation afin de corriger la faute, ou abandonner.
L'abandon provoque le retour à prolog, les règles non insérées sont supprimées.
Note:
Il peut arriver que le texte que vous venez d'éditer comporte une erreur difficile à déceler.
Sachez qu'à la suite d'une utilisation de l'éditeur résident, un fichier nommé
PROEDIT.PRO a été créé dans le répertoire courant: il contient les règles
éditées dans leur état final.
Ce fichier peut vous aider à reconstituer l'état courant de votre programme.
Pour cela, abandonnez l'édition. Vous vous retrouvez alors sous Prolog, sans les règles éditées non encore lues. Sauvez ce programme et, hors
Prolog, utilisez un éditeur de textes pour analyser le morceau manquant depuis PROEDIT.PRO.
6.3.2. Editer et recompiler un module source
Cette fonctionnalité permet de modifier et recompiler le texte source d'un module déjà chargé: l'édition se fait avec la mise en forme originale.
editm(m)
m est une chaîne. Supprime toutes les règles du module m, et lance l'édition du fichier source correspondant. La version éditée est recompilée et vient remplacer l'ancienne version.
Si le fichier source ne peut être trouvé, alors l'édition se fait sur un source créé par décompilation du module. Ce fichier source est appelé PROEDIT.Mx où x est un numéro engendré par Prolog et de valeur différente pour des modules de nom différent.
Note:
Les règles qui auraient pu être ajoutées par insert ou assert dans le module pendant l'exécution sont effacées avant rechargement du module.
©PrologIA
R 6 - 4
Manuel de Référence
6.3.3. Editer un fichier de texte quelconque
edit(s)
s est une chaîne. Permet d'éditer le fichier de nom s.
6.4. Date, temps et mesures
reset_chrono, chrono(x)
chrono unifie x avec le temps en secondes écoulé depuis le dernier
reset_chrono.
reset_cpu_time, cpu_time(x)
cpu_time unifie x avec le temps cpu en millisecondes écoulé depuis le dernier
reset_cpu_time.
date(j,m,a,s)
Calcule les arguments j,m,a,s de la date courante. Les quatre arguments désignent respectivement le jour du mois, le mois, l'année et le rang du jour dans la semaine (dimanche=0, lundi=1,...).
date_string(s)
Calcule la date courante, exprimée en anglais, et unifie la chaîne correspondante avec s.
date_stringF(s)
Calcule la date courante, exprimée en français, et unifie la chaîne correspondante avec s.
week(n,s)
Donne dans s la chaîne correspondant au jour de la semaine dont le rang est n et vice versa.
month(n,s)
Donne dans s la chaîne correspondant au mois dont le rang est n et vice versa.
delay(n)
Temporise n millisecondes.
time(x)
Donne un temps absolu en secondes. L'origine de ce temps est variable d'une machine à une autre.
time(h,m,s)
Calcule les arguments h, m, s de l'heure courante. Ils représentent respectivement l'heure, les minutes et les secondes.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 5
6.5. Lien avec le système
sys_command(s)
Permet d'exécuter une commande du système d'exploitation représentée par la chaîne s. Quand la commande appelée provoque une erreur, sys_command génère une erreur "ERREUR SYSTEME" avec en complément d'erreur le status retourné par le système d'exploitation.
Sous certains systèmes d'exploitation (UNIX par exemple), ce prédicat peut
être grand consommateur de mémoire. On pourra alors le redéfinir à l'aide d'un coprocessus (sur ces systèmes, voir le chapitre sur les coprocessus).
getenv(s,x)
Unifie x avec la chaîne représentant la valeur de la variable système d'environnement dont le nom est défini dans la chaîne s. Si la variable n'est pas définie, x est unifié avec une chaîne vide.
set_import_dir(s)
Définit un répertoire de référence, qui est consulté après le répertoire courant quand les fichiers n'y ont pas été trouvés, par les primitives qui manipulent des fichiers en lecture telles que : input, insert, load. La chaîne de caractère s concaténée à un nom de fichier, doit définir un nom complet compréhensible par le système d'exploitation.
lkload(s1,s2)
Cette primitive n'est disponible que sous certains systèmes d'exploitation, voir le manuel d'utilisation. Lorsqu'elle est présente, elle permet d'effectuer un lien et un chargement dynamiques de programmes externes.
ms_err(n, s)
Unifie s avec le texte de l'erreur numéro n. Pour cela, Prolog fait une recherche dans le fichier d'erreurs err.txt qui doit être composé d'une suite de lignes du format suivant:
entier espace suite de caractères qui compose le texte de l'erreur.
Il est possible de rajouter des messages dans ce fichier en respectant le format indiqué. Il est conseillé d'utiliser des numéros de message au delà de 1000.
6.6. Outil de mise au point de programmes
Le debugger Prolog, ou outil de mise au point de programmes, permet une trace interactive des programmes sans modifier leur sémantique, et sans consommation supplémentaire de mémoire. Il permet, entre autres, de positionner des points d'arrêt, de pister en pas à pas avec saut éventuel de morceaux de programme, d'imprimer l'état de la démonstration en cours, et de visualiser les backtracking.
Le prédicat prédéfini statistics, après une éxécution du programme avec le debugger actif, peut aider à configurer les espaces de Prolog.
©PrologIA
R 6 - 6
Manuel de Référence
Note: Le code optimisé ne permet de reconstruire le source que de manière
équivalente, et ne permet pas au debugger de visualiser toutes les informations s'y rapportant. Si l'utilisateur désire suivre une représentation exacte de son programme, il faut que les règles aient été compilées sans optimisation avec l'option adéquate, au lancement de Prolog.
6.6.1. Mode trace
L'outil de mise au point mixe deux modes de fonctionnement :
• un mode trace, où il visualise l'exécution,
• un mode interactif, où il permet de prendre le contrôle au cours de l'exécution et d'effectuer des actions.
Dans un premier temps, si l'on ne veut pas se plonger dans le détail des commandes du mode interactif, le mode trace permet d'afficher toutes les informations nécessaires à la compréhension du programme.
Le mode trace est donc une approche simple et immédiate pour se rendre compte de l'exécution d'un programme.
Chaque but effacé est affiché avec ses arguments unifiés sur la règle choisie pour l'effacement courant. Les backtracking sont annoncés en précisant le prédicat pour lequel il restait des choix et le rang dans le paquet de la nouvelle règle.
Si le programme d'exemple menu.p2 est chargé et si le mode trace est actif, la séquence suivante montre la présentation de ces informations:
> repas(e,Chapon_farci,d); hors_d_oeuvre( Artichauts_Melanie) plat( Chapon_farci)
RECALL(2): plat / 1 plat( Chapon_farci) poisson( Chapon_farci) dessert( Sorbet_aux_poires)
{e=Artichauts_Melanie,d=Sorbet_aux_poires}
RECALL(2): dessert / 1 dessert( Fraises_chantilly)
{e=Artichauts_Melanie,d=Fraises_chantilly}
RECALL(3): dessert / 1 dessert( Melon_en_surprise)
{e=Artichauts_Melanie,d=Melon_en_surprise}
…
Pour faciliter la lisibilité (pour des programmes qui font eux-mêmes des écritures sur la sortie courante), les informations fournies par la trace peuvent être redirigées sur un fichier.
Activation, désactivation
Le mode trace est actif dès l'effacement d'un des prédicats prédéfinis trace/0 ou
trace/1. Il est désactivé par l'effacement du prédicat prédéfini no_trace/0.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 7
trace
Active la trace.
trace(f)
Active la trace. Redirigera toutes les impressions de la trace dans le fichier de nom f (chaîne Prolog). Le fichier est fermé à l'exécution d'un prédicat de désactivation de l'outil de mise au point. Si les impressions de mise au point
étaient déjà redirigées, le précédent fichier est fermé pour être remplacé par f.
no_trace
Désactive la trace.
6.6.2. Mode interactif
Si la trace est simple à activer, elle peut rapidement donner un flot d'informations important, où il serait difficile de se repérer. A ce moment là, le mode interactif devient d'une grande utilité puisqu'il permet d'aller directement à l'information importante. Ses atouts sont :
• les commandes de progression dans le code,
• le paramétrage de l'information à produire,
• les commandes annexes sur l'exécution.
Le mode interactif permet de choisir les informations qu'on veut extraire du programme, dans le format approprié, sans modifier le programme, et seulement dans les portions de code désignées. Il permet surtout, de prendre le contrôle où l'on souhaite, pour lancer des actions.
6.6.2.1.
Points d'arrêt
En mode interactif, le debugger va proposer sa banière d'invite (DBG) et attendre une commande sur chaque point d'arrêt. L'important pour avoir accès à l'interpréteur de commande est de bien définir les points d'arrêt. On en distingue deux types:
• les points d'arrêt fixes: ils subsistent tant que le mode debug est actif et tant qu'ils ne sont pas désactivés par commande;
• les points d'arrêt momentanés: ils ne sont valides que le temps d'une commande, qui définit en fait le prochain point de contrôle du debugger.
Un point d'arrêt se définit sur un prédicat, le debugger s'arrête ensuite dès que le programme courant fait appel à ce prédicat. L'arrêt de la machine Prolog se fera donc au moment où le but doit être effacé, avant que l'unification avec la tête de la règle choisie soit faite.
Des commandes spécifiques, ainsi que des prédicats prédéfinis, permettent de définir les points d'arrêt fixes. Les points d'arrêt momentanés sont définis implicitement par une commande de progression ou à l'activation du debugger.
©PrologIA
R 6 - 8
Manuel de Référence
Quand la machine Prolog tourne, avec le debugger actif, une interruption utilisateur poste un point d'arrêt momentané sur le but courant et le mode interactif est réactivé.
Les interruptions utilisateur sont ainsi interceptées et ne produisent pas d'erreur.
Voir les commandes du debugger +, -, b et les règles prédéfinies spy, no_spy,
show_spy.
6.6.2.2.
Progression dans le code
La progression dans le code se fait de point d'arrêt en point d'arrêt.
Trois granularités de progression sont possibles. On les utilisera en fonction de la
"proximité" de l'erreur, ou du type d'information que l'on veut extraire d'une partie du programme.
Les progressions possibles, à partir d'un point d'arrêt, sont:
1er cas: on connait l'endroit du programme qui fait défaut, on veut y aller directement et rapidement.
Réponse: (go to spy) aller jusqu'au prochain point d'arrêt fixe, qui peut se trouver n'importe où dans le code.
2ème cas: on veut voir "de près" l'exécution de la fonction, sans descendre au niveau bas de ses "sous-programmes".
Réponse: (next) aller jusqu'au prochain but de même niveau ou de niveau supérieur. On ne s'intéresse pas aux niveaux 1 inférieurs, c'est à dire que l'on ignore le corps du but courant.
3ème cas: on veut voir exactement toute l'exécution de la fonction, sans oublier aucune instruction.
Réponse: (step) aller jusqu'au prochain but.
A!ociation
Prolog
HERITAGE
1
Définition du niveau d'un but : on dira qu'un but de la question est de niveau 0. Si un but est de niveau n, les buts apparaissant dans la queue de sa règle sont tous du même niveau, égal à n+1.
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 9
Schématisons sur notre exemple de menu, les progressions possibles par next et step : repas
...
hors_d_oeuvre plat dessert step next viande
On remarquera que sur les assertions, step et next ont le même effet.
Les commandes de progression dans le code permettent de définir quel doit être le prochain arrêt et quelles informations doivent être obtenues entre temps. En effet, à chaque progression, on pourra choisir ou pas d'afficher les informations sur l'exécution de la portion de programme.
Voir les commandes du debugger RC, n, N, g, t.
6.6.2.3.
Terminer l'exécution
Ayant suffisamment dégrossi une partie du programme, il faut maintenant le terminer. Il est possible de :
- (go to end) continuer jusqu'à la fin du programme : en imprimant toutes les informations, en imprimant seulement celles concernant les points d'arrêt fixes, en quittant le mode debug et continuant normalement.
- (abort) interrompre le programme.
- (quit) quitter Prolog.
Voir les commandes du debugger a, e, G, T, q.
6.6.2.4.
Affichage des informations
Nous allons décrire maintenant à quels moments se font les affichages et sous quelle forme.
©PrologIA
R 6 - 10
Manuel de Référence
Quand?
Un Backtracking est généré
L'information est affichée. Elle indique que c'est une alternative (
RECALL
), le numéro de la nouvelle règle choisie et le but qu'on essaie à nouveau d'effacer. Le but à réessayer fait partie des buts à effacer, il peut donc être utilisé comme point d'arrêt.
Par exemple, sur l'éxécution :
> plat(Chapon_farci);
CALL: plat( Chapon_farci)
DBG:
CALL: viande( Chapon_farci)
DBG:
RECALL(2): plat( Chapon_farci)
DBG:
CALL: poisson( Chapon_farci)
DBG:
{}
L'information sur le backtracking est :
RECALL(2): plat( Chapon_farci)
Le numéro de la règle à réessayer est noté entre parenthèse
(2)
. Un point d'arrêt est mis au nouvel appel du but, sur : plat( Chapon_farci)
On a alors accès, sur cette autre alternative, à toutes les possibilités offertes par l'outil de mise au point.
Autre exemple, si un point d'arrêt fixe est mis sur plat/1, le debugger s'arrête sur toutes ses alternatives :
> repas(e,Chapon_farci,d);
CALL: repas( v147, Chapon_farci, v284)
DBG: +plat/1
DBG: g
(ajoute le point d'arrêt)
(go to spy)
CALL: plat( Chapon_farci)
DBG: g
RECALL(2): plat( Chapon_farci)
DBG: g
{e=Artichauts_Melanie,d=Sorbet_aux_poires}
{e=Artichauts_Melanie,d=Fraises_chantilly}
{e=Artichauts_Melanie,d=Melon_en_surprise}
CALL: plat( Chapon_farci)
DBG: g
RECALL(2): plat( Chapon_farci)
DBG: g
…
Une erreur est générée par la machine ou par un prédicat externe
Les points d'arrêt ne sont pas modifiés. Le message d'erreur est affiché, même si l'erreur est récupérée par un block, ainsi que la branche de résolution, au moment de l'erreur. Par exemple, sur l'exécution suivante :
> insert; toto(x,y) -> block(x,titi(y)) outl(x);
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 11 titi(y) -> string_ident(y,z) outl(z);
;
{}
> debug toto(x,12);
CALL: toto( v156, 12)
DBG: block
CALL: titi( 12)
DBG:
CALL: string_ident( 12, v360)
DBG:
ARGUMENT DE MAUVAIS TYPE
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^ titi / 1, rule number 1
^^^^^^^^ toto / 2, rule number 1
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
CALL: outl( 253)
DBG:
L'information sur l'erreur est :
ARGUMENT DE MAUVAIS TYPE
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^ titi / 1, rule number 1
^^^^^^^^ toto / 2, rule number 1
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
Il est possible de supprimer par commande, pour une session de mise au point, l'affichage de ces informations sur les erreurs. Voir la commande du debugger s.
Les erreurs produites en Prolog par block_exit ne sont pas mises en évidence par ce mécanisme. La règle prédéfinie block_exit, est traitée comme un prédicat ordinaire.
Pour être averti de ce type d'erreur, il suffit de mettre un point d'arrêt sur
block_exit/1 et block_exit/2.
Sur un point d'arrêt, c'est à dire quand un but est appelé
L'impression des buts peut se faire à deux moments, nous les appellerons avant unification et après unification. Voir les commandes du debugger p, P.
Les buts peuvent être imprimés avant l'unification, c'est-à-dire l'impression montre le but tel qu'il apparait dans la résolvante avant son unification avec la tête de la règle choisie pour son effacement.
Ils peuvent être imprimés après unification, c'est-à-dire l'impression a lieu après le choix de la règle et juste avant le prochain point d'arrêt. Par conséquent, on pourra avoir des résultats différents d'une exécution à l'autre pour des progressions de pas différents. Par exemple pour un but donné, si le pas de progression sous le debugger est celui d'un step, la machine Prolog aura simplement unifié le but avec la tête de la règle. Alors qu'après un pas tel que next, la machine Prolog aura exécuté le corps de la règle où des unifications se seront passées, et les arguments du but n'auront pas forcément les mêmes valeurs que précédemment.
©PrologIA
R 6 - 12
Manuel de Référence
Voyons sur notre exemple, la valeur des arguments après unification, dans les deux cas de progression :
> repas(e,p,d);
CALL: repas( v156, v191, v226)
DBG: P1
DBG:
CALL: hors_d_oeuvre( v156)
DBG:
(valide l'impression après unification) hors_d_oeuvre( Artichauts_Melanie)
CALL: plat( v191)
DBG: plat( v191)
CALL: viande( v191)
DBG: viande( Grillade_de_boeuf)
CALL: dessert( v226)
}
…
DBG: g
(go to spy)
{e=Artichauts_Melanie,p=Grillade_de_boeuf,d=Sorbet_aux_poires
> repas(e,p,d);
CALL: repas( v156, v191, v226)
DBG: P1
DBG:
CALL: hors_d_oeuvre( v156)
DBG: hors_d_oeuvre( Artichauts_Melanie)
CALL: plat( v191)
(next)
DBG: n plat( Grillade_de_boeuf)
CALL: dessert( v226)
DBG: g
{e=Artichauts_Melanie,p=Grillade_de_boeuf,d=Sorbet_aux_poires
}
…
Comment?
Présentation
D'une manière générale pour faciliter la lisibilité de la mise au point et différencier visuellement les messages du programme et ceux du debugger, il est possible de définir une marge (en nombre de caractères) pour les impressions du debugger.
Quelque soit le message transmis par le debugger, il sera indenté du nombre de caractères de la marge, pour l'affichage.
> repas(e,p,d);
CALL: repas( v156, v191, v226)
DBG:
CALL: hors_d_oeuvre( v156)
DBG:
CALL: plat( v191)
DBG: n
(next)
CALL: dessert( v226)
DBG:
{e=Artichauts_Melanie,p=Grillade_de_boeuf,d=Sorbet_aux_poires
}
RECALL(2): dessert(v190)
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 13
DBG:
{e=Artichauts_Melanie,p=Grillade_de_boeuf,d=Fraises_chantilly
}
RECALL(3): dessert(v190)
DBG:
{e=Artichauts_Melanie,p=Grillade_de_boeuf,d=Melon_en_surprise
}
RECALL(2): viande(v165)
DBG:
CALL: dessert( v226)
DBG:
Une autre alternative est de rediriger les messages du debugger dans un fichier.
Dans ce cas, seule la séquence d'invite apparaîtra dans l'unité de trace.
Voir la commande du debugger i, ou les règles prédéfinies debug(f) et debug(n,f).
Précision
La précision de l'affichage des buts est paramétrable, selon la quantité d'informations que l'on souhaite obtenir. Trois modes sont disponibles :
- impression des prédicats sans leurs arguments éventuels mais précisant l'arité, sous la forme identificateur/arité.
- impression des prédicats avec leurs arguments éventuels, en notation fonctionnelle avec une limite en profondeur paramétrable. A partir d'une profondeur de 1000, la totalité du terme est imprimé. L'impression des arbres infinis en totalité utilise sa représentation en système d'équations. On ne sait pas dire à priori si l'impression restreinte d'un arbre infini est possible.
- impression des prédicats avec leurs arguments éventuels, sous forme d'arbre par le prédicat Dessin:draw_tree. Le module de dessin d'arbre doit
être chargé pour pouvoir utiliser ce mode. Il est possible de modifier la règle
Dessin:draw_tree, de manière à redéfinir ce qui est imprimé par le debugger.
Détail de la notation fonctionnelle en profondeur limitée :
On attribue une profondeur à chaque atome du terme à imprimer. Ne seront affichés que les atomes dont la profondeur est inférieure à celle fixée.
Le comptage se fait récursivement à partir du but à afficher, il démarre à 1. La profondeur est incrémentée lorsqu'on passe d'un terme à un sous terme de la manière suivante :
Si un terme fonctionnel f(a1, t2, …, as) apparaît dans un terme de profondeur n alors le foncteur f est de profondeur n, et ses arguments a1, …, as sont de profondeur n+1.
©PrologIA
R 6 - 14
Manuel de Référence
Si un n-uplet <t1, t2, …, ts> (t1 n'est pas un identificateur) apparaît dans un terme de profondeur n alors l'atome <> est de profondeur n, et les arguments du n-uplet t1, t2, …, ts sont de profondeur n+1.
Si une liste x.y apparaît dans un terme de profondeur n, alors x est de profondeur n et y est de profondeur n+1.
Par exemple, voici comment sont définies les profondeurs des atomes pour le terme suivant : element_de( 12, 1.
1 2 2
2.
3
3.
4
4.
5
5.
6 nil,
7 2 non)
En limite de profondeur 3, il sera imprimé : element_de( 12, [ 1, 2,…], non);
Voir la commande du debugger m.
Choix de l'information
Certaines parties du programme ont déjà été prouvées, il n'est pas nécessaire de les vérifier. En mode interactif, par la progression next, on peut éviter de développer le corps d'un prédicat. En mode trace (trace totale ou impression des informations entre deux points d'arrêt) la même possibilité est offerte.
Par exemple, si plat/1
ne doit pas être développé, on obtient :
> trace repas(e,p,d) !; hors_d_oeuvre( Artichauts_Melanie) plat( Grillade_de_boeuf) dessert( Sorbet_aux_poires)
{e=Artichauts_Melanie,p=Grillade_de_boeuf,d=Sorbet_aux_poires
}
… au lieu de :
> repas(e,p,d) !; hors_d_oeuvre( Artichauts_Melanie) plat( v191) viande( Grillade_de_boeuf)
}
… dessert( Sorbet_aux_poires)
{e=Artichauts_Melanie,p=Grillade_de_boeuf,d=Sorbet_aux_poires
Voir les commandes du debugger >, <, j.
6.6.2.5.
Informations annexes sur l'exécution
Pour détailler l'état du programme en cours d'exécution, sur un but particulier, on peut se poser les questions : où en est-on dans l'exécution du programme? Et quelle règle essaie-t-on d'effacer pour ça?
Sur chaque point d'arrêt, le debugger permet de situer le but appelé par rapport à l'arbre de résolution et par rapport à la base de règles.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 15
Quelle est la branche courante de l'arbre de résolution?
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^ titi / 1, rule number 1
^^^^^^^^ toto / 2, rule number 1
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
Quelle est la position du but dans le programme?
---> repas / 3, rule number 1, goal number 3 in queue
En fait, la branche courante de la résolution est donnée par l'état de la pile de récursion. L'optimisation de l'appel terminal pratiquée par la machine Prolog, ne permet pas de visualiser les règles pour lesquelles le but en cours d'exécution est le dernier de la queue. Par exemple :
> repas(e,p,d);
CALL: repas( v156, v191, v226)
DBG: s ici la pile est vide, puisque repas n'est pas encore installé.
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
DBG:
CALL: hors_d_oeuvre( v156)
DBG: s ici elle contient repas, puisque c'est le but appelant.
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^ repas / 3, rule number 1
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
DBG:
CALL: plat( v191)
DBG: s ici aussi, puisque plat et hors_d_oeuvre sont de même profondeur.
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^ repas / 3, rule number 1
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
DBG:
CALL: viande( v191)
DBG: s ici plat n'apparait pas puisque viande est son appel terminal.
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^ repas / 3, rule number 1
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
DBG:
CALL: dessert( v226)
DBG: s
^^^^^^^^^^^^^^^^TOP^^^^^^^^^^^^^^^^^^
^^^^^^^^^^^^^^^BOTTOM^^^^^^^^^^^^^^^^
DBG: ici repas n'apparait plus puisque dessert est son appel terminal.
Enfin, pour avoir accès au plus grand nombre d'informations, il est possible d'ouvrir une session Prolog. Ainsi, il est possible de vérifier les effets de bords du programme, de relancer une partie du programme, de lancer un programme annexe, de vérifier la base de règles, de consulter l'occupation des piles, … .
A l'ouverture de la session, le prompt de la ligne de commande est affiché dans la console et l'utilisateur peut entrer n'importe quel but Prolog. A la fermeture de la session (par l'effacement de quit par exemple) la mise au point continue où elle en
était. Il faut noter que les effets de bord de la session sont gardés: affectations, ajout/suppression de règles, ouverture/fermeture de fichier…
©PrologIA
R 6 - 16
Manuel de Référence
Par exemple, l'exécution de repas(e,Poulet,d)
provoque un échec. Mettons au point :
> repas(e,Poulet,d);
CALL: repas( v156, Poulet, v313)
DBG:
CALL: hors_d_oeuvre( v156)
DBG:
CALL: plat( Poulet)
DBG:
CALL: viande( Poulet)
DBG:
RECALL(2): plat( Poulet)
DBG:E pourquoi viande(Poulet) a échoué?
on ouvre une session,
> list(viande/1); le prompt Prolog apparait, viande(Grillade_de_boeuf) -> ; viande(Poulet_au_tilleul) -> ;
{}
> quit;
Elapsed time: 19s
Goodbye...
on consulte la base de règles, on termine la session,
DBG: w on est de retour sous le debugger!
---> plat / 1 , rule number 2, on backtracking
DBG: g
>
Voir les commandes du debugger s, w, E.
6.6.2.6.
Configuration
Comme nous l'avons déjà vu précédemment, il est possible de définir les comportements du debugger. L'ensemble des paramètres de comportement est appelé la configuration.
Certains paramètres de la configuration peuvent être précisés au lancement de l'outil, ils seront valides durant la session de mise au point (c'est à dire tant qu'on n'a pas désactivé l'outil ou tant qu'on ne l'a pas ré-activé avec d'autres paramètres) tant qu'une commande ne les modifie pas.
Les comportements paramétrables sont :
à l'activation ou sous l'interpréteur de commande du debugger : la progression dans le code, le moment d'impression des arguments, sous l'interpréteur de commande du debugger uniquement : la présentation des informations, dont : un paramètre d'indentation des messages, un paramètre de précision ou profondeur d'impression, le choix des informations : sur les erreurs, entre deux points d'arrêt, pendant la trace.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 17
Une configuration standard est prédéfinie. Elle comprend : arrêt sur le prochain but, impression avant unification, aucune indentation, impression des termes par out en profondeur 4, impression d'un message et de la pile de récursion sur les erreurs.
Des commandes de l'outil de mise au point permettent de connaître sa configuration et toute autre information relative à la mise au point : l'état des options, la liste des points d'arrêt actifs, la liste des prédicats non développés en trace, la liste des commandes de l'outil.
Voir les commandes du debugger S, b, j, h et la règle prédéfinie show_spy.
6.6.3. Activation d'un mode de mise au point
L'outil de mise au point est activé dès l'effacement de l'un des prédicats prédéfinis
trace/0, trace/1, debug/0, debug/1 ou debug/2. Il est désactivé par l'effacement de l'un des prédicats prédéfinis no_trace/0 ou no_debug/0.
Un drapeau permet de définir la configuration de démarrage. Il vaut la somme des valeurs choisies parmi :
1 arrêt sur le premier appel de but.
2 arrêt sur le premier point d'arrêt fixe.
8 Option impression après unification validée
16 Option impression avant unification validée
Un drapeau égal à 0 correspond à la configuration standard prédéfinie.
debug(n) ou
debug(n, f)
sont les prédicats généraux d'activation du debugger. n doit être un entier connu au moment de l'appel. Il précise la configuration de démarrage, telle qu'elle a été définie ci-dessus. Quand n vaut 0, cela correspond à la configuration prédéfinie standard. f est un nom de fichier dans lequel seront redirigées les impressions du debugger. Le fichier est fermé à l'exécution d'un prédicat de désactivation de l'outil de mise au point. Si les impressions de mise au point étaient déjà redirigées, le précédent fichier est fermé pour être remplacé par f.
debug ou
debug(f)
sont les prédicats de réactivation du debugger. Ils ne modifient pas la configuration en place. f est un nom de fichier dans lequel seront redirigées les impressions du debugger. Le fichier est fermé à l'exécution d'un prédicat de désactivation de l'outil de mise au point. Si les impressions de mise au point étaient déjà redirigées, le précédent fichier est fermé pour être remplacé par f.
©PrologIA
R 6 - 18
Manuel de Référence
trace ou
trace(f)
sont des prédicats particuliers du debugger. Ils l'activent dans une configuration précise, celle du mode trace, représentée par le drapeau 19. f est un nom de fichier dans lequel seront redirigées les impressions du debugger.
Le fichier est fermé à l'exécution d'un prédicat de désactivation de l'outil de mise au point. Si les impressions de mise au point étaient déjà redirigées, le précédent fichier est fermé pour être remplacé par f.
no_trace ou
no_debug
sont des prédicats de désactivation de l'outil de mise au point, quel que soit son mode, sans altérer la configuration en place.
6.6.4. Gestion des points d'arrêt
Les règles prédéfinies qui permettent de manipuler les points d'arrêt sont:
spy(i/a)
Place un point d'arrêt sur l'entrée de la règle de nom i et d'arité a. Ce point d'arrêt n'est pris en compte que lorsque le debugger est activé avec un mode le permettant.
no_spy(i/a)
Enlève tout point d'arrêt placé sur la règle de nom i et d'arité a.
show_spy(l)
Unifie l avec la liste des primitives sur lesquelles un point d'arrêt est actif. Les primitives sont mentionnées sous la forme habituelle i/a avec i son identificateur d'accès et a son arité.
6.6.5. Commandes du mode interactif
A chaque point d'arrêt, le debugger imprime la séquence d'invite "DBG:" pour indiquer son attente d'une commande utilisateur. La frappe d'un retour chariot seul, noté RC dans ce chapitre, correspond à la commande par défaut step du mode interactif. Les autres commandes se terminent par un retour chariot.
Les commandes disponibles sont:
RC (step) Continue jusqu'au prochain appel de règle.
a
(abort program) Génère une interruption utilisateur.
b
(breakpoints) Affiche les points d'arrêt actifs. Voir aussi le prédicat show_spy.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 19
e
(end debugging and go) Provoque la terminaison du mode debug et poursuit l'exécution.
f
E
(Execute) Empile une session Prolog.
(fail) Provoque un échec sur le but affiché.
g
(go to spy) Continue sans impression jusqu'au prochain point d'arrêt fixe.
G entier
(Go to end) Continue jusqu'à la fin du programme en imprimant seulement les points d'arrêt fixes si entier vaut 0. Si entier est différent de 0, imprime aussi les buts figurant dans la queue des points d'arrêt fixes.
h
(help) Liste les commandes disponibles.
i entier
(indent) La valeur de entier définit la marge, en nombre de caractères pour l'impression.
j
(jump) Donne la liste des règles dont on ignore le corps, en mode trace.
l entier
(length) La valeur de entier définit la longueur de la ligne pour les sorties.
m entier
(mode) La valeur de entier permet de choisir le mode d'affichage des buts
Prolog:
0
1
[2, 1000]
> 1000
Afficher sous la forme: identificateur/arité.
Affichage par le programme de dessin d'arbres.
Affichage en profondeur limité par entier.
Affichage complet du terme.
n
(next) Arrêt sur le prochain but qui n'est pas un sous but.
N
(Next while tracing) Arrêt sur le prochain but qui n'est pas un sous but, en imprimant les informations de l'exécution de la portion de programme, selon les options courantes choisies. L'effet de la commande > est ignoré dans ce cas.
p booléen
(print before unification) L'impression des buts avant unification est activée si
booléen vaut 1 ou désactivée si booléen vaut 0. Si booléen n'est pas mentionné, il équivaut à 0.
P booléen
(Print after unification) L'impression des buts après unification est activée si
booléen vaut 1 ou désactivée si booléen vaut 0. Si booléen n'est pas mentionné, il équivaut à 0.
©PrologIA
R 6 - 20
Manuel de Référence s q
(quit) Quitte Prolog, et retourne au niveau système hôte.
(stack) Affiche la pile Prolog des règles en cours d'exécution.
s 0 Permet d'éliminer l'impression du message et de la pile Prolog lors d'une erreur. s 1 la rétablit.
t
S
(State) Affiche les options courantes.
(trace to spy) Continue jusqu'au prochain point d'arrêt fixe, en imprimant les informations de l'exécution de la portion de programme, selon les options courantes choisies.
T
(Trace to end) Continue jusqu'à la fin du programme, en imprimant les informations d'exécution, selon les options courantes choisies.
w
(where) Affiche la position du but courant dans le programme.
+ ident/arité
Met un point d'arrêt sur le paquet de règles d'identificateur d'accès ident et d'arité arité. Voir aussi le prédicat spy.
- ident/arité
Supprime tout point d'arrêt sur le paquet de règles d'identificateur d'accès
ident et d'arité arité. Voir aussi le prédicat no_spy.
- a Permet de supprimer tous les points d'arrêt en une seule commande.
> ident/arité
Indique que le prédicat d'identificateur d'accès ident et d'arité arité ne devra pas être développé en trace.
< ident/arité
Annule l'effet de la commande inverse (>) sur le prédicat d'identificateur d'accès ident et d'arité arité.
< a Permet d'annuler l'effet de toutes les commandes inverses (>) qui ont eu lieu sur des prédicats.
Les points d'arrêt fixes, les prédicats à ne pas développer en trace, le mode d'impression et la définition de la marge, sont permanents. Ils ne sont pas modifiés par une activation spécifique ou par défaut de l'outil. Ils ne peuvent être modifiés ou supprimés qu'explicitement.
6.6.6. Exemple
A titre d'illustration, intéressons nous au programme d'exemple des menus livré dans le kit. Les commentaires de l'exécution sont donnés en italique.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 21
On vérifie les règles chargées, puis on se place en mode debug. Enfin, on lance le but dif(d, Sorbet_aux_poires) balanced_meal( Truffes_sous_le_sel, p, d)
.
Ce qui est en gras est entré par l'utilisateur. Les lignes données par l'utilisateur doivent être terminées par un retour-chariot que nous n'indiquons pas, sauf dans le cas d'une ligne vide notée RC.
> dictionary;
DICTIONARY CONTENT OF "" balanced_meal / 3 calories / 2 dessert / 1 fish / 1 hors_d_oeuvre / 1 main_course / 1 meal / 3 meat / 1 smaller / 2 sumof / 4 value / 4
{}
> debug dif(d, Sorbet_aux_poires)
balanced_meal(Truffes_sous_le_sel,p,d);
CALL: dif( v254, Sorbet_aux_poires)
DBG: RC
CALL: balanced_meal( Truffes_sous_le_sel, v623, v743)
On ajoute à la configuration par défaut l'impression après unification et une indentation.
DBG: P1
DBG: i5
On vérifie l'état des options.
DBG: S
Print after unification
Print before unification
Print mode = out, depth: 4
Errors are printed
Indentation= 5
On poursuit.
DBG: RC
CALL: meal( Truffes_sous_le_sel, v623, v743)
C'est un prédicat important, on y met un point d'arrêt fixe et on vérifie les points d'arrêt fixes actifs.
DBG: +meal/3
DBG: b
meal/3
On poursuit.
DBG: RC
meal( Truffes_sous_le_sel, v623, v743)
CALL: hors_d_oeuvre( Truffes_sous_le_sel)
DBG: RC
hors_d_oeuvre( Truffes_sous_le_sel)
CALL: main_course( v623)
C'est un prédicat important, on y met un point d'arrêt fixe et on vérifie les points d'arrêt fixes actifs.
DBG: +main_course/1
DBG: b
main_course/1
©PrologIA
R 6 - 22
Manuel de Référence
meal/3
On poursuit.
DBG: RC
main_course( v623)
CALL: meat( v623)
DBG: RC
meat( Grillade_de_boeuf)
CALL: dessert( v743)
DBG: RC
RECALL(2): dessert( v743)
DBG: RC
dessert( Fraises_chantilly)
CALL: value( Truffes_sous_le_sel, Grillade_de_boeuf,
Fraises_chantilly, v817)
On ne veut pas s'arrêter dans ce prédicat, mais voir ce qui s'y passe.
DBG: N
value( Truffes_sous_le_sel, Grillade_de_boeuf,
Fraises_chantilly, v817)
CALL: calories( Truffes_sous_le_sel, v828)
calories( Truffes_sous_le_sel, 212)
CALL: calories( Grillade_de_boeuf, v833)
calories( Grillade_de_boeuf, 532)
CALL: calories( Fraises_chantilly, v838)
calories( Fraises_chantilly, 289)
CALL: sumof( 212, 532, 289, v817)
Les expressions arithmétiques ont été optimisées à la compilation.
add
add
sumof( 212, 532, 289, 1033)
CALL: smaller( 1033, 800)
DBG: N
inf
RECALL(3): dessert( v743)
DBG: RC
dessert( Melon_en_surprise)
CALL: value( Truffes_sous_le_sel, Grillade_de_boeuf,
Melon_en_surprise, v817)
On vérifie la position du prédicat.
DBG: w
---> balanced_meal / 3 , rule number 1, goal number 2 in queue
Pour la suite, le détail du prédicat n'est pas intéressant, on supprime son développement et on vérifie la liste des prédicats dans ce cas.
DBG: >value/4
DBG: j
value/4
On poursuit.
DBG: n
value( Truffes_sous_le_sel, Grillade_de_boeuf,
Melon_en_surprise, 866)
CALL: smaller( 866, 800)
DBG: >smaller/2
DBG: j
smaller/2
value/4
DBG: n
RECALL(2): dessert( v743)
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 23
On souhaite avoir la trace jusqu'au prochain point d'arrêt avec seulement l'impression après unification, on désactive donc l'impression avant unification.
DBG: p0
DBG: t
dessert( Fraises_chantilly)
value( Truffes_sous_le_sel, Poulet_au_tilleul,
Fraises_chantilly, 901)
RECALL(3): dessert( Melon_en_surprise)
value( Truffes_sous_le_sel, Poulet_au_tilleul,
Melon_en_surprise, 734)
smaller( 734, 800)
{d=Melon_en_surprise,p=Poulet_au_tilleul}
RECALL(2): main_course(v165)
. DBG: p1
On se trouve arrêté sur un point d'arrêt fixe, on s'intéresse aux prédicats importants (ils ont un point d'arrêt fixe) jusqu'à la fin.
DBG: +meat/1
DBG: +fish/1
DBG: b
fish/1
main_course/1
meal/3
meat/1
DBG: G
main_course( v623)
CALL: fish( v623)
fish( Bar_aux_algues)
{d=Fraises_chantilly,p=Bar_aux_algues}
{d=Melon_en_surprise,p=Bar_aux_algues}
RECALL(2): fish( v623)
fish( Chapon_farci)
{d=Fraises_chantilly,p=Chapon_farci}
{d=Melon_en_surprise,p=Chapon_farci}
>
6.7. Modification et visualisation de l'état courant
alloc alloc/12
Décrit l'état des piles Prolog. La version sans argument écrit les valeurs courantes sur la sortie courante. Les arguments s'il sont présents, sont unifiés respectivement avec l'espace occupé et l'espace libre de:
1,2: espace du code,
3,4: pile de copie des structures (heap),
5,6: pile des environnements et variables locales (stack),
7,8: pile de restauration (trail),
9,10: espace du dictionnaire,
11,12: aucun espace.
©PrologIA
R 6 - 24
Manuel de Référence edinburgh
Permet de passer en mode Edinburgh lorsque l'on est en mode Prolog II : change de syntaxe, ajoute des règles prédéfinies, ajoute et modifie des opérateurs. Cette primitive provoque le chargement du module edinburg.mo.
get_option(o,x)
Permet de connaître la valeur d'une option de comportement dans la session. o doit être une chaîne d'un caractère représentant l'option. x est unifié avec une chaîne d'un caractère représentant la valeur de l'option. La signification des caractères est celle définie par les conventions de l'option de la ligne de commande -f (cf. §U2.3). Par exemple pour connaître la syntaxe des réels choisie pour la session:
> get_option("r",x);
{x="P"}
prologII
Permet de passer en mode Prolog II lorsque l'on est en mode Edinburgh : change de syntaxe, modifie les opérateurs, supprime les règles prédéfinies spécifiques Edinburgh.
prologIIE
Permet de passer en mode Prolog II lorsque l'on est en mode Edinburgh, en gardant les règles prédéfinies spécifiques Edinburgh : change de syntaxe, modifie les opérateurs.
state
Affiche l'état courant des piles, et le nom des modules se trouvant en mémoire.
statistics statistics/12
Permet de visualiser les valeurs maximales atteintes durant une session dans les espaces Prolog. La mémorisation de ces valeurs est effectuée en mode
debug uniquement. Si debug n'a jamais été effacé avant l'appel à statistics, les valeurs affichées ne sont pas significatives. Les arguments ont la même signification que pour le prédicat alloc.
set_options(o)
Permet de changer les options ayant pu être définies au lancement par l'option
-f de la ligne de commande, sans sortir de Prolog. o est une chaîne de caractères. Exemple pour passer en syntaxe de variable Edinburgh et en syntaxe de réels standard:
> set_options("vErS");
version(o)
Permet d'obtenir la version courante de Prolog. Par exemple, si la version courante est la version 3.1,
> version(o);
{o=301}
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
L'environnement
R 6 - 25
6.8. Gestion automatique des espaces et des piles
Le fonctionnement d'une machine suppose des piles, pour gérer l'exécution, et des espaces pour stocker les structures du programme.
Le système Prolog II+, qui est une machine virtuelle, possède trois piles :
- pile de récursion (stack) : qui contient les environnements successifs des appels de la démonstration en cours;
- pile de copie (heap) : qui contient une copie des structures de la démonstration en cours;
- pile de restauration (trail) : qui contient la mémorisation des valeurs des arguments, pour le backtracking; et deux espaces pour les structures internes :
- espace pour le code : qui contient toutes les règles compilées et les données statiques de tous les modules;
- espace pour le dictionnaire : qui contient tous les identificateurs et les accès aux règles créés durant la session.
Le système Prolog II+ comporte un mécanisme de récupération et tassage des piles et des espaces, ainsi qu'un mécanisme de réallocation.
Pendant les opérations qui consomment les piles ou les espaces (assert, insert,
def_array, récursivité non terminale…), ces piles ou espaces peuvent arriver à saturation. Le mécanisme de récupération d'espace et de tassage est alors déclenché automatiquement. Si malgré cela, la place libre restant dans la pile ou l'espace est toujours insuffisante, le mécanisme de réallocation est à son tour déclenché automatiquement. Il est chargé de réallouer la pile ou l'espace en cause, avec une taille supérieure de 25% à ce qu'elle était. Ce pourcentage est celui par défaut. Il est possible de le modifier à l'aide d'une option sur la ligne de commande au démarrage de Prolog (cf U2.3).
Il est possible de déclencher explicitement un des deux mécanismes sur une pile ou un espace, à l'aide des prédicats gc et realloc.
gc(x)
déclenche la récupération et le tassage de l'espace désigné par x. x peut prendre les valeurs suivantes:
:code
pour l'espace du code,
:dictionary pour l'espace du dictionnaire,
:stacks
pour les piles (qui ne peuvent pas être dissociées pour cette opération).
©PrologIA
R 6 - 26
Manuel de Référence
N.B. : Lorsque le récupérateur de l'espace du code est activé, toutes les règles en cours d'utilisation dans l'effacement courant sont considérées comme devant être conservées, même si elles ont été précédemment supprimées et sont donc inaccessibles pour une nouvelle exécution.
N.B. : Le récupérateur d'espace doit déterminer les objets inutiles pour pouvoir réutiliser leur place. Les structures succeptibles d'être manipulées depuis un langage externe et sur lesquelles le récupérateur d'espace peut agir, sont les identificateurs
Prolog. Pour permettre à Prolog de savoir si un identificateur est toujours utile et pour éviter à l'utilisateur de continuer à manipuler une représentation éventuellement obsolète, l'utilisateur doit préciser à Prolog s'il utilise ou pas un identificateur particulier depuis l'extérieur, afin que le récupérateur d'espace ne le supprime pas.
Ceci peut être fait grâce aux fonctions set_permanent_symbol et
reset_permanent_symbol (cf. Chapitre 7 de ce manuel).
realloc(x,y)
déclenche la réallocation de l'espace désigné par x, d'une taille réajustée par le coefficient y. x peut prendre les valeurs :code, :dictionary, :stack, :heap, trail, selon qu'il s'agisse respectivement : du code, du dictionnaire, de la pile de récursion, de la pile de copie ou de la pile de restauration. y est un entier, il indique le pourcentage d'augmentation (s'il est positif) ou de diminution (s'il est négatif) de la taille précédente.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
7. Extensions de Prolog avec des langages externes.
7.1. Principes des fonctions de communication de données
7.2. Fonctions de communication de données simples
7.3. Fonctions de communication de termes quelconques
7.4. Méthode des descripteurs
7.5. Données partagées
7.6. Ajout de fonctions externes
7.7. Ajout de fonctions externes à appel direct
Ce chapitre montre comment faire des liens avec des données externes ou comment ajouter de nouvelles règles prédéfinies écrites en C ou tout autre langage compatible avec C. Les exemples qui y sont décrits, se trouvent déjà dans le fichier expredef.c du kit. Un équivalent pour Fortran est donné dans le fichier fprouser.eg du kit.
Pour étendre Prolog avec des fonctions d'un langage externe, il est nécessaire de disposer d'un module de communication pour:
- créer un lien entre le prédicat prolog et la fonction externe,
- faire la transformation des données échangées, entre la structure Prolog et la structure externe.
Différentes méthodes sont proposées pour réaliser ces deux traitements:
- le lien se définit en C: c'est la méthode des descripteurs décrite dans ce chapitre,
- le lien se définit en Prolog: c'est la méthode des parasites exposée en annexe D,
- la transformation des données se fait en Prolog: c'est la méthode des descripteurs concernant les fonctions à appel direct, c'est à dire qui ne nécessitent pas d'interface C pour le passage d'arguments,
- la transformation des données se fait en C ou en Fortran: dans les autres cas.
Pour étendre Prolog avec certains types de données, qui seront alors partagées entre
Prolog et C, il suffira de:
- créer un lien entre le terme Prolog et la donnée externe, par la méthode des descripteurs.
© PrologIA
R 7 - 2
Manuel de référence
Nous débuterons ce chapitre par la description des fonctions de communication de termes, dans le cas où la récupération et l'affectation de la valeur des termes se font en C ou en Fortran. En effet ces fonctions sont utiles dans de nombreux cas. Nous exposerons ensuite, comment faire le lien avec des objets externes par la méthode des descripteurs. Nous terminerons en détaillant les possibilités offertes par cette méthode.
A!ociation
Prolog
HERITAGE
7.1. Principes des fonctions de communication de données
Nous allons décrire dans les paragraphes 7.2. et 7.3. suivants, les fonctions de communication qui sont utilisables lorsque dans une procédure d'un langage externe, il est nécessaire de connaître la valeur d'un argument du prédicat Prolog lié
à la procédure, ou bien de lui affecter une valeur.
Nous donnons ici quelques principes de fonctionnement communs à toutes ces fonctions.
Etant donné les différences de conventions entre langages en ce qui concerne le passage d'argument, un jeu de fonctions est fourni pour le langage C, et un jeu
équivalent est fourni pour le langage FORTRAN. Les routines FORTRAN ont les mêmes arguments, mais passés par adresse. Leur nom commence par la lettre "f" et ne contient pas de signe "_". Les routines FORTRAN peuvent également être utilisées pour des programmes PASCAL en déclarant tous les arguments en var.
Dans tous les cas, une donnée doit être transmise entre Prolog et un langage externe. Par conséquent, à l'appel de la fonction de communication, il est nécessaire de préciser le rang de l'argument du prédicat Prolog, et la structure de la donnée externe associée. Puis, selon le sens de communication demandé, il s'agira de
"copier" l'argument Prolog dans la structure externe choisie, ou bien il s'agira de construire dans les piles de Prolog le terme représenté par la structure externe et de l'unifier avec l'argument du prédicat associé.
D'autre part, il peut arriver que la communication ne puisse pas se faire. Il est alors nécessaire d'en être informé. Les fonctions de communication ont pour cela un argument qui sert d'indicateur. Il est nul quand la fonction a pu être réalisée, strictement positif quand une erreur doit être générée, et égal à -1 quand un
backtracking doit être provoqué.
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 3
Le système de gestion des erreurs de Prolog impose que le programme externe soit immédiatement quitté dès qu'une erreur ou un backtracking est notifié, autrement dit dès que l'indicateur devient non nul. En effet quand l'indicateur est non nul, cela reflète une situation anormale qui nécessite dans le programme Prolog un backtracking ou une erreur, qui pourront être réalisés seulement lorsque la machine
Prolog sera relancée, c'est à dire lorsque le programme externe se terminera. Il peut s'agir également d'une erreur plus grave, ayant pour conséquence l'arrêt de la machine Prolog.
7.2. Fonctions de communication de données simples
Les fonctions décrites ici conviennent dans les cas simples où le type des données à transférer est connu au moment de l'appel et correspond à un type du langage externe, typiquement les entiers, les réels et les chaînes de caractères. Pour les autres cas, d'autres fonctions de communication plus générales existent, elles sont décrites au § 7.3.
Chaque type de données a une fonction 1 qui lui est associée pour transférer cette donnée depuis Prolog vers le programme externe, et une autre pour la transférer depuis la fonction externe vers Prolog.
7.2.1. Test du type d'un argument
La fonction get_arg_type permet de tester le type d'un argument Prolog, afin de pouvoir choisir la procédure de communication appropriée pour récupérer sa valeur.
Cette fonction permet également de connaître la taille en octets qu'occuperait l'argument. Ceci est très utile en particulier pour connaître la taille à allouer pour récupérer une chaîne. La fonction renvoie une erreur uniquement lorsqu'il n'existe pas d'argument du rang demandé.
get_arg_type(no_arg, value, lgPtr, err) int no_arg; char *value; int *lgPtr; int *err; integer*4 function fgetargtype(no_arg, value, lgPtr, err) integer*4 no_arg,lgPtr,err; character** value;
no_arg
Entier donnant le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite.
1Des fonctions supplémentaires existent pour le transfert de chaînes. Voir l'annexe E.
© PrologIA
R 7 - 4
Manuel de référence value
Adresse d'une variable de type caractère dans laquelle sera écrit le "code" du type de l'argument. La signification des caractères retournés est la suivante:
"code" type
'I'
'R'
'S'
'N'
'E'
'V'
'D'
'T' entier réel chaîne identificateur
nil
variable libre liste n-uplet
Le codage des types est le même que celui choisi pour les procédures
get_term, put_term (cf. §7.2.1.), excepté pour l'identificateur nil qui est repéré par le 'E'.
lgPtr
Adresse d'une variable entière dans laquelle sera écrite la taille en octets occupée par l'argument. Lorsque l'argument n'est pas un terme simple, autrement dit lorsque c'est une liste ou un n-uplet, la taille est non spécifiée.
err
La variable pointée est différente de 0 si une erreur s'est produite, égale à 0 sinon.
La fonction retourne la valeur correspondant à
*err
.
7.2.2. Transfert de données simples de Prolog vers un autre langage.
Ces fonctions sont appelées par le programme externe pour obtenir les valeurs effectives des arguments du prédicat Prolog.
Si le type de l'argument effectif n'est pas celui attendu, ou bien s'il n'existe pas d'argument du rang demandé, la fonction de communication notifie une erreur en affectant à l'indicateur err une valeur non nulle. Si, pour un argument de type entier, la valeur ne peut être représentée dans le type externe associé, une erreur est notifiée de la même manière.
Attention: Pour un comportement correct du système de gestion des erreurs, on doit immédiatement sortir du programme externe si la
variable représentée par err est non nulle.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 5
Voici les fonctions disponibles pour les types simples:
Interface C: int get_integer(no_arg, value, err) int no_arg; long *value; int *err; int get_real(no_arg, value, err) int no_arg; float *value; int *err; int get_double(no_arg, value, err) int no_arg; double *value; int *err; int get_string(no_arg, value, err) int no_arg; char *value; int *err; int get_max_string(no_arg, lg_max, value, in_external_code, err) int no_arg; int lg_max; char *value; int in_external_code; int *err;
Interface Fortran: integer*4 function fgetinteger(no_arg, value, err) integer*4 no_arg,value,err integer*4 function fgetreal(no_arg, value, err) integer*4 no_arg,err real*4 value integer*4 function fgetdouble(no_arg, value, err) integer*4 no_arg,err real*8 value integer*4 function fgetstring(no_arg, lg, value, err) integer*4 no_arg,lg,err character** value integer*4 function fgetmaxstring(no_arg, lg_max, lg, value, in_external_code, err) integer*4 no_arg,lg_max,lg,in_external_code,err character** value
no_arg
Entier donnant le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite.
© PrologIA
R 7 - 6
Manuel de référence value
Adresse de la donnée qui doit recevoir la valeur de l'argument de rang no_arg dans le prédicat Prolog.
lg
Entier, utilisé dans les fonctions Fortran qui manipulent des chaînes, qui doit recevoir la longueur effective de la chaîne de caractères.
lg_max
est la taille de la zone de caractères.
in_external_code
est un booléen qui indique si la chaîne doit être en code caractère externe (ceci est utile lorsque l'option caractères ISO est activée et que l'on utilise des caractères non présents dans la première moitié du jeu ISO; voir Annexe E).
Si la valeur est 0 aucune transformation n'est faite sur la chaîne Prolog.
err
La variable pointée est différente de 0 si une erreur ou un backtracking a été demandé.
Ces fonctions retournent zéro lorsqu'une erreur s'est produite ou une valeur non nulle lorsqu'il n'y a pas eu d'erreur.
get_max_string copie la chaîne de caractères originale (depuis la mémoire de travail de Prolog) dans une zone définie dans le programme externe, pointée par value, d'une taille de lg_max caractères. Un caractère nul indique la fin de la chaîne. Une erreur est provoquée si la taille de la zone de caractères définie par lg_max est insuffisante pour la chaîne à récupérer.
get_string fait la même chose que get_max_string mais ne fait pas de test de débordement. Le tableau pointé par value doit donc avoir une taille suffisante pour contenir la chaîne. Pour éviter les risques d'écrasement mémoire, il est préférable d'utiliser get_max_string.
7.2.3. Transfert de données simples d'un langage externe vers Prolog
Ces fonctions sont appelées par le programme externe pour unifier une valeur avec un argument du prédicat Prolog associé. Si l'unification échoue, un backtracking est annoncé par la fonction de communication.
Si le type de l'argument effectif n'est pas celui attendu, ou bien s'il n'existe pas d'argument du rang demandé, la fonction de communication notifie une erreur en affectant à l'indicateur err une valeur non nulle.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 7
Attention: Pour un comportement correct du système de gestion des erreurs, on doit immédiatement sortir du programme externe si la
variable représentée par err est non nulle.
Voici les fonctions disponibles pour les types simples:
Interface C: int put_integer(no_arg, value, err) int no_arg; long value; int *err; int put_real(no_arg, value, err) int no_arg; float value; int *err; int put_double(no_arg, value, err) int no_arg; double value; int *err; int put_string(no_arg, value, err) int no_arg; char *value; int *err;
Interface Fortran: integer*4 function fputinteger(no_arg, value, err) integer*4 no_arg,value,err integer*4 function fputreal(no_arg, value, err) integer*4 no_arg,err real*4 value integer*4 function fputdouble(no_arg, value, err) integer*4 no_arg,err real*8 value integer*4 function fputstring(no_arg, lg, value, err) integer*4 no_arg,lg,err character** value
no_arg
Entier donnant le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite.
© PrologIA
R 7 - 8
Manuel de référence value
Valeur qui sera unifiée sur l'argument de rang no_arg dans le prédicat Prolog associé. Pour le transfert de données de type chaîne de caractères, value est l'adresse d'une chaîne de caractères terminée par zéro, définie dans le programme externe. La fonction put_string copie alors cette chaîne de caractères dans la mémoire de travail de Prolog avant d'unifier la valeur avec l'argument du prédicat associé.
err
La variable pointée est différente de 0 si une erreur ou un backtracking a été demandé.
Ces fonctions retournent zéro lorsqu'une erreur s'est produite ou une valeur non nulle lorsqu'il n'y a pas eu d'erreur.
A!ociation
Prolog
HERITAGE
7.3. Fonctions de communication de termes quelconques
Les termes dont il est question dans ce chapitre, sont des termes qui ne représentent pas des arbres infinis, et qui peuvent être d'un type Prolog quelconque. Par exemple, une liste de n-uplets contenant des variables est un tel terme.
Pour pouvoir communiquer ces termes, et en particulier des types de données qui sont propres à Prolog, il faut disposer d'une codification de ces données pour les identifier hors Prolog.
Pour cela, Prolog permet de choisir entre:
- des chaînes de caractères, qui existent dans tous les langages, mais qui nécessitent l'utilisation d'un analyseur pour identifier les termes représentés.
- une structure arborescente d'éléments (type, valeur) codée dans des tableaux de données communes à tous les langages. Pour identifier les termes représentés, il s'agira de parcourir cette structure.
7.3.1. Au moyen de chaînes de caractères
Les fonctions décrites ici sont simples à employer et faciles à mettre en œuvre. Elles peuvent être choisies par exemple pour une première réalisation d'interfaçage de
Prolog avec des langages externes.
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 9
Elles sont beaucoup moins efficaces que la solution proposée avec les structures de tableaux qui sera décrite au paragraphe suivant. Elles offrent moins de possibilités d'exploitation des arguments. Elles comportent une restriction sur la manipulation des variables: il n'est pas possible de voir ou d'imposer que plusieurs arguments du prédicat lié à la procédure externe, utilisent la même variable libre. En effet le dictionnaire des variables est local à la procédure de communication, c'est à dire local à un argument (et non pas local à la règle).
Interface C: get_strterm(no_arg, lg_max, value, in_external_code, err) int no_arg; int lg_max; char *value; int in_external_code; int *err; put_strterm(no_arg, value, in_external_code, err) int no_arg; char *value; int in_external_code; int *err;
Interface Fortran: integer*4 function fgetstrterm(no_arg, lg_max, lg, value, in_external_code, err) integer*4 no_arg,lg_max,lg,in_external_code,err character** value integer*4 function fputstrterm(no_arg, lg, value, in_external_code, err) integer*4 no_arg,lg_max,in_external_code,err character** value
no_arg
est le rang de l'argument choisi dans le prédicat Prolog. Le premier argument a le rang 1, le second a le rang 2 et ainsi de suite. S'il n'existe pas d'argument de ce rang, une erreur est annoncée.
value
est l'adresse d'une zone de caractères.
lg_max
est la taille de la zone de caractères.
© PrologIA
R 7 - 10
Manuel de référence in_external_code
est un booléen qui indique si la chaîne est (put_strterm), ou doit être
(get_strterm) en code caractère externe (ceci est utile lorsque l'option caractères ISO est activée et que l'on utilise des caractères non présents dans la première moitié du jeu ISO; voir Annexe E).
err
indique si une erreur s'est produite ou pas ou si un backtracking doit être généré.
Dans le cas du transfert de Prolog vers le langage externe, la zone de caractères pointée par value doit être suffisamment grande pour contenir la chaîne résultat;
Prolog transforme (à la manière du prédicat out) l'argument de rang no_arg en une chaîne qu'il copie à l'adresse value. Si la chaîne à copier est plus grande que lg_max, une erreur est signifiée.
Dans le cas du transfert du langage externe vers Prolog, Prolog analyse la chaîne contenue dans value, construit le terme Prolog qu'elle représente (à la manière du prédicat in) et l'unifie avec l'argument de rang no_arg.
Par exemple, dans une règle externe, pour unifier le troisième argument avec une liste de 5 entiers, on écrira : put_strterm( 3, "[1,2,3,4,5]", 0, &err);
Voyons ci-dessous un petit exemple d'utilisation, avec une partie en C à ajouter à
Prolog et la partie Prolog qui le teste. On suppose que le lien entre le prédicat
Prolog et la fonction C a été déclaré.
int test_ccom()
{ int err; char s[80]; get_strterm(1,80,s,1,&err); if (err) return err; fprintf(stderr,">>>>%s\n",s); put_strterm(2,s,1,&err); if (err) return err; return 0;
}
> test_ccom(term(1.2.x,"string",[]),z);
>>>>term(1.2.v64,"string",nil)
{z=term(1.2.v150,"string",nil)}
>
7.3.2. Au moyen de structures de tableaux
Les deux fonctions de communication de termes, get_term et put_term, décrites ici sont les fonctions de communication de données, les plus générales.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 11
Sachant que les fonctions de communication de termes simples sont typées, cette interface peut servir également dans le cas où le type de l'argument à traiter n'est pas connu à priori.
Dans le cas de la communication du langage externe vers Prolog, il s'agit d'unifier le terme proposé avec l'argument du prédicat Prolog associé. Si l'unification échoue, un backtracking est annoncé par la fonction de communication.
Les structures de Prolog sont transformées par ces procédures d'interface en une structure de tableaux facilement manipulable dans des langages algorithmiques typés comme Pascal ou C. Les structures obtenues peuvent être conservées d'une exécution à une autre et peuvent être écrites ou chargées à partir d'un fichier. Pour cela les termes de Prolog sont éclatés dans quatre tableaux:
Deux tableaux pour les constantes:
- un tableau de caractères pour les chaînes.
- un tableau de doubles pour les réels.
Deux tableaux pour coder respectivement le type et la valeur d'un terme et de ses sous-termes.
- un tableau de caractères pour les types (ou 'tags') de terme/sous-termes.
- un tableau d'entiers longs pour les valeurs de terme/sous-termes. La signification de chaque élément est donnée par le contenu de l'élément de même indice dans le tableau de types.
Le terme correspondant à l'argument no_arg est décrit par la première case des tableaux de types et de valeurs de terme/sous-termes:
tag_tab[0], val_tab[0]
7.3.2.1. Description du codage d'un terme
Un terme est représenté par un tag et une valeur entière dont l'interprétation dépend du tag.
© PrologIA
R 7 - 12
Manuel de référence
terme:
Entier:
Réel:
Réel:
Chaîne:
Ident: variable:
'R'
'X'
(tag, valeur)
'I' la valeur de l'entier. Si l'entier venant de Prolog est trop grand pour être codé, une erreur est notifiée.
'S' un index dans le tableau des réels doubles real_tab.
un index dans le tableau des réels doubles real_tab.
un index i dans le tableau des caractères str_tab.
»
i indique le premier caractère de la chaîne. La chaîne est terminée par un caractère NULL.
'N' un entier représentant l'identificateur de manière unique.
»
Cette représentation est unique tant que l'identificateur est déclaré utilisé (cf. set_permanent_symbol). Sinon il peut être supprimé par le récupérateur de mémoire.
'V' un numéro de variable libre.
»
Le numéro est unique pour un état donné de la machine (voir § 7.3). Si deux numéros sont différents, il s'agit de deux variables libres différentes (c'est à dire non liées entre elles). Dans un même appel de procédure externe, ces numéros sont cohérents pour différents appels des fonctions get_term et put_term. Entre deux activations ou deux états, il n'y a aucun lien entre ces numéros.
De nouvelles variables peuvent être créées en appelant
put_term avec des numéros non encore utilisés. Il est plus efficace de créer des numéros en ordre croissant par rapport aux numéros déjà attribués. Prolog attribue ces numéros par incréments de 1.
séquence: 'D' un index dans les tableaux de terme/sous-termes vers une séquence de sous-termes.
»
La séquence pointée est terminée par 'E' ou 'F' suivant le cas.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 13
terme:
n-uplet: fin liste:
(tag, valeur)
'T' un index dans les tableaux de terme/sous-termes
tag_tab et val_tab.
»
Pointe sur une suite de termes dont le premier est un entier représentant le nombre d'arguments n du n-uplet, suivi des n sous-termes représentant les arguments.
'E' indéfinie.
»
Marque la fin d'une séquence se terminant par nil.
Cette marque peut être le premier élément d'une séquence pointée par 'D'.
fin séquence:'F' indéfinie.
»
Marque la fin d'une séquence ne se terminant pas par
nil: le terme qui suit immédiatement cette marque est le dernier élément de la séquence. Cette marque ne peut
être le premier élément d'une séquence pointée par 'D'.
On a deux représentations équivalentes du terme 1.2.3.nil, l'une sous forme vectorisée, la deuxième étant de type paire pointée (ici une paire est représentée par 3 entrées). Prolog fournit toujours les données sous la première forme qui est plus compacte, la deuxième étant surtout utile pour construire des listes dont la longueur n'est pas connue au début de la construction.
Le couple ('N', 0) représente par convention l'identificateur nil. (dans ce qui suit, "-" représente des valeurs indéfinies) forme compacte
00 'D' 1
!
forme type paire pointée
'D' 1 terme: 1.2.3.nil
05
06
07
08
09
01 'I'
02 'I'
03 'I'
04 'E' -
1
2
3
'I' 1
'F' -
'D' 4
'I' 2
'F' -
'D' 7
'I' 3
'F' -
'N' 0 exemple:
Codage du terme 1.2.(3."Hi!".x).father(x,y).nil
© PrologIA
R 7 - 14
Manuel de référence
10
11
12
13
14
06
07
08
09 indice
00
01
02
03
04
05
-
'I'
'S'
'F'
'V'
'I'
'N'
'V'
'V' tag_tab
'D'
'I'
'I'
'D'
'T'
'E'
10 vers:
-
6
1
2 val_tab (32 bits)
1 argument de type séquence
* codage des sous-termes de l'arg.
...
vers: nil
(3."Hi!".x) father(x,y)
!
!
début sous-terme: father(x,y)
-
-
0
3
0
0
1
3
<entier représentant father> x y
* début sous-terme: (3."Hi!".x) référence vers 1er élt de str_tab x
00
01
02
03
04
Tableau des caractères indice caractère
'H'
-
'i'
'!' null (0)
Tableau des réels indice
00
01 -
réel
7.3.2.2. Identificateurs
Dans la structure de tableaux, un identificateur est codé par une clé (notamment un entier) dans le dictionnaire de Prolog. Au moment de la transformation des données par les fonctions d'interfaçage (get_term, put_term), des fonctions de conversions entre la clé et la représentation de l'identificateur (chaîne de caractères) peuvent être nécessaires.
La fonction pro_symbol, ou son équivalent en Fortran, permet à partir d'une chaîne de caractères, de construire l'identificateur Prolog correspondant (identificateur qui pourra par exemple faire partie d'un terme qui sera transmis à Prolog par la procédure put_term). La chaîne de caractères doit avoir la syntaxe d'un identificateur. Si la chaîne de caractères ne comprend pas de préfixe (c'est une notation abrégée), l'identificateur créé sera fonction du contexte courant (Voir à ce propos le Chapitre 3 de ce manuel). Si une erreur s'est produite, la fonction retourne la valeur 0.
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 15 long pro_symbol(str) char *str; integer*8 function fprosymbol(lg, str) integer*4 lg character** str
La fonction symbol_string, ou son équivalent en Fortran, permet d'obtenir la représentation, sous forme de chaîne, d'un identificateur Prolog. Cet identificateur aura été obtenu par une des fonctions de communication de termes, get_term par exemple. Les arguments fournis à la fonction sont la clé key
, l'adresse d'une zone mémoire où la chaîne pourra être copiée str
, la longueur maximum de la chaîne de caractères copiée lg_max
. La chaîne obtenue est la notation de l'identificateur en fonction du contexte courant, notation abrégée si possible. La fonction donnera dans le paramètre de sortie lg
, la longueur effective de la chaîne. La fonction retourne 0 si elle a pu s'exécuter, un entier positif correspondant à un numéro d'erreur si une erreur s'est produite.
NB: key
doit correspondre à un identificateur valide.
int symbol_string(key, str, lg, lg_max) long key; char *str; int *lg; int lg_max; integer*4 function fsymbolstring(key, str, lg, lg_max) integer*4 key,lg_max,lg character** str
Le caractère choisi pour noter la séparation entre le préfixe et le suffixe de la représentation d'un identificateur complet, peut être modifié depuis Prolog. La fonction suivante permet donc de connaître le caractère courant utilisé : char prefix_limit(); character*1 function fprefixlimit();
Les fonctions set_permanent_symbol et reset_permanent_symbol, ou leur
équivalent en Fortran, permettent de prévenir le récupérateur de mémoire de Prolog, dans le cas où il serait activé sur le dictionnaire, qu'un identificateur est utilisé et respectivement non utilisé, par une procédure externe.
NB: key
doit correspondre à un identificateur valide.
set_permanent_symbol(key) long key; reset_permanent_symbol(key) long key; integer*4 function fsetpermanentsymbol(key) integer*4 key integer*4 function fresetpermanentsymbol(key) integer*4 key
7.3.2.3. Description des fonctions de communication
© PrologIA
R 7 - 16
Manuel de référence
Les paramètres de ces fonctions sont:
- Le numéro de l'argument no_arg.
- La taille des tableaux: tab_size, str_tab_size, real_tab_size.
- Les tableaux utilisés pour coder le terme: tag_tab, val_tab, str_tab, real_tab.
Lorsqu'un tableau n'est pas référencé (par exemple s'il n'y a ni chaînes, ni réels), il peut être remplacé par NULL. Une erreur est signifiée lorsqu'un des tableaux n'a pas une taille suffisante pour coder le terme.
- Un entier indiquant la dernière case utilisée pour coder le terme dans les tableaux
tag_tab et val_tab: max_used. Dans l'exemple décrit plus haut, il vaut 13.
- L'indicateur err, indiquant si une erreur ou un backtracking doit être provoqué.
Cet indicateur doit être testé à chaque appel, et il faut sortir de la procédure externe immédiatement s'il est non nul.
int get_term(no_arg, tab_size, str_tab_size, real_tab_size, tag_tab, val_tab, str_tab, real_tab, max_used, err ); int no_arg; int tab_size, str_tab_size, real_tab_size; char tag_tab[]; long val_tab[]; char str_tab[]; double real_array[]; int * max_used; int *err; int put_term(no_arg, tab_size, str_tab_size, real_tab_size, tag_tab, val_tab, str_tab, real_tab, max_used, err ); int no_arg; int tab_size, str_tab_size, real_tab_size; char tag_tab[]; long val_tab[]; char str_tab[]; double real_array[]; int max_used; int *err; integer*4 function fgetterm(no_arg, tabsize, strtabsize, realtabsize, tagtab, valtab, strtab, realtab, maxused, err ); integer*4 function fputterm(no_arg, tabsize, strtabsize, realtabsize, tagtab, valtab, strtab, realtab, maxused, err );
Des exemples d'utilisation de ces procédures d'interface sera trouvé dans le fichier
expredef.c ou fprouser.eg du kit.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 17
7.4. Principe de la méthode des descripteurs
La méthode des descripteurs permet de définir des objets relais Prolog et leur lien avec un objet externe. Elle s'applique pour les fonctions externes et les données partagées. Dans cette méthode, la déclaration des objets et des liens se fait en C.
Nous verrons d'abord quels sont les éléments à décrire en C pour réaliser cette définition et ensuite les deux manières de les déclarer: statiquement ou dynamiquement.
7.4.1. Eléments descriptifs
Pour pouvoir réaliser cette association, il est nécessaire de connaître:
- l'objet externe: dans un langage tel que C, cela se traduit par son adresse et son type.
- l'objet Prolog: c'est à dire son nom, son type et sa taille ou son arité selon qu'il s'agisse d'un tableau ou respectivement d'un prédicat.
Nous identifions donc les éléments nécessaires suivants:
name
identifie le terme Prolog associé à l'objet externe. C'est un pointeur vers une chaîne de type C contenant la représentation d'un identificateur Prolog complet.
Il a une signification différente dans le cas d'un objet de type
SYMBOL_ARRAY
.
Il représente alors seulement le module auquel appartiennent les identificateurs du tableau. Il contient donc la représentation du préfixe de ces symboles.
type
définit le type de l'objet externe déclaré. Les valeurs possibles sont:
INT_ARRAY
pour un tableau d'entiers,
CHAR_ARRAY
pour un tableau de caractères,
STRING_ARRAY
pour un tableau de chaînes,
SINGLE_FLOAT_ARRAY pour un tableau de réels en simple précision,
DOUBLE_ARRAY
pour un tableau de réels en double précision,
SYMBOL_ARRAY
pour un tableau d'identificateurs,
C_FUNCTION
C_FUNCTION
ou
C_FUNCTION_PROTECTED
pour une fonction externe,
_
BACKTRACK
ou
C_FUNCTION
_
BACKTRACK_PROTECTED
pour une fonction externe non déterministe,
DIRECT_C_FUNCTION
pour une fonction externe à appel direct. Pour les tableaux de données, il est possible de leur ajouter la constante
OFFSET_ZERO_BASED
pour permettre en Prolog de les indexer à partir de zéro.
© PrologIA
R 7 - 18
Manuel de référence size
définit l'arité de la règle dans le cas d'une fonction externe, ou bien la taille du tableau s'il s'agit d'une donnée commune. N'est pas significatif dans le cas de
SYMBOL_ARRAY
.
adresse
Définit l'adresse effective de l'objet C déclaré (ou d'un sous-tableau de descripteurs pour
SYMBOL_ARRAY
).
7.4.2. Déclaration statique
La déclaration des objets externes et des objets Prolog associés, peut se faire statiquement par des tables de descripteurs qui sont parcourues par Prolog au moment de l'initialisation. Ces tables, au moment du chargement de Prolog, vont créer les liens et les déclarations Prolog nécessaires. Les objets Prolog ainsi créés et leur lien avec l'objet externe, sont permanents durant toute la session. En particulier, ils ne sont pas supprimés par les règles prédéfinies kill_module, kill_array,
suppress …
Un descripteur de référence externe (voir fichier proext.h) est une structure composée de quatre champs qui contiennent les éléments nécessaires que nous avons identifiés: typedef struct
{
char *name;
int type;
int size;
POINTER adresse;
} EXTERNAL_DESCRIPTOR;
Pour ajouter ces objets à Prolog, la machine Prolog doit être étendue par un ou plusieurs modules externes contenant la déclaration de ces descripteurs. Un descripteur doit être affecté à une table de descripteurs, qui doit être pointée par le tableau général PRO_EXTERNAL. Le programme prolink d'édition de liens, construit automatiquement ce tableau PRO_EXTERNAL dans le module prodesc, puis reconstruit la machine Prolog.
PRO_EXTERNAL est un tableau d'adresses de tables de descripteurs, il doit être terminé par une adresse nulle(0). Dans sa version initiale du kit, il contient la table du fichier prouser:
EXTERNAL_DESCRIPTOR *PRO_EXTERNAL[] = { prouser_desc,
0 };
Table de descripteurs
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 19
Une table de descripteurs est un tableau de descripteurs, terminé par un descripteur contenant 0 dans le champ name. Par exemple:
EXTERNAL_DESCRIPTOR sample_desc[] =
{
{":term_vars", C_FUNCTION, 2, (POINTER) term_vars},
{":qsort", DIRECT_C_FUNCTION, 1, (POINTER) quicksort},
{":enumerate", C_FUNCTION_BACKTRACK,3,(POINTER)enumerate},
{ 0, 0, 0, 0}
};
L'utilisateur peut créer autant de tables qu'il le désire; le plus naturel est d'en réaliser une par fichier externe.
7.4.3. Déclaration dynamique
La possibilité de créer des associations d'objets Prolog-C dynamiquement, est offerte grâce à la fonction PRO_BIND. Les objets externes doivent être connus de l'exécutable Prolog (c'est à dire que la machine Prolog doit être étendue par des modules externes contenant ces objets), par contre la création de l'objet Prolog et le lien avec cet objet sont faits à l'exécution de la fonction PRO_BIND.
Comme dans le cas de la déclaration statique, les objets Prolog ainsi créés et leur lien avec l'objet externe, sont permanents durant toute la session. Une fois la déclaration faite, il n'est pas possible d'en annuler l'effet.
La fonction PRO_BIND a 4 arguments, chaque argument représentant un élément nécessaire que nous avons identifié précédemment : int PRO_BIND(name, type, size, adresse) char *name; int type, size; void *adresse;
Par exemple, il est possible de créer dynamiquement une zone de données partagées. Dès que la fonction PRO_BIND a été appelée, le tableau est connu de
Prolog.
L'exemple suivant alloue une zone de 1000 réels commune à Prolog et C, et accessible dans Prolog sous le nom mymodule:data
#include <malloc.h>
#include "proext.h"
...
double *t; t = (double *) malloc( 1000*sizeof(double));
PRO_BIND( "mymodule:data"
, DOUBLE_ARRAY
, 1000
, (POINTER) t);
© PrologIA
R 7 - 20
Manuel de référence
à partir de l'exécution de cette séquence, le programme Prolog suivant s'exécute sans erreur, et référence la zone de données t[4]: assign(mymodule:data(5), 1.5e );
Dans les paragraphes suivants qui exposent les différents types d'objets accessibles par la méthode des descripteurs, les exemples sont décrits avec des déclarations statiques. Ils peuvent évidemment aussi être déclarés de manière dynamique.
A!ociation
Prolog
HERITAGE
7.5. Données partagées
Les données qui peuvent être partagées entre Prolog et C sont des données d'un type manipulable par les deux langages. Les données qui s'y prêtent sont les entiers, les réels, les caractères, les chaînes de caractères et les identificateurs.
Prolog peut être étendu par une ou plusieurs zones de données partagées. Ces zones de données sont assimilées à des tableaux et sont manipulées comme des tableaux dans les deux langages.
La définition de ces données communes se fait par la déclaration d'un descripteur dont le champ type aura une des valeurs suivantes:
INT_ARRAY
,
SINGLE_FLOAT_ARRAY
,
DOUBLE_ARRAY, CHAR_ARRAY
,
STRING_ARRAY
,
SYMBOL_ARRAY,
ou bien une de ces valeurs augmentée de
OFFSET_ZERO_BASED
.
Le champ name sera alors la représentation Prolog du tableau. La primitive
def_array ne doit pas être utilisée pour définir ce tableau. Le champ size indique le nombre d'éléments du tableau.
Le champ adresse est l'adresse de la zone mémoire réservée en C pour ce tableau.
Ce sera, en fonction du type de l'objet :
INT_ARRAY
l'adresse d'une zone statique d'entiers (type int du C).
CHAR_ARRAY
l'adresse d'une zone de caractères 2 (1 octet par caractère).
STRING_ARRAY
l'adresse d'un tableau de pointeurs vers des chaînes 3 terminées par le caractère nul. Il peut être déclaré sous la forme : char *tab[size];
2Attention au mode de codage des caratères. Voir annexe E.
3Attention au mode de codage des chaînes. Voir annexe E.
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 21
Attention: il n'y a pas de test de débordement sur la taille des chaînes manipulées par l'interface. Ne pas oublier d'allouer effectivement l'espace pour les chaînes, et d'initialiser le tableau de pointeurs.
SINGLE_FLOAT_ARRAY
l'adresse d'une zone de réels 32 bits IEEE.
DOUBLE_ARRAY
l'adresse d'une zone de réels 64 bits IEEE.
SYMBOL_ARRAY
l'adresse d'une sous-table d'associations (chaîne d'un identificateur,
représentation interne). La partie chaîne est initialisée par le programmeur, la partie représentation interne est initialisée par Prolog.
Pour ce type de descripteur, le champ name définit le préfixe par défaut pour les symboles de la table (c'est-à-dire que si la chaîne id_name correspond à un nom abrégé, c'est ce préfixe par défaut qui est utilisé).
Ce type d'objet permet de construire automatiquement une table des valeurs internes des identificateurs Prolog que l'on désire manipuler. Tous les identificateurs de la tables sont permanents (cf. set_permanent_symbol) par défaut.
7.5.1. Exemple de zone commune de données
On déclare ici deux tableaux partagés par C et Prolog. Le premier est un tableau de
100 entiers, nommé com en C et :com en Prolog. Le deuxième est un tableau de 3 chaînes de 10 caractères au plus, nommé m:str en Prolog, et str en C.
#include "proext.h" int com[100]; char *str[3] =
{
"0123456789",
" ",
" "
};
EXTERNAL_DESCRIPTOR descTable1 [] =
{{":com",INT_ARRAY+OFFSET_ZERO_BASED, 100, (POINTER)com },
{"m:str", STRING_ARRAY, 3, (POINTER) str },
{ 0, 0, 0, 0 }
};
Supposons que ces déclarations soient contenues dans le module source C table.c, il peut être lié à Prolog après l'avoir compilé, en exécutant prolink pour l'édition de liens avec le module objet de table et le descripteur descTable1. (Voir le manuel d'utilisation §2.8.)
© PrologIA
R 7 - 22
Manuel de référence
Au moment du chargement, Prolog créera automatiquement un lien avec le tableau
C. Ce tableau pourra être modifié et consulté à partir des commandes standard
Prolog assign et val. Il faut noter que pour les données partagées déclarées avec
OFFSET_ZERO_BASED les indices Prolog commencent à 0, que pour les autres tableaux ils commencent à 1, et que les indices C commencent à 0. Les commandes suivantes
C et Prolog ont strictement le même effet, et travaillent sur la même zone mémoire:
Prolog C assign(:com[4], 100); val(:com[4],_x) outl(_x); val(m:str[1],_x) outm(_x); assign(m:str[2],"abc"); com[4] = 100; printf("%ld\n",com[4]); printf("%s",str[0]); strcpy(str[1],"abc");
A!ociation
Prolog
HERITAGE
7.6. Ajout de fonctions externes
La définition d'une fonction externe se fait par la déclaration d'un descripteur. On distingue deux types de fonctions externes :
- les fonctions qui effectuent un traitement et se terminent; le champ type du descripteur vaudra alors
C_FUNCTION
ou
DIRECT_C_FUNCTION
. Les objets de type
DIRECT_C_FUNCTION
seront décrits au paragraphe 7.7.
- les fonctions non déterministes qui peuvent effectuer plusieurs traitements différents et donc retourner plusieurs résultats possibles; le champ type du descripteur vaudra alors
C_FUNCTION_BACKTRACK
.
On trouvera parmi les types d'objets possibles à déclarer par les descripteurs :
C_FUNCTION_PROTECTED
et
C_FUNCTION_BACKTRACK_PROTECTED
. Ces types identifient les mêmes objets que les types
C_FUNCTION
et
C_FUNCTION_BACKTRACK
, seulement leur "visibilité" change. En effet les objets de type
PROTECTED
seront cachés pour le mode de mise au point.
C_FUNCTION
La déclaration d'un objet de ce type, permet de créer automatiquement une règle relais Prolog, de nom name et d'arité size. L'exécution de cette règle
Prolog se fera par l'appel de la fonction C dont l'adresse se trouve dans le champ adresse. Les arguments de la règle peuvent être accédés depuis la fonction C par les procédures standard de communication de données get_...
et put_... . La fonction C pointée par adresse doit être déclarée de type int et est appelée par Prolog sans paramètres. Elle doit retourner la condition de terminaison (-1 pour ECHEC, 0 pour SUCCES, > 0 pour ERREUR).
Permet de construire des règles prédéfinies externes plus simplement que par l'utilisation directe des parasites (/?n) et de la procédure relais user_rule (cf.
Annexe D).
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 23
C_FUNCTION
_
BACKTRACK
La déclaration d'un objet de ce type, permet de créer automatiquement une règle relais Prolog, de nom name et d'arité size. L'exécution de cette règle
Prolog se fera par l'appel de la fonction C dont l'adresse se trouve dans le champ adresse. Les arguments de la règle peuvent être accédés depuis la fonction C par les procédures standard de communication de données get_...
et put_... . La fonction doit être déclarée de type int. Elle est appelée par
Prolog avec un seul paramètre de type long indiquant le numéro de l'appel courant. Elle doit retourner la condition de terminaison (ECHEC pour un
échec, SUCCES pour un succès, > 0 pour une erreur,
SUCCESS_END_OF_C_BACKTRACK pour indiquer le succès du dernier appel ou FAIL_END_OF_C_BACKTRACK pour indiquer l'échec du dernier appel). Lors d'une coupure, un appel supplémentaire à cette fonction sera fait avec en argument, la valeur conventionnelle CUT_OF_C_BACKTRACK. Le but de cet appel n'est pas de trouver une autre solution, mais de signifier qu'il n'y aura plus d'autres appels et ainsi permettre de terminer les traitements en cours (comme par exemple: libérer de la mémoire allouée, fermer des fichiers,
…). Pendant un appel à cette fonction, il est possible de mémoriser une valeur entière dans l'espace de Prolog, à l'aide de la fonction
store_C_backtrack_data(long ptr) ou de récupérer cette valeur par le retour de la fonction long restore_C_backtrack_data(void). Permet de construire des règles prédéfinies en langage externe non déterministes.
7.6.1. Exemple de déclaration de règle prédéfinie
Le fichier suivant, lié à Prolog, produit automatiquement la nouvelle règle prédéfinie
sys:reverse(s1,s2), unifiant s2 avec la chaîne s1 inversée, ainsi que la règle
sys:enumerate(x,b,e) qui unifie successivement x avec les entiers compris entre b et
e. Si l'on veut utiliser le nom abrégé, il faut rajouter le nom dans le contexte fermé
"sys" en effaçant la commande:
> add_implicit("sys","reverse")
add_implicit("sys","enumerate");
#include "proext.h"
#include <stdio.h>
#include <string.h>
#define MAX_STRING 512 int reverse();
EXTERNAL_DESCRIPTOR descTable2[] =
{{"sys:reverse", C_FUNCTION, 2, (POINTER) reverse},
{"sys:enumerate", C_FUNCTION_BACKTRACK, 3,
(POINTER) enumerate},
{ 0, 0, 0, 0 }
}; reverse()
© PrologIA
R 7 - 24
Manuel de référence
{ int err, i, j, lg; char c1, c[MAX_STRING]; if ( ! get_string(1,c,&err) ) return err; lg = strlen(c); for (i=0; i<lg/2; i++)
{ j = lg -1 -i; c1 = c[i]; c[i] = c[j]; c[j] = c1;
} if ( ! put_string(2,c,&err) ) return err; return 0;
} typedef struct { int direction, current, start, end;
} enumRecord, *EnumMemory; int enumerate(call) long call;
{
EnumMemory mem; int err; if (call==CUT_OF_C_BACKTRACK) /* cut */
{ mem = (EnumMemory) restore_C_backtrack_data(); free(mem); return;
} if (call==1) /*first call */
{
/* mem constituera la mémoire du prédicat d'un appel
à l'autre */ mem = (EnumMemory) malloc(sizeof(enumRecord)); get_integer(2, &mem->start, &err); if (err) { free(mem); return 253;} get_integer(3, &mem->end, &err); if (err) { free(mem); return 253;} mem->direction = (mem->start > mem->end) ? -1 : 1; mem->current = mem->start; store_C_backtrack_data(mem);
}
/* on récupère l'adresse des données qui a été mémorisée
*/ if (call != 1) mem = (EnumMemory) restore_C_backtrack_data();
/* instantiation du premier argument du prédicat */
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 25 put_integer(1,mem->current,&err); if (mem->current == mem->end)
{
/* c'est fini, il faut libérer */ free(mem); return SUCCESS_END_OF_C_BACKTRACK;
} else
/* on incrémente le compteur */ mem->current += mem->direction;
} return err;
Il faut maintenant compiler le fichier ci-dessus, faire l'édition de liens (voir le manuel d'utilisation §2.8.), puis activer le nouveau Prolog pour obtenir :
Prolog II+, ..
...
>sys:reverse("0123456",x);
{x="6543210"}
>sys:enumerate(x,-2,2);
{x=-2}
{x=-1}
{x=0}
{x=1}
{x=2}
>
7.7. Ajout de fonctions externes à appel direct
Les fonctions externes à appel direct, contrairement aux autres fonctions externes accessibles par la méthode des descripteurs, sont des fonctions qui n'ont pas d'interface C pour le passage des paramètres. Une telle fonction C peut être écrite avec ses paramètres, comme si elle devait être appelée par un programme C classique. En particulier cela peut être une fonction d'une librairie dont on n'a pas la maîtrise des sources. En effet, pour ce type de fonction, la transformation des données (Prolog - C et C - Prolog) se fera en Prolog, par l'intermédiaire de la règle prédéfinie callC.
Cette méthode permet donc de communiquer des types de données communs aux deux langages, à savoir : des entiers, des réels, des chaînes de caractères et des tableaux homogènes de ces types.
La déclaration d'une fonction externe à appel direct se fait par la déclaration d'un descripteur dont le champ type vaut
DIRECT_C_FUNCTION
.
© PrologIA
R 7 - 26
Manuel de référence
DIRECT_C_FUNCTION
La déclaration d'un objet de ce type permet de créer automatiquement une règle relais Prolog de nom name et d'arité size, qui ne peut être effacée qu'à travers la primitive callC et dont l'exécution consiste à appeler la fonction C pointée par le champ adresse du descripteur.
Cette fonction C ne doit pas avoir plus de 20 arguments. Si le champ size du descripteur vaut -1, le type et le nombre d'arguments sont variables suivant l'appel. Il est possible d'appeler sprintf par exemple (voir § 5.2 de ce manuel).
7.7.1. Primitive CallC
La primitive prédéfinie callC détermine, au moment de l'appel, le mode de passage des arguments à la fonction C, leur type et le type de la valeur de retour, d'après les arguments effectifs du prédicat Prolog et conformément aux conventions adoptées que nous décrirons dans le prochain paragraphe. Ceci permet d'appeler des fonctions avec nombre et type d'argument variables.
callC(t1) callC(t1,t2)
t1 est un terme représentant l'appel du prédicat Prolog, avec ses paramètres conformément aux conventions choisies. t2 est un terme représentant le résultat. Si t2 est nil le résultat de la fonction est ignoré. La forme callC(t1) est
équivalente à callC(t1,nil).
Les fonctions qui peuvent être appelées à travers ce prédicats, sont les fonctions décrites par l'utilisateur avec un descripteur dont le type est
DIRECT_C_FUNCTION
, ainsi que les fonctions sprintf et sscanf qui sont prédéclarées. Sous les systèmes permettant l'implantation de la primitive
lkload, il est possible d'appeler sans déclaration des fonctions existant dans l'environnement Prolog. La primitive callC réalise alors dynamiquement le lien lors du premier appel. Il faut pour cela que le fichier exécutable prolog se trouve dans le répertoire courant.
Exemples:
> callC(sscanf("123","%lf",<"R",y>));
{y=1.230000000000000e+02}
> eq(x,"123") callC(sscanf(x,"%2f",<"R",y>));
{x="123", y=1.200000000000000e+01}
> eq(f,"%x %o") callC(sscanf("12 12",f,<"I",x>,<"I",y>));
{x=18, y=10}
> callC(sprintf(<"",x,80>,"valeur: %ld",200));
{x="valeur: 200"}
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 27
7.7.2. Conventions Prolog pour la communication des données
Il s'agit ici de pouvoir exprimer en Prolog toutes les informations nécessaires pour pouvoir faire l'appel d'une fonction C, concernant ses arguments et sa valeur de retour. On s'intéressera donc :
au mode de passage des arguments,
au type des arguments et de la valeur de retour de la fonction,
à la valeur initiale des arguments,
à la valeur résultat des arguments et de la fonction,
à d'autres informations techniques liées au type de l'objet.
Plus qu'au mode de passage d'une donnée, on s'intéressera au rôle de la donnée, à savoir : donnée d'entrée et/ou de sortie.
En effet en Prolog, une donnée se voyant affecter une valeur, ne pourra plus en changer (sauf par backtracking, mais c'est assimilable en comparant avec C, à une autre exécution). Une procédure Prolog qui attendrait une valeur en entrée et un résultat en sortie, devrait utiliser deux données. C'est là la différence essentielle entre
Prolog et C, il est donc important de connaître si une donnée (initialisée ou pas) doit changer de valeur au cours de l'exécution de la fonction C. Dans la suite de ce chapitre, on fera donc la distinction entre argument avec valeur de retour et argument sans valeur de retour. Le mode de passage par adresse ou par valeur en sera déduit automatiquement.
Il est possible de passer en paramètre:
1. un entier long avec ou sans valeur de retour,
2. un réel double précision avec ou sans valeur de retour,
3. une chaîne de caractères avec ou sans valeur de retour,
4. un tableau d'entiers, de chaînes de caractères ou de réels double précision, avec ou sans valeur de retour.
Il est possible d'attendre en résultat de la fonction:
1. un entier long,
2. un réel double précision.
Voyons à présent, quelles sont les conventions. Pour les illustrer sur des exemples, on supposera qu'un lien a été déclaré entre le prédicat relaiProlog
et la fonction fonctionC
.
7.7.2.1. Convention pour des paramètres sans valeur de retour
Dans ce cas, il n'est pas utile de spécifier le type de la donnée, Prolog le connaît puisque la donnée a déjà une valeur.
Les données de type entier, réel ou chaîne sont représentées par la donnée Prolog elle même.
© PrologIA
R 7 - 28
Manuel de référence
Les données de type tableau homogène d'entiers, de réels ou de chaînes sont représentées par une liste Prolog (terminée éventuellement par nil), d'entiers de réels ou de chaînes.
Par exemple:
:relaiProlog(180, 3.14e0, "pi", 2.3.5.7.11.13.17.nil,
"b"."a"."ba".nil) est équivalent aux instructions C suivantes:
{ long arg1 = 180L; double arg2 = 3.14; char arg3[] = {'p','i','\0'}; long arg4[] = {2,3,5,7,11,11,13,17}; char *arg5[] = {"b","a","ba"}; fonctionC(arg1,arg2,arg3,arg4,arg5); }
Les chaînes et les tableaux sont toujours passés par adresse selon la convention habituelle en C. Prolog effectue de toute façon une copie des arguments dans une zone intermédiaire dont la taille peut être paramétrée sur la ligne de commande (voir le chapitre 2 du Manuel d'Utilisation).
7.7.2.2. Convention pour le retour de la fonction
Il est nécessaire de connaître le type du retour de la fonction, et de disposer d'un terme Prolog qui sera unifié avec sa valeur. La convention est la suivante: le terme qui représente le résultat doit être un doublet :
< type_résultat, variable_résultat >
- dont le premier élément type_résultat est une chaîne qui indique le type: "I" pour un retour entier, "R" ou "X" pour un retour réel.
- dont le deuxième élément variable_résultat sera unifié avec la valeur retournée par la fonction (entier ou réel).
7.7.2.3. Convention pour des paramètres avec valeur de retour
Pour une donnée C avec valeur de retour attendue, il est nécessaire d'avoir deux données Prolog: une première qui doit être connue au moment de l'appel et qui sert
à transmettre la valeur initiale à la fonction C, une deuxième qui sera unifiée avec la nouvelle valeur obtenue de la fonction C.
On distingue parmi les types de données : des types simples et des types qui nécessitent une allocation de zone mémoire.
Note : Certaines conventions avec valeur(s) de retour, spécifient des tailles de zones mémoire à allouer pour ranger le(s) résultat(s) des fonctions C appelées. L'utilisateur doit donc impérativement s'assurer que les zones sont suffisantes pour les résultats attendus. Il s'assurera
également que les types des arguments sont cohérents avec les déclarations faites en C.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 29
La convention pour les données de type simple, c'est à dire les entiers et les réels, est la suivante : l'argument effectif du prédicat Prolog est un doublet :
< valeur_initiale, variable_résultat >
- dont le premier élément valeur_initiale est la valeur initiale de la zone dont l'adresse est passée en paramètre à la fonction C. Si la valeur est quelconque on peut mettre comme valeur initiale la marque "I" (pour indiquer un entier) ou "R"
(pour un réel).
- dont le deuxième élément variable_résultat sera unifié avec la nouvelle valeur de l'argument rendue par la fonction (entier ou réel).
Par exemple:
:relaiProlog(<180,x>, <3.14e0,y>) est équivalent aux instructions C suivantes:
{ long arg1 = 180L; double arg2 = 3.14; fonctionC(&arg1,&arg2); } suivies des affectations des variables x et y avec les nouvelles valeurs de arg1 et arg2.
La convention pour les données qui nécessitent une zone mémoire dépend du type de la donnée :
Pour une chaîne de caractère, il est nécessaire d'avoir une zone de caractères, dont on doit définir la taille, susceptible de contenir dans un premier temps la chaîne initiale puis la chaîne résultat: l'argument effectif du prédicat Prolog est un triplet:
< valeur_initiale, variable_résultat, taille_max_résultat >
- dont l'élément taille_max_résultat est un entier spécifiant la taille de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C.
- dont l'élément valeur_initiale est la valeur initiale copiée dans la zone allouée pour l'opération.
- dont l'élément variable_résultat sera unifié avec le résultat (chaîne Prolog).
Par exemple:
:relaiProlog(<"pi",x,100>) est équivalent aux instructions C suivantes:
{ char arg1[100]; strcpy(arg1,"pi"); fonctionC(arg1); } suivies de l'affectation de la variable x avec la nouvelle valeur de arg1.
© PrologIA
R 7 - 30
Manuel de référence
Pour un tableau d'entiers ou de réels, il est nécessaire d'avoir une zone d'entiers ou de réels qui tient lieu de tableau, dont on définit le nombre d'éléments, et susceptible de contenir dans un premier temps les éléments du tableau en entrée puis les éléments du tableau en sortie: l'argument effectif du prédicat Prolog est un quadruplet:
< valeur_initiale, variable_résultat, taille_résultat, taille_tableau >
- dont l'élément taille_tableau est un entier spécifiant le nombre d'éléments de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C.
- dont l'élément taille_résultat indique le nombre d'éléments valides du tableau à prendre en compte pour le retour.
- dont l'élément valeur_initiale est la liste Prolog terminée par nil, des valeurs initiales des éléments du tableau. Le type de ces éléments détermine le type du paramètre.
- dont l'élément variable_résultat sera unifié avec le résultat (liste Prolog terminée par nil, d'entiers ou de réels).
Par exemple:
:relaiProlog(<2.4.6.8.12,x,1,5>, <0e0.nil,y,5,5>) est équivalent aux instructions C suivantes:
{ long arg1[]={2,4,5,6,8,12}; double arg2[5]; arg2[0] = 0.0; fonctionC(arg1,arg2); } suivies des affectations des variables x et y avec les nouvelles valeurs de arg1 et arg2.
Pour un tableau de chaînes de caractères, il est nécessaire d'avoir une zone de pointeurs pour représenter le tableau, dont on doit définir le nombre d'éléments, et
éventuellement une zone de caractères pour stocker tous les caractères de toutes les chaînes du tableau, dont on doit également définir la taille.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 31 l'argument effectif du prédicat Prolog est un quadruplet:
< valeur_initiale, variable_résultat, nbre_max_de_caractères, taille_tableau
>
- dont l'argument taille_tableau est un entier spécifiant le nombre d'éléments de la zone à allouer pour l'opération, dont l'adresse est transmise à la fonction C. La fin du tableau doit être indiquée par le pointeur NULL après le dernier élément.
- dont l'argument valeur_initiale est une liste Prolog terminée par nil de chaînes pointées par le tableau en entrée ("".nil au minimum).
- dont l'argument est un entier, il offre la possibilité de laisser à Prolog la gestion (allocation et libération) du buffer de caractères pour les chaînes en sortie. Si l'utilisateur gère le(s) espace(s) des chaînes en sortie,
nbre_max_de_caractères doit valoir 0. Si l'utilisateur laisse à Prolog le soin d'allouer et par la suite libérer un buffer de caractères, nbre_max_de_caractères doit être non nul, il indique alors le nombre de caractères total maximal nécessaire pour stocker tous les caractères de toutes les chaînes du tableau. La valeur du premier élément du tableau (pointeur de chaîne de caractères) sera dans ce cas l'adresse de cet espace (où est copiée la première chaîne en entrée).
- dont l'élément variable_résultat sera unifié avec le résultat, une liste Prolog terminée par nil, de chaînes.
Par exemple:
:relaiProlog(<"il"."était"."une"."fois",x,100,5>) est équivalent aux instructions C suivantes:
{ char * arg1[5]; char buffer[100]; strcpy(buffer,"il"); arg1[0] = buffer; arg1[1] = "était"; arg1[2] = "une"; arg1[3] = "fois"; fonctionC(arg1); } suivies de l'affectation de la variable x avec les nouvelles valeurs de arg1.
7.7.3. Exemple : méthode simple pour appeler une fonction C
Nous montrons ici des exemples avec divers types d'arguments, utilisant des fonctions déclarées par l'utilisateur, et une fonction système.
#include <stdio.h>
#include <string.h>
#include "proext.h"
#define REAL double
#define INTEGER long static REAL complete_example(ii1,io1,ri1,ro1,si1,so1,tii1, tri1,tio1,tro1,tsi1,tso1,reserved)
/*-------------------------------------------------------*/
INTEGER ii1,*io1;
REAL ri1,*ro1; char *si1; char *so1;
© PrologIA
R 7 - 32
Manuel de référence
REAL *tri1;
REAL *tro1;
INTEGER *tio1,*tii1; char **tsi1,**tso1; int reserved;
{ int i; printf("ii1= %d\n",ii1); printf("io1= %d\n",*io1); printf("ri1= %g\n",ri1); printf("ro1= %g\n",*ro1);
/*integer in input*/
/*integer in output*/
/*real in input*/
/*real in output*/ printf("si1= %s\n",si1); printf("so1= %s\n",so1);
/*string in input*/
/*string in output*/ for (i=0;i<2;i++) /*integer array*/ printf("tii1[%d]= %d\n",i,tii1[i]); /*in input*/ for (i=0;i<2;i++) /*real array*/ printf("tri1[%d]= %g\n",i,tri1[i]); /*in input*/ for (i=0;i<3;i++) /*integer array*/ printf("tio1[%d]= %d\n",i,tio1[i]); /*in output*/ for (i=0;tsi1[i];i++) /*string array*/ printf("tsi1[%d]= %s\n",i,tsi1[i]); /*in input*/ for (i=0;i<3;i++) /*real array*/ printf("tro1[%d]= %g\n",i,tro1[i]); /*in output*/
*io1 = 3333;
*ro1 = - 8888.; strcpy(so1,"bonjour"); for (i=0;i<10;i++) tio1[i] = i+200; for (i=0;i<10;i++) tro1[i] = (float) i +200.; for (i=0;tso1[i];i++)
/*integer in output*/
/*real in output*/
/*string in output*/
/*integer array*/
/*in output*/
/*real array*/
/*in output*/
/*string array*/ printf("strin(%d)=%s\n",i,tso1[i]);/*in output*/ if(reserved) /*space for strings reserved by prolog */
{ strcpy(tso1[0],"Zone1"); /*first pointer(tso1[0])*/ tso1[1] = tso1[0]+6;
/*initialized by Prolog */
/*others pointers initialized*/
/*by the user */ strcpy(tso1[1],"Zone2");
/*end of array */ tso1[2] = NULL;
} else /*space for strings reserved by the user */
{ tso1[0] = "Zone1"; tso1[1] = (char *) malloc(10); strcpy(tso1[1],"Zone2"); tso1[2] = NULL;
} return 1234.; /*returned value */
} static REAL average(tab,n)/*
----------------------------*/
INTEGER *tab,n;
{
INTEGER i,sum=0; for (i=0;i<n;i++) sum += tab[i]; return (sum/n);
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
R 7- 33
} static mystrcmp(a1,a2)/*
------------------------*/ char **a1,**a2;
{ return strcmp(*a1,*a2);
} static quicksort(tab_in,tab_out)/*
-------------------------------*/ char **tab_in, **tab_out;
{ int i,nbr; for (nbr=0;tab_in[nbr];nbr++) tab_out[nbr] = tab_in[nbr]; tab_out[nbr] = NULL; qsort(tab_out,nbr,sizeof(char *),mystrcmp);
} static reverse(str)/*
------------------------*/ char *str;
{ char c;
INTEGER i,len; len = strlen(str); for (i=0;i<len/2;i++)
{ c = str[i]; str[i] = str[len - (i+1)]; str[len - (i+1) ] = c;
}
}
EXTERNAL_DESCRIPTOR calld_desc[] =
{
{":complete_example", DIRECT_C_FUNCTION, 13, (POINTER) complete_example},
{":average", DIRECT_C_FUNCTION, -1, (POINTER) average},
{":reverse", DIRECT_C_FUNCTION, 1, (POINTER) reverse},
{":qsort", DIRECT_C_FUNCTION, 2, (POINTER) quicksort},
{0, 0, 0, 0}
};
En supposant que le fichier ci-dessus s'appelle callc.c, il faut le compiler, faire l'édition de liens (voir le manuel d'utilisation §2.8.), puis activer le nouveau Prolog pour obtenir :
Prolog II+, ..
...
> eq(r_eserved,30) callC(complete_example
(
11, /*integer in input */
<155,i>, /* integer in output with initialization*/
© PrologIA
R 7 - 34
Manuel de référence
+13.0e0, /* real in input*/
<+222.0e0,r>,/* real in output with initialization*/
"str_input", /* string in input*/
<"ee",s,10>, /* string in output with initialization*/
123.456.nil, /*array of integers in input */
14.0e0.15.0e0.nil, /*array of reals in input */
<11.22.33,w,5,20>, /*array of integers in output
/*with initialization*/
<+44.0e0.+55.0e0.+66.0e0,t1,5,20>, /*array of reals in output with initialization*/
"list_str_in1"."list_str_in2".nil, /*array of STRINGS*/
/*in input */
<"list_str_out1"."list_str_out2"."list_str_out3".nil,
T_S,r_eserved,5>, r_eserved /*integer in input */
),
<"R",m>) /*result */
; ii1= 11 io1= 155 ri1= 13 ro1= 222 si1= str_input so1= ee tii1[0]= 123 tii1[1]= 456 tri1[0]= 14 tri1[1]= 15 tio1[0]= 11 tio1[1]= 22 tio1[2]= 33 tsi1[0]= list_str_in1 tsi1[1]= list_str_in2 tro1[0]= 44 tro1[1]= 55 tro1[2]= 66 strin(0)=list_str_out1 strin(1)=list_str_out2
{r_eserved=30,i=3333, r=-8.888000000000000e+03, s="bonjour", w=200.201.202.203.204.nil, t1=2.000000000000e+02.2.010000000000e+02.2.020000000000e+02.
2.030000000000e+02.2.040000000000e+02.nil,
T_S="Zone1" ."Zone2".nil, m=1.234000000000e+03
}
> callC(average(1.3.5.nil,3),<"R",x>);
{x=3.000000000000e+00}
> callC(reverse(<"abcde",s,20>));
{s="edcba"}
> callC(reverse(<"abcdef",s,20>));
{s="fedcba"}
> callC(qsort("qqq"."zzz"."aaa"."iii".nil,<"".nil,s,0,50>));
/*0 because strings not created (already exist)*/
{s="aaa"."iii"."qqq"."zzz".nil}
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Extensions avec des langages externes
A!ociation
Prolog
R 7- 35
© PrologIA
A!ociation
Prolog
HERITAGE
8. Lancement d'un but
Prolog par un programme C
8.1. Principes de base
8.2. Initialisation de Prolog
8.3. Empilement d'un but Prolog
8.4. P r o g r a m m a t i o n
8.5. Méthode simple d'appel d'un but Prolog
8.6. Autres fonctions
Ce chapitre suppose une bonne connaissance du chapitre 7, les lecteurs non expérimentés peuvent sauter ce chapitre en première lecture.
La machine Prolog peut être utilisée comme un sous-programme d'un programme quelconque. Pour simplifier la compréhension de sa manipulation depuis un langage procédural, on peut présenter Prolog comme une machine à états (ou points d'arrêt): un jeu de procédures permet de faire transiter la machine Prolog de l'état courant vers un nouvel état.
Hormis aux points d'arrêt de la machine Prolog, un programme C ne peut être appelé que par une règle prédéfinie. Celle-ci peut empiler un nouveau but, ou provoquer une erreur.
A un point d'arrêt de la machine Prolog, un programme C peut: empiler un nouveau but, réactiver la machine jusqu'au prochain point d'arrêt pour avoir une autre solution, ou provoquer l'abandon du but en cours, avec retour à l'état précédent.
8 . 1 .
Principes de base
L' «état normal», pour une machine Prolog, consiste évidemment en l'exécution de la boucle d'effacement d'une suite de buts (cf. chapitre 2 de ce manuel); cette boucle est appelée parfois horloge Prolog. Lorsqu'elle se trouve dans cette situation, nous dirons que la machine Prolog est active.
Quand la machine Prolog s'active, elle possède déjà une suite initiale de buts à effacer; le processus d'effacement (choix d'une règle, unification de la tête, etc…) dure tant que la suite de buts courante n'est pas vide; quand cette suite est vide, la machine tombe dans un point d'arrêt.
© PrologIA
U 3 - 2
Manuel d'Utilisation Windows
Dans ce paragraphe on définit les divers états dans lesquels la machine Prolog peut se trouver lorsqu'elle n'est pas active, tels que les voit le programmeur C (ou Pascal, ou autre…); ces états seront appelés des points d'arrêt. On introduit aussi les procédures qui font passer la machine d'un point d'arrêt à un autre.
A un point d'arrêt, les états possibles de la machine sont :
0.
Machine non initialisée. Ceci n'est pas vraiment un état de la machine, mais la situation dans laquelle on se trouve avant l'initialisation de celle-ci (allocation de l'espace, etc…). La procédure ProStart(…), appelée une seule fois par session, fait passer dans l'état suivant :
1.
Rien a exécuter (
NO_GOAL
). La machine est initialisée, mais la suite de buts à
effacer est vide.
La machine vient aussi dans cet état par son fonctionnement normal, lorsqu'il n'y a plus aucune manière possible d'effacer la suite de buts courante, c'est-àdire lorsque toutes les solutions du problème courant ont été précédemment obtenues. Dans cet état, toute activation de la machine (par next_solution() ) la ramène immédiatement dans le même état NO_GOAL.
2.
But prêt à être exécuté. Ceci est un étape préalable dans l'activation de la machine : la procédure new_goal() vient d'installer la suite de buts
exec(_b, _x); le programme appelant doit maintenant, à l'aide des routines standard (put_integer, put_term, etc…), unifier _b avec le but ou la liste de buts à effacer.
3.
En exécution Prolog, dans une règle prédéfinie. Pendant l'effacement d'une règle externe, le programme C (ou Pascal, etc…) en question obtient le contrôle de l'exécution et la machine Prolog «paraît» à l'arrêt; cependant, cette situation diffère des états précédents dans le fait qu'il ne correspond pas à un point d'arrêt : la machine Prolog n'est pas naturellement arrêtée au début ou a la fin d'un effacement, mais au contraire elle se trouve en plein milieu d'un effacement, en attente de la terminaison d'une procédure externe.
4.
Solution trouvée (
SOLUTION_EXISTS
). C'est le point d'arrêt le plus fréquent : la suite de buts courante vient d'être entièrement effacée. Le programme appelant peut récupérer les solutions à travers l'argument _x de l'appel exec(_b,_x).
La machine Prolog a mémorisé tous les points de choix de l'effacement en cours. A partir de cet état, on peut appeler la procédure
next_solution() pour relancer la machine afin d'obtenir les autres solutions.
La séquence d'états la plus fréquente sera donc:
NO_GOAL ! «But prêt» ! SOLUTION_EXISTS !!…
… !!SOLUTION_EXISTS ! NO_GOAL
5.
Erreur rencontrée (
ERROR
). C'est l'état dans lequel la machine est mise lorsqu'une erreur a fait avorter son fonctionnement normal. Une activation
(par next_solution() ) la fera passer alors dans l'état NO_GOAL.
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 3
Dans n'importe lequel des états 1 à 4 ci-dessus, la procédure new_goal() peut être appelée pour installer un nouveau problème par-dessus le problème courant, lequel est conservé exactement dans l'état où il se trouve afin de pouvoir ultérieurement continuer à obtenir ses solutions.
De la même manière, dans les états 2 à 5 on peut appeler la procédure kill_goal(), qui provoque l'abandon du problème en cours et le retour au problème précédent, qui se retrouve exactement dans l'état où il était quand on a appelé new_goal().
Les fonctions permettant d'activer la machine Prolog (i.e. de la faire transiter vers un autre état) sont:
ProStart(..)
Fonction permettant d'initialiser la machine Prolog. Cette fonction doit être appelée une fois, avant tout autre appel concernant Prolog. Initialise la machine dans l'état 1.
ProFinal(..)
Fonction permettant de terminer et libérer la machine et l'environnement
Prolog. Elle a un argument de type entier qui spécifie le status de terminaison de Prolog.
new_goal()
Mémorise l'état courant de la machine, et fait passer dans l'état 2 (but prêt à
être exécuté). L'état 2 est initialisé avec l'appel exec(_b,_s) dont les variables doivent être instanciées par le programme appelant APRES l'appel de
new_goal.
Cette procédure permet d'empiler des activations de buts _b jusqu'à une profondeur limitée seulement par la taille des piles de Prolog.
kill_goal()
Revient à l'état exact dans lequel se trouvait la machine avant le dernier appel de new_goal. new_goal et kill_goal fonctionnent comme des parenthèses encadrant l'effacement d'un nouveau but Prolog.
next_solution()
Fonction d'activation de la machine, la faisant transiter vers l'état d'arrêt suivant
(1, 4, ou 5). La fonction a pour valeur le nouvel état de la machine Prolog:
SOLUTION_EXISTS (0), NO_GOAL (-1), ERROR (>0).
© PrologIA
U 3 - 4
Manuel d'Utilisation Windows
Pour tout état Prolog différent de l'état non initialisé, le diagramme suivant définit les transitions possibles:
A!ociation
Prolog
HERITAGE
8 . 2 .
Initialisation et terminaison de Prolog
Au démarrage d'une application, Prolog se trouve dans un état non initialisé. La fonction ProStart permet d'initialiser Prolog avec une configuration définie dans la chaîne de caractères passée en paramètre, chaîne représentant les options de lancement de Prolog (taille des piles, fichier d'entrée,..). La fonction retourne 0 si tout c'est bien passé, un nombre non nul positif, correspondant au numéro de l'erreur, si une erreur s'est produite et l'initialisation ne s'est pas faite.
L'appel de toute autre procédure que ProStart dans l'état non initialisé est illégal.
Les types de la fonction et de son argument doivent être comme ci décrit: int ProStart(params)
© PrologIA
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 5 char *params;
Lorsque l'utilisation de Prolog est terminée, la fonction ProFinal permet de libérer les structures et terminer Prolog. La fonction n'a pas de valeur de retour, elle a un argument qui spécifie le status de terminaison de Prolog, en fonction duquel
ProFinal imprime un message. Ce message doit être défini dans le fichier d'erreurs de Prolog. Le status doit valoir 0 pour une terminaison normale, un nombre strictement positif pour un status d'erreur. Ce status d'erreur pourra être, par exemple, l'erreur retournée par une des fonctions suivantes new_goal, next_solution ou kill_goal.
Après l'exécution de ProFinal, Prolog se trouve à nouveau dans un état non initialisé.
Les types de la fonction et de son argument doivent être comme ci décrit:
ProFinal(status) int status;
Voici un exemple d'utilisation : mon_initialisation_prolog()
{ int err; if ((err = ProStart("-c 400 -f cM prolog.po")) == 0) else fprintf(stderr,"initialisation reussie");
{fprintf(stderr,"initialisation echouee!"); exit(err);}
} ma_terminaison_prolog(condition) int condition;
{ if (mon_erreur(condition))
} else
{ mon_message_erreur(condition);
ProFinal(0);
ProFinal(condition);
}
L'utilisation de la machine Prolog dans une application est délimitée par l'appel aux fonctions ProStart et ProFinal.
8 . 3 .
Empilement d'un but Prolog
L'empilement d'un but _b se fait à travers l'appel d'une règle relais exec(_b,_s), qui garantit la conservation du but en cours et l'arrêt dans un état précis. Cette règle a deux arguments: le premier est un terme représentant le but _b à effacer, le deuxième est le terme qui sera obtenu en solution (c'est en général la liste des variables intéressantes du but _b).
© PrologIA
U 3 - 6
Manuel d'Utilisation Windows
Lorsqu'on a appelé new_goal, l'appel exec(_b,_s) est prêt à être exécuté. Il suffit donc d'utiliser les routines standard pour instancier les arguments (put_term(1,..) pour instancier _b, et put_term(2,..) pour définir les variables correspondant à la solution intéressante).
L'appel de la fonction next_solution provoque la transition de la machine Prolog vers un des états 1,4 ou 5. La valeur retournée par la fonction est l'état atteint:
NO_GOAL (-1), ERROR (>0), ou SOLUTION_EXISTS (0).
La règle relais est l'équivalent de: exec(_b,_s) -> block(_e,_b) condition(_e,_k) point_arret1(_k,_s) fail ; exec(_b,_s) -> point_arret2(NO_GOAL,nil); condition(_e,SOLUTION_EXISTS) -> free(_e) !; condition(_e,_e) -> integer(_e) !; condition(_e,BLOCK_EXIT_WITHOUT_BLOCK) ->; point_arret1(K,S) -> stop; point_arret2(K,S)-> stop point_arret2(NO_GOAL,[]);
Conceptuellement, tout se passe comme si l'utilisateur se trouvait dans la règle
point_arret: de par la manière dont est écrit exec, la valeur du 1er argument (obtenu par get_integer(1,..)) contient également l'état atteint. Si cet état est
SOLUTION_EXISTS, le deuxième argument (obtenu avec get_term(2,..)) contient le terme définissant la solution intéressante.
A!ociation
Prolog
HERITAGE
8 . 4 .
Programmation
Le fichier proext.h contient les déclarations des procédures et des structures d'interface. Il faut mettre un #include de ce fichier en tête des modules utilisant ces procédures.
Voici un exemple de programme C imprimant toutes les solutions de enum(i,-2,8), que l'on peut écrire tel quel dans le module C d'interface utilisateur : prouser.c, à la place de la déclaration EXTERNAL_DESCRIPTOR qui s'y trouve.
© PrologIA
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 7
#define MAX_TAB 10 exemple()
{ int err; long i;
P_SYMBOL pro_symbol(); char tags[MAX_TAB]; int vals[MAX_TAB]; int n = 1;
/* initialisation du but */
new_goal(); tags[0] = 'T'; vals[0] = 1; tags[1] = 'I'; vals[1] = 4; tags[2] = 'N'; vals[2] = pro_symbol("sys:enum"); tags[3] = 'V'; vals[3] = 0; tags[4] = 'I'; vals[4] = -2;
/* i */ tags[5] = 'I'; vals[5] = 8;
put_term(1, MAX_TAB,0,0, tags,vals, 0,0, 5, &err); if (err) goto error;
/* creation solution interessante */ tags[0] = 'V'; vals[0] = 0;
put_term(2, MAX_TAB,0,0, tags,vals, 0,0, 0, &err); if (err) goto error;
/* impression des solutions */
while ((err = next_solution()) == SOLUTION_EXISTS)
{
get_integer(2, &i, &err); /*valeur de i*/ if (err) goto error; printf("solution %d : %ld\n", n++,i);
} if (err > 0)
goto error;
kill_goal();
return 0; error: printf("erreur %d a la %d ieme etape\n",err,n); kill_goal(); return err;
}
EXTERNAL_DESCRIPTOR prouser_desc[] =
{{":exemple", C_FUNCTION, 0, (POINTER)exemple},
{0,0,0,0}};
Vous pouvez trouver un exemple d'utilisation de Prolog comme un sous programme, dans le fichier princip.c qui est le module principal de Prolog.
8 . 5 .
Méthode simple d'appel d'un but Prolog
Une autre méthode, plus simple à utiliser, permet également depuis un langage externe, d'installer un but Prolog et d'en récupérer les solutions.
© PrologIA
U 3 - 8
Manuel d'Utilisation Windows
Elle est plus simple pour installer un but, grâce à la fonction new_pattern,
équivalente à l'ensemble new_goal, put_term du but et put_term du résultat. Elle est moins efficace sur ce point là car elle utilise l'analyseur Prolog.
Elle est plus simple pour extraire des valeurs de la solution, grâce à la fonction
get_formats, équivalente à l'ensemble get_term et traitement des différents tableaux.
Le principe de cette méthode est de spécifier au moment de l'installation du but, la structure des arguments et les valeurs qui y sont intéressantes, de façon à simplement les nommer à l'obtention d'une solution, plutôt que faire une recherche algorithmique des sous_termes voulus de la solution.
Les arguments du but appelé peuvent être un terme quelconque (sauf infini) et les valeurs à extraire sont des termes simples tels que entier, réel, double, identificateur ou chaîne.
8 . 5 . 1 .
D e s c r i p t i o n
int new_pattern(char * but)
est une fonction avec en argument unique: une chaîne de caractères représentant un terme Prolog qui est le but à effacer, et où les résultats intéressants sont représentés par des formats. La fonction retourne 0 si tout c'est bien passé, un numéro d'erreur si une erreur s'est produite.
Les différents formats acceptés sont:
%d
pour les entiers
%s pour les chaînes de caractères
%f
pour les réels simples
%F
pour les réels doubles
%i
pour les identificateurs sous forme d'entier
%I
pour les identificateurs sous forme de chaîne de caractères
Voici un exemple d'appels de new_pattern: erreur = new_pattern("sys:enum(%d,-2,10)"); erreur = new_pattern(":essai(<%i,x,y>,%d,0)");
int get_formats(int no, char *p1, char *p2, …)
est une fonction à nombre variable d'arguments où:
no, le 1er argument, est un entier qui indique le numéro d'ordre de la valeur à récupérer (numéro d'ordre du format concerné, dans la chaîne transmise à
new_pattern), ou s'il vaut 0 indique que toutes les valeurs sont à récupérer.
Le ou les arguments suivants sont les adresses des variables C dans lesquelles seront mémorisées la ou les valeurs voulues, si ce ne sont pas des chaînes.
Dans le cas où l'on attend une chaîne de caractères (formats %s ou %I), deux arguments sont nécessaires. Le premier des deux doit être un pointeur sur une zone mémoire dans laquelle Prolog pourra ranger la chaîne, le second doit être un entier qui indique la taille maximum de la chaîne. La fonction retourne 0 si tout c'est bien passé, un numéro d'erreur si une erreur s'est produite.
Voici pour chaque format le type de(s) variable(s) C associée(s): format type C mode de passage
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 9
%d
%s
%f
%F
%i
%I long adresse char *, int valeur float double adresse adresse long adresse char *, int valeur
Voici un exemple d'appel de get_formats: long i; erreur = get_formats(0, &i);
La fonction get_formats va se charger de garnir l'entier i avec la valeur associée au premier format (l'entier -2 dans notre exemple). A noter que cet appel est strictement équivalent à: erreur = get_formats(1, &i);
8 . 5 . 2 .
Exemple
Voici un exemple d'utilisation des fonctions new_pattern et get_formats: regles_test(recuperation, "recuperation", 1.2e0)->; regles_test(des, "de chaînes", 4444.2e0)->; regles_test(idents, "de", 0.2e0)->; regles_test(Prolog, "caractères", 1.2e0)->; int exemple2()
{
int erreur;
long i;
char s[100];
float f;
erreur = new_pattern("regles_test(%i,%s,%f)");
if (erreur) return error;
while (next_solution() == SOLUTION_EXISTS)
{
erreur = get_formats(0, &i, s, 100, &f);
if (erreur) goto error;
printf("%d %s %f\n",i,s,f);
}
kill_goal();
return 0; error:
printf("erreur\n");
kill_goal();
return erreur;
}
© PrologIA
U 3 - 10
Manuel d'Utilisation Windows
8 . 6 .
Autres fonctions
ConnectDescriptors( EXTERNAL_DESCRIPTOR * paD[] )
Cette routine permet de déclarer dans le code de l'application le tableau de descripteurs paD, écrit comme on l'aurait fait pour une extension C directe. Ce tableau de descripteurs doit être persistant durant toute la session Prolog qui l'utilise (par exemple, qualifié de static s'il est en position de variable locale), et la déclaration doit être faite avant le début de la session (avant l'appel de
ProStart() ).
Si l'argument est NULL, les descripteurs seront supprimés dans la prochaine session. Si la routine est invoquée plusieurs fois, c'est le dernier tableau qui est pris en compte. La routine retourne 0 en cas de succès, -1 en cas d'échec
(session déjà lancée).
ConnectInString( InStringFunction * pfIS )
Cette routine permet de redéfinir la fonction de lecture dans la console, par la déclaration d'une fonction d'entrée de texte pfIS() à laquelle Prolog soumettra toutes les entrées (prédicats). Le remplacement et la suppression de cette fonction fonctionnent comme la précédente. Cette routine retourne la foncion qui était installée avant son appel.
Le format de la fonction de lecture est imposé: son premier argument est l'adresse d'un buffer prêt à recevoir le texte (donc alloué), son second argument est la capacité maximale de ce buffer. Le code de retour de la fonction est ignoré: en cas d'erreur, elle doit rendre une chaîne vide dans le buffer.
Intérieurement, la fonction peut effectuer toute opération même bloquante (en traitant les événements) nécessaire pour obtenir le texte à retourner.
ConnectOutString( OutStringFunction * pfOS )
Cette routine permet de redéfinir la fonction d'écriture dans la console, par la déclaration d'une fonction de sortie de texte pfOS() à laquelle Prolog soumettra toutes les sorties (prédicats, messages). Le remplacement et la suppression de cette fonction fonctionnent comme la précédente. Cette routine retourne la foncion qui était installée avant son appel.
Le format de la fonction d'écriture est imposé: son unique argument est l'adresse d'un buffer contenant le texte à imprimer. Le code de retour de la fonction est ignoré: aucune erreur n'est attendue.
Intérieurement, la fonction peut effectuer toute opération même bloquante (en traitant les événements) nécessaire pour afficher le texte. Toutefois, on remarquera que Prolog peut émettre des lignes vides, et donc un filtrage peut
être nécessaire si par exemple des boîtes de messages sont utilisées.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 11
get_error_complement( int lgmax, char *str, int in_external_code )
Cette routine permet d'obtenir sous forme de chaîne de caractères le terme correspondant à l'éventuel complément d'erreur remonté par l'exécution d'un but. Elle doit être appelée avant la fonction kill_goal(). La variable str (chaîne de caractères) doit avoir été allouée auparavant et doit être d'une longueur supérieure ou égale à l'entier lgmax passé en premier paramètre. Si le booléen
in_external_code est non nul, cela signifie que l'on désire obtenir la chaîne en code caractère externe.
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
9. Interruptions
9.1. Concepts
9.2. Description des interfaces
9.3. Exemple complet
On décrit dans ce chapitre comment lier des programmes Prolog à la survenue d'événements asynchrones tels que des interruptions matérielles. Lorsqu'une interruption survient, le programme Prolog courant est automatiquement suspendu, et la fonction gérant l'interruption est activée dès que la machine Prolog se trouve dans un état où l'on peut réaliser des appels croisés (c'est à dire à la prochaine inférence). Lorsque la fonction se termine, la démonstration suspendue reprend son cours normalement. Il est possible de communiquer entre les deux environnements par l'intermédiaire des données statiques (base de faits, tableaux, assignations).
Ce mécanisme permet donc de suspendre l'exécution en cours et d'empiler l'activation d'une nouvelle machine Prolog pour traiter l'interruption.
9.1. Concepts
La machine Prolog possède un vecteur de bits d'événements externes. Les huit premiers bits (bits 0 à 7) sont réservés pour des événements définis par l'utilisateur.
A chaque bit doit être associée une fonction C pour la construction des arguments et l'appel d'un but Prolog. Cette fonction C est installée par l'utilisateur en appelant la procédure pro_signal.
L'interruption d'un programme Prolog ne peut être servie qu'en certains états précis tels que la fin d'une unification, ou à l'intérieur d'un prédicat évaluable. Cette interruption doit donc être mémorisée jusqu'à ce que la machine Prolog soit en état de la traiter. Ceci est réalisé par la procédure send_prolog_interrupt qui doit être appelée par le gestionnaire d'interruption pour signaler à Prolog cet événement.
Pour lier un programme Prolog à un événement asynchrone donné, il faut donc réaliser les opérations suivantes :
1.
Choisir un bit (0 à 7) et le représenter par un masque (par exemple myEvent =
(1 << numéro du bit)).
2.
Ecrire une fonction (par exemple myHandler) qui servira à activer le programme Prolog gérant l'interruption.
3.
Associer, dans l'environnement Prolog, l'événement myEvent avec la fonction le traitant en appelant la fonction :
pro_signal(myEvent,myHandler).
© PrologIA
R 9 - 2
Manuel de Référence
4.
Dans la routine appelée lors de l'interruption, transmettre l'événement à Prolog en appelant la fonction :
send_prolog_interrupt(myEvent ).
Un exemple de séquence d'événements et de traitements peut être représenté par le schéma suivant : pro_signal(myEvent,myHandler) myHandler()
{
new_goal();...
next_solution();
kill_goal();
}
I
O
N
R
U
P
T
I
E
R
N
T
T
I
T
R
A
E
R
Prolog machine new_goal() next_solution() kill_goal()
(*myHandler)() reset myEvent flag prolog_event_handler(myEvent) interruption asynchrone utilisateur myAsynchronousHandler()
{
send_prolog_interrupt(myEvent);
}
Prolog machine set myEvent flag
...
A!ociation
Prolog
HERITAGE
9.2. Description des interfaces
Les fonctions définies ici permettent de lier Prolog II+ à des procédures d'interruption. Les prototypes de ces fonctions sont donnés à la fin de ce chapitre.
pro_signal(event_mask,event_handler)
Permet d'associer à l'événement event_mask la fonction d'activation
event_handler. Cette fonction renvoie -1L en cas d'erreur (notamment s'il existe déjà une fonction associée à event_mask), NULL sinon. Si le deuxième argument est NULL, la fonction d'activation associée au premier argument est supprimée et sa valeur est renvoyée comme résultat de la fonction. L'argument
event_mask doit être une des valeurs parmi 1, 2, 4, 8, 16, 32, 64, 128.
© PrologIA
A!ociation
Prolog
HERITAGE
Interruptiions
R 9 - 3
Lorsque la fonction event_handler est appelée, si son résultat n est supérieur à
0, l'erreur Prolog correspondante est déclenchée.
send_prolog_interrupt(event_mask)
Déclenche le traitement associé à l'interruption event_mask dès que la machine
Prolog le permet.
Conventions pour l'appel de la fonction d'activation event_handler.
Cette fonction doit retourner un entier qui est interprété comme un code erreur
Prolog si cet entier est positif.
Lorsque plusieurs interruptions sont en attente, elles sont traitées dans l'ordre défini par les numéros des bits (bit 0 d'abord).
Il est déconseillé d'utiliser les fonctions get_strterm et put_strterm si le temps est critique, ces fonctions étant relativement longues d'exécution.
Prototypes des fonctions d'interface
long pro_signal(event_mask,event_handler)
unsigned short event_mask;
int (*event_handler)(); void send_prolog_interrupt(event_mask) unsigned short event_mask;
9.3. Exemple complet
Nous décrirons dans cette section, à titre d'exemple, un cas précis construit pour le système UNIX et utilisant la primitive système kill pour envoyer des signaux à la tâche Prolog à partir du Shell.
Pour mettre en oeuvre cet exemple, suivre les étapes suivantes :
1.
Ecrire l'extension Prolog pour réagir au signal SIGUSR1 1 : 30 et y associer la règle message dans le fichier test.c.
#include <signal.h>
#include "proext.h"
#define MYEVENT 8 extern long pro_signal(); extern void send_prolog_interrupt(); extern P_SYMBOL pro_symbol();
1
C'est un signal asynchrone que l'on peut générer hors Prolog, depuis un autre processus UNIX.
UNIX est un système multitâches, tous les programmes qui s'exécutent sont des processus.
© PrologIA
R 9 - 4
Manuel de Référence
static int my_P_Handler()
{ int err=0; new_goal(); put_strterm(1,":message",&err); if (!err) err = next_solution(); kill_goal(); return err>0 ? err : 0 ;
} static void my_C_signal_handler()
{ send_prolog_interrupt(MYEVENT);
}
/* Cet appel pourrait être mis dans le programme principal pour faire l'initialisation automatiquement.
Le tableau de descripteurs n'est alors plus nécessaire
*/ static long install_handler()
{ pro_signal(MYEVENT,my_P_Handler); signal(SIGUSR1,my_C_signal_handler); return 0;
}
EXTERNAL_DESCRIPTOR testTable[] =
{{":install_handler", C_FUNCTION, 0,
(POINTER) install_handler},
{ 0, 0, 0, 0 }};
2.
Compiler test.c et faire l'édition de lien avec Prolog 1 . Ajouter dans Prolog le programme pour traiter les interruptions.
$ cc -c test.c
2
$ prolink test.o testTable
$ prolog
...
> insert;
message -> val(counter,n) outl(n); count(N) -> val(mod(N+1,1000000),N1) assign(counter,N1) count(N1);
;
> install_handler count(0);
3.
Se connecter sur une autre console, déterminer le numéro de processus (pid) de Prolog avec la commande ps, et envoyer le signal 30 à Prolog. A chaque envoi du signal, la valeur courante du compteur s'imprime sur la console
Prolog.
A!ociation
Prolog
HERITAGE
1
Voir le manuel d'utilisation § 2.8 pour trouver les instructions de compilation et d'édition de lien avec Prolog, adaptées à votre système.
2
Commande pour créer le module objet, compilé de test.c.
© PrologIA
A!ociation
Prolog
HERITAGE shell$ ps -ax
....2525...Prolog
shell$ kill -30 2525
1 shell$ kill -30 2525
Interruptiions
R 9 - 5
> install_handler
count(0);
64790
301267
1
Cette commande envoie le signal SIGUSR1 (auquel doit réagir Prolog) au processus Prolog.
© PrologIA
R 9 - 6
Manuel de Référence
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
10.
Extensions Edinburgh
10.1. Syntaxe
10.2. Le contrôle
10.3. Manipulation des règles
10.4. Opérations prédéfinies sur les données
10.5. Les entrées / sorties
10.6. L'environnement
10.7. Traduction de DCG
Ce chapitre décrit les possibilités supplémentaires présentes lorsque le module de compatibilité Edinburgh est chargé.
Le plan de ce document suit le plan du manuel de référence en décrivant pour chaque rubrique les spécificités du mode Edinburgh.
Un certain nombre de règles prédéfinies dupliquent sous un autre nom les règles prédéfinies Prolog II+.
Toutes les fonctionnalités Prolog II+ sont également disponibles en syntaxe
Edinburgh.
IMPORTANT: Dans ce chapitre, tous les atomes donnés en position d'argument ont le préfixe vide (""). Dans l'écriture des exemples, il est supposé que l'option permettant que "tous les atomes en position d'argument non préfixés explicitement ont le préfixe vide" est activée. Cette option est décrite en annexe A. Si cette option n'est pas activée, il faut avoir à l'esprit que les atomes dont la représentation abrégée est déjà connue du système doivent être explicitement préfixés avec le préfixe vide, sinon c'est le préfixe sys qui sera pris par défaut (du moins dans le contexte user).
C'est notamment le cas des atomes debug, string, fail (utilisés dans le prédicat
set_prolog_flag/2), true, false (utilisés par exemple dans le prédicat write_term/2) et
input , output, read, write (pour stream_property/2 ou open/4).
10.1. Syntaxe
10.1.1. Généralités
La syntaxe Edinburgh est acceptée en entrée dans l'un des cas suivants:
1) à l'activation de Prolog II + a) si l'on utilise l'option -E (équivalent de -m edinburg.mo) b) si l'on utilise un état binaire sauvé alors que la syntaxe Edinburgh était active,
2) sur la ligne de commande Prolog II+
©PrologIA
R 10 - 2
Manuel de Référence
a) si le fichier edinburg.mo est chargé (par le prédicat load ou reload).
b) si le but edinburgh est tapé,
La syntaxe exacte est décrite au chapitre 1 en utilisant la variante E des règles syntaxiques. Les principales différences avec la syntaxe Edinburgh définie dans la norme ISO/IEC sont décrites en annexe A.
Sur la ligne de commande ou dans une directive, lorsque le premier terme rencontré est une liste, celle-ci est interprétée comme une liste de fichiers à consulter. user est
équivalent à "console".
A!ociation
Prolog
HERITAGE
10.1.2. Les opérateurs
En syntaxe Edinburgh , les opérateurs suivants sont prédéfinis:
-
/\
=:=
+
\/
mod
rem
@>
@>=
@=<
\=
==
=\=
\==
=..
\+
is
=
<
>
>=
=<
@<
-->
;
->
, opérateur
:-
:-
?-
700
500
500
500
500
400
400
700
700
700
700
700
700
700
700
700
700
700
700
900
700
700
700 précédence
1200
1200
1200
1200
1100
1050
1001 xfx yfx yfx yfx yfx yfx yfx xfx xfx xfx xfx xfx xfx xfx xfx xfx xfx xfx xfx fy xfx xfx xfx xfx xfy xfy xfy fx fx type xfx terme construit sys:':-'(T1,T2) sys:':-'(T1) sys:'?-'(T1) sys:'-->'(T1,T2) sys:';'(T1,T2) sys:'->'(T1,T2) sys:','(T1,T2) sys:not(T1) sys:is(T1,T2) sys:'='(T1,T2) sys:'<'(T1,T2) sys:'>'(T1,T2) sys:'>='(T1,T2) sys:'=<'(T1,T2) sys:'@<'(T1,T2) sys:'@>'(T1,T2) sys:'@>='(T1,T2) sys:'@=<'(T1,T2) sys:'\='(T1,T2) sys:'=='(T1,T2) sys:'=\='(T1,T2) sys:'\=='(T1,T2) sys:'=..'(T1,T2) sys:'=:='(t1,t2) sys:'+'(t1,t2) sys:'-'(t1,t2) sys:'/\'(T1,T2) sys:'\/'(T1,T2) sys:mod(t1,t2) sys:rem(t1,t2)
©PrologIA
A!ociation
Prolog
HERITAGE
+
-
^
>>
//
\
** opérateur
*
/
<<
400
400
200
200 précédence
400
400
400
200
200
200 yfx yfx fy xfx type yfx yfx yfx fy fy xfy
Extensions Edinburgh
terme construit sys:'*'(t1,t2) sys:'/'(t1,t2) sys:'<<'(T1,T2) sys:'>>'(T1,T2) sys:'//'(T1,T2) sys:'\'(t1,t2) sys:'**'(t1,t2) sys:'+'(t1) sys:'-'(t1) sys:'^'(T1,T2)
R 10 - 3
current_op(P,M,O)
Unifie respectivement les arguments P, M et O avec la précédence, le type de parenthèsage et l'opérateur. Exemple:
?- current_op(1200,M,O).
{M=fx,O=?-}
{M=fx,O=:-}
{M=xfx,O=:-}
{M=xfx,O=-->}
10.2. Le contrôle
\+ X ou \+(X)
Equivalent à not(X).
X =Y ou =(X,Y)
Succès si X est unifiable avec Y, sinon échec. Est défini par la règle:
X = X.
X \= Y ou \=(X,Y)
Succès si X n'est pas unifiable avec Y, sinon échec. Exemples:
?- 1 \= 1.
?- X \= 1.
?- X \= Y.
?- 1 \= 1.0.
{}
X -> Y ou ->(X,Y)
Si-alors. Est défini par la règle (les parenthèses ne servent qu'à améliorer la lisibilité):
(X -> Y) :- X,!,Y.
X , Y ou ,(X,Y)
"Et" propositionnel. X est exécuté puis Y.
X ; Y ou ;(X,Y)
"Ou" propositionnel. Est défini par les règles suivantes (Note: en Prolog II la coupure est transparente):
©PrologIA
R 10 - 4
Manuel de Référence
X;Y :- X.
X;Y :- Y.
call(X)
Est défini par la règle: call(X) :- X.
catch(G,C,R)
Lance l'exécution du but G. Fait un succès dans deux cas:
- l'exécution de G réussit sans interruption de type "throw"
- une interruption de type "throw" dont l'argument s'unifie avec l'argument C se produit et l'exécution du but R réussit. Si l'unification échoue, l'interruption est propagée selon le mécanisme de block/block_exit. Exemples:
?- reconsult(user).
foo(X) :- Y is X*2, throw(test(Y)).
bar(X) :- X=Y, throw(Y).
coo(X) :- throw(X).
car(X) :- X=1, throw(X).
g :- catch(p, B, write(h2)), coo(c).
p.
p :- throw(b). .
{}
?- catch(foo(5),test(Y),true).
{Y=10}
?- catch(bar(3),Z,true).
{Z=3}
?- catch(true,C,write(foo)), throw(bla).
-> bla([]) 'block_exit' SANS 'block' CORRESPONDANT
?- catch(coo(X),Y,true).
{}
?- catch(car(X),Y,true).
{X=1,Y=1}
?- catch(g, C, write(h1)). h1{C=c}
fail_if(X)
Est équivalent à l'appel de not(call(X)).
?- fail_if(true).
?- fail_if(4=5).
{}
once(G)
Efface le but G de la première manière possible. Est défini par la règle: once(G) :- G, ! .
throw(X)
Est équivalent à un appel de block_exit(X1) , X1 étant une copie du terme X
(renommage des variables).
true
S'efface toujours avec succès.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 5
unify_with_occurs_check(X, Y)
Tente l'unification des termes X et Y avec une vérification de non production d'arbres infinis. Echoue si l'unification échoue ou si elle génère un arbre infini. Exemples:
?- unify_with_occurs_check(1,1).
{}
?- unify_with_occurs_check(1,2).
?- unify_with_occurs_check(X,1).
{X=1}
?- unify_with_occurs_check(X,a(X)).
?- unify_with_occurs_check(X,[1|X]).
?- unify_with_occurs_check(X,[X|1]).
?-
10.3. Manipulation des règles
abolish(P)
Se comporte comme suppress(P) où P est un identificateur ou un terme de la forme identificateur/arité.
assert(X) assertz(X)
Ajoute en queue de paquet la règle ou le fait X. Voir assert décrit pour
Prolog II. Attention, en syntaxe Edinburgh, la virgule est à la fois un séparateur et un opérateur, il faut donc parenthéser l'argument dans le cas des règles:
?- assert(a).
{}
?- assert((a :- b,c)).
{}
asserta(X)
Ajoute en tête de paquet la règle ou le fait X. Voir asserta décrit pour
Prolog II. Même remarque sur la virgule que ci dessus.
clause(T,Q)
T ne doit pas être une variable libre. Unifie T et Q respectivement avec la tête et la queue de toutes les clauses dont l'accès est défini par T. Se comporte comme rule avec la différence qu'une queue vide est représentée par true, et qu'une queue d'au moins deux littéraux est représentée par une structure avec le noeud ','.
consult(F) reconsult(F)
Est équivalent respectivement à insert et reinsert. Si F est un identificateur, c'est la chaîne correspondant à son abréviation qui est prise en compte comme nom de fichier (c.à.d. le préfixe est ignoré). Si F vaut user ou "user" lit sur l'unité courante de lecture.
©PrologIA
R 10 - 6
Manuel de Référence listing listing(X)
Est équivalent respectivement à list et list(X).
retract(X)
Même comportement que retract à deux arguments. X doit être un fait ou une règle dont le prédicat de tête est instancié.
retract((T:- true)) est équivalent à retract(T),
retract(T) est équivalent à retract(T,[]), si T ne s'unifie pas avec :- (T1,T2),
retract((T :- Q)) est équivalent à retract(T,Q).
retractall(X)
Toutes les règles dont la tête s'unifie avec X sont supprimées (sans unification de X ni backtracking). X doit avoir la forme d'une tête de règle, sinon, le prédicat n'aura aucun effet. Fait toujours un succès.
A!ociation
Prolog
HERITAGE
10.4. Opérations prédéfinies sur les données
10.4.1. Les tests de type
atom(X)
Succès si X est un identificateur, sinon échec. Equivalent à ident(X).
atomic(X)
Succès si X est une constante, sinon échec.
compound(X)
Succès si X est une liste ou un n-uplet, sinon échec.
float(X)
Est équivalent à real(X).
nonvar(X)
Est équivalent à bound(X).
number(X)
Succès si X est un entier ou un réel, sinon échec.
var(X)
Succès si X est une variable libre, sinon échec. Est équivalent à free(X).
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 7
10.4.2. Les opérations arithmétiques
L'évaluation d'une expression est faite au moyen de la règle prédéfinie val. Certaines expressions peuvent être utilisées en syntaxe Edinburgh directement comme des termes à effacer.
Les termes suivants peuvent être effacés, sans l'intermédiaire du prédicat val.
X =:= Y ou =:=(X,Y)
Est équivalent à val(eql(X,Y),1).
X =\= Y ou =\= (X,Y)
Est équivalent à val('=\='(X,Y),1).
X < Y ou <(X,Y)
X =< Y ou =<(X,Y)
X > Y ou >(X,Y)
X >= Y ou >=(X,Y)
Sont respectivement équivalents à:
val('<'(X,Y),1) val('=<'(X,Y),1) val('>'(X,Y),1) val('>='(X,Y),1)
X is Y ou is(X,Y)
Est équivalent à val(Y,X).
Les fonctions suivantes sont spécifiques au mode Edinburgh. Elles sont évaluables par les règles prédéfinies val, tval ou is. Elles doivent être appliquées à des arguments de type entier ou réel.
\X ou \(X) valeur (\X) = valeur('~'(X)) = complément bit à bit de X.
La valeur de X doit être de type entier. Le résultat est de type entier.
X // Y ou //(X,Y) valeur(//(X, Y)) = division entière de valeur(X) par valeur(Y).
Le résultat est de type entier.
log(t)
valeur(log(t)) = valeur(ln(t)) = logarithme népérien(valeur(t)).
Le résultat est de type réel.
truncate(t)
valeur(truncate(t)) = valeur(trunc(t)) = conversion en entier de la valeur de t.
Le résultat est de type entier.
©PrologIA
R 10 - 8
Manuel de Référence
10.4.3. Composition et décomposition d'objets
X =.. Y ou =..(X,Y)
Si X est instancié, Y est unifié avec une liste dont le premier élément est le foncteur de X, et les éléments suivants les éventuels arguments dans l'ordre. Si
X est libre, Y doit être instancié avec une liste; X est alors unifié avec le n-uplet construit avec le premier élément de la liste comme foncteur, et les autres
éléments comme arguments. A un élément atomique correspond une liste constituée de ce seul élément. Exemples:
?- '=..'(foo(a,b),[foo,a,b]).
{}
?- '=..'(X,[foo,a,b]).
{X=foo(a,b)}
?- '=..'(foo(a,b),L).
{L=[foo,a,b]}
?- '=..'(foo(X,b),[foo,a,Y]).
{X=a,Y=b}
?- '=..'(1,[1]).
{}
?- '=..'(foo(a,b),[foo,b,a]).
?- '=..'(f(X),[f,u(X)]).
{X=_714, _714=u(_714)}
arg(N,T,X)
S'efface si X est le Nième élément du terme T. N entier et T tuple ou paire pointée, doivent être connus au moment de l'appel.
Si T est une paire pointée alors si
N=1, X est unifié avec le premier élément de la paire.
N=2, X est unifié avec le deuxième élément de la paire.
Si N est un entier et T est un n-uplet de taille M>1, alors si
0<N<M, X est unifié avec l' argument N+1 du n-uplet T.
(c.à.d. avec l'argument N de T=f(a
1
,...,a n
,...,a
M-1
)).
Sinon échec.
Exemple :
?- arg(1,eq(john,fred), X).
{X=john}
?- arg(0,eq(john,fred), Y).
?-
atom_chars(A,L)
Associe à un identificateur A la liste L des caractères qui le constituent et viceversa. Le préfixe de l'identificateur A est ignoré (en entrée) ou égal à "" (en sortie). Exemples:
?- atom_chars('',L).
{L=[]}
?- atom_chars('''', L).
{L=['''']}
?- atom_chars('ant',L).
{L=[a,n,t]}
?- atom_chars(Str, ['s', 'o', 'p']).
{Str=sop}
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 9
atom_codes(A,L)
Associe à un identificateur A la liste L des codes internes (codes ISO ou codes de la machine hôte) qui le constituent et vice-versa. Le préfixe de l'identificateur A est ignoré (en entrée) ou égal à "" (en sortie).
atom_concat(A1,A2,A3)
Fonctionne sur les identificateurs de la même manière que conc_string sur les chaînes de caractères. Le préfixe des identificateurs A1, A2, A3 sont ignorés
(en entrée) ou égaux à "" (en sortie). Exemples:
?- atom_concat('hello',' world',S3).
{S3='hello world'}
?- atom_concat(T,' world','small world').
{T=small}
?- atom_concat(T1,T2,'hello').
{T1='',T2=hello}
{T1=h,T2=ello}
{T1=he,T2=llo}
{T1=hel,T2=lo}
{T1=hell,T2=o}
{T1=hello,T2=''}
atom_length(A,N)
Unifie N avec la longueur de l'identificateur A . Le préfixe de l'identificateur A est ignoré. Exemples:
?- atom_length('enchanted evening',N).
{N=17}
?- atom_length('',N).
{N=0}
functor(T,F,N)
Associe à un arbre T son foncteur F et son arité N, et vice-versa.
?- functor(foo(a,b,c),X,Y).
{X=foo,Y=3}
?- functor(X,foo,3).
{X=foo(_515,_516,_517)}
?- functor(X,foo,0).
{X=foo}
?- functor(foo(a),foo,2).
?- functor(foo(a),fo,1).
?- functor(1,X,Y).
{X=1,Y=0}
?- functor(X,1.1,0).
{X=1.1}
?- functor([_|_],'.',2).
{}
?- functor([],[],0).
{}
name(X,L)
Si X est un identificateur ou un nombre, L est unifié avec la liste des codes internes (codes ISO ou codes de la machine hôte) des caractères constituant la représentation de l'identificateur X.
Si L est une liste de codes internes des lettres d'un identificateur, X est instancié avec l'identificateur déterminé par les conventions courantes de préfixage.
?- name(abc:def,L).
©PrologIA
R 10 - 10
Manuel de Référence
{L=[97,98,99,58,100,101,102]}
?- name(123,L).
{L=[49,50,51]}
?- name(X,[65,66|4]).
?- name("asd",X).
?- name(Y,X).
?- name(ab:'cd',L).
{L=[97,98,58,99,100]}
?- name(ab:' %',L).
{L=[97,98,58,39,32,37,39]}
?- name(I,[97,98,58,99,100]).
{I=ab:cd}
?- name(M,[97,98,58,39,32,37,39]).
{M=ab:' %'}
?-
number_chars(N,L)
Associe à un nombre N la liste L des caractères qui le constituent.
Réciproquement, considère la liste L des caractères comme une entrée et y associe le nombre N (imprimé sous sa forme décimale). Exemples:
?- number_chars(33, L).
{L=['3','3']}
?- number_chars(33.0, L).
{L=['3','3','.','0']}
?- number_chars(X, ['3', '.', '3', 'E', '+', '0']).
{X=3.3}
?- number_chars(3.3, ['3', '.', '3', 'E', '+', '0']).
{}
?- number_chars(A, ['-', '2', '5']).
{A=-25}
?- number_chars(A, [' ', '3']).
{A=3}
?- number_chars(A, ['0', 'x', 'f']).
{A=15}
?- number_chars(A, ['0', '''', 'a']).
{A=97}
?- number_chars(A, ['4', '.', '2']).
{A=4.2}
?- number_chars(A, ['4', '2', '.', '0', 'e', '-', '1']).
{A=4.2}
number_codes(N,L)
Même description que number_chars/2 mais L est une liste de codes internes
(codes ISO ou codes de la machine hôte).
phrase(X,Y) phrase(X,Y,Z)
Essaie de décomposer la liste Y en une phrase de la grammaire et un reste. X doit être une tête de règle de grammaire, Y et Z peuvent être des variables libres ou des listes. Enumère les solutions possibles en unifiant le reste avec
Z. La forme à deux arguments phrase(X,Y) équivaut à phrase(X,Y,[]).
Note: Si Y ou Z sont des chaînes de caractères, celles-ci sont d'abord transformées en listes de caractères avant de réaliser l'appel :
phrase(somme(X),"1+2+3") est transformé automatiquement dans l'appel
phrase(somme(X), ["1","+","2","+","3"]).
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 11
sub_atom(A1,N1,N2,N3,A2)
Fait un succès si l'identificateur A1, qui doit être connu lors de l'appel, peut
être vu comme la concaténation de trois parties telles que N1 soit la longueur de la première partie, N2 la longueur de la seconde partie qui n'est rien d'autre que l'identificateur A2, et N3 la longueur de la troisième partie. Les préfixes des identificateurs A1 et A2 sont ignorés (en entrée) ou égaux à "" (en sortie).
Exemples:
?- sub_atom(abracadabra, 0, 5, A, S2).
{A=6,S2=abrac}
?- sub_atom(abracadabra, _, 5, 0, S2).
{S2=dabra}
?- sub_atom(abracadabra, 3, L, 3, S2).
{L=5,S2=acada}
?- sub_atom(abracadabra, B, 2, A, ab).
{B=0,A=9}
{B=7,A=2}
?- sub_atom('Banana', 3, 2, A, S2).
{A=1,S2=an}
?- sub_atom('charity', B, 3, A, S2).
{B=0,A=4,S2=cha}
{B=1,A=3,S2=har}
{B=2,A=2,S2=ari}
{B=3,A=1,S2=rit}
{B=4,A=0,S2=ity}
?- sub_atom('ab', Start, Length, A, Sub_atom).
{Start=0,Length=0,A=2,Sub_atom=''}
{Start=0,Length=1,A=1,Sub_atom=a}
{Start=0,Length=2,A=0,Sub_atom=ab}
{Start=1,Length=0,A=1,Sub_atom=''}
{Start=1,Length=1,A=0,Sub_atom=b}
{Start=2,Length=0,A=0,Sub_atom=''}
10.4.3. Comparaison de termes quelconques
X == Y ou ==(X,Y)
Succès si X est formellement égal à Y, sinon échec. Exemples:
?- 1 == 1.
{}
?- X == X.
{}
?- 1 == 2.
?- X == 1.
?- X == Y.
?- _ == 1.
?- _ == _.
?-
X \== Y ou \== (X,Y)
Succès si X n'est pas formellement égal à Y, sinon échec. Exemples:
?- 1 \== 1.
?- 1 \== 2.
{}
?- X \== 1.
{}
?- _ \== _.
{}
?-
©PrologIA
R 10 - 12
Manuel de Référence
X @< Y
Compare les termes X et Y, fait un succès si X précède Y, échoue sinon.
X @> Y
Compare les termes X et Y, fait un succès si Y précède X, échoue sinon.
X @>= Y
Compare les termes X et Y, fait un succès si Y précède ou est formellement
égal à X, échoue sinon.
X @=< Y
Compare les termes X et Y, fait un succès si X précède ou est formellement
égal à Y, échoue sinon. Quelques exemples:
?- 1.0 @< 1.
{}
?- @<(aardvark,zebra).
{}
?- @<(short,short).
?- short @< shorter.
{}
?- @<(foo(a),foo(b)).
{}
?- @<(foo(a,b),north(a)).
?- @<(X,X).
?- Y @< X.
{}
?- @<(_,_).
{}
?- @<(foo(X,a),foo(Y,b)).
{}
?- "foo" @> foo.
{}
?- [1,2,3] @> [1,1,3,4].
{}
?- [1,2] @> <>(1,2).
?- [1,2] @> <>(X).
{}
A!ociation
Prolog
HERITAGE
10.5. Les entrées / sorties
10.5.1. Généralités
A l'ouverture d'une unité d'entrée/sortie (prédicats open/3 et open/4), un argument de sortie, qui doit obligatoirement être une variable libre lors de l'appel, est unifié avec une constante (en l'occurence un entier négatif). Cette constante permettra dans les prédicats de désigner de manière unique le canal d'accès à l'unité. Une option d'ouverture permet d'associer à l'unité un identificateur, appelé "alias", qui sera dans les sources de programmes un moyen plus clair et plus global de désigner ce numéro de canal.
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 13
close(C,O), close(C)
Ferme l'unité associée au canal C. L'argument O, s'il existe, doit être une liste d'options prises parmi:
force(true): une erreur sur la fermeture de l'unité sera remontée.
force(false): une erreur sur la fermeture de l'unité sera ignorée.
open(S,M,C,O), open(S,M,C)
Ouvre selon le mode M, l'unité ayant pour nom l'atome S et renvoie le canal associé C. L'argument O, s'il existe, doit être une liste d'options prises parmi:
- alias(A) où A doit être un identificateur et indiquera un alias par lequel l'unité pourra être nommée dans les prédicats.
- eof_action(A) qui indique l'action à accomplir sur une fin de fichier suivant la valeur de A:
- error: une erreur est générée (c'est le défaut).
- eof_code: un code spécial, dépendant du prédicat de lecture, est rendu à chaque tentative d'accès au fichier (Voir les prédicats de lecture).
- reset: le même code que dans le cas de eof_code est rendu et la lecture de la fin de fichier est annulée. La valeur de la propriété end_of_stream reste at. Ceci est utile pour des terminaux.
- lg_buffer(L) où L doit être un entier et indiquera la taille du buffer associé à l'unité.
- reposition(B) où B peut prendre la valeur :true ou :false et indique si l'index de lecture (ou d'écriture) peut être repositionné (appel à la primitive set_stream_position/2). Défaut: true.
- type(T) où T désignera le type de l'unité.
Le canal C associé à l'unité doit être une variable libre lors de l'appel et est unifié avec une valeur entière négative (transparent pour l'utilisateur).
Exemple:
?- open(bfile,write,X,[alias(bfile),type(binary)]).
{X=-7}
set_stream_position(C,N)
Positionne au Nième octet le pointeur de lecture ou d'écriture de l'unité associée au canal C. N doit être un entier et l'unité doit être un fichier disque.
stream_property(C,P)
Réussit si l'unité associée au canal C a la propriété P. C peut être une variable libre, auquel cas tous les canaux ayant la propriété P seront unifiés successivement avec C. P peut soit être libre, soit indiquer une propriété prise parmi:
- alias(A) où A sera unifié avec l'alias de l'unité.
- file_name(S) où S sera unifié avec le nom de l'unité.
- input qui sera vrai si l'unité est une unité d'entrée.
- mode(M) où M sera unifié avec le mode d'ouverture de l'unité.
- output qui sera vrai si l'unité est une unité de sortie.
- position(P) où P sera unifié avec la position du pointeur de l'unité.
- type(T) où T sera unifié avec le type de l'unité.
©PrologIA
R 10 - 14
Manuel de Référence
- reposition(B) où B sera unifié avec la valeur true ou false suivant l'option choisie à l'ouverture de l'unité.
- eof_action(A) où A sera unifié avec l'action à accomplir sur une fin de fichier.
- end_of_stream(E) où E sera unifié avec:
- not si l'on n'est pas sur une fin de fichier.
- at si l'on se trouve sur une fin de fichier.
- past si l'on a dépassé la fin de fichier (une lecture a encore été tentée après une fin de fichier signalée).
C ne peut pas être un alias car il peut aussi être utilisé en sortie.
Exemples
?- stream_property(S,P).
{S=-6,P=file_name(foo)}
{S=-7,P=file_name(bfoo)}
{S=-6,P=mode(append)}
{S=-7,P=mode(read)}
{S=-6,P=alias(momo)}
{S=-7,P=alias(bfile)}
{S=-6,P=type(text)}
{S=-7,P=type(binary)}
{S=-7,P=input}
{S=-6,P=output}
{S=-6,P=position(69)}
{S=-7,P=position(4)}
{S=-6,P=reposition(:true)}
{S=-7,P=reposition(false)}
{S=-6,P=eof_action(error)}
{S=-7,P=eof_action(eof_code)}
{S=-6,P=end_of_stream(:not)}
{S=-7,P=end_of_stream(:not)}
?- stream_property(S,type(text)).
{S=-6}
?- stream_property(-7,type(B)).
{B=binary}
A!ociation
Prolog
HERITAGE
10.5.2. Entrées
10.5.2.1. Interprétation des chaînes de caractères
Un texte en position d'argument noté entre doubles quotes peut être interprété par l'analyseur syntaxique de différentes manières suivant l'option choisie (sur la ligne de commande ou par exécution du prédicat set_prolog_flag/2):
- Soit comme une vraie chaîne de caractères (type à part entière).
- Soit comme un atome: il y aura alors une équivalence avec l'écriture entre simples quotes.
- Soit comme une liste composée des caractères (atomes préfixés par le préfixe vide) constituant la chaîne.
- Soit comme une liste composée des codes des caractères constituant la chaîne.
Exemples:
?- set_prolog_flag(double_quotes,string), read(R).
"hello world".
{R="hello world"}
?- set_prolog_flag(double_quotes,chars), read(R).
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 15
"hello world".
{R=[h,e,l,l,o,' ',w,o,r,l,d]}
?- set_prolog_flag(double_quotes,codes), read(R).
"hello world".
{R=[104,101,108,108,111,32,119,111,114,108,100]}
?- set_prolog_flag(double_quotes,atom), read(R).
"hello world".
{R='hello world'}
?-
10.5.2.2. Prédicats
Si l'argument désignant l'unité d'entrée est un argument en entrée, on pourra utiliser indifféremment le n° de canal ou bien l'alias associé à cette l'unité. Dans le cas contraire (cas du prédicat current_input/1), c'est le n° de canal qui sera rendu.
La plupart des prédicats d'entrée peuvent s'utiliser soit sur l'unité spécifiée en argument (n° de canal, alias), soit sur l'unité courante d'entrée, auquel cas l'argument indiquant l'unité est absent.
Le comportement des prédicats de lecture, quand une fin de fichier est rencontrée, est conforme à l'option eof_action choisie à l'ouverture de l'unité sur laquelle se fait la lecture.
at_end_of_stream(C), at_end_of_stream
Réussit si une lecture sur le canal C a déjà signalé une fin de fichier, échoue sinon. Contrairement au prédicat eof, ce prédicat n'essaie pas d'effectuer une nouvelle lecture sur le canal C mais se contente d'examiner le status de la lecture précédente.
current_input(C)
C est unifié avec le canal associé à l'unité d'entrée courante. Attention: C ne sera pas un alias.
get(C,X), get(X)
X est unifié avec l'entier égal au code interne (code ISO ou code de la machine hôte) du premier caractère non blanc lu sur l'unité d'entrée associée au canal
C. Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option
eof_action(eof_code), X est unifié avec la valeur -1.
get_byte(C,X), get_byte(B)
X est unifié avec le premier octet (entier >= 0) lu sur l'unité d'entrée associée au canal C. Cette unité doit être de type :binary. Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option eof_action(eof_code), X est unifié avec la valeur -1.
get_char(C,X), get_char(X)
X est unifié avec le premier caractère lu sur l'unité d'entrée associée au canal C.
Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option
eof_action(eof_code), X est unifié avec la valeur end_of_file.
©PrologIA
R 10 - 16
Manuel de Référence get_code(C,X), get_code(X)
X est unifié avec l'entier égal au code interne (code ISO ou code de la machine hôte) du premier caractère lu sur l'unité d'entrée associée au canal C. Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option
eof_action(eof_code), X est unifié avec la valeur -1.
A!ociation
Prolog
HERITAGE
get0(X)
Identique à get_code/1.
peek_byte(C,X), peek_byte(X)
Essaie d'unifier X avec le premier octet (entier >= 0) en entrée sur l'unité associée au canal C. Cette unité doit être de type :binary. L'octet n'est pas lu.
Ce prédicat ne modifie donc pas les propriétés position et end_of_stream de l'unité. Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option eof_action(eof_code), X est unifié avec la valeur -1.
peek_char(C,X), peek_char(X)
Essaie d'unifier X avec le premier caractère en entrée sur l'unité associée au canal C. Le caractère n'est pas lu. Ce prédicat ne modifie donc pas les propriétés position et end_of_stream de l'unité. Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option eof_action(eof_code), X est unifié avec la valeur end_of_file.
peek_code(C,X), peek_code(X)
Essaie d'unifier X avec l'entier égal au code interne (code ISO ou code de la machine hôte) du premier caractère en entrée sur l'unité associée au canal C.
Le caractère n'est pas lu. Ce prédicat ne modifie donc pas les propriétés
position et end_of_stream de l'unité. Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option eof_action(eof_code), X est unifié avec la valeur -1.
read(C,X), read(X)
Lit le prochain terme se trouvant sur l'unité associée au canal C, plus le premier caractère non blanc qui le suit. Equivalent à in(X,_) sur l'unité C. Si une fin de fichier est rencontrée et que l'unité a été ouverte avec l'option
eof_action(eof_code), X est unifié avec la valeur end_of_file.
Exemple:
?- read(X).
[1,2].
{X=[1,2]}
read_line(C,S,N), read_line(S,N), read_line(S)
Equivalent au prédicat inl(S,N) sur l'unité associée au canal C.
read_term(C,X,O), read_term(X,O)
Même chose que le prédicat read, mais l'on peut en plus obtenir une liste d'options prises parmi:
- variables(L_V) où L_V sera la liste des variables du terme X.
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 17
- variable_names(L_VN) où L_VN sera le dictionnaire des variables du terme X. Ce dictionnaire sera composé d'éléments de la forme:
"nom de la variable" = variable.
- singletons(L_S) où L_S sera le dictionnaire des variables qui n'apparaissent qu'une seule fois dans le terme X. Ce dictionnaire sera composé d'éléments de la forme: "nom de la variable" = variable.
Exemple:
?- read_term(T,[variables(L_V),variable_names(L_VN),
singletons(S)]).
aa(X,[Y,X,Y,Z],<>(Z,U)).
{T=aa(_940,[_969,_940,_969,_1042],_1042(_1208)),
L_V=[_940,_969,_1042,_1208],
L_VN=["X" = _940,"Y" = _969,"Z" = _1042,
"U" = _1208],
S=["U" = _1208]}
see(F)
Est équivalent à input(F). Si F est un identificateur, c'est la chaîne correspondant à son abréviation qui est prise en compte comme nom d'unité
(c.à.d. le préfixe est ignoré).
seeing(F)
Est équivalent à input_is(F). Si F est un identificateur, c'est la chaîne correspondant à son abréviation qui est prise en compte comme nom d'unité
(c.à.d. le préfixe est ignoré).
seen
Est équivalent à close_input.
set_input(C)
Redirige l'entrée courante vers l'unité associée au canal C.
10.5.3. Sorties
Si l'argument désignant l'unité de sortie est un argument en entrée, on pourra utiliser indifféremment le n° de canal ou bien l'alias associé à cette l'unité. Dans le cas contraire (cas du prédicat current_output/1), c'est le n° de canal qui sera rendu.
La plupart des prédicats de sortie peuvent s'utiliser soit sur l'unité spécifiée en argument (n° de canal, alias), soit sur l'unité courante de sortie, auquel cas l'argument indiquant l'unité est absent.
current_output(C)
C est unifié avec le canal associé à l'unité de sortie courante. Attention: C ne sera pas un alias.
flush_output(C), flush_output
Equivalent au prédicat flush/0 pour l'unité de sortie associée au canal C.
©PrologIA
R 10 - 18
Manuel de Référence nl(C), nl
Est équivalent à line/0 pour l'unité de sortie associée au canal C.
put(X)
Equivalent à put_code/1.
put_byte(C,X), put_byte(X)
L'octet X (entier >= 0) est envoyé sur l'unité de sortie associée au canal C.
Cette unité doit être de type :binary.
A!ociation
Prolog
HERITAGE
put_char(C,X), put_char(X)
Le caractère X est envoyé sur l'unité de sortie associée au canal C.
put_code(C,X), put_code(X)
Le code interne de caractère X est envoyé sur l'unité de sortie associée au canal
C.
set_output(C)
Redirige la sortie courante vers l'unité associée au canal C.
tab(N)
Envoie N blancs sur l'unité courante de sortie.
tell(F)
Est équivalent à output(F). Si F est un identificateur, c'est la chaîne correspondant à son abréviation qui est prise en compte comme nom d'unité
(c.à.d. le préfixe est ignoré).
telling(F)
Est équivalent à output_is(F). Si F est un identificateur, c'est la chaîne correspondant à son abréviation qui est prise en compte comme nom d'unité
(c.à.d. le préfixe est ignoré).
told
Est équivalent à close_output.
write(C,X), write(X)
Est équivalent à write_term avec les options [quoted(false),
numbervars(true), ignore_ops(false)].
writeq(C,X), writeq(X)
Est équivalent à write_term avec les options [quoted(true), numbervars(true),
ignore_ops(false)].
write_canonical(C,X), write_canonical(X)
Est équivalent à write_term avec les options [quoted(true),
numbervars(false), ignore_ops(true)].
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 19
write_term(C,X,O), write_term(X,O)
Ecrit le terme X sur l'unité de sortie associée au canal C, en tenant compte de la liste d'options O, options prises parmi:
- quoted(true) ou quoted(false): indique si les constantes chaînes et les identificateurs contenus dans X doivent être écrits sous forme quotée.
- ignore_ops(true) ou ignore_ops(false): indique si les déclarations courantes d'opérateurs doivent être ignorées.
- numbervars(true) ou numbervars(false): indique si les termes de la forme '$VAR'(N), N étant un entier positif doivent être écrits comme une variable constituée d'une lettre majuscule suivie d'un entier. La lettre est la (i+1)ième lettre de l'alphabet et l'entier est j, tels que:
i = N modulo 26
j = N / 26 (division entière).
Si une option n'est pas spécifiée, elle a pour valeur par défaut :false.
Exemples:
?- write_term(['quoted atom', "string", 1+2,
'$VAR'(35)],[]).
[quoted atom,string,1 + 2,'$VAR'(35)]{}
?- write_term(['quoted atom', "string", 1+2,
'$VAR'(35)],[quoted(:true),numbervars(true), ignore_ops(true)]).
['quoted atom',"string",+(1,2),J1]{}
?-
10.6. L'environnement
Un certain nombre de paramètres globaux appelés "variables d'état" peuvent être modifiés ou consultés. Ce sont:
- l'état du débogueur: nom de la variable d'état: debug valeurs possibles: on (actif) ou off (inactif).
- le comportement de Prolog sur une règle appelée et non définie: nom de la variable d'état: unknown valeurs possibles: error (génération d'une erreur), warning (génération d'un avertissement) ou fail (échec).
- l'interprétation des doubles quotes en position d'argument: nom de la variable d'état: double_quotes valeurs possibles: chars (liste de caractères), codes (liste des codes des caractères), atom (atome) ou string (vraies chaînes).
- la possibilité de convertir des caractères en d'autres: nom de la variable d'état: char_conversion
Seule valeur possible actuellement: off (inactif).
- l'affirmation que l'arithmétique entière n'est pas limitée: nom de la variable d'état: bounded valeur non modifiable: false.
- l'arité maximale (nombre d'arguments) d'une règle ou d'une structure: nom de la variable d'état: max_arity valeur non modifiable: 8388607.
- la définition de la division entière et du reste de celle-ci:
©PrologIA
R 10 - 20
Manuel de Référence
nom de la variable d'état: integer_rounding_function valeur non modifiable: toward_zero.
current_prolog_flag(X,Y)
Renseigne sur l'état courant des variables d'état de l'environnement. Les variables d'état: max_integer et min_integer provoquent toujours un échec car l'arithmétique entière n'est pas bornée. Exemple: On veut connaître la valeur de toutes les variables d'état:
?- current_prolog_flag(F, V).
{F=bounded,V=false}
{F=integer_rounding_function,V=toward_zero}
{F=char_conversion,V=off}
{F=max_arity,V=8388607}
{F=:debug,V=off}
{F=unknown,V=warning}
{F=double_quotes,V=:string}
halt(X), halt
Sont respectivement équivalents à quit(X) et quit.
set_prolog_flag(X,Y)
Positionne la variable d'état X de l'environnement à la valeur Y. Ce prédicat est aussi une directive de compilation, c'est à dire que les variables d'environnement peuvent être modifiées au milieu d'un source que l'on compile. Exemples:
?- consult.
set_prolog_flag(double_quotes, codes).
foo :- myrule("abc").
.
{}
?- set_prolog_flag(debug, on).
{}
A!ociation
Prolog
HERITAGE
10.7. Traduction de DCG
1
En mode insertion, Prolog transforme en règles Prolog II+ les règles de grammaire de la forme
A --> B.
où A est un terme et B est une suite de termes. Pour chaque terme de la suite de termes B :
- Si c'est une liste, il est interprété comme une suite de symboles terminaux.
1Definite Clause Grammar, extension des grammaires hors-contexte et sous-ensemble des grammaires de métamorphose.
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 21
- Si c'est une chaîne de caractères, celle-ci est transformée en une liste de caractères qui est donc ensuite considérée également comme une liste de symboles terminaux.
- Si c'est un terme entre accolades, il est interprété comme un appel à une règle à effectuer tel quel.
- Sinon le terme est considéré comme un non-terminal représentant une règle de réécriture.
La règle prédéfinie phrase/2 ou phrase/3 permet d'analyser ou de synthétiser les chaînes grammaticales définies par cette grammaire (voir § 10.2).
Pour traduire la règle de grammaire en règle Prolog, Prolog appellera le prédicat
:term_expansion/2 s'il a été défini, sinon il utilisera son propre programme de transformation. Il est ainsi possible de réaliser automatiquement des transformations sur les termes de la forme (A --> B). Si l'on désire définir un programme de transformation, il doit définir la règle suivante :
:term_expansion(X,Y)
Lors de l'appel, X est le terme que Prolog vient de lire et Y est une variable libre. X est de la forme (A --> B).
Après appel, Y doit représenter la règle Prolog qui doit être associée à la règle de réécriture X (c'est à dire la règle Prolog qui sera assertée).
Voici deux exemples de compilation et d'interrogation d'une grammaire DCG :
?- [user].
somme(Z) --> chiffre(Z).
somme(X+Y) --> chiffre(X), "+", somme(Y).
chiffre(1) --> "1".
...
chiffre(9) --> "9".
.
{}
?- list.
somme(_53,_54,_55) :-
chiffre(_53,_54,_55).
somme(_57 + _58,_54,_55) :-
chiffre(_57,_54,["+" | _59]),
somme(_58,_59,_55).
chiffre(1,["1"|_54],_54).
...
chiffre(9,["9"|_54],_54).
{}
?- phrase(somme(Z),"1+2+3").
{Z=1 + (2 + 3)}
?- phrase(somme(1+(2+9)),L).
{L=["1","+","2","+","9"]}
©PrologIA
R 10 - 22
Manuel de Référence
Exemple 2 :
?- [user].
somme(Z) --> chiffre(Z).
somme(Z) --> chiffre(X), "+", somme(Y),{Z is X+Y}.
chiffre(N) --> [C],{string_integer(C,N)}.
liste3 --> elt, elt, elt.
elt --> [a].
elt --> [b].
elt --> [c]. .
{}
?- phrase(somme(Z),"1+2+3").
{Z=6}
?- phrase(liste3,L).
{L=[a,a,a]}
{L=[a,a,b]}
{L=[a,a,c]}
...
{L=[c,c,c]
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Extensions Edinburgh
R 10 - 23
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Annexe A
Différences Prolog II+ / Norme
A.1 Niveau général
A.2 Fonctionalités non implantées
A.3 Fonctionalités restreintes
A.1 Niveau général
Portée de la coupure dans un méta-appel
En Prolog II+, la coupure dans un méta-appel a une portée supérieure à celle définie dans la normalisation. Ce qui signifie que l'on a par exemple les différences de fonctionnement suivantes par rapport à la norme:
?- insert.
a(1).
a(2).
.
?- call( (Z=!, a(X), Z) ).
{Z=!,X=1} /* 1 seule solution au lieu de 2 */
Les chaînes de caractères
En Prolog II+, une option supplémentaire par rapport à celles définies par la norme permet de classer un texte "entre doubles quotes" dans un type à part entière qui est le type "chaînes de caractères". Cette option est celle en vigueur par défaut au lancement de Prolog.
Préfixage des identificateurs
En Prolog II+, les identificateurs lus se voient toujours attribuer un préfixe en fonction du contexte courant, qu'ils se trouvent en position d'argument (foncteurs) ou bien en position de but (tête ou queue de règle). Néanmoins, nous fournissons la possibilité d'attribuer automatiquement le préfixe vide ("") aux identificateurs non préfixés, en position d'argument. Il suffit pour cela de démarrer Prolog avec l'option
"-f a0".
Par exemple, dans ce mode là, la lecture de call(nl) fera appel à une règle sans doute non définie (:nl), tandis que call(sys:nl) imprimera bien un saut de ligne.
Pour annuler cette possibilité, exécuter le prédicat:
?- set_options("a1").
©PrologIA
A - 2
Annexe A
Vue logique et vue immédiate
Dans le mécanisme de la modification dynamique d'un paquet de règles (assert,
retract, suppress), la norme définit la vue dite "logique", c'est à dire que tout se passe comme si l'exécution d'un paquet de règles se faisait sur une copie de celui-ci, donc indépendamment des modifications éventuelles subites lors de cette exécution.
Une autre vue possible est celle dite "immédiate", qui consiste à prendre immédiatement en considération toute modification d'un paquet.
La vue de Prolog II+ est la vue immédiate.
Les seuls cas où le comportement peut être différent d'une prise en compte immédiate sont ceux de la suppression d'une règle dans un paquet où il reste encore des alternatives à exécuter. L'énumération de tous les cas de figures est alors assez complexe: cela dépend du fait qu'il y ait ou pas une indexation sur le paquet de règles en question, de la position de la règle modifiée par rapport à la règle du même paquet en cours d'exécution, du type du premier argument du but (variable, constante, ...).
Les n-uplets
Le type n-uplet n'est pas défini dans la norme Prolog. En termes d'équivalence, nous pouvons dire que le terme f(a1, a2, ..., an) où f est un atome, est équivalent au nuplet<>(f, a1, a2, ..., an), sauf dans le cas où n=0 (f n'est pas équivalent à <>(f) ).
A!ociation
Prolog
HERITAGE
A.2 Fonctionalités non implantées
Les directives char_conversion/2 et current_char_conversion/2 ne sont pas implantées.
A.3 Fonctionalités restreintes
La directive multifile/1 est équivalente à la directive discontiguous/1, la notion de fichier n'étant pas prise en compte par la compilation.
Seule la valeur :off est prise en compte pour le second paramètre de la directive de compilation et primitive set_prolog_flag/2 dans le cas où le premier vaut
:char_conversion.
©PrologIA
A!ociation
Prolog
HERITAGE
Annexe B
Liste des directives et prédicats prédéfinis
a : constante (identificateur, entier, réel, ou chaîne) b : 0 ou 1 c : chaîne d'un caractère d : réel double précision f : identificateur ou chaîne h : canal ou alias (entier opaque ou identificateur) i : identificateur l : liste m : entier ou réel n : entier r : réel quelconque s : chaîne t : terme u : unité: "console" ou nom de fichier ou nom de fenêtre (chaîne ou atome) v : variable
+ : l'argument doit être instancié (même partiellement)
- : l'argument doit être une variable (instancié si succès)
? : l'argument peut être quelconque, il sera instancié si succès
@ : l'argument peut être quelconque, il restera inchangé après exécution
1 Directives de compilation
discontiguous( +i / +n)
dynamic( +i / +n)
ensure_loaded( +u)
include( +u)
initialization( +t)
module( +s)
module( +s, +l)
module( +s, +l1, +l2)
module( +t, +l1, +l2, +s)
multifile( +i / +n)
omodule( +s)
op( +n, +i1, +i2)
set_alias( +i, +a)
discontiguous(foo/2) dynamic(aa/2) ensure_loaded("myfile") include("myfile") initialization(write(end)) module("Interface") module("room",["",["table","glass"]]) module("lex",["",["uncle","son"]],
["1"]) module(lexicon,[],["sys"],"lex") multifile
(foo/2) omodule("Interface") op(900,fy,not)
set_alias(foo,122)
©PrologIA
B - 2
Annexe B
set_prolog_flag( +i1, +i2)
set_prolog_flag(unknown,warning)
2 Prédicats
A
abolish( @t)
abs( +m)
add( +m1, +m2)
add_implicit( +s1, +s2)
add_sentence_terminator(+c)
add_tlv( -v, ?t, +i)
alloc abolish(myrule/2) val(abs(-1.5e0),1.5e) val(add(2.3E0,3.1E1),3.33E1) add_implicit("sys","outml") add_sentence_terminator(";") add_tlv(V, 33, foo)
alloc( ?n1, ?n2, ?n3, ?n4, ?n5, ?n6, ?n7, ?n8, ?n9, ?n10, ?n11, ?n12)
arg( +n, +t1, ?t2)
arg(0,<>(aa,bb),aa) arg2(2,<>(aa,bb),bb)
arg2( +n, +t1, ?t2)
assert( +t)
assert( +t1, +t2)
assert''( +t1, +t2)
asserta( +t)
asserta( +t1, +t2)
assertn( +t1, +t2,+n)
assertz( +t1, +t2)
assertz( +t)
assert(brother(jean,paul)) assert(pp(X),[qq(X),rr(X)]) assert''(pp(X),[qq(X),rr(X)]) asserta(brother(jean,paul)) asserta(pp(X),[qq(X),rr(X)]) assertn(pp(x),[qq(X),rr(X)],2) assertz(pp(x),[qq(X),rr(X)]) assertz(brother(X,Y):-son(X,Z), son(Y,Z))
assign( +i, @t)
atan( +m)
atom( @t)
atomic( @t)
atom_chars( +i, ?l)
atom_chars( -i, +l)
atom_codes( +i, ?l)
atom_codes( -i, +l)
atom_concat( ?i1, ?i2, +i)
atom_concat( +i1, +i2, -l)
atom_length( +i, ?n)
at_end_of_stream
at_end_of_stream( +h)
assign(fo,tt(3,[1,2,x],tab[5],"str")) val(atan(1),X) atom(foo) atomic(1) atom_chars(foo,X) atom_chars(X,["a","b"]) atom_codes(foo,X) atom_codes(X,[65,66]) atom_concat(X,Y,foo) atom_concat(fo1,fo2,X) atom_length(foo,X) at_end_of_stream(alias1)
B
bagof( ?v, +t, ?l)
beep
block( ?t1, +t2)
block( ?t1, ?t2, +t3)
block_exit( @t)
block_exit( @t1, @t2)
bound( @t)
bagof(X,frere(X,Y),L) block(edit,call_editor) block(edit,_info,call_editor) block_exit(edit) block_exit(edit, "this error") bound(X)
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Liste des directives et prédicats prédéfinies
B - 3
C
call( +t )
callC( +t1)
callC( +t1, ?t2)
catch( +t1, ?t2, +t3)
char_code( +c, ?n)
char_code( -c, +n)
chrono( ?v)
clause( +t1, ?t2)
clear_input
close( +h)
close( +h, +l)
close_context_dictionary( +s)
close_input
close_input( +u)
close_output
close_output( +u)
compound(@t)
conc_list_string( +l, ?s)
conc_string( ?s1, ?s2, +s3),
call(outml("Hello")) callC(sscanf("12.","%f",<>("R",X))) callC(open("myfile"),<>("I",X)) catch(foo(5),test(Y),outml("Hello")) char_code("A",65) char_code(X,65) clause(brother(X,Y),Q) close(alias1) close(alias1, force(true)) close_context_dictionary("data") close_input("myfile") close_output("myfile") compound([1,2]) conc_list_string(["ab","cd"],"abcd")
conc_string( +s1, +s2, -s3)
consult( +f)
conc_string("ab",X,"abcd") conc_string("ab","cd",X) consult("prog1.p2E")
copy_term(@t1, ?t2)
copy_term([1,2,<>("zz",X),Z], K)
copy_term_with_constraints(@t1, ?t2)
dif(X,3) copy_term_with_constraints(
[1,2,<>(X),Z], K) val(cos(+3.14e0),X)
cos( +m)
cpu_time( ?v)
current_context( ?t)
current_context( ?t, ?l1, ?l2, ?s)
current_file( ?u)
current_file( ?u , ?i1, ?i2)
current_input( ?n)
current_op( ?n, ?i, ?f)
current_output( ?n)
current_predicate( ?i / ?n)
current_prolog_flag( ?i1, ?i2)
current_context(X) current_context(X,Y,Z,D) current_file(X) current_file(X,T,M) current_input(X) current_op(200,xfx,Y) current_output(X) current_predicate(:to_begin/0) current_prolog_flag(unknown,X)
D
date(?v1, ?v2, ?v3, ?v4)
date_string( ?v)
date_stringF( ?v)
debug
debug( +u)
debug( +n)
debug( +n, +u)
def_array( +i, +n)
default( +t1, +t2)
date(J,M,A,S) debug("echodebug.dat") debug(3) debug(3, "echodebug.dat") def_array(stack,100) default(man(X),eq(x,[]))
©PrologIA
B - 4
Annexe B
delay( +n)
dictionary
dictionary( ?l)
dictionary( +s, ?l)
dif( ?t1, ?t2)
div( +m1, +m2)
dot( @t)
double( +m)
draw_equ( @t)
draw_mode( ?s)
draw_tree( @t)
E
echo edinburgh
edit( +i / +n)
edit( +l)
edit( +s)
editm( +s)
enum( ?v, +n)
enum( ?v, +n1, +n2)
eof eol
eq( ?t1, ?t2)
eql( +a1, +a2)
equations( +t1, ?t2, ?l)
exit
exit( +s)
exp( +m)
FGH
fail
fail_if( +t)
fasserta( +t)
fassertz( +t)
file_dictionary( +s, ?l)
find_pattern( +s1, +s2, ?n)
findall( ?V, +T, ?L)
float( +m)
flush flush_output
flush_output( +h)
free( @t)
freeze( -v, +t)
freplace( +t, +n, +t')
freplace( +i / +a, +n1, +n2, +t)
fretract( +t)
fretractall( @t)
delay(5000) dictionary("sys",X) dif(X,toto) val(div(8.0e,3),X) dot([1,X]) val(double(1),1e0) draw_equ(aa(bb(cc),bb(cc))) draw_tree(aa(bb(cc),bb(cc,dd))) edit(hors_d_oeuvre/1) edit([meat/1,fish/1]) edit("file.dat") editm("my_module") enum(X,10) enum(X,10,20) eq(X,father(john,mary)) val(eql(3,3),1) equations(X,T,l) exit("myfile.psv") val(exp(+1.0e0),X) fail_if(fail) fasserta(town("Marseille",13)) fassertz(town("Marignane",13)) file_dictionary("foo.mo",X) find_pattern("abcdef","de",4) findall(X,brother(X,Y),L) val(float(5),+5.0e0) flush_output(alias1) free(X) freeze(X,list(X)) freplace(old(X,Y),1,"010180") freplace(old/2,10,2,"011293") fretract(town(X,83)) fretractall(town(X,83))
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
functor( -t, +i, +n)
functor( +t, ?i, ?n)
gc( +i)
gensymbol( -v)
get( ?n)
get( +h, ?n)
get_byte( ?n)
get_byte( +h, ?n)
get_char( ?c)
get_char( +h, ?c)
get_code( ?n)
get_code( +h, ?n)
get_option( +c, ?c)
get_tlv( -v, ?l)
get0( ?n)
getenv( +s1, ?s2)
halt
halt( +n)
hidden( +i / +n)
hidden( +s)
hidden_debug( +i / +n)
hidden_debug( +s)
hidden_rule( +i / +n)
hidden_rule( +s)
IK
ident( @t)
if( +b, +a1, +a2)
import_dir(?s)
in( ?t , ?c)
in( ?t , ?l, ?c)
in_char( ?c)
in_char'( ?c)
in_double( ?d)
in_ident( ?i)
in_integer( ?n)
in_real( ?r)
in_sentence( ?t1, ?t2)
in_string( ?s)
in_word( ?s, ?a)
include( +s)
index( +i / +n)
inf( +a1, +a2)
infe( +a1, +a2)
infinite infinite_flag
init_fassert( +i / +a, +l)
inl( ?v)
Liste des directives et prédicats prédéfinies functor(X,aa,2) functor(aa(bb(X),bb(cc)),aa,X) gc(:dictionary) gensymbol(X) char_code(";",C), get(C) get(alias1,C) get_byte(alias1, C) get_char(alias1, C) char_code(";",C), get_code(C) get_code(alias1, C) get_option("r", C) get_tlv(V, L) get0(X) getenv("PrologEdit",X) halt(1) hidden(my_rule/0) hidden("private") hidden_debug(my_rule/0) hidden_debug("private") hidden_rule(my_rule/0) hidden_rule("private") ident(toto) val(if(inf(1,5),add(1,1),3),2) in(X,C) in(X,D,C) in_char(X) in_char'(X) in_double(X) in_ident(X) in_integer(X) in_real(X) in_sentence(X,Y) in_string(X) in_word(X,Y) include("myfile") index(database/1) val(inf("to","zou"),1) val(infe("to","zou"),1) val(infinite_flag,X) init_fassert(town/2,[[1,2],1,2]) inl(X)
B - 5
©PrologIA
B - 6
Annexe B
inl( ?v1, ?v2)
input( +u)
input( +u, +n)
input_is( ?u)
insert
insert( +s)
insertz
insertz( +s)
integer( @t)
is( ?t1, @t2)
is_array( + i, ?v)
keysort( +l, ?l)
keysort( +l, ?l, +i)
kill_array( +i)
kill_module( +s)
L
line
line_width( ?n)
list
list( +i / +n)
list( +i / +n, n)
list( +s)
list_of( ?v, +l1, +t, ?l2)
list_string( +l, ?s)
list_tuple( +l, ?t)
listing
listing( +s)
lkload( +s1, +s2)
ln( +m)
log( +m)
load( +s)
load( +s, +l)
MN
member( ?t, ?l)
memory_file( +s)
memory_file( +s, +n)
mod( +n1, +n2)
month( ?i, ?s)
ms_err( +i, ?s)
mul( +m1, +m2)
name( +f, ?l)
name( -f, +l)
new_tlv( -v, ?t, +i)
next_char( ?c)
next_char'( ?c)
nil inl(X,Y) input("console") input("myfile", 1000) input_is("console") insert("mutant.p2") insert("mutant.p2") integer(3)
X is 1+2 is_array(tab,X) keysort([43,31,52,12], L) keysort([43,31,52,12], L, mycompar) kill_array(tab1) kill_module("lex") line_width(X) list(foo/2) list(foo/2,4) list("mymodule") list_of(X,[],data(X),L) list_string(["a","b"],X) list_tuple([aa,bb,cc],x) listing("mymodule") lkload("my_file","my_table") val(ln(1.0e0),0.0e0) val(log(1.0e0),0.0e0) load("myfile") load("myfile", [<>("aa","bb")]) member(1,[1,2,3,4]) memory_file("non-terminal") memory_file("lexical",32000) val(mod(7,3),1) month(5,"may") ms_err(104,X) val(mul(add(3.1e0,0.077d0),5),X) name(toto,X) name(X,[116,111,116,111]) new_tlv(V, 33, foo) next_char(",") next_char'(X)
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
nl
nl( +h)
no_debug no_echo
no_index( +i / +a)
no_infinite no_paper
no_spy( +i / +n)
no_trace
nonvar( @t)
not( @t)
not_defined( ?l)
number( @t)
number_chars( +n, ?l)
number_chars( -n, +l)
number_codes( +n, ?l)
number_codes( -n, +l)
OPQ
once( +t)
op( ?n, ?i1, ?i2)
op( +n, +i1, +i2, +i3)
open( +s, +i, -v)
open( +s, +i, -v, +l)
or( +t1, +t2)
out( @t)
out_equ( @t)
outl( @t)
outm( +s)
outm( +s, +n)
outml( +s)
output( +s)
output( +u , +n)
output_is( ?s)
page paper
peek_byte( ?n)
peek_byte( +h, ?n)
peek_char( ?c)
peek_char( +h, ?c)
peek_code( ?n)
peek_code( +h, ?n)
phrase( ?t1, ?t2)
phrase( ?t1, ?t2, ?t3)
predefined( +t)
prefix_limit( ?c)
prologII prologIIE
Liste des directives et prédicats prédéfinies
B - 7
nl(alias1) no_index(foo/2) no_spy(foo/2) nonvar([1|X]) not(true) number(10) number_chars(123,X) number_chars(X,["1","2","3"]) number_codes(123,X) number_codes(X,[49,50,51]) once(out(foo)) op(900,fy,not) op(700,xfx,"=",eq) open("file",:read,X) open("file",:read,X,[alias(alias1)) or(eq(X,3),outl(X).fail) out([a,b]) out_equ(aa(bb)) outl([a,b]) outm("bonjour") outm("_",30) outml("The cat and the dog") output("myfile.pro") output("myfile", 1000) output_is("myfile.pro") peek_byte(alias1,X) peek_char(alias1,X) peek_code(alias1,X) phrase(somme(X),"1+2+3") phrase(somme(X),"1+2+3",Y) predefined(outm("Ok")) prefix_limit(X)
©PrologIA
B - 8
Annexe B
put( +n)
put_byte( +n)
put_byte( +h, +n)
put_char( +c)
put_char( +h, +c)
put_code( +n)
put_code( +h, +n)
quit
quit( +n)
S
save( +l, +s)
save_state( +s)
see( +f)
seeing( +f)
char_code("!",C), put(C) put_byte(12) put_byte(alias1,12) put_char("e") put_char(alias1,"e") put_code(12) put_code(alias1,12) quit(1)
R
rad( +m)
read( ?t)
read( +h, ?t)
read_rule( ?v1, ?v2)
read_term(?t, +l)
read_term( +h, ?t, +l)
read_unit( ?v1, ?v2)
real( @t)
realloc( +i, +n)
reconsult( +f)
recv_double( +n1, ?v2)
recv_integer( +n1, ?v2)
recv_real( +n1, ?v2)
recv_string( +n1, ?v2)
redef_array( +i, +n)
repeat reset_chrono reset_cpu_time
retract( +t1, ?t2)
retract( +t)
retractall( @T)
rule( +t1, ?t2)
rule( +n, +t1, ?t2)
rule( +n, +i, +t1, ?t2)
rule_nb( +i / +n, ?v)
val(rad(+90.0e0),X) read(X) read(alias1,X) read_rule(T,Q) read_term(X,[variables(L)]) read_term(alias1,X,[variables(L)]) read_unit(_type,_valeur) real(-5.9e0) realloc(:code,50) reconsult("prog1.p2E") recv_double(5,D) recv_integer(5,N) recv_real(5,R) recv_string(5,S) reinsert
reinsert( +u)
reload( +s)
reload( +s, +l)
redef_array(foo,1000) reinsert("prog.p2") reload("myfile") reload("myfile", [<>("aa","bb")])
remove_implicit( +s1, +s2)
remove_implicit("sys","outml")
remove_sentence_terminator(+c)
remove_sentence_terminator(";") retract(father(john,X),Y) retract(brother(X,Y):- true) retractall(father(john,X)) rule(father(john,x),Y) rule(r,father(john,X),Y) rule(5,father,T,Q) rule_nb(foo/2,X) save(["aa","bb"], "myfile") save_state("myfile") see(filei) seeing(filei)
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Liste des directives et prédicats prédéfinies
B - 9
seen
send_double( +n, +d)
send_integer( +n1, +n2)
send_real( +n, +r)
send_string( +n, +s)
set_context( +s)
set_context( +s1, +l1, +l2, +s2)
set_cursor( +n1, +n2)
set_draw_mode( +s)
set_import_dir( +s)
set_input( +h)
set_line_cursor( +n)
set_line_width( +n)
set_options( +s)
set_output( +h)
set_prefix_limit( +c)
set_prolog_flag( +i1, +i2)
set_stream_position( +h, +n)
setarg( +n, +t1, @t2)
setof( ?v, +t, ?l)
show_spy( ?v)
sin( +m)
sort( +l, ?l)
sort( +l, ?l, +i)
split( @t, ?l)
split( +s, ?l)
spy( +i / +n)
sqrt( +m)
state statistics
string_integer( -s, +n)
string_real( +s, ?r)
string_real( -s, +r)
string_term( +s, ?t)
string_term( -s, +t)
string_term( +s, ?t1, ?t2),
string_term( -s, +t1, ?t2)
string_term( +s, ?t1, ?t2, +n),
send_double(5,12d0) send_integer(5,1000) send_real(5,3.14e0) send_string(5,"error")
set_context("user")
set_context("user",[],["sys"],"")
set_cursor(1,24) set_draw_mode("TTY") set_import_dir("/home/user/") set_input(alias1) set_line_cursor(10) set_line_width(132) set_options("vEw2") set_output(alias1) set_prefix_limit("$") set_prolog_flag(unknown,warning) set_stream_position(alias1,200) setarg(3,[1,2,3,4,5],[1,2,3]) setof(X,frere(X,Y),L) val(sin(+3.14e0),X) sort([43,31,52,12], L) sort([43,31,52,12], L, mycompar)
split(father(john),[father,john])
split("abc",["a","b","c"]) spy(foo/2) val(sqrt(+9.0e0),+3.0e0)
statistics( ?n1, ?n2, ?n3, ?n4, ?n5, ?n6, ?n7, ?n8, ?n9, ?n10, ?n11, ?n12)
stream_property( +h, ?l)
stream_property(alias1,alias(X))
string( @t)
string_double( +s, ?d)
string_double( -s, +d)
string_ident( ?s1, ?s2, +i)
string_ident( +s1, +s2, -i)
string_ident( +s, ?i)
string_ident( -s, +i)
string_integer( +s, ?n)
string("abc") string_double(1d0,X) string_double(X,1.2345678d15) string_ident(X,"john",john) string_ident("user","john",X) string_ident("john",X) string_ident(X,john) string_integer("123",X) string_integer(X,123) string_real("+12.E15",X) string_real(X,+1.2E16) string_term("err(500,X)",X) string_term(X,err(500,V60)) string_term("err(500,X) ss",X,Y) string_term(X,err(1),Y) string_term("err(500,X) ss",X,Y,1000)
©PrologIA
B - 10
Annexe B
string_term( -s, +t1, ?t2, +n)
sub( +m1, +m2)
string_term(X,err(1),Y,1000) val(sub(2d1,1e0),X)
sub_atom( +i1, ?n1, ?n2, ?n3, ?i2) sub_atom(abracadabra,0,5,X,Y)
substring("abcdef",4,2,X)
substring( +s1, +n1, +n2, ?s2)
sup( +a1, +a2)
supe( +a1, +a2)
suppress( +i / +n)
suppress( +i / +n, +n)
sys_command( +s)
val(sup("to","zou"),0) val(sup("zou","zou"),1) suppress(foo/2) suppress(foo/2,5) sys_command("showusers")
TUVW
tab( +n)
tan( +m)
tassign( +i, @t)
tassign( +i [ +n], @t)
tell( +f)
telling( +f)
term_cmp(@t1, @t2, ?n)
term_cmpv(@t1, @t2, ?n)
term_vars(@t1, ?l)
throw( @t)
time( ?n)
time( ?n1, ?n2, ?n3)
told trace
trace( +s)
true
trunc( +m)
truncate( +m)
tuple( @t)
tab(10) val(tan(-3.14e0),X) tassign(aa,tt(3,[1,X],tab[5],"str")) tassign(tab[1],123) tell(file) telling(file) term_cmp(1,"foo",X) term_cmpv(1,"foo",X) term_vars(one(2.X,Y,Y),L) throw(12) trace("debug.dat") val(trunc(+3.8e0),3) val(truncate(+3.8e0),3)
tval(@t1, ?t2)
unify_tlv( -v, ?t2)
tuple(ff(a)) tval(1+2*3+pile[1],X) unify_tlv(V, 22)
unify_with_occurs_check( ?t1, ?t2) unify_with_occurs_check(X,1)
val(3,3)
val( @t1, ?t 2)
var( @t)
var_time( -v, ?n)
version(?n)
week( ?n, ?s)
write( @t)
write( +h,@t)
write_canonical( @t)
write_canonical( +h, @t)
var(X) var_time(X,Y) version(X) week(0,"sunday") write("Hello world!") write(alias1,"Hello world!") write_canonical("Hello world!") write_canonical(alias1,
"Hello world!")
write_term( @t ,+l)
write_term( +h, @t , +l)
write_term("foo",[quoted(:true)]) write_term(alias1,"f",
[quoted(:false)])
writeq( @t)
writeq( +h, @t)
writeq(foo(1,2,3) writeq(alias1,foo(1,2,3)
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
\( +n)
Autres
~( +n)
+m1 * +m2
+m1 + +m2
+t1 , +t2
+t1 ; +t2
- +m
+m1 - +m2
+t1 --> +t2
+t1 -> +t2
+m1 / +m2
+m1 // +m2
+n1 /\ +n2
+a1 < +a2
+n1 << +n2
?t1 = ?t2
+t1 =.. ?t2
-t1 =.. +t2
+a1 =< +a2
@t1 == @t2
+t1 =:= +t2
+t1 =\= +t2
@t1 \= @t2
+a1 > +a2
+a1 >= +a2
n1 >> +n2
+n1 \/ +n2
@t1 \== @t2
+a1 @=< +a2
+a1 @> +a2
+a1 @>= +a2
+a1 @< +a2
\+(
@T)
Liste des directives et prédicats prédéfinies
B - 11
val(\(4),X) val(~(4),X) val(-3.14e0 * 0.5e,X) val(1+2,X) write("ca va"),nl integer(X);real(X)
-2 val(1-2,X) phrase --> gn, gv.
string(X) -> see(X) val(3/4,X)
Y is 3 // 4 val(4 '/\' 8,X) val(1 '<' 2,X) val(1 '<<'4,X)
1 = X toto(1,2) =.. X
Y =.. [toto,X] val(1 '=<' 1,X) func(X) == func(Y) tab[I] =:= I
(1+2) =\= (2*3)
1
\= 2 val(2 '>' 1,X) val(2 '>=' 1,X) val(1024 '>>' 2,X) val(14 '\/' 687,X) func(1,X) \== func(1,2)
\+(fail)
©PrologIA
B - 12
Annexe B
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Annexe C
Quelques exemples de programmes Prolog II+
C.1. Les grammaires
C.2. Dérivation formelle
C.3. Les mutants
C.4. Interrogation par évaluation d'une formule logique
C.5. Un casse-tête
C.6. Construction d'un chemin
C.7. Les arbres infinis
C.1.
Les grammaires
Description du problème
La grammaire d'un langage est un ensemble de règles qui permet de déterminer si oui ou non une suite de mots construits sur un certain alphabet appartient au langage. Si oui, on peut alors mettre en évidence la structure sous-jacente de la phrase analysée.
Les langage hors-contexte forment une classe importante : voici par exemple la grammaire définissant l'ensemble de toutes les expressions arithmétiques que l'on peut construire sur les nombres entiers, les opérateurs +, -, * avec leurs priorités habituelles :
(0) <expression> ::= <somme>
(1) <somme> ::= <produit> <reste de somme>
(2) <produit> ::= <primaire> <reste de produit>
(3)
(4)
<primaire>
::= <nombre>
::= ( <expression> )
(5)
(6)
<reste de somme>
::= <op add> <produit> <reste de somme>
::= <vide>
<reste de produit>
©PrologIA
C - 2
Annexe C
(7)
(8)
::= <op mul> <primaire> <reste de produit>
::= <vide>
(9) <op mul> ::= *
(10)
(11)
<op add>
::= +
::= -
(12)
(13)
(21)
<nombre>
::= 0
::= 1
...
::= 9
(22) <vide> ::=
Par exemple, la décomposition de l'expression : 2 * 3 + 6 est donnée dans la figure
C.1 (les noms des symboles non-terminaux ont été abrégés).
C . 1 . 1 .
Représentation de la grammaire en Prolog
Examinons comment écrire cet analyseur en Prolog. Pour cela, considérons la chaîne d'entrée comme un graphe : le premier sommet du graphe est placé au début de la phrase, et on ajoute un sommet après chaque mot de la phrase. Chacun des mots va alors étiqueter l'arc qui joint les deux sommets qui l'encadrent. Avec notre exemple nous obtenons :
2 * 3 + 6
• -----> • -----> • -----> • -----> • -----> • a b c d e f
A!ociation
Prolog
HERITAGE
<expr>
|
|
<somme>
__/ \__
___/ \__
/ \
<prod> <r-d-s->
/ \ / | \
/ \ / | \
<prim> <r-d-p> <op-a> <prod> <r-d-s>
| / | \ | / \ |
| / | \ | / \ |
<nb> <op-m> <prim> <r-d-p> | <prim> <r-d-p> |
| | | | | | | |
| | | | | | | |
| | <nb> <vide> | <nb> <vide> <vide>
| | | | |
| | | | |
2 * 3 + 6
©PrologIA
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 3
Figure C.1
Les règles de la grammaire peuvent alors être vues comme des instructions permettant de compléter ce graphe. C'est ainsi qu'une règle comme (21) sera interprétée par : s'il y a un arc étiqueté par "9" entre le noeud x et le noeud y du graphe, rajouter entre x et y un arc étiqueté "nombre".
Une règle comme (1) donnera : s'il y a un arc étiqueté produit entre les noeuds x et y et un arc étiqueté reste de somme entre les noeuds y et z, rajouter un arc étiqueté
expression entre x et z. Avec ces conventions, analyser une phrase revient à chercher un arc étiqueté expression entre le premier et le dernier sommet du graphe associé. Si on y parvient, la phrase sera acceptée, sinon elle sera rejetée.
Ecrivons maintenant le programme en Prolog : à chaque symbole non -terminal N de la grammaire, nous associerons un prédicat du même nom N(<x, y>) qui sera interprété par : il existe un arc étiqueté N entre les noeuds x et y du graphe.
Pour la règle (1) ceci donne la clause :
somme(<x, y>) -> produit(<x, z>) reste_de_somme(<z, y>);
Revenons un peu sur le graphe, celui-ci peut être simplement représenté par la liste des mots constituant la phrase d'entrée :
• sommet a
/ \
2 •
/ \
* •
/ \
3 •
/ \ sommet b sommet c sommet d
+ •
/ \ sommet e
6 nil sommet f
Chacun des sommets du graphe correspond à un sous-arbre de l'arbre précédent.
Avec cette façon de faire, dans N(<x, y>), x représente la chaîne d'entrée avant d'effacer N et y représente la chaîne restant à analyser après avoir effacé N. De plus, cette représentation permet de traduire les règles terminales comme (12), (13), etc… par :
nombre(<x, y>) -> mot(n, <x, y>) integer(n) ;
qui utilise la règle standard :
mot(a, <a.x, x>) -> ;
et la règle prédéfinie integer. Enfin, reconnaître si une phrase appartient au langage défini par notre grammaire revient à effacer :
expression(<p, nil>)
où p représente la phrase d'entrée sous forme de liste.
©PrologIA
C - 4
Annexe C
C . 1 . 2 .
Mise en évidence de la structure profonde
Nous sommes donc rendus au point où nous savons traduire une grammaire en un ensemble de clauses qui diront si oui ou non une phrase appartient au langage.
Nous pouvons faire mieux en complétant nos relations pour faire ressortir l'arbre construit par l'analyse de la phrase : pour l'exemple précédent.
add
/ \
mul 6
/ \
2 3
Pour cela, nous n'aurons besoin que d'ajouter un argument aux prédicats associés aux non-terminaux de la grammaire. Celui-ci ne fera qu'exprimer comment une phrase est construite à partir des sous-phrases qui la composent. Nous obtenons donc :
nombre(n, <x, y>) -> mot(n, <x, y>) ; primaire(n, <x, y>) -> nombre (n, <x, y>) ;
Finalement, notre règle de départ deviendra :
somme(e, <x, y>) ->
produit(p, <x, z>) reste_de_somme(p, e, <z, y>);
où e représentera l'arbre associé à l'expression analysée. Voici le programme définitif :
"grammaire des expressions" expression(e,<x,y>) -> somme(e,<x,y>); somme(e,<x,y>) ->
produit(p,<x,z>)
reste_de_somme(p,e,<z,y>); produit(p,<x,y>) ->
primaire(f,<x,z>)
reste_de_produit(f,p,<z,y>); primaire(n,<x,y>) -> mot(n,<x,y>) integer(n); primaire(e,<x,y>) ->
mot("(",<x,z>)
expression(e,<z,t>)
mot(")",<t,y>); reste_de_somme(p,e,<x,y>) ->
mot(o,<x,z>)
op_add(o,o_a)
produit(p',<z,t>)
reste_de_somme(<o_a,p,p'>,e,<t,y>); reste_de_somme(e,e,<x,x>) ->; reste_de_produit(f,p,<x,y>) ->
mot(o,<x,z>)
op_mul(o,o_m)
primaire(f',<z,t>)
reste_de_produit(<o_m,f,f'>,p,<t,y>);
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 5
reste_de_produit(f,f,<x,x>) ->; mot(a,<a.x,x>) ->; op_add("+",add) ->; op_add("-",sub) ->; op_mul("*",mul) ->;
"lecture" read(l) -> in_sentence(s,l);
"lancement" run ->
outm("l'expression ")
lire(p)
analyse(p,e)
val(e,f)
outm("vaut")
outl(f); analyse(p,e) -> expression(e,<p,".".nil>) !; analyse(p,e) -> outm("... est incorrecte") fail;
On utilise le prédicat read qui lit une phrase terminée par "." et la transforme en une liste de mots. Par exemple :
> read(p);
12+(23-4)*5+210-34-43.
{p=12."+"."(".23."-".4.")"."*".5."+".210."-".34."-
".43.".".nil}
Le prédicat expression construit l'arbre syntaxique associé à la phrase lue (si elle appartient au langage). On peut ensuite communiquer cet arbre au prédicat évaluable
val qui calcule et imprime sa valeur. Tout ceci est fait par l'appel de la règle run.
C.2.
Dérivation formelle
En analyse mathématique, la dérivation est une opération qui, à une expression algébrique associe une autre expression algébrique appelée dérivée. Les expressions sont construites sur les nombres, les opérateurs +, -, *, des symboles fonctionnels comme sin, cos… et un certain nombre d'inconnues. La dérivation se fait par rapport à l'une de ces inconnues. Dans notre exemple, les nombres seront toujours entiers. Nous n'aurons qu'une inconnue : x, et les seuls symboles fonctionnels unaires seront sin et cos. La première partie du programme est la grammaire d'analyse de ces expressions : c'est celle de l'exemple précédent qui a été complétée. La voici :
"grammaire des expressions" expression(e,<x,y>) -> somme(e,<x,y>); somme(e,<x,y>) ->
©PrologIA
C - 6
Annexe C
produit(p,<x,z>)
reste_de_somme(p,e,<z,y>); produit(p,<x,y>) ->
facteur(f,<x,z>)
reste_de_produit(f,p,<z,y>); facteur(f,<x,y>) ->
primaire(p,<x,z>)
reste_de_facteur(p,f,<z,y>); primaire(n,<x,y>) -> mot(n,<x,y>) integer(n); primaire("x",<x,y>) -> mot("x",<x,y>); primaire(<o_u,p>,<x,y>) ->
mot(o,<x,z>)
op_un(o,o_u)
primaire(p,<z,y>); primaire(e,<x,y>) ->
mot("(",<x,z>)
expression(e,<z,t>)
mot(")",<t,y>); reste_de_somme(p,e,<x,y>) ->
mot(o,<x,z>)
op_add(o,o_a)
produit(p',<z,t>)
reste_de_somme(<o_a,p,p'>,e,<t,y>); reste_de_somme(e,e,<x,x>) ->; reste_de_produit(f,p,<x,y>) ->
mot(o,<x,z>)
op_mul(o,o_m)
facteur(f',<z,t>)
reste_de_produit(<o_m,f,f'>,p,<t,y>); reste_de_produit(f,f,<x,x>) ->; reste_de_facteur(p,f,<x,y>) ->
mot(o,<x,z>)
op_exp(o,o_e)
primaire(p',<z,t>)
reste_de_facteur(<o_e,p,p'>,f,<t,y>); reste_de_facteur(f,f,<x,x>) ->; mot(a,<a.x,x>) ->; op_add("+",plus) ->; op_add("-",minus) ->; op_mul("*",mult) ->; op_exp("^",exp) ->; op_un("-",minus) ->; op_un(sin,sin) ->; op_un(cos,cos) ->;
"règles de dérivation" derivee(x,x,1) ->; derivee(n,x,0) -> integer(n); derivee(plus(u,v),x,plus(u',v')) ->
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 7
derivee(u,x,u')
derivee(v,x,v'); derivee(minus(u,v),x,minus(u',v')) ->
derivee(u,x,u')
derivee(v,x,v'); derivee(mult(u,v),x,plus(mult(u,v'),mult(v,u'))) ->
derivee(u,x,u')
derivee(v,x,v'); derivee(exp(u,n),x,mult(n,mult(u',exp(u,minus(n,1))))) ->
derivee(u,x,u'); derivee(minus(u),x,minus(u')) -> derivee(u,x,u'); derivee(sin(u),x,mult(u',cos(u))) -> derivee(u,x,u'); derivee(cos(u),x,minus(mult(u',sin(u)))) -> derivee(u,x,u');
"règles de simplification" simplifier(<o_b,x,y>,u) ->
simplifier(x,x')
simplifier(y,y')
simp(o_b,x',y',u); simplifier(<o_u,x>,u) -> simplifier(x,x') simp(o_u,x',u); simplifier(x,x) ->; simp(plus,0,x,x) ->; simp(plus,x,0,x) ->; simp(minus,x,0,x) ->; simp(minus,0,x,y) -> simp(minus,x,y); simp(mult,0,x,0) ->; simp(mult,x,0,0) ->; simp(mult,1,x,x) ->; simp(mult,x,1,x) ->; simp(exp,x,0,1) ->; simp(mult,minus(x),y,u) ->
simp(mult,x,y,v)
simp(minus,v,u); simp(mult,x,minus(y),u) ->
simp(mult,x,y,v)
simp(minus,v,u); simp(exp,x,1,x) ->; simp(exp,0,x,0) ->; simp(exp,1,x,1) ->; simp(o_b,x,y,u) ->
dif(o_b,exp)
integer(x)
integer(y)
evalCst(<o_b,x,y>,u); simp(o_b,x,<o_b,u,v>,t) -> simp(o_b,x,u,z) simp(o_b,z,v,t); simp(o_b,x,y,<o_b,x,y>) ->; simp(minus,0,0) ->; simp(minus,minus(x),x) ->; simp(sin,0,0) ->; simp(cos,0,1) ->; simp(o_u,x,<o_u,x>) ->; evalCst(plus(x,y),u) -> val(x+y,u); evalCst(minus(x,y),u) -> val(x-y,u); evalCst(mult(x,y),u) -> val(x*y,u);
"lecture"
©PrologIA
C - 8
Annexe C read(nil) -> next_char'(".") ! in_char'("."); read(a.b) -> in_ident(a) ! read(b); read("+".b) -> next_char'("+") ! in_char'("+") read(b); read("-".b) -> next_char'("-") ! in_char'("-") read(b); read(a.b) -> in_integer(a) ! read(b); read(a.b) -> in_char'(a) read(b);
"écriture" ecrire(<o_b,x,y>) ->
op_bin(o,o_b)
outm("(")
ecrire(x)
outm(o)
ecrire(y)
outm(")"); ecrire(minus(x)) -> ! outm("-") ecrire(x); ecrire(<o_u,x>) -> ! op_un(o,o_u) out(o) ecrire(x); ecrire(x) -> string(x) ! outm(x); ecrire(x) -> out(x); op_bin(o,o_b) -> op_add(o,o_b); op_bin(o,o_b) -> op_mul(o,o_b); op_bin(o,o_b) -> op_exp(o,o_b);
"lancement" run ->
outm("l'expression ")
read(p)
analyse(p,e)
derivee(e,"x",e')
simplifier(e',f)
outm("a pour derivee")
ecrire(f)
line
!; analyse(p,e) -> expression(e,<p,nil>) !; analyse(p,e) -> outm("... est incorrecte") fail;
Voici par exemple le résultat de l'analyse d'une expression:
$ prolog
> insert("deriv.p2");
{}
> read(p) expression(e, <p, nil>);
3*x^2+6*x+5.
{p=3."*"."x"."^".2."+".6."*"."x"."+".5.".".nil, e=plus(plus(mult(3,exp("x",2)),mult(6,"x")),5)}
Nous avons ensuite défini la relation de dérivation : derivee(f, x, f') signifie : f' est la dérivée de f par rapport à x. Cette relation comporte une règle par opérateur ou symbole fonctionnel et s'exprime de manière très naturelle. Par exemple, la dérivée de l'expression précédente est donnée par :
> read(p) expression(e, <p, nil>) derivee(e, "x", e');
3*x2^+6*x+5.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 9
..., e'=plus(plus(plus(mult(3,mult(2,mult(1,exp("x",minus(2,1)))))
, mult(exp("x",2),0)),plus(mult(6,1),mult("x",0))),0)}
Comme on le voit, le résultat est loin d'être simplifié ! Nous avons donc adjoint un programme de simplification (très incomplet) qui permet d'obtenir une écriture plus condensée. Tout cela est résumé par le prédicat run dont voici un exemple d'utilisation :
> run; l'expression 3*x^2+6*x+5. a pour derivee ((6*x)+6)
> run; l'expression cos(-3*x^2+2). a pour derivee ((6*x)*sin(-(3*(x^2))+2))
C.3.
Les mutants
Il s'agit de produire des mutants issus d'animaux différents. Pour cela les animaux sont connus par leur nom sous forme de chaîne de caractères. Deux animaux donnent naissance à un mutant si la fin du nom du premier animal est identique au début du nom du second. L'aspect intéressant de ce programme est qu'on utilise une même relation, conc, de deux façons différentes : d'une part pour réunir deux listes, d'autre part pour décomposer une liste en deux sous-listes.
Voici les résultats produits en partant de l'ensemble d'animaux : alligator, tortue, caribou, ours, cheval, vache, lapin, pintade, hibou, bouquetin et chèvre.
> joli_mutant; alligatortue caribours caribouquetin chevalligator chevalapin vacheval vachevre lapintade hibours hibouquetin
> et voici le programme (avec l'option -f vU):
"MUTANTS" mutant(_z) ->
animal(_x)
animal(_y)
conc(_a,_b,_x)
dif(_b,nil)
conc(_b,_c,_y)
dif(_c,nil)
conc(_x,_c,_z); conc(nil,_y,_y) ->;
©PrologIA
C - 10
Annexe C
conc(_e._x,_y,_e._z) -> conc(_x,_y,_z); joli_mutant -> mutant(_z) expl(_z) line fail; expl(nil) ->; expl(_a._l) -> out(_a) expl(_l); animal(a.l.l.i.g.a.t.o.r.nil) ->; animal(t.o.r.t.u.e.nil) ->; animal(c.a.r.i.b.o.u.nil) ->; animal(o.u.r.s.nil) ->; animal(c.h.e.v.a.l.nil) ->; animal(v.a.c.h.e.nil) ->; animal(l.a.p.i.n.nil) ->; animal(p.i.n.t.a.d.e.nil) ->; animal(h.i.b.o.u.nil) ->; animal(b.o.u.q.u.e.t.i.n.nil) ->; animal(c.h.e.v.r.e.nil) ->;
C.4.
logique
Interrogation par évaluation d'une formule
Dans cet exemple on a constitué une banque de données portant sur des individus dont on connaît le nom, l'âge, la ville d'origine et le fait qu'ils portent ou non des lunettes. Tous ces renseignements sont résumés par une assertion telle que :
individu(candide, 20, constantinople, non) -> ;
qui indique que l'individu nommé candide est âgé de 20 ans, qu'il est né à
constantinople et ne porte pas de lunettes. On dispose également de relations
élémentaires (les formules atomiques) portant sur ces données.
Le programme consiste à évaluer une formule logique construite à partir des formule atomiques, des connecteurs "et" et "ou" et des quantificateurs existentiel et universel portant sur des variables typées, c'est à dire dont le domaine des valeurs est précisé. Ainsi, la question type que l'on peut poser est du genre :
« quels sont les valeurs de x appartenant au domaine D pour lesquelles la propriété P est vraie ? »
ce qui est traduit par la formule :
element(x, ens(x, D, P)).
Par exemple, la question : (1) dans quelle ville habite mimosa ? se traduit par la formule.
element(x, ens(x, ville, habite_a(mimosa, x)))
de même : (2) olive porte-t-elle des lunettes ? se traduit par :
element(x, ens(x, booleen, lunette(olive, x)))
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 11 et enfin : (3) quelles sont les villes ayant au moins un habitant age de moins de 20
ans et portant des lunettes ? correspondant à :
element(x, ens(x, ville, existe(y, nom, et(habite_a(y, x),
et(est_age_de(y, a),
et(inferieur(a, 20),
lunette(y, oui)))))))
Ces trois questions ont été pré-enregistrées et sont activées par reponse-a-tout qui
écrit la question en clair suivie des réponses. Voici ce que cela donne (les réponses de la machine sont précédées de "-->") :
> reponse_a_tout; dans quelle ville habite mimosa ?
--> aspres_sur_buech olive porte-t-elle des lunettes ?
--> non quelles sont les villes ayant au moins un habitant age de moins de 20 ans et portant des lunettes ?
--> severac_le_chateau
--> aspres_sur_buech
Voici le programme complet :
"(1) banque de donnees" individu(candide,20,constantinople,non) ->; individu(cunegonde,20,constantinople,oui) ->; individu(gontran,94,aspres_sur_buech,non) ->; individu(casimir,2,severac_le_chateau,oui) ->; individu(clementine,1,cucugnan,non) ->; individu(popeye,99,aspres_sur_buech,oui) ->; individu(olive,99,aspres_sur_buech,non) ->; individu(mimosa,1,aspres_sur_buech,oui) ->; individu(bip,15,pampelune,non) ->; individu(ignace,114,loyola,oui) ->; individu(balthazar,87,jerusalem,non) ->; individu(gaspard,96,smyrne,oui) ->; individu(melchior,34,kartoum,non) ->;
"(2) definition des types" type(x,nom) -> nom(x); type(x,age) -> age(x); type(x,ville) -> ville(x); type(x,booleen) -> booleen(x); nom(candide) ->; nom(cunegonde) ->; nom(gontran) ->; nom(casimir) ->; nom(clementine) ->; nom(popeye) ->; nom(olive) ->; nom(mimosa) ->; nom(bip) ->; nom(ignace) ->; nom(balthazar) ->; nom(gaspard) ->;
©PrologIA
C - 12
Annexe C
nom(melchior) ->; age(20) ->; age(94) ->; age(2) ->; age(1) ->; age(99) ->; age(15) ->; age(114) ->; age(87) ->; age(96) ->; age(34) ->; ville(constantinople) ->; ville(aspres_sur_buech) ->; ville(severac_le_chateau) ->; ville(cucugnan) ->; ville(pampelune) ->; ville(loyola) ->; ville(jerusalem) ->; ville(smyrne) ->; ville(kartoum) ->; booleen(oui) ->; booleen(non) ->;
"(3) listes des formules atomiques" atomique(habite_a(x,y)) ->; atomique(est_age_de(x,y)) ->; atomique(lunette(x,y)) ->; atomique(plus_age(x,y)) ->; atomique(inferieur(x,y)) ->; atomique(different(x,y)) ->;
"(4) evaluation des formules atomiques" habite_a(x,y) -> individu(x,a,y,b); est_age_de(x,y) -> individu(x,y,v,b); plus_age(x,y) ->
individu(x,a,v,b)
individu(y,a',v',b')
val(inf(a',a),1); lunette(x,y) -> individu(x,a,v,y); inferieur(x,y) -> val(inf(x,y),1); different(x,y) -> dif(x,y);
"(5) evaluation des formules" vrai(p) -> atomique(p) p; vrai(non(p)) -> non(vrai(p)); vrai(et(p,q)) -> vrai(p) vrai(q); vrai(ou(p,q)) -> vrai(p); vrai(ou(p,q)) -> vrai(q); vrai(existe(x,t,p)) -> type(x,t) vrai(p); vrai(tout(x,t,p)) -> non(vrai(existe(x,t,non(p))));
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 13
"(6) definitions utiles" non(p) -> p ! fail; non(p) ->;
"(7) calcul des reponses" reponse_a_tout ->
question(i,q)
element(y,q)
line
outm("-->")
out(y)
line; element(x,ens(x,t,p)) ->
type(x,t)
vrai(p);
"(8) listes des questions" question(1,ens(x,ville,habite_a(mimosa,x))) ->
outm("(1) dans quelle ville habite mimosa ?"); question(2,ens(x,booleen,lunette(olive,x))) ->
outm("(2) olive porte-t-elle des lunettes ?"); question(3,ens(x,ville,existe(y,nom,et(habite_a(y,x),
et(est_age_de(y,a),et(inferieur(a,20),lunette(y,oui)))))))
->
outm("(3) quelles sont les villes ayant au moins un habitant")
outm("age de moins de 20 ans et portant des lunettes ?");
C.5.
Un casse-tête
Dans cet exemple, il s'agit de résoudre le célèbre problème de crypto-arithmétique dans lequel en remplaçant chaque occurrence des lettres s, e, n, d, m, o, r, y par un même chiffre, on ait :
SEND
+MORE
-----
MONEY
Une programmation conventionnelle oblige à prendre en compte deux problèmes simultanément : celui de l'addition proprement dite et le fait que deux lettres différentes sont remplacées par deux chiffres différents. Au contraire, avec le dif retardé, ces deux problèmes sont bien séparés : le prédicat differents met en place toutes les inéquations à l'avance. On n'a plus ensuite qu'à exprimer l'addition et les
dif se débloquent progressivement au fur et à mesure que l'on avance dans la résolution du problème. Le programme est rendu plus clair, mais aussi plus efficace. Voici la solution :
> jolie_solution;
9567
+1085
-----
©PrologIA
C - 14
Annexe C
10652
{}
Voici le programme :
"resolution de SEND+MORE=MONEY" solution(s.e.n.d.m.o.r.y) ->
different(s.e.n.d.m.o.r.y.nil)
somme(r1,0,0,m,0)
somme(r2,s,m,o,r1)
somme(r3,e,o,n,r2)
somme(r4,n,r,e,r3)
somme(0,d,e,y,r4); somme(r,0,0,r,0) -> ! retenue(r); somme(r,x,y,z,r') ->
retenue(r)
chiffre(x)
chiffre(y)
val(add(r,add(x,y)),t)
val(div(t,10),r')
val(mod(t,10),z); chiffre(0) ->; chiffre(1) ->; chiffre(2) ->; chiffre(3) ->; chiffre(4) ->; chiffre(5) ->; chiffre(6) ->; chiffre(7) ->; chiffre(8) ->; chiffre(9) ->; retenue(1) ->; retenue(0) ->; different(nil) ->; different(x.l) -> hors_de(x,l) different(l); hors_de(x,nil) ->; hors_de(x,a.l) -> dif(x,a) hors_de(x,l); jolie_solution -> solution(s) jolie_sortie(s); jolie_sortie(s.e.n.d.m.o.r.y) ->
outm(" ") out(s) out(e) out(n) out(d) line
outm("+") out(m) out(o) out(r) out(e) line
outm("----") line
out(m) out(o) out(n) out(e) out(y) line;
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 15
C.6.
Construction d'un chemin
Ce programme énumère des chemins sans boucle par utilisation de la règle freeze.
Rappelons que freeze retarde l'effacement du littéral sur lequel il porte tant qu'une certaine variable n'est pas affectée. Il est utilisé ici pour construire un bon chemin qui est un chemin qui ne passe pas deux fois par la même étape.
Pour cela, on calcule un chemin possible par la règle chemin que l'on valide au fur et
à mesure par la règle bonne-liste, ce qui permet de rejeter automatiquement le chemin en cours de construction dès qu'on tente de lui adjoindre une étape qui y figure déjà. Voici la liste des chemins sans boucle passant par Marseille, Londres, et Los Angeles suivie du programme :
> bon_chemin(l);
{l=Marseille.nil}
{l=London.nil}
{l=LosAngeles.nil}
{l=Marseille.London.nil}
{l=Marseille.London.LosAngeles.nil}
}l=Marseille.LosAngeles.nil}
{l=Marseille.LosAngeles.London.nil}
{l=London.Marseille.nil}
{l=London.Marseille.LosAngeles.nil}
{l=London.LosAngeles.nil}
{l=London.LosAngeles.Marseille.nil}
{l=LosAngeles.Marseille.nil}
{l=LosAngeles.Marseille.London.nil}
{l=LosAngeles.London.nil}
{l=LosAngeles.London.Marseille.nil}
>
Et voici le programme :
"chemins sans boucles" bon_chemin(l) -> bonne_liste(l) chemin(l); chemin(x.nil) -> etape(x); chemin(x.x'.l) -> route(x,x') chemin(x'.l); route(x,x') -> etape(x) etape(x'); etape(Marseille) ->; etape(London) ->; etape(LosAngeles) ->;
©PrologIA
C - 16
Annexe C
"liste sans repetition" bonne_liste(l) -> freeze(l,bonne_liste'(l)); bonne_liste'(nil) ->; bonne_liste'(x.l) -> hors_de(x,l) bonne_liste(l); hors_de(x,l) -> freeze(l,hors_de'(x,l)); hors_de'(x,nil) ->; hors_de'(x,x'.l) -> dif(x,x') hors_de(x,l);
C.7.
Les arbres infinis
Les automates d'états finis sont un bon exemple d'objets qui peuvent être représentés par des arbres infinis (rationnels). Le programme présenté ici calcule un automate minimal qui accepte certaines chaînes et en refuse d'autres. Toutes ces chaînes sont faites à partir de a et b. La règle prédéfinie draw-equ(x) est utilisée à la fois pour dessiner l'automate et pour minimiser sa taille, c'est à dire le nombre de ses
états. Son utilisation suppose que le module de dessin soit chargé.
> infinite;
> accepte(s, "a"."b".nil) accepte(s, "b"."a".nil)
refuse(s, "a".nil) refuse(s, "b".nil)
refuse(s, "a"."a".nil) refuse(s, "b"."b".nil)
refuse(s, "a"."a"."b".nil) refuse(s, "b"."b"."a".nil)
entier_de_Peano(n)
automate_de_taille(s, n)
!
draw_equ(s)
fail;
x x = final
/ \
aa bb
| |
y z y = non_final
/ \
aa bb
| |
z x z = non_final
/ \
aa bb
| |
x y
Le terme fail force le backtracking pour éviter l'impression finale. Voici le programme complet permettant de produire tout cela. Il faut noter que les buts
accepte(s,x) et refuse(s,x) sont effacés de façon complétement déterministe. Pour cette raison, afin d'obtenir l'automate minimal, il n'est pas nécessaire de placer
entier_de_Peano(n) comme premier but de la suite des buts précédents (à effacer).
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Quelques exemples de programmes Prolog II+
C - 17
"Automate d'etat fini" accepte(s,nil) -> etat_final(s); accepte(s,e.x) -> fleche(s,e,s') accepte(s',x); refuse(s,nil) -> non_etat_final(s); refuse(s,e.x) -> fleche(s,e,s') refuse(s',x); etat_final(<final,aa(s1),bb(s2)>) ->; non_etat_final(<non_final,aa(s1),bb(s2)>) ->; fleche(<f,aa(s1),bb(s2)>,"a",s1) ->; fleche(<f,aa(s1),bb(s2)>,"b",s2) ->;
"Calcul d'un automate de taille donnee" automate_de_taille(s,n) -> automate_de_taille(s.nil,n,nil); automate_de_taille(nil,0,l) ->; automate_de_taille(s.l,n,l') ->
element_de(s,l')
automate_de_taille(l,n,l'); automate_de_taille(s.l,suc(n),l') ->
non_element_de(s,l')
fleche(s,"a",s1)
fleche(s,"b",s2)
automate_de_taille(s1.s2.l,n,s.l'); element_de(s,s.l) ->; element_de(s,s'.l) -> dif(s,s') element_de(s,l); non_element_de(s,nil) ->; non_element_de(s,s'.l) -> dif(s,s') non_element_de(s,l);
"Enumeration des entiers de Peano" entier_de_Peano(0) ->; entier_de_Peano(suc(n)) -> entier_de_Peano(n);
©PrologIA
C - 18
Annexe C
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Annexe D ajout de règles externes
(méthode des parasites)
D.1. Description générale
D.2. Règle Prolog réalisant l'interface
D.3. Appel du programme externe
D.4. Comment provoquer un backtracking ou renvoyer une erreur
Ce chapitre montre comment ajouter de nouvelles règles faisant référence à des fonctions externes écrites en C ou tout autre langage compatible avec C, et décrit les procédures d'interface. La méthode décrite ici est la seule qui fonctionne en mode 16 bits sur PC. Sinon, une méthode plus simple utilisant des descripteurs peut être utilisée avec le compilateur (voir le chapitre 7. du Manuel de Référence).
La fonction externe peut provoquer un backtracking, renvoyer une erreur qui sera traitée par le mécanisme block, ou afficher un message défini par l'utilisateur.
Le module utilisateur prouser sert de relais entre Prolog et les routines utilisateur
écrites en C, de même le module utilisateur fprouser sert de relais entre Prolog et les routines utilisateur écrites en Fortran. De cette manière il est possible d'avoir simultanément des règles prédéfinies dans les deux langages. Les fichiers sources sont inclus dans le volume de distribution.
D.1. Description générale
Nous rappelons que l'appel depuis Prolog d'une fonction externe définie en C peut se faire de deux manières:
1.
Par la méthode des descripteurs (voir § R 7.6). C'est une méthode d'appel direct avec génération automatique de la règle d'appel Prolog.
2.
En utilisant un parasite via une procédure relais définie dans un module utilisateur fourni dans le kit. C'est la méthode décrite dans ce chapitre.
Quand Prolog est lancé sans spécifier d'état initial, l'état initial standard (fichier
initial.po) est chargé. Ajouter de nouvelles règles écrites dans un autre langage, par la méthode des parasites décrite ici, consiste à:
1.
Définir en Prolog l'appel à ces règles par Prolog, et l'inclure dans un état initial
(cette étape se fait automatiquement dans la méthode des descripteurs).
©PrologIA
D - 2
Annexe D
-
2.
Définir l'implantation externe en C (ou Fortran ou tout autre langage externe compatible), et l'inclure dans la Machine prolog.
-
Cette méthode nécessite deux relais d'appels : l'un entre le programme Prolog et le module utilisateur prouser l'autre entre ce module et la fonction externe.
A!ociation
Prolog
HERITAGE prolog prouser.c
my_rule(...) -> /?300; switch(nb) {
...
case 300:
my_rule(...);
break;
...} autre module C my_rule(...)
{
...
}
Prenons un exemple. Pour ajouter une nouvelle règle, il faut d'abord choisir le module dans lequel on désire la définir. On peut utiliser un module réservé à cet usage, par exemple "Interface". Nous nous placerons dans ce cas pour l'exemple cidessous.
Les deux étapes de la méthode sont :
1.
Définir la règle d'appel dans le module "Interface".
module("Interface"); my_rule(x,y,z) -> /?300; end_module("Interface");
Ceci peut se faire dans un module source séparé, ou directement sur la ligne de commande de Prolog. Vous pouvez sauver ce module par la commande :
save( ["Interface"], "Interface.mo");
Il peut alors être rechargé dans n'importe quel état initial avec la commande
load. Vous pouvez aussi créer directement un nouvel état initial par la commande:
exit("nouvel_etat.po");.
Nous nous placerons dans ce dernier cas pour la suite : nouvel_etat.po est alors un fichier contenant l'état initial augmenté de la règle d'appel compilée par Prolog.
©PrologIA
A!ociation
Prolog
HERITAGE
Ajout de règles externes (méthode des parasites)
D - 3
2.
Créer une nouvelle Machine prolog contenant le programme externe :
Ecrire le programme externe en utilisant les fonctions de communication décrites au § R 7.1 et le compiler avec le compilateur approprié.
Mettre à jour le module utilisateur prouser correspondant (voir § D.3.) et le compiler.
Refaire l'édition de liens entre les nouveaux modules et ceux qui constituent Prolog (se référer au § U 2.7 pour les commandes exactes pour votre machine)
Il suffit ensuite d'utiliser la nouvelle Machine Prolog avec l'état initial augmenté.
D.2. Règle Prolog réalisant l'interface
L'appel sous Prolog d'une fonction externe se fait au moyen d'une règle satisfaisant les conditions suivantes:
- la tête de la règle est un identificateur suivi par des arguments qui sont des variables libres.
- la queue de la règle contient un parasite, représenté par un nombre précédé de /?.
Exemple: la règle relais pour find_pattern est: sys:find_pattern(s1,s2,n) -> /?201;
Le nombre sélectionne la fonction externe adéquate (voir la fonction user_rule dans le module prouser). Pour chaque nombre, il y a un appel de fonction externe correspondant dans la fonction C user_rule ou la fonction Fortran fuserrule.
Note inportante:
Les nombres inférieurs à 250 sont réservés au système (mais aucune vérification n'est faite). Les nombres supérieurs à 250 sont disponibles pour l'utilisateur. Il est conseillé d'utiliser les nombres compris entre 250 et 9999 pour les routines C dont l'appel se trouve dans le module prouser, les nombres compris entre 10000 et
20000 pour les routines Fortran (ou d'autres langages de type Fortran) dont l'appel se trouve dans le module fprouser, et les nombres supérieurs à 20000 pour les routines du mode 16 bits sous DOS/WINDOWS3 dont l'appel se trouve dans le module prowuser.
D.3. Appel du programme externe
Un programme externe ne peut être appelé depuis Prolog par la méthode des parasites, que si le nombre associé et l'instruction d'appel ont été ajoutés dans la fonction user_rule du module C prouser (respectivement fuserrule du module
Fortarn fprouser).
©PrologIA
D - 4
Annexe D
Voici un extrait du texte de la fonction C user_rule: user_rule(nb, err, err_nb) int nb, *err, *err_nb;
{
*err_nb = 0; if (num >10000)
fuserrule(&nb, err, err_nb);
else if (num >20000)
real_mode_user_rule(nb, err, err_nb);
else
switch (nb)
{ case 201: find_pattern(err, err_nb); break;
...
/* add procedure calls here */ default:
*err_nb = 500;
}
}
La terminaison normale du programme externe (qui se traduit par un succès en
Prolog) intervient quand le paramètre err_nb de la fonction C user_rule vaut zéro.
Selon la nature de votre programme externe, vous pouvez choisir de l'insérer directement dans le module prouser ou bien de l'écrire dans un fichier séparé.
Si vous avez écrit vos règles externes dans un ou plusieurs modules différents des modules prouser et fprouser, alors vous devez compiler ce(s) module(s) puis donner le(s) nom(s) au programme prolink pour l'édition de liens.
Tel qu'il est livré, le module prouser établit les relais d'appel entre le compilateur
Prolog et la fonction externe find_pattern, qui constitue pour vous un exemple d'écriture de règles externes.
Attention: Le programme externe find_pattern donné en exemple met en
œuvre la règle prédéfinie find-pattern(s1,s2,n). Sa modification
inconsidérée peut introduire un mauvais fonctionnement de cette règle.
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Ajout de règles externes (méthode des parasites)
D - 5
D.4. Comment provoquer un backtracking ou renvoyer une erreur
Pour renvoyer une erreur depuis une fonction externe, celle-ci doit positionner le paramètre err_nb de la fonction user_rule.
Note importante :
A parir de la version 4.1, le paramètre err n'est plus significatif, il est ignoré par
Prolog. La compatibilité ascendante des versions est toujours assurée.
Si err_nb est positionné à une valeur strictement positive, alors une commande
équivalente à la commande Prolog block_exit(err_nb) est exécutée. Pour y associer un message, ajoutez la ligne correspondante dans le fichier err.txt, sous la forme:
Numéro Texte_décrivant_l'erreur
La règle prédéfinie ms_err permet d'accéder au message associé à un numéro donné. Les numéros d'erreurs inférieurs à 1000 sont réservés pour Prolog, les nombres supérieurs à 1000 sont disponibles pour l'utilisateur. Quelques définitions actuelles de messages d'erreur concernant les modules externes, sont:
500 ERREUR DANS UNE REGLE PREDEFINIE
501 ARGUMENT ERRONE DANS LE MODULE UTILISATEUR
502 VALEUR HORS LIMITES DANS LE MODULE UTILISATEUR
503 CHAINE TROP LONGUE DANS LE MODULE UTILISATEUR
520 get_term: TABLEAU TROP PETIT POUR CODER LE TERME
521 get_term: TABLEAU DES CHAINES TROP PETIT
522 get_term: TABLEAU DES REELS TROP PETIT
523 put_term: INDEX ERRONE DANS LE TABLEAU DES TERMES
!
indique qu'à un tag de type 'T' ou 'D', correspond une valeur en dehors des index possibles pour le tableau val_tab.
524 put_term: INDEX ERRONE DANS LE TABLEAU DES CHAINES
!
indique qu'à un tag de type 'S' correspond une valeur en dehors des index possibles pour le tableau des chaînes.
525 put_term: INDEX ERRONE DANS LE TABLEAU DES REELS
!
indique qu'à un tag de type 'R', correspond une valeur en dehors des index possibles pour le tableau des réels.
526 put_term: CODAGE DE TUPLE ERRONE
!
un index de tuple référence une suite dont le premier élément n'est pas de type entier.
527 put_term: TAG NON VALIDE
528 put_term: L'ARGUMENT PROLOG N'EST PAS UNE VARIABLE LIBRE
Un backtracking est provoqué quand on retourne à Prolog avec err_nb égal à un entier strictement négatif.
©PrologIA
D - 6
Annexe D
A!ociation
Prolog
HERITAGE
©PrologIA
A!ociation
Prolog
HERITAGE
Annexe E
Prolog II+ et les caractères.
E.1. Présentation
E.2. Jeu ISO : avantages et inconvénients
E.3. Jeu hôte : avantages et inconvénients
E.4 Remarques
E.5 Conventions
E.1. Présentation
Il existe deux jeux de caractères utilisables en Prolog II+ :
• un jeu défini par le code ISO 8859-1.
• le jeu de la machine hôte.
La détermination du jeu choisi se fait au lancement d'une session Prolog et est valable jusqu'à la fin de la session (voir le manuel d'utilisation §2.3).
Avoir deux jeux de caractères implique avoir deux modes de codage interne de ces caractères :
• Un mode dans lequel quelque soit le système hôte, le codage interne est invariant, c'est celui du code ISO 8859-1.
• Un second mode dans lequel le codage interne utilisé est celui du système hôte.
Tant que l'on manipule des caractères, il n'y a pas de problème. Cependant si l'on manipule les codes des caractères, seul le jeu ISO assure d'avoir le même code pour toutes les machines. Par exemple en jeu ISO char_code("é",x) a la même valeur sur
Macintosh et sur PC, ce n'est pas le cas lorsque l'on choisit le jeu hôte.
Avoir deux modes de codage, c'est aussi avoir deux types de fichiers binaires:
• Des fichiers contenant des chaînes codées d'après le code ISO.
• Des fichiers contenant des chaînes codées avec les caractères du système hôte.
Lors de la sauvegarde, le codage choisi pour la session est mémorisé dans le fichier que l'on crée; un message d'avertissement s'affichera au moment du chargement si le codage mémorisé dans le fichier binaire n'est pas le même que celui choisi pour la session en cours.
©PrologIA
E - 2
Annexe E
D'autre part, en mode ISO, puisque Prolog manipule des données (caractères) qui peuvent ne pas exister sur la machine hôte, il faut leur associer une représentation 1 externe (suite de caractères) pour les visualiser. Il existe deux modes de représentation des caractères accentués n'appartenant pas au jeu hôte:
• Un mode dans lequel ils sont représentés par une séquence accent_escape (cf
R1.31) qui est très lisible.
• Un mode dans lequel ils sont représentés par une séquence octale.
Le choix de ce mode se fait au lancement de Prolog et est valable pour toute la session (voir le manuel d'utilisation §2.3).
Dans ce qui suit, nous ferons la distinction entre une représentation interne (binary string), qui est une valeur en mémoire, et la visualisation de cette donnée (print string), c'est à dire sa représentation sous forme de caractères sur un canal d'entrée ou de sortie.
A!ociation
Prolog
HERITAGE
E.2. Jeu ISO : avantages et inconvénients
Le choix du jeu ISO a l'avantage d'assurer la portabilité des modules (sources ou binaires) inter-machines. Il permet également de manipuler des caractères qui n'existent pas forcément sur la machine hôte (par exemple les machines UNIX qui utilisent le jeu ASCII 7 bits), notamment les caractères accentués.
L'inconvénient de ce choix, pour Prolog qui est un langage très ouvert et qui réalise beaucoup d'échanges ou de partage de données, est la différence de codage interne de certaines données (chaînes) entre Prolog et l'extérieur. Ce qui nécessiterait de faire une transformation à chaque communication avec l'extérieur. En fait, l'utilisateur n'en a pas forcément toujours besoin, cela dépend de l'opération qu'il veut réaliser sur ces données. Il peut les vouloir:
• en code ISO.
• en code hôte en tant que donnée (binary string).
• en code hôte en tant que représentation (print string).
En effet, des procédures qui manipulent les chaînes sans en connaître le sens, par exemple find_pattern qui compare ou substring qui décompose, sont indépendantes d'un code particulier et n'ont donc pas besoin de transformations. Par contre des procédures qui font appel au système hôte en auront besoin.
1
La visualisation est une représentation externe à Prolog et donc nécessairement codée par le système hôte; Prolog lui ne manipule que des données, donc des codes ISO. Ce sont les entrées sorties qui font la transformation entre la représentation graphique et le codage interne.
©PrologIA
A!ociation
Prolog
HERITAGE
Prolog et les caractères
E - 3
En définitive, il suffira de disposer de procédures de transformation des chaînes entre le codage ISO et le codage hôte, et de savoir quel type de codage est supposé dans les procédures de communication de Prolog.
E.3. Jeu hôte : avantages et inconvénients
Le choix du jeu de la machine hôte a l'avantage de simplifier le passage de la représentation des chaînes dans le langage hôte au codage des chaînes en Prolog
II+: il n'est pas nécessaire de transformer les données entre Prolog et l'extérieur.
L'inconvénient de ce choix tient à la pauvreté du jeu ASCII et à la non standardisation des caractères étendus sur l'ensemble des machines, ce qui rend les programmes non portables.
E.4. Remarques
•En utilisant le même codage que celui de la machine hôte, la donnée et sa représentation sont confondues, tandis qu'en utilisant le codage ISO il arrive que la donnée et sa représention soient différentes et que la représentation externe occupe plus d'octets en mémoire que la donnée.
•La première moitié de la table du code ISO est identique à la table du code ASCII
US. Si un programme Prolog ne contient pas de caractères étendus, il est inutile de faire des transformations entre ISO et hôte.
•Le fait de vouloir utiliser des caractères qui n'existent pas sur la machine hôte, impose d'une part à choisir le mode ISO, d'autre part, pour les communications, soit
à les transmettre en ISO, soit à les transmettre sous forme imprimable (print string).
•Le mode ISO sans accent_escape, comme le mode hôte, réduit le nombre de caractères qui peuvent créer une ambiguité d'interprétation s'ils sont précédés du caractère '\'. Ce mode peut donc éviter aux programmes qui utilisent beaucoup de '\', de masquer ce '\' (utile pour les programmes Edinburgh qui manipulent assez souvent des identificateurs ou graphic_symbol contenant des '\').
E.5. Conventions pour le mode ISO
Les données partagées entre Prolog et les langages externes, décrites dans les
EXTERNAL_DESCRIPTOR, doivent être codées en ISO.
La fonction PRO_BIND, qui permet de créer dynamiquement une zone de données partagées, référence des données qui doivent être codées en ISO; par contre son premier argument qui est le nom Prolog de la zone de données est une chaîne hôte.
©PrologIA
E - 4
Annexe E
Les échanges de chaînes par pipe - entre Prolog et des coprocessus - sont considérés comme des entrées sorties et doivent donc être des chaînes hôtes sous forme imprimable. Les fonctions concernées sont send_string et receive_string.
Pour les transferts de données entre Prolog et les langages externes ou vice versa, les conventions sont les suivantes:
get_string, put_string, get_term, put_term:
passent et reçoivent des chaînes ISO.
get_O_string, put_O_string:
ont les mêmes arguments et le même fonctionnement que respectivement
get_string et put_string mais passent et reçoivent des chaînes hôtes sous forme imprimable.
Et enfin voici quatre procédures de traduction de chaînes:
bin_str_iso_to_host(source,dest)
1
bin_str_host_to_iso(source,dest)
1 réalisent la conversion d'une chaîne ISO en une chaîne hôte et vice versa, en assimilant les chaînes à des données (binary string); c'est à dire la longueur est conservée, il peut y avoir des caractères non imprimables, et les caractères de la chaîne d'entrée qui n'existent pas dans le jeu de sortie sont remplacés par le '?'.
print_str_iso_to_host(source,dest)
1
print_str_host_to_iso(source,dest)
1 réalisent la conversion d'une chaîne ISO en une chaîne hôte et vice versa, où la chaîne hôte est la représentation (printable string) de la chaîne ISO; la longueur des deux chaînes n'est pas forcément la même, un caractère peut être représenté par une chaîne à quatre caractères.
A!ociation
Prolog
HERITAGE
1source est l'adresse d'une zone mémoire où se trouve la chaîne de référence terminée par le caractère nul. dest est l'adresse de la zone mémoire où Prolog va copier la chaîne résultat, à charge
à l'utilisateur de réserver la place nécessaire.
©PrologIA
A!ociation
Prolog
HERITAGE
Prolog et les caractères
E - 5
©PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Annexe F
Table ISO 8859-1
D
E
F
9
A
B
C
7
8
5
6
3
4
1
2
0 1 2 3 4 5 6 7
"
!
SP
0
1
2
@
A
B
P
Q
#
3 C
R
S
` a b c p q r s
$
4
D
T d t
%
5
E
U e u
)
(
*
+
'
&
;
6
7
8
:
9 I
F
G
H
J
K
V
W
X
Y
[
Z j i f g h
{ v w x y z
/
.
-
, <
=
>
?
L
M
N
O
\
]
^
_ l k m n o
|
}
~
D
E
F
9
A
B
C
7
8
5
6
3
4
1
2
8 9 A B C D E F
NBSP
¡
¢
£
"
ª
¨
Y-
§
©
«
¬
SHY
®
–
º
, ı
·
°
±
2
3
´
µ
¶
»
1/4
1/2
3/4
¿
À
Ã
Ä
A°
¨I
`I
E
Ë
`E
É
^
A’
Â
Æ
Ç
Ñ
O`
Ó
Ô
Õ
Ö
Ø
U`
U’
U^
Ü
Y’
ß ï
î
à
á
â
ã
ä a°
æ
ç
è
é
ê
ë
í i`
ñ o`
ó
ô
õ
ö
÷
ø
û
ü
’y u`
ú
ÿ
©PrologIA
F - 2
Annexe F
A!ociation
Prolog
HERITAGE
©PrologIA

Публичная ссылка обновлена
Публичная ссылка на ваш чат обновлена.