Prolog
Prolog
Prolog
Prolog
Jacques Savoy
Bibliographie
[O'Keefe 90] Richard A. O'Keefe : The Craft of Prolog. The MIT Press,
Cambridge (MA), 1990. (G6-1415)
[Sterling 86] Leon Sterling, Ehud Shapiro : The Art of Prolog: Advanced
Programming Techniques. The MIT Press, Cambridge (MA),
1986. (G6-139).
Plan
Chapitre 3 : Structures
- Intelligence artificielle.
- SWI-Prolog (www.swi-prolog.org) ;
On doit déduire objets et relations entre objets d’un monde connu, comme par
exemple des phrases comme « Jean est le père d’Anne » ou « Pierre est riche »
ou encore « Les objets rares sont chers ». Les objets Prolog ne correspondent
pas aux objets des langages de programmation par objets !
Et si, à première vue, la réponse se limite à un oui ou non, Prolog peut faire plus.
Programmation logique (Prolog) 3
Sicstus-Prolog
|?- halt.
(fenêtre disparaît)
Programmation logique (Prolog) 4
SWI-Prolog
unix% /usr/local/bin/swipl
Welcome to SWI-Prolog (Multi-threaded, Version 5.2.0)
Copyright (c) 1990-2003 University of Amsterdam.
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free
software, and you are welcome to redistribute it under
certain conditions.
Please visit http://www.swi-prolog.org for details.
For help, use ?- help(Topic). or ?- apropos(Word).
?- help.
?- help(consult).
?- [nom_fichier].
?- consult(‘Geographie.pl’).
% Geographie.pl compiled 0.00 sec, 1,952 bytes
Yes
?-
?- halt.
unix%
Programmation logique (Prolog) 5
Les faits
• Les faits : « Jean aime Marie » ou « Anne aime Jean » sont traduits en Prolog
par :
aime(jean, marie). % car Jean aime Marie
aime (paul , marie). % Paul est amoureux de Marie
aime (marie , paul). % et Marie aime Paul
Avec
- puis le/les arguments (ici « jean » et « marie ») séparés par une virgule,
dans un ordre qui possède un sens (qui est le sujet et le complément
de l’action décrite),
Autres exemples :
« Socrate est un homme »
« Socrate est un Grec »
« Aristote est le disciple de Socrate »
« L’éléphant est un grand animal gris »
« Tous les hommes sont faillibles » % C’est parfois trop compliqué
Programmation logique (Prolog) 6
Autres faits (dans la base de connaissance car ce n’est pas une base de données
ou une simple collection de données).
aime(jean,peche).
homme(tintin).
pere(henri, paul).
livre(marx, leCapital).
donne(tintin, milou, os).
etudiant(tintin,20,informatique,1).
roi(tintin,belgique).
Si une relation comprend un seul argument, on parlera plutôt de propriété que de
relation (par exemple, homme(tintin), grand(babar)).
• Une représentation graphique à l’aide d’un arbre est possible avec le nom de la
relation comme racine et les arguments (ordonnés) comme branches.
Programmation logique (Prolog) 7
Les questions
Par exemple, on peut se poser la question de savoir si « Est-ce que Jean aime
Marie ? ». Cette question se traduit par :
?- aime(jean, marie).
Pour y répondre, l’interprète Prolog va essayer d’unifier la question posée avec
un des faits de sa base de connaissance. S’il réussit, il répond « Yes » et « No »
dans le cas contraire. Sur la base des connaissances suivantes :
aime(jean, marie).
aime(paul, marie).
aime(marie, paul).
aime(jean, peche).
aime(paul, biere).
roi(tintin,belgique).
?- aime(jean, biere).
No
?- aime(jean, marie).
Yes
?- roi(tintin,belgique).
Yes
?- homme(tintin).
ERROR: Undefined procedure: homme/1
La réponse « No » ne signifie pas que c’est faux mais que sur la base des
connaissances dont dispose Prolog, la question n’a pas pu être unifiée. De
même, la réponse « Yes » n’indique pas que cela est vrai dans le monde réel
mais que la question a pu être unifiée (peut-être qu’un jour Tintin sera le roi de
Belgique …). Finalement, on a utilisé un prédicat non défini homme avec un seul
argument.
Programmation logique (Prolog) 8
• Jusqu’à présent, nous pouvons écrire et interroger Prolog sur des questions
fermées (ou des faits précis) dont la réponse est binaire (« oui / non »).
Mais on aimerait savoir ce qu’aime Jean (tout ce qu’il aime), sans connaître
a priori l’ensemble des choses que Jean aime. On a alors besoin d’une variable
pour énumérer toutes ces choses. (Je préfère appeler ceci une inconnue mais
de facto, on a choisit le mot de variable). On peut écrire quelque chose comme
?- aime(jean, «ce qui rend vrai cette relation »).
Dans ce but, Prolog permet l’introduction de variables (dont le nom commence
par une majuscule et dont la portée lexicale se limite à un énoncé).
?- aime(jean, CeQueJeanAime).
ou (plus simplement)
?- aime(jean, X).
X = marie
Yes
L’interpréteur doit alors unifier la question (qui comprend une variable libre) avec
ses connaissances. Unifier ne signifie pas seulement voir s’il y a une égalité
entre deux faits mais « rendre égaux » les deux faits. On peut trouver un (ou
dans ce cas plusieurs) fait(s) qui s’unifie(nt) avec la question. La force de Prolog
est de vous donner la possibilité de vous arrêter à la première réponse, de
continuer pour voir la deuxième, … ou toutes les réponses possibles.
Mais les variables peuvent aussi être introduites dans des faits (et cela est parfois
très commode). Par exemple, pour indique qu’ « Anne aime tout ».
aime(anne, Z).
aime(anne, _). % car le _ indique aussi une variable
Programmation logique (Prolog) 9
Les conjonctions
Les règles
• Les faits sont toujours vrais (dans le monde que Prolog connaît). On ne limite
d’aucune manière la véracité de ces faits. Par exemple, on aimerait dire que
« Benjamin aime toutes les boissons sucrées ».
D’autre part, on aimerait généraliser des faits (au moyen d’une règle) afin d’éviter
d’introduire tous les faits (qui sont vrais). Par exemple, on aimerait dire que
« Paul aime toutes les femmes » ou que « Tous les oiseaux ont des plumes »,
que « deux personnes appartiennent à la même fratrie s’ils ont les mêmes
parents », etc.
Une règle correspond à une affirmation générale sur les objets et leurs relations.
Par exemple, on sait que « Paul aime tous ceux qui aiment la bière » que l’on
écrit en Prolog comme suit :
aime(paul, X) :- aime(X, biere).
Et cette règle se compose :
et d’un « . » final.
parent(hubert, denis).
parent(hubert, martine).
parent(nelly, denis).
parent(nelly, martine).
parent(georges, jeanne).
parent(georges, henri).
Définissez une règle pere(Pere, Enfant) qui est vrai si Pere est le père de
Enfant. Faites de même avec la relation mere(Mere, Enfant).
Définissez une règle fils(Fils, Parent) qui est vrai si Fils est le fils du
parent Parent. Faites de même avec la relation fille(Fille, Parent).
• Regardez bien que l’on peut interroger une règle de plusieurs manières. Par
exemple avec frere(Frere,Personne) :
Ouranos Gaia
Kronos Rhea
• Une syntaxe très simple (notation BNF – Backus-Naur Form -) qui repose sur la
notion de terme (servant de base pour décrire les données et les programmes).
avec
• Un terme est donc soit une constante (qui se subdivise en nombre ou atome), soit
une variable soit une structure (ou terme composé).
• A l’aide des faits et des règles, on crée donc des programmes en Prolog qui est
une séquence (donc l’ordre a de l’importance) de clauses. Une clause s’est :
• Il est assez simple de faire tourner en rond l’interpréteur Prolog. Par exemple
avec :
p :- p.
?- p.
Un peu plus subtil ; en principe on peut appeler une règle de plusieurs façons
(e.g, frere(jean,V) ou frere(B,anne)) mais il arrive que l’interpréteur
réponde que sous une seule forme et que pour l’autre il tourne en rond.
Programmation logique (Prolog) 16
Les atomes comprennent les noms (roi, jules, marie05, nil), les
symboles (comme « :- » ou « + ») et les chaînes de caractères (délimités par
« ‘ »).
Les nombres peuvent êtres des entiers (34, -45, +12) ou des réels (-34.14,
0.987, -0.4e+2).
• Les variables qui débutent par une majuscule (Fils, X, Z67, U_02) ou « _ » (qui
correspondent à une inconnue anonyme ou muette comme _, _X1).
• Les structures (ou terme composé) permettent de regrouper des éléments reliés
logiquement. Elles débutent par un atome (dénommé foncteur) puis entre
parenthèses, les arguments séparés d’un virgule (« , »).
Exemple :
parent(anne,hubert).
roi(louisXV, france, regne(1715, 1774), bourbon).
employe(nom,numero,date(jour,mois,an)).
point(x,y).
segment(point(x1,y1),point(x2,y2)).
triangle(point(x1,y1),point(x2,y2),point(x3,y3)).
Ouvrage (auteur(‘Adam Smith’, annee(1723, 1790)),
economie,
ouvrage(‘la Richesse des Nations', 1776),
cote(yl8,2345)) .
Le foncteur est le nom de la relation et l’arité le nombre d’arguments d’une
relation.
Programmation logique (Prolog) 17
Opérateurs
• Les opérateurs en Prolog sont des atomes (composés de symbole(s)) qui sont
des foncteurs. On les rencontre essentiellement pour le calcul arithmétique.
Prolog permet d’écrire les opérations de base comme :
+(3,4)
*(3,4)
+(4,(5,5))
Mais la notation préfixée n’est pas toujours la meilleure présentation et l’on
préfère écrire : 3+4, 3 * 4 ou 4 + 5 * 5 ou, pour certains opérateurs, soit en
notation préfixée (-3, √n), soit en notation postfixée (n !).
Opérateurs (suite)
L’unification (« = »)
Appel
?- X = Y
Dans ce cas, l’interprète utilise son algorithme d’unification pour rendre égaux X
et Y. Trois cas sont possibles pour une unification réussie, à savoir :
Appel
?- alpha = alpha
?- 23 = 24
?- 23 = alpha
?- alpha = X
?- N = 24
?- N = M
?- N = date(6,juin,1944).
?- lettre(C) = mot(toto)
?- syntagme(D,Nom,Adj) = syntagme(X,Y,Z)
?- auteur(X, action) = auteur(smith, action)
?- auteur(dijsktra, routing) = auteur(X, Y, A)
?- foo(X, X) = foo(a, b)
?- foo(X, a(b,c)) = foo(Z, a(Z,c))
?- a(b,C,d(e,F,g(h,i,J))) = a(B,C,d(E,f,g(H,i,j)))
Programmation logique (Prolog) 20
Arithmétique
Arithmétique (suite)
1. si X = Y, alors D = X ;
2. si X < Y, alors D est le gcd de X et de la différence Y – X ;
3. si Y < X, alors utiliser la règle 2 en changeant X et Y.
Chapitre 3 : Structures
Les éléments d’une liste sont séparés (comme les arguments) par une virgule
« , ». Exemple de listes :
[a] % ou .(a, []).
[a,b,c] % ou .(a, .(b, .(c, []))).
[la,souris,grise,trottine].
[jean,aime,[les,vins,canadiens]].
[la,souris,grise,trottine].
[jan,fev,mar,avr,mia,juin,juil,aout,sept,oct,nov,dec].
[tennis, ski, musique].
[bach, mozart, beethoven, beatles].
Les deux notations sont possibles mais évidemment la notation avec des
parenthèses droites est plus simple. Mais les formes suivantes sont aussi
équivalentes :
[a,b,c] = [a|[b,c]] = [a,b|[c]] = [a,b,c|[]]
On utilise aussi des modèles (pattern) pour imposer un certain type de listes,
comme, par exemple :
[X]. % liste composée d’un élément
[X,Y]. % liste composée de deux éléments
[X,Y,Z]. % liste composée de trois éléments
Les unifications suivantes sont possibles :
[X] = [tintin].
[X] = [123].
[X] = [roi(tintin,Belgique)].
[X] = [[la, souris, grise]].
Programmation logique (Prolog) 23
Les listes
• Grâce à la notation [T|R], on peut traiter des listes de longueur variable car on
dit simplement qu’une liste est composée d’un élément en tête (ou d’une tête) et
d’un reste (éventuellement vide) qui est la liste sans son premier élément.
[a,b,c] a [b,c]
[a] a []
[a,b] a [b]
[[le,chat],mange,S] [le,chat] [mange,S]
[[X+Y],2+5] [X+Y] [2+5]
[le,chat,mange,[S]] le [chat,mange,[S]]
[] échec échec
• Il est très utile de savoir si un élément apparaît (au moins une fois) dans une liste.
Habituellement, ce prédicat se nomme member et correspond aux règles
suivantes :
• Ecrivez le prédicat islist(L) qui retourne true si L est une liste. C’est vrai si :
• Ecrivez le prédicat length(L,N) qui retourne true si L est une liste composée
de N éléments. Les règles sont les suivantes :
• Grâce à ce prédicat, on peut définir très simplement les deux prédicats suivants :
prefix(P,L) :- append(P,X,L).
suffix(S,L) :- append(X,S,L).
Par exemple :
add(X,L,[X|L]).
Une solution :
del(X,[X|R],R).
del(X,[T|R1],[T|R2]) :- del(X,R1,R2).
Programmation logique (Prolog) 26
Une solution :
permutation([],[]).
permutation(L, [T|R]) :- del(T,L,L1), permutation(L1,R).
• Autre exemple :
reverse(List,Reverse).
reverse([],[]).
reverse([T|R],L) :- reverse(R,L1), append(L1,[T],L).
% palindrome(List).
palindrome([]).
palindrome([_]).
palindrome(L) :- append([T|R],[T],L), palindrome(R).
• Autre exemple avec des nombres :
% sumList(List,Sum).
sumList([],0).
sumList([Tete|Reste],Sum) :- sumList(Reste,N), Sum is N+T.
% maxList(List, Max).
maxList([X],X).
maxList([X,Y|Reste],Max) :- maxList([Y|Reste],MaxReste),
max(X,MaxReste,Max).
% ordered(List). si List est une liste ordonnée de nombres
ordered([X]).
ordered([X,Y|Reste]) :- X =< Y, ordered([Y|Reste]).
Programmation logique (Prolog) 27
Mapping
• Parfois, on désire parcourir une structure pour en générer une nouvelle. Par
exemple, on peut écrire une phrase en français et vouloir la traduire en anglais.
Une phrase s’écrit comme une liste comme, par exemple :
[le, chat, court].
[le, chat, court, et, la, chatte, mange].
Voici notre dictionnaire bilingue :
translate(le, the).
translate(la, the).
translate(les, the).
translate(et, and).
translate(chat, cat).
translate(chatte, cat).
translate(chats, cats).
translate(chattes, cats).
translate(court, run).
translate(souris, mouse).
translate(souris, mice).
translate(courrent, run).
translate(mange, eats).
translate(mangent, eat).
translate(Other, Other). % pour les autres cas
Et le mécanisme de traduction :
transSentence([], []).
transSentence([T|R], [X|Y]) :- translate(T,X),
transSentence (R,Y).
Et un exemple :
transSentence([le,chat,mange,la,souris], E).
E = [the, cat, eats, the, mouse] ;
E = [the, cat, eats, the, mice]
Yes
Faites de même avec :
enTouteslettres([1,5,7], Ex).
Ex = [un, cinq, sept] ;
Yes
Programmation logique (Prolog) 28
Arbres
Arbres (suite)
?- nom(chien).
No
?- accord(la,souris).
Yes
?- genre(Nom,feminin), nom(Nom).
Nom = souris ;
No
• On peut alors définir des éléments (sous-arbres syntaxiques) en définissant le
syntagme nominal (sn()), le syntagme verbal (sv()) et la phrase (p()).
sn(D,N) :- determinant(D), nom(N), accord(D,N).
sn(N,A) :- nom(N), adjectif(A), accord(N,A).
sn(D,N,A) :- determinant(D), nom(N), adjectif(A),
accord(D,N), accord(N,A).
p(snm(det(D),nom(N),G),D,N) :- sn(D,N), accord(D,G).
p(snm(nom(N),adj(A),G),N,A) :- sn(N,A), adjectif(A),
accord(N,A).
p(snm(det(D),nom(N),adj(A),G),D,N,A) :- sn(D,N,A),
determinant(D), adjectif(A),
accord(N,A).
On vérifie le syntagme nominal.
?- sn(M1,M2).
M1 = le
M2 = chat ;
M1 = la
M2 = souris ;
M1 = chat
M2 = blanc ;
M1 = souris
M2 = blanche ;
No
Programmation logique (Prolog) 30
Arbres (suite)
?- P(S,le,chat).
S = snm(det(le), nom(chat), masculin) ;
No
?- P(S,N,rouge).
P = snm(nom(chat), adj(rouge), masculin)
N = chat ;
P = snm(nom(souris), adj(rouge), feminin)
N = souris ;
No
?- P(S,D,N,A).
S = snm(det(le), nom(chat), adj(rouge), masculin)
D = le
N = chat
A = rouge ;
S = snm(det(le), nom(chat), adj(blanc), masculin)
D = le
N = chat
A = blanc ;
No
On ajoute le verbe et la structure que l’on veut est un arbre plus complexe.
?- p(PH,le,chat,blanc,mange).
PH = p(s(snm(det(le), nom(chat), adj(blanc), masculin)),
v(verb(mange))) ;
No
Pensez-vous que cette approche soit vraiment la bonne ?
Exemples de graphe
Le prédicat relie(V1,V2) est vrai s’il existe un vol direct entre les deux villes,
ou bien si l’on peut joindre (avec une ou plusieurs villes intermédiaires) les villes
V1 et V2.
relie(V1,V2) :- vol(_,V1,V2,_,_).
relie(V1,V2) :- vol(_,V2,V1,_,_).
relie(V1,V2) :- relie(V1,V3), (vol(_,V3,V2,_,_);
vol(_,V2,V3,_,_))
?- relie(V,montreal).
V = toronto ;
V = paris ;
V = new_york ;
V = montreal ;
Yes
Attention, la logique peut vous faire faire le tour de la planète plusieurs fois pour
trouver un autre chemin de V1 vers V2 !
Programmation logique (Prolog) 32
Londres
Toronto Montreal
Paris
New-York
Chicago Rome
Tampa
Cancun
Programmation logique (Prolog) 33
• Pour bien programmer en Prolog, un modèle à suivre est le « generate and test »
c’est-à-dire que l’on génère une solution candidate que l’on teste ensuite pour
vérifier que cette solution candidate soit acceptable. Le modèle est le suivant :
conclusion(S) :- generer(S), verifier(S).
• Une bonne programmation (en Prolog) cherche à atteindre les buts suivants :
1. correct
2. convivilal
3. efficace
4. lisible
5. facile à modifier
6. robuste
7. documenté
• Nous avons proposé un prédicat length(L,N) qui retourne true si L est une
liste composé de N éléments avec les règles suivantes :
length([],0).
length([_|R],N) :- length(R,Nr), N is Nr + 1.
Cette solution s’appuie sur un appel récursif jusqu’à rencontrer la liste vide.
Ensuite on dépile en ajoutant un à la variable N.
Au lieu d’aller vers le cas simple (ici, la liste vide) sans accumuler d’information,
on peut tenir compte de ce dépillement pour accumuler le résultat (ici un entier
via la variable A).
listlen(L,N) :- listlenacc(L,0,N).
listlenacc([],A,A).
listlenacc([T|R],A,N) :- A1 is A + 1, listlenacc(R,A1,N).
?- listlen([a,b,c,d],N).
listlenacc([a,b,c,d],0,N).
listlenacc([b,c,d],1,N).
listlenacc([c,d],2,N).
listlenacc([d],3,N).
listlenacc([],4,N).
Programmation logique (Prolog) 36
Automate
Si, en partant de l’état initial avec une chaîne (de caractères dans le cas présent),
on arrive à trouver un chemin pour aboutir à l’état final avec une chaîne vide,
alors la chaîne est correcte (ou respecte les conditions de notre automate).
• Imaginons un automate qui reconnaît les nombres réels introduits par l’usager
(par exemple, « 345 », « -12 », « +3.15 »).
Graphiquement, on a
QuickTime™ and a
TIFF (Uncompressed) decompressor
are needed to see this picture.
Programmation logique (Prolog) 37
Automate
Et en Prolog,
final(s3).
trans(s0,'+',s1). % le signe + ou – devant le nombre
trans(s0,'-',s1).
trans(s1,N, s1) :- digit(N).
trans(s1,'.',s2). % le point décimal
trans(s2, N, s2) :- digit(N).
silent(s0,s1). % +- optionel
silent(s1,s3). % partie décimale optionel
silent(s2,s3). % +- optionel
digit(0).
digit(1).
digit(2).
digit(3).
digit(4).
digit(5).
digit(6).
digit(7).
digit(8).
digit(9).
Automate (suite)
• Prolog se base sur l’hypothèse du monde clos (closed world assumption) selon
laquelle tous les faits qui sont connus (ou dérivables) sont inclus dans les
clauses. Selon cette hypothèse, si l’interpréteur échoue, il annonce « faux ».
• Rien ne nous empêche d’utiliser Prolog pour étendre la logique vers une
approche de la logique non classique comme, par exemple la logique floue ou
une logique à trois valeurs. Dans le cas de la logique floue, une proposition n’est
pas vraie ou fausse mais on lui associe une valeur qui varie entre 0 (faux) et 1
(vrai). Voici quelques exemples :
celebre(napoleon,1).
celebre(godel,1).
celebre(turing,1).
celebre(bush,0.5).
celebre(jean,0.6).
celebre(van_gogh,1).
riche(bush,1).
riche(napoleon,0.6).
riche(turing,0.1).
riche(jean,0.3).
riche(van_gogh,0).
La modélisation gagne en précision et on peut combiner ces affirmations avec les
opérateurs « et », « ou » et « non », par exemple pour répondre à la question
« Est-ce que Turing fait partie des gens riches et célèbres ? ».
• Une manière de faire fonctionner Prolog avec une autre logique est de faire ceci :
?- question([aime(jean,marie), aime(marie,jean)], Valeur).
Ou de manière générale :
?- question([ButA, ButB], Valeur).
question([ButA, ButB],Verite) :- demontrer(ButA, Va),
demontrer(ButB, Vb),
combin(Va, Vb, Verite),
write(Verite).
combin(Va, Vb, Vb) :- Va => Vb, !.
combin(Va, Vb, Va).
demontrer(ButA, Va) :- ButA,
ButA =..L,
renverse(L),
valeur(L, Va).
demontrer(ButA, 0). % si ButA échoue
notFuzzy(P, V) :- P, !, P = ..L, renverse(L, L1),
valeur(L1, Vf), V is 1-Vf.
notFuzzy(P, 0). % je ne sais pas
renverse(L, R) :- renverse(L,[],R).
renverse([H|T], L1, L) :- renverse(T, [H|L1], L).
renverse([], L, L).
valeur([Tete|_], Tete).
Programmation logique (Prolog) 41
• On peut imaginer une autre logique dans laquelle une proposition peut être
fausse 0, vraie 1 ou inconnue (0.5). Chaque fait aura alors une de ces trois
valeurs et il faut définir la valeur de vérité pour les opérateurs et, ou et non.
fait A fait B A et B A ou B
min max
1 1 1 1
1 0 0 1
1 0.5 0.5 1
0 1 0 1
0 0 0 0
0 0.5 0 0.5
0.5 1 0.5 1
0.5 0 0 0.5
0.5 0.5 0.5 0.5
fait A not A
1 0
0 1
0.5 0.5
• Le problème de placer huit reines sur un échiquier sans qu’elles s’attaquent est
un problème classique en IA. L’espace des solutions à considérer est
relativement important, soit 64 . 63 . 62 . …. . 57 = 1,7 . 1014 possibilités.
Mais une astuce pour réduire immédiatement cet espace de recherche est
prendre en compte le fait que chacune des huit reines doivent être placés sur une
colonne différentes.
Pour contrôler si une solution est acceptable, il ne doit pas y avoir d’attaque
possible entre les huit reines.
Si la liste des autres positions de reines est vide, il n’y a pas de menace et
c’est une solution acceptable.
Mais comment savoir si deux positions (X/Y) et (X1/Y1) sont sur la même
diagonale ou non ?
Programmation logique (Prolog) 44
Pour que les valeurs X et Y soient comprises entre 1 et 8, on peut générer les X
(en imposant une solution de départ avec une reine dans chaque colonne) et
imposer que Y soit membre de la liste [1,2,3,4,5,6,7,8].
Le cut (« ! »)
Pour démontrer a(N), j’ai deux choix (N=1 avec b, N=2 avec e), comme pour
démontrer b. Pour d et e, j’ai une seule démonstration et c est toujours faux.
a(1) :- b.
a(2) :- e.
b :- c.
b :- d.
c :- fail.
d.
e.
?- a(N).
N = 1 ;
N = 2 ;
No.
a(X)
b e
c d
fail
Programmation logique (Prolog) 47
Le cut (« ! ») (suite)
a(X)
b e
!,c d
fail
Programmation logique (Prolog) 48
Le cut (suite)
• Prenez le prédicat max(N,M,Max) qui est vérifié si Max est le nombre maximum
entre N et M. Une écriture possible est :
max(N,M,N) :- N >= M.
max(N,M,M) :- M > N.
Mais une solution plus efficace est la suivante car dès que le premier but de la
première règle est vérifiée, il n’y a plus d’autres solutions.
max(N,M,N) :- N >= M, !.
max(N,M,M).
Le modèle de cet usage est le suivant. Le but B peut être démontré si X est vrai
(règle R1) ou, en alternative, si X est faux (règle R2).
/* R1 */ B :- X , Z B :- X , ! , Z
/* R2 */ B :- \+X , Y. B :- Y.
• Autre exemple. Nous disposons du prédicat name() qui retourne les codes ASCII
d’une constante. Par exemple :
?- name(aime,L).
L = [97, 105, 109, 101] ;
No
Programmation logique (Prolog) 49
Le cut (suite)
On peut l’utiliser pour former une liste de lettres sur la base des codes ASCII de la
manière suivante : D’abord l’explosion et l’implosion des noms.
nom(X,L) :- var(L),!, name(X,Codes), nom1(Codes,L).
nom(X,L) :- var(X), !, nom1(Codes,L), name(X,Codes).
nom1([],[]).
nom1([X|Xs],[X1|X1s]):- name(X1,[X]), nom1(Xs,X1s).
?- nom(aime,L).
L = [a, i, m, e] ;
No
?- nom(X,[a,i,m,e,r]).
X = aimer ;
No
Dans le premier cas, on sait que L est une variable, alors on empêche le retour
arrière (cette règle s’avère le bon choix, il n’y en a pas d’autre). Dans le second
cas (implosion), on sait que l’on a une séquence de lettres. On bloque le choix
sur cette règle.
Le cut (suite)
Par exemple, on sait que Heidi aime tous les animaux. On écrit :
aime(heidi,A) :- animal(A).
mais il faudrait exclure les serpents …
aime(heidi,A) :- serpent(A), !, fail.
aime(heidi,A) :- animal(A).
Deux autres exemples de l’emploi judicieux du cut.
Ecrivez un prédicat different(X,Y) qui est vérifé si X est différent de Y.
different(X,X) :- !, fail.
different(X,Y).
Ecrivez un prédicat not(P) qui est vérifé si P n’est pas vrai.
not(P) :- P, !, fail. % si P réussi, stop et échec
not(_). % si P échoue, alors not() réussit.
Ce prédicat est disponible et il se note \+(P).
Programmation logique (Prolog) 51
Le cut (suite)
• Mais l’opérateur cut pose parfois des problèmes et il faut bien réfléchir avant de
l’utiliser. Voici un exemple.
nombre_parents(adam,0) :- !.
nombre_parents(eve,0) :- !.
nombre_parents(Autre,2).
Et si on l’utilise :
?- nombre_parents(adam,N).
N=0 ;
No
?- nombre_parents(tintin,N).
N=2 ;
No
Cela semble parfait mais …
?- nombre_parents(eve,2).
Yes
Pourquoi ?
Exemple d’emploi :
?- question(X), write(X), nl, fail.
• Types de termes :
atom(X) % vrai si X est une chaîne ou un nom
number(X) %
atomic(X) % atomic(2) est true
var(X)
nonvar(X) % e.g nonvar(_) est toujours true
• Pour vous aider dans le déverminage (debugging) de vos programmes :
trace
notrace
spy P
nospy
debugging
nodebug
Programmation logique (Prolog) 54
Entrée - sortie
Entrée - sortie
% fin de phrase
lirePhrase1(C,[]) :- carFinMot(C), !.
% construit un mot
lireMot(C,W,C1):- carsDansMot(C,Cs,C1), nom(W,Cs).
carsDansMot(C,[C|Cs],C0) :- carDansMot(C), !,
lireCar(C1), carsDansMot(C1,Cs,C0).
carsDansMot(C,[],C) :- not(carDansMot(C)).
On l’utilise ainsi :
?- lirePhrase(Ph).
|: le chat mange la souris.
Ph = [le, chat, mange, la, souris] ;
No
De même :
?- ecrirePhrase([le,chat,mange]).
le chat mange
Yes
Avec la définition suivante :
ecrirePhrase([H|T]) :-write(H),write(' ') ecrirePhrase(T).
ecrirePhrase([]) :- nl.
Programmation logique (Prolog) 57
Conclusion
Le contrôle est donné par l’ordre d’évaluation des buts (l’interpréteur débute par
le sous-but le plus à gauche) et l’ordre des règles (prendre la première au début).
• Prolog et logique
Prolog est basé sur la logique des prédicats du 1er ordre (vous ne pouvez pas
interroger N(jean,marie) pour savoir les relations qui relient les objets jean et
marie ou, par exemple, imposer que les prédicats soient tous d'arité 2).
Prolog se limite aux clauses de Horn (un seul littéral positif, le but). Mais on peut
transformer toute clause en clause de Horn. (voir appendix B [Clocksin 03])
P :- C1 , C2 , … , Cn
P ⇐ C1 ∧ C2 … ∧ Cn
P ∨ ¬C1 ∨ C2 … ∨ ¬Cn
Conclusion (suite)
Prolog inclut des prédicats extra-logiques avec des effets de bord (imprimer les
résultats, lire les informations, assert(), retract(), etc., et le coupe-choix
« ! » qui évite l’explosion combinatoire).
Prolog est semi-décidable (s’il trouve une réponse, c’est OK, mais il peut tourner
en rond et, dans ce cas, on ne sait rien).
Programmation logique (Prolog) 59
• Listes des TP
Introduction à Prolog
La dérivation symbolique.