Mémoire de Licence, site web dynamique sous JEE, application aux entreprises ...
5 installation de prolog
1. Mais comment utiliser Prolog !
L’interprète de prolog :
Différents interpréteurs Prolog sont disponibles, aussi bien sous Windows que sous Linux, parmi lesquels :
- SWI-Prolog (www.swi-prolog.org).
- SICStus Prolog (www.sics.se/sicstus).
- Open Prolog (www.cs.tcd.ie/open-prolog).
- GNU-Prolog (http://www.gprolog.org/).
- Visual Prolog (http://www.visual-prolog.com/). … etc.
Dans la suite on va travailler avec l’interpréteur SWI-Prolog, c’est le plus conseillé, d’ailleurs un sondage sur
le site http://www.developpez.com/ le montre très bien.
SWI-Prolog qui est un interpréteur Prolog gratuit et disponible pour Windows et pour Linux.
Pour installer SWI-Prolog sous Windows téléchargez la dernière version stable sur le site
http://www.swi-prolog.org
Ou cliquez sur le lien suivant pour télécharger la version 6.2.2 datant de 2012 :
Télécharger SWI-Prolog 6.2.2 (version 2012) pour Windows :
http://www.gecif.net/articles/linux/swi-prolog.exe
Étapes pour programmer en Prolog :
On va avoir besoin d’un éditeur de texte et de la console SWI-prolog.
1 - Créer un fichier avec Bloc-notes ou WordPad qu’on nommeras par exemple ( exProlog ) avec
l’extension ‘.pl’.
2 - Lancer l’interpréteur SWI-Prolog en cliquant directement sur le fichier ou sinon par la commande
appropriée (généralement pl). Dans le deuxième cas, charger le fichier en tapant « [Le chemin d’accès au
fichier exProlog.pl]. »
Si vous avez mis une majuscule à l’initiale du nom de fichier, vous devrez mettre entre apostrophes :
['ExProlog'].
Le signal d'invite ‘?-‘ attend que vous saisissiez un but que le moteur de résolution Prolog tentera de
satisfaire.
3 - Recharger le fichier après chaque modification.
4 - Pour sortir: halt. ou Ctrl-D
Dans l'interprète, vous pouvez obtenir une aide sur tout prédicat prédéfini à l'aide du prédicat help.
Par example help(halt).
Pour interrompre une exécution problématique (et passer à une autre commande ou question) : Ctrl C
(puis a - pour "abort").
Un manuel de référence très détaillé est disponible sur le site de SWI-Prolog :
(http://www.swi-prolog.org/pldoc/refman/ ).
2. Sur ce qui vient nous verrons en détails les différentes caractéristiques de Prolog.
Pour l’instant, nous nous contenterons de nous familiariser avec le Langage.
Exemple Pratique :
1 - Éditons un nouveau fichier texte nommé ‘Fichier1’ (avec l’éditeur de test Bloc-Note par exemple) et
enregistrons le avec l’extension .pl.
Voici le code source en Prolog de ce programme qui se contente de déclarer un ensemble de faits :
animal(ours).
animal(chat).
prenom(hypathie).
prenom(alex).
prenom(bissette).
poss_ani(bissette,chat).
poss_ani(alex,chat).
*Veuillez consulter le fichier [Exemples/Fichier1.pl] du CD.
Un tel fichier s’appelle base de faits, parce qu’il regroupe des faits que l’on déclare être vrais.
Voici les faits affirmés par notre programme :
- ours et chat sont des animaux.
- hypathie, alex et bissette sont des prénoms.
2 - Ouvrons le programme source Prolog.pl dans l'interpréteur Prolog en tapant :
[‘CheminD’accèsFichier1.pl’]. ou consult(‘CheminD’accèsFichier1’). , l'extension .pl n'est pas
indiquée dans l'instruction consult.
Ceci dit qu’il faut charger le programme.
Nous n'allons pas maintenant "exécuter" le programme comme on le fait d’habitude, mais nous
allons plutôt poser à l'interpréteur Prolog un ensemble de questions pour lesquelles l'interpréteur consultera
les faits et les règles inscrits dans le programme du fichier ‘Fichier1.pl’, nous allons donc interroger notre
base de connaissance. Dans la terminologie Prolog, on les appelle des requêtes ou buts.
Question 1: hypathie est-il un prénom ?
?- prenom(hypathie).
Remarque : On doit Vérifiés en Particulier :
1. Que nous n’avons pas pour l’instant utilisé aucune majuscule.
2. Que tous les faits et votre requête se terminent par un point.
L'interpréteur nous répond true : hypathie est bien un prénom dans la base des faits inscrits dans le
programme.
Question 2: ouria est-il un prénom ?
?- prenom(ouria).
L'interpréteur nous répond false : ouria n'est pas un prénom dans la base des faits inscrits dans le
programme.
3. Les Variables :
Nous allons introduire la notion de variables.
Question 3: quels sont tous les animaux ?
?- animal(X).
La variable X va prendre pour valeur chaque nom d'animal. Pour passer d'un valeur à une autre il
faut appuyer sur la touche point-virgule. Une fois que la liste est terminée, Prolog la fini par un point. Le
résultat est alors :
X = ours;
X = chat.
Question 4: quels sont tous les prenoms ?
?- prenom(X).
Cette fois la variable X va prendre pour valeur chaque prénom. Le point-virgule permet toujours de
passer d'une réponse à la suivante. Le résultat final est alors :
X = hypathie;
X = alex;
X = bissette.
Question 5: existe-t-il des animaux ?
?- animal(_).
Cette fois l'expression animal(_) sera vraie chaque fois que Prolog rencontre un animal (peu-importe
lequel) dans la base des faits du programme. Comme Prolog rencontre 2 animaux, la réponse à notre
question est :
true ;
true.
Ici, Prolog vérifie l’existence d’une valeur pour cette variable (une substitution) mais ne cherche pas à
énumérer les valeurs Possibles.
Les Conjonctions :
Nous nous sommes pour l’instant limités à satisfaire un seul but à la fois, mais on peut aussi tenter de
les combiner : si l’on entre plusieurs buts séparés par des virgules, Prolog essaiera de tous les satisfaire
simultanément.
?- animal(X),poss_ani(alex,X).
Nous donne bien le X = chat .
?− animal(X) ,prenom(X) .
Nous donne heureusement False.
Maintenant nous allons introduire la notion de Règles:
Exemple 1 : l'arbre généalogique
Les données du problème sont : Alice et Luc sont mariés. Luc est le père de Jean.
La problématique à résoudre est : Qui est la mère de Jean ?
4. Dans le code source nous indiquons que :
alice est l'épouse de luc
luc est le père de jean
de telles affirmations sont appelées des faits
Puis nous indiquons à Prolog que si un père est marié à une femme, alors cette dernière est la mère du fils :
Une telle déclaration est appelée une règle.
Le symbole :- dans une règle se lit "si".
Le symbole , (virgule) dans une règle se lit "et".
% les faits :
epouse(alice,luc). % alice est l'épouse de luc
pere(luc,jean). % luc est le père de jean
% les règles :
mere(M,E) :- pere(P,E) , epouse(M,P). % M est la mère de E si P est le père de E et si M est l'épouse de P
*Veuillez consulter le fichier [Exemples/Fichier2.pl] du CD.
Remarque : en Prolog tout ce qui suit le caractère % sur une ligne est un commentaire.
Après avoir ouvert le programme dans l'interpréteur Prolog (par la commande consult), posons-lui quelques
questions :
Question 1 : qui est l'épouse de luc ?
?- epouse(X,luc).
X = alice.
Question 2 : qui est l'époux d'alice ? (qui a pour épouse alice)
?- epouse(alice,X).
X = luc.
Question 3 : qui est le père de jean ?
?- pere(X,jean).
X = luc.
Question 4 : qui est le fils de luc ? (qui a pour père luc)
?- pere(luc,X).
X = jean.
Remarque : pour répondre à ces 4 premières questions Prolog n’à utiliser que les faits, sans utiliser la règle
définissant la mère.
Question 5 : qui est la mère de jean ?
?- mere(X,jean).
X = alice.
Question 6 : qui est le fils d'alice ? (qui a pour mère alice)
?- mere(alice,X).
X = jean.
Remarque : pour répondre à ces 2 dernières questions Prolog à utiliser cette fois la règle définissant la
mère.
5. Question 7 : qui est le fils de jean ? (qui a pour père jean)
?- pere(jean,X).
false.
Question 8 : qui est le fils de lucienne ? (qui a pour mère lucienne)
?- mere(lucienne,X).
false.
Remarque : Prolog n'a pas la réponse à ces 2 dernières questions : il répond alors false.
Question 9 : qui est la mère de qui ?
?- mere(X,Y).
X = alice,
Y = jean.
Question 10 : combien de liens de parenté "mère/fils" existe-t-il ?
?- mere(_,_).
1 seul
true.
Question 11 : existe-t-il une personne qui est sa propre mère ?
?- mere(X,X).
NON
false.
Remarque : en Prolog le nom des variables commencent toujours par une lettre majuscule (exemple : X,
Voiture, NUMERO, etc.).
Pour bien comprendre les étapes de résolution de cette requêtes, SWI-Prolog nous propose
le ‘Débogage’ :
Pour comprendre ce qui se passe, nous allons utiliser les capacités de débogage de SWI-Prolog.
On va entrer la commande :
trace , prenom(X).
Ceci va donc lancer la requête prenom(X)., mais en demandant en plus une trace d’exécution. Prolog
répondra Call: (7) prenom(_G156) ?, ce qui signifie qu’il a projet d’appeler le prédicat prenom avec une
variable comme argument.
Pour savoir quelles sont vos possibilités à ce moment précis, tapons ‘h’. Prolog nous donnera alors la liste des
commandes disponibles. Pour l’instant, nous désirons simplement continuer l’exécution :
Tapons la barre d’espacement (ou enter).
Continuons à suivre pas à pas l’exécution de notre programme en frappant la barre d’espacement,
chaque fois que nécessaire, jusqu’à la fin de l’exécution.
SWI-Prolog (dans sa version pour Windows seulement) propose aussi une version graphique du
débogueur. Dans le menu ‘Debug’ choisissons ‘Graphical Debugger’ (ou tapons simplement guitracer.),
puis relancez la commande trace , prenom(X).
Une belle fenêtre apparaît nous permettant de suivre l’exécution du programme.
Enfin, la commande halt permet de sortir de l'interpréteur Prolog : ?- halt.
6. Exemple :
Soit le programme source suivant :
pere(abraham,isaac).
pere(isaac,jacob).
pere(jacob,joseph).
ancetre(X,Y):-pere(X,Y).
ancetre(X,Y):-pere(X,Z),ancetre(Z,Y).
C’est un programme récursif, et donc la trace va faire apparaître l'arbre des appels.
La question est : ?- ancetre(abraham,joseph). La réponse est donc : true
Voici donc les étapes que SWI-Prolog exécute avant de nous donner cette réponse :
Comme on l’a déjà expliqué, on doit entrer la commande : ?- trace, ancetre(abraham,joseph).
1- Lancement de la trace :
Lorsque le calcul se termine, Prolog este en "mode trace", et le manifeste en demandant [trace] ?-
au lieu de ?-
Pour revenir à la boucle d'interaction ordinaire, dites :
[trace] ?- notrace, nodebug.
true.
3 ?-
7. 2- Le littéral-but est considéré comme placé dans une boîte munie de 2 entrées et de 2 sorties (globalement appelées les 4
ports)
Call désigne le lancement de la démonstration
Fail signifie que tout espoir de démontrer le but doit être abandonné
Exit annonce une démonstration réussie (mais il peut s'en trouver d'autres...)
Redo signale qu'on essaie une nouvelle fois de démontrer le but, en essayant la clause suivante dans le paquet de clauses
compétent.
Dans le cas d'un prédicat évaluable comme tab, write ou nl, Call est immédiatement suivi de Exit.
Dans celui d'un prédicat défini par un paquet de clauses, chaque Call vise une clause du paquet, et il est suivi d'une
séquence de Calls aux différents littéraux qui composent le corps de la clause.
Ces appels correspondent à une descente dans l'arbre des appels récursifs à l'exécuteur, et la profondeur courante est
affichée entre parenthèses.
Utilisation des listes :
Les tableaux tels qu'on les connaît dans les langages impératifs n'existent pas en Prolog. Ils sont
remplacés par les listes.
Une liste en Prolog s'écrit entre crochets. Exemple : [a,b,c] est une liste contenant les 3 éléments a, b et
c. A l'intérieur d'une liste les éléments sont séparés par une virgule lorsqu’ils sont énumérés un à un.
La liste vide est représentée par [].
L'opérateur | (barre verticale) permet de définir une liste constituée de :
- Son premier élément (à gauche de l'opérateur |)
- La suite de la liste (c'est une liste, placée à droite de l'opérateur |)
[X|L] est une liste obtenue en ajoutant l'élément X au début de la liste L.
Par exemple les écritures suivantes sont toutes équivalentes : [a|[b,c]] = [a|[b|[c]]] = [a|[b|[c|[]]]] = [a,b,c]
Affichage de tous les éléments d'une liste :
Le programme source est :
afficher_liste([X|L]) :- writeln(X), afficher_liste(L).
Après avoir ouvert ce programme simple dans l'interpréteur Prolog (par la commande consult),
utilisons la fonction afficher_liste :
Question 1 : ?- afficher_liste([a,b,c]).
a
b
c
false.
Question 2 : ?- afficher_liste([a|[b|[c|[]]]]).
a
b
c
false.
8. Test de l'appartenance à une liste :
Le programme source est :
appartient_a(X,[X|_]).
appartient_a(X,[_|L]) :- appartient_a(X,L).
Après avoir ouvert ce programme simple dans l'interpréteur Prolog (par la commande consult),
utilisons la fonction appartient_a :
Question 1 : ?- appartient_a(a,[a,b,c]).
true
Question 2 : ?- appartient_a(c,[a,b,c]).
true
Question 3 : ?- appartient_a(e,[a,b,c]).
false
Veuillez consulter le fichier [Exemples/Fichier3.pl] du CD.
Utilisation de la coupure :
La coupure est un prédicat prédéfini très important qui a pour effet de stopper la recherche de Prolog
dès qu'il aura trouvé une solution (permettant d’agir sur le comportement de l’interprète Prolog lors du
retour arrière).
Ce prédicat permet de contrôler l'espace de recherche et d'économiser le travail de Prolog en limitant
l'exploration de l'arbre de recherche.
La coupure se note ! (un point d'exclamation) dans la plupart des versions de Prolog.
Rappelons que le prédicat prédéfini is est l'opérateur d'affectation en Prolog : il affecte à la variable de
gauche la valeur de l'expression de droite, après avoir évalué cette expression. Par exemple, X is A+2.
Évalue en un premier temps l'expression A+2 puis affecte le résultat à la variable X.
Example simple:
?- X is 2, Y is X+3.
X = 2,
Y = 5.
Passons à 3 exemples d’utilisation concrète de la coupure en Prolog.
Exemple 1 de la coupure : affichage de tous les entiers de N à 1 dans l'ordre décroissant.
L'idée de base est d'écrire un prédicat afficher(N) qui s'appelle récursivement en passant en
paramètre au prédicat appelé la valeur N-1 :
afficher(N):-writeln(N),K is N-1,afficher(K). *Veuillez consulter le fichier [Exemples/Fichier4.1.pl] du CD.
9. Le problème est que cet appel récursif ne s'arrête jamais, et affiche toutes les valeurs négatives sans fin :
?- afficher(5).
5
4
.
.
.
-3
-4
.
.
.
etc. sans arrêt ...
Une solution pour arrêter l'affichage en dessous de zéro est de rajouter une condition dans le prédicat
afficher(N) :
afficher(N):-N>0,writeln(N),K is N-1,afficher(K). *Veuillez consulter le fichier [Exemples/Fichier4.2.pl] du CD.
Cela stop en effet l'affichage à 1, mais Prolog répond alors par la négation (false) en sortant lorsque
N=0 car la condition N>0 n'est pas vérifiée :
?- afficher(5).
5
4
3
2
1
false.
La solution pour stopper l'affichage lorsque N=0 tout en répondant positivement (true) est d'utiliser la
coupure : on stoppe positivement la recherche lorsque N=0, c'est-à-dire lorsque le prédicat afficher() reçoit 0
en paramètre :
afficher(0):-!. % ne fait rien et arrête la recherche en répondant true
afficher(N):-writeln(N),K is N-1,afficher(K). % appel récursif du prédicat afficher()
*Veuillez consulter le fichier [Exemples/Fichier4.3.pl] du CD.
Et le résultat est :
?- afficher(5).
5
4
3
2
1
true.
10. Exemple 2 de la coupure : affichage de tous les éléments d'une liste.
L'idée de base est d'écrire un prédicat afficher([X|Y]) qui affiche le premier élément X puis qui appelle
récursivement le prédicat afficher en lui passant en paramètre le reste Y de la liste:
afficher([X|Y]):-writeln(X),afficher(Y). *Veuillez consulter le fichier [Exemples/Fichier5.1.pl] du CD.
Mais là encore Prolog répond par la négation (false) lorsqu'il n'y a plus rien à afficher, c'est-à-dire
lorsque la liste reçue en paramètre par le prédicat afficher est vide :
?- afficher([a,b,c,d]).
a
b
c
d
false.
La solution consiste donc à préciser grâce à la coupure que si la liste reçue est vide, on arrête la
recherche et on répond positivement :
afficher([]):-!. % arrête si la liste est vide
afficher([X|Y]):-writeln(X),afficher(Y). % appel récursif si la liste n'est pas vide
*Veuillez consulter le fichier [Exemples/Fichier5.2.pl] du CD.
Et le résultat est :
?- afficher([a,b,c,d]).
a
b
c
d
true.
Exemple 3 de la coupure : répétition d'une proposition quelconque.
Le prédicat repeter(N,P) suivant répète N fois la proposition P :
repeter(0,_):-!. % arrête si N=0 quelque soit la proposition P
repeter(N,P):-P,X is N-1,repeter(X,P). % exécute P puis appel récursif
*Veuillez consulter le fichier [Exemples/Fichier6.pl] du CD.
Exemples d'utilisation :
?- repeter(5,write('bonjour ')).
bonjour bonjour bonjour bonjour bonjour
true.
?- repeter(2,repeter(1,writeln('3Info'))).
3Info
3Info
true.
11. ?- repeter(3,(write('prolog'),nl)).
prolog
prolog
prolog
true.
Remarque : Pour passer plusieurs prédicats dans le second paramètre de repeter on les a encadrés par des
parenthèses. le prédicat nl affiche un retour à la ligne sur la sortie standard, il est équivalent à writeln('') :
?- repeter(4,(write('prolog'),writeln(''))).
prolog
prolog
prolog
prolog
true.
Exemple 4 de la coupure : se limiter à la première réponse dans le cas de réponses multiples.
Nous retrouvons un arbre généalogique très simple : jean est le père de 3 enfants :
pere(jean,luc). % jean est le père de luc
pere(jean,remi). % jean est le père de rémi
pere(jean,laurent). % jean est le père de laurent *Veuillez consulter le fichier [Exemples/Fichier7.pl] du CD.
Posons quelques questions à l'interpréteur avec ou sans coupure :
Qui est le père de laurent ? Réponse : jean :
?- pere(X,laurent).
X = jean.
Qui est le fils de jean (qui a pour père jean) ? Réponse : jean a 3 fils luc, rémi et laurent, il y a donc 3
solutions :
?- pere(jean,X).
X = luc ;
X = remi ;
X = laurent.
Rémi a-t-il un père ? Réponse : oui :
?- pere(_,remi).
true.
Jean est-il un père ? Et là encore la réponse est triple puisque l'interpréteur a trouvé 3 fois la réponse à la
question :
?- pere(jean,_).
true ;
true ;
true.
12. Si on veut juste savoir si Jean est un père ou pas, peu importe le nombre de fils. Dans ce cas on va
poser la question en utilisant la coupure afin de s'arrêter dès la première réponse positive :
?- pere(jean,_),!.
true.
Ainsi l'interpréteur arrête sa recherche dès le premier fils trouvé, sans perdre de temps à se
demander si jean a d'autres fils, ce qui n'a aucune importance pour répondre à la question "Jean est-il un
père ?".
De même, si on pose la question "Qui sont les fils de jean ?" avec une coupure à la fin, l'interpréteur
arrêtera la recherche dès la première solution trouvée :
?- pere(jean,X),!.
X = luc.
La question précédente peut alors se formuler ainsi : "Donnez-moi un fils de jean et un seul, peu importe
lequel".
NB : Nous détaillerons ses points dans les exemples qui suivent.