Académique Documents
Professionnel Documents
Culture Documents
MASTER MRI
Programmation Logique
et Intelligence Artificielle
A. EL HARRAJ
2016-2017
Partie I
Introduction au Prolog ............................................................................................................................ 1
Introduction......................................................................................................................................... 2
Principe ................................................................................................................................................ 3
1. Les faits .................................................................................................................................... 3
2. Les questions ........................................................................................................................... 5
3. Les règles ................................................................................................................................. 7
4. Prédicats et clauses ................................................................................................................. 9
5. Mécanisme de résolution ...................................................................................................... 10
Données en Prolog ............................................................................................................................ 13
1. Constantes ............................................................................................................................. 13
2. Variables ................................................................................................................................ 13
3. Structures .............................................................................................................................. 14
Opérateurs ........................................................................................................................................ 15
Unification ......................................................................................................................................... 17
Arithmétique ..................................................................................................................................... 19
Listes .................................................................................................................................................. 20
1. Définition ............................................................................................................................... 20
2. Opérations sur les listes ........................................................................................................ 21
La coupure (cut) ................................................................................................................................ 25
1. Principe .................................................................................................................................. 25
2. La négation comme échec ..................................................................................................... 29
Prédicats prédéfinis ........................................................................................................................... 31
1. Chargement, débogage et compilation des programmes..................................................... 31
2. Les prédicats d'entrée/sortie ................................................................................................ 31
3. Contrôle de la base de connaissance .................................................................................... 32
4. Tests sur le type d'un terme .................................................................................................. 35
5. Unification et comparaison des termes ................................................................................ 36
6. Construction et décomposition des termes .......................................................................... 37
7. Prédicats de contrôle ............................................................................................................ 38
8. Prédicats de second ordre : bagof, setof, findall................................................................... 38
Introduction au Prolog
2
Introduction
Prolog comme son nom l'indique est un langage de la programmation logique.
La programmation logique est un paradigme de programmation qui définit une application
comme un ensemble de faits et de règles logiques. Cet ensemble est ensuite exploité par un
moteur d'inférence pour répondre à des questions ou des requêtes.
Contrairement aux autres paradigmes de programmation telles que la programmation
impérative, fonctionnelle ou la programmation orientée objet, où les programmes sont des
suites d'instructions qui précisent comment résoudre un problème, la programmation logique est
considérée comme une programmation déclarative puisque les programmes y sont constitués
par des informations décrivant le problème à résoudre lui-même.
Le moteur d'inférence est un programme qui diffère d'un interpréteur ou d'un compilateur du
fait qu'il ne traite pas des instructions, mais utilise les informations contenues dans le code du
programme pour répondre à des questions.
Le Prolog est né aux besoins de pouvoir traiter la langue naturelle par ordinateur (Alain
Colmerauer et Robert A. Kowalski - 1970), mais son utilisation s'est étendue vers d'autres
domaines :
− Intelligence artificielle (Systèmes experts, …)
− Résolution symbolique des équations, Logique et mathématiques.
− Bases de données relationnelles.
− Compilation.
− Preuve des programmes.
Il existe différentes implémentations du Prolog:
− SWI Prolog (www.swi-prolog.org)
− GNU Prolog (http://gnu-prolog.inria.fr)
− Sicstus Prolog (www.sics.se/sicstus)
Dans cette partie du cours nous utiliserons SWI-Prolog et nous baserons essentiellement sur le
livre de référence :
Ivan Bratko : Prolog Programming for the Artificial Intelligence.
3rd Ed., Addison-Wesley
A. EL HARRAJ
3
Principe
• La programmation en Prolog consiste donc à décrire les entités (ou les objets) d'un problème
donné et les relations entre ces objets, et établir les règles qui les gèrent.
• Ainsi, dans un programme :
− On spécifie les faits.
− On déclare les règles.
Et pour exécuter ce programme :
− On pose des questions.
1. Les faits
• Les faits sont les hypothèses du problème posé qu'on suppose vraies. Ils décrivent les relations et
les propriétés des objets de ce problème. L'ensemble des faits constitue ce qu'on appelle une base
de connaissance.
• En Prolog, chaque fait est représenté par une clause. Une clause est constituée par un
identificateur de la relation correspondante au fait représenté, suivi entre parenthèses de la liste
ordonnée des objets en question et terminée par un point.
Par exemple, les faits suivants:
Cloé aime le chocolat.
Paul est un parent de Marc.
Paul est un parent de Cloé.
Socrate est une personne.
Cloé est une femme.
Le train de ligne numéro 12 relie Rabat et Fès.
Ali est un étudiant en master d'informatique.
L'étudiant Ali a eu 16 en java et s'est classé premier.
0! = 1.
peuvent être représentés en Prolog par les clauses suivantes :
aime(cleo,chocolat).
parent(paul,marc).
parent(paul,cloe).
personne(socrate).
femme(cloe).
train(train_ligne,12,rabat,fes).
etudiant(ali,master,informatique).
etudiant(ali,java,16,1).
factoriel(0,1).
• Les noms des relations et des objets (ou entités) utilisés doivent commencer par une lettre
minuscule.
• Si une relation à un seul argument, alors il s'agit d'une propriété (personne, femme).
• Plusieurs clauses peuvent correspondre à une même relation (parent). Ainsi une relation est
définie par l'ensemble des clauses correspondantes décrites dans le programme.
• D'autre part, on peut utiliser le même nom pour designer deux relations différentes à condition
qu'elles ne possèdent pas le même nombre d'arguments.
Dans l'exemple, on a défini deux relations avec le même identificateur 'etudiant'. Ces deux
relations sont différentes, du fait qu'elles n'ont pas le même nombre d'arguments.
A. EL HARRAJ
4
Exemple 1 :
On se propose d'écrire un programme qui décrit les faits contenus dans l'arbre généalogique
suivant :
Aude Paul
Marc Cloé
Lisa Dana
Abel
Remarque :
− L'extension d'un programme source en Prolog est 'pl'.
− Les commentaires s’écrivent entre /* et */ et % est utilisé pour les commentaires de fin
de ligne.
A. EL HARRAJ
5
2. Les questions
• Pour exécuter un programme, on le charge dans l'interpréteur du Prolog et on pose des questions.
• Pour charger un programme source dans l'interpréteur du Prolog on utilise la procédure :
consult(nom_programme).
A. EL HARRAJ
6
Après la première réponse, Prolog attend une entrée de l’utilisateur qui lui permettra de savoir s’il
continu à chercher les autres réponses ou arrêter la recherche :
− Un point virgule, lui permet de continuer
− Un point, lui demandera de s’arrêter
?- parent(paul,X).
X = cloe ;
X = marc.
• En Prolog, le point virgule signifie 'OU'. Ainsi, Prolog nous informe que la question posée est vraie
si :
X=cloe OU X= marc
• Pour répondre à cette question, Prolog essai de trouver parmi les clauses du programme, celles
qui peuvent être égales à la question posée. Il commence par comparer terme à terme (y compris
le nom de la relation) en commençant par le début du fichier programme. Si l'égalité est possible
en substituant la variable libre par une donnée alors cette dernière sera retournée en tant que
réponse. On parle alors d'unification : Prolog tente, au fait, d'unifier (de rendre égales) la question
et les clauses décrites dans le programme.
Autre exemple :
% Qui sont les femmes citées dans famille_1.pl ?
?- feminin(X).
X = aude ;
X = cloe ;
X = lisa ;
X = dana.
Notez que :
− Les identificateurs de variables doivent commencer par une lettre majuscule.
− La portée de la variable se limite à l’énoncé.
Pour répondre à cette question, Prolog commence par unifier le premier but :
parent(X,dana)
avec les clauses du programme.
Si l'unification réussit pour le premier, alors il sauvegarde la position, substitue les variables libres par
les données correspondantes réalisant l'unification :
A. EL HARRAJ
7
X=marc
dans le but, ce qui donne :
parent(marc,dana),masculin(marc)
le premier étant vrai, alors il est retiré de la liste et le nouveau but devient :
masculin(marc)
et passe à l'unification du nouveau second but.
Ce nouveau but ne contient pas de variable, il est soit vrai soit faux, dans les deux cas de figure, à la
fin de l'unification de ce second but, il retourne à la position sauvegardée lors de l'unification du
premier but pour continuer la recherche.
Autre exemple :
% Qui sont les filles de Paul ?
?- parent(paul,X), feminin(X).
X = cloe.
3. Les règles
3.1. Définition
• Les règles sont des relations qui sont établies à partir d’autres relations existantes. Leur utilisation
permet d’ajouter des faits qui peuvent être déduites d’autres faits.
Dans l’exemple famille_1.pl, la relation 'père de' peut être déduite des relations 'parent' et
'masculin'. Ainsi, il est inutile d'ajouter tous les faits décrivant cette relation, il suffit de définir une
règle qui permet de la définir.
En effet, la règle qui permet de définir la relation 'pere' est :
Pour tout X,Y parent(X,Y) ET masculin(X) ⇒ pere(X,Y)
La clause correspondante en Prolog s'écrit comme suit :
pere(X,Y) :- parent(X,Y), masculin(X).
• L'écriture se fait dans l'ordre inverse. Ainsi, le symbole ':-' représente '⇐' et la virgule représente
la conjonction ET.
On peut ajouter d'autres règles, à notre programme, par exemple les relations mere, enfant,
grand_parent, etc.
/* fichier : famille_2.pl */
/*---------------------FAITS ------------------*/
...
/*---------------------- REGLES ------------------*/
% R1 : pere(X,Y) -> X est le père de Y
pere(X,Y) :-
parent(X,Y),
masculin(X).
A. EL HARRAJ
8
• Le moteur d'inférence de Prolog utilise le 'OU' entre les clauses d'un programme. En effet, un but
(ou question) est réalisé s'il est unifiable avec l'une au moins des clauses du programme. D'autre
part, la consultation des clauses se fait dans l'ordre de début du fichier programme jusqu'à la fin si
nécessaire. Ceci, nous permet d'écrire la règle 'descendant' en 2 clauses comme suit :
descendant(X,Z) :- parent(Z,X).
descendant(X,Z) :-
parent(Z,Y),
descendant(X,Y).
Cette présentation est la plus couramment utilisée en Prolog. Elle rend le code beaucoup plus
lisible.
A. EL HARRAJ
9
4. Prédicats et clauses
• Les relations, les propriétés et les règles décrites en Prolog sont au fait des prédicats, puisque pour
une liste d'arguments donnés, elles sont soient vraies, soient fausses.
• Dans certains manuels, on utilise parfois le mot 'procédure' pour désigner une règle.
• Un prédicat (ou procédure) est caractérisé par un identificateur dit foncteur et le nombre
d'argument dit arité, et il est référencé par foncteur/arité (parent/2, feminin/1,…).
• Un prédicat est définit par l'ensemble des clauses dont l'en-tête est constitué du nom du prédicat
suivi entre parenthèses d'un nombre d'arguments égale à l'arité du prédicat.
• Deux prédicats différents peuvent avoir le même nom, mais dans ce cas ils ne devront pas avoir la
même arité.
• En Prolog, une clause est formée de 2 parties : l'en-tête (head) et le corps (body), sous la forme :
en_tete :- corps.
où le corps est une suite de buts séparés par des virgules représentants la conjonction ET.
− Les faits sont des clauses sans corps.
− Les questions sont des clauses sans en-tête.
− Les règles sont des clauses avec un en-tête et un corps non vide.
• Il n'est pas nécessaire de regrouper les clauses définissant un prédicat, mais il est préférable de le
faire pour assurer la lisibilité du programme.
Par ailleurs, Dans un programme, l'ordre des clauses ne change pas le sens déclaratif du
programme et il peut changer le sens procédural, autrement dit, la définition d'une relation ou
d'une règle est donnée par le contenu des ses clauses indépendamment de leur ordre, mais
l'utilisation de cette relation (ou de cette règle) par Prolog dépend de l'ordre des clauses dans le
programme :
Par exemple, les deux formulations suivantes de 'descendant' :
descendant_1(X,Z) :- parent(Z,X).
descendant_1(X,Z) :-
parent(Z,Y),
descendant_1(X,Y).
et
descendant_2(X,Z) :-
descendant_2(X,Y),
parent(Z,Y).
descendant_2(X,Z) :- parent(Z,X).
ont le même sens logique, et définissent bien la relation 'descendant', mais le mécanisme de
résolution du Prolog ne les traite pas de la même manière.
A. EL HARRAJ
10
5. Mécanisme de résolution
• Une question est une suite de un ou plusieurs buts et répondre à une question revient à satisfaire
ces buts.
• Satisfaire un but, c'est démontrer que ce but découle logiquement des faits et règles définis dans
le programme.
• Pour satisfaire un but simple, Prolog cherche du haut vers le bas (dans l'ordre) dans le fichier
programme une clause qui peut s'unifier avec le but, autrement dit :
− Il cherche une clause dont le foncteur est le même que celui du but.
− Puis il compare les arguments, en substituant éventuellement les variables par les objets
pour lesquels le but pourrait être satisfait.
− Si le but est satisfait, alors sa réponse serait 'true' (En cas où la question comporte des
variables, Prolog affiche les objets utilisés lors de la substitution pour satisfaire le but et
attend l'ordre de continuer ou d'arrêter la recherche d'autres objets pouvant satisfaire le
but).
− Si aucune clause ne permet de satisfaire le but, alors la véracité du but ne peut être
démontrée et la réponse du Prolog serait 'false'.
• Si une question est composée par plusieurs buts avec la conjonction ET (représentée par une
virgule), alors Prolog tente de satisfaire les buts un par un de gauche à droite.
− Si un but est satisfait, alors il est retiré de la liste après avoir remplacé dans les autres buts
les variables par les objets correspondants qui permettent de satisfaire ce but.
− Si un but échoue, alors Prolog retourne en arrière en rétablissant les buts originaux et tente
de trouver une autre alternative, jusqu'à ce que toutes les possibilités soient consultées.
− Si Prolog ne peut démontrer la véracité des buts, alors sa réponse sera 'false'.
Et posons la question :
?- descendant(dana,paul).
A. EL HARRAJ
11
1. Prolog cherche la première clause correspondante au but (celle dont l'en-tête correspond au but).
Il trouve la clause :
descendant(X,Z) :- parent(Z,X). (R5_a)
1.1. Il l'applique (R5_a) en substituant les variables X et Z par 'dana' et 'paul', le nouveau but
devient :
parent(paul, dana) (B1)
1.2. Il cherche à satisfaire ce nouveau but, donc à chercher les clauses correspondantes.
Puisqu'aucune clause ne s'unifie avec ce but, alors le but échoue.
Puisque le but échoue, Prolog revient au premier but (B0).
2. Prolog cherche alors une autre clause correspondante à (B0). Il trouve (R5_b).
descendant(X,Z) :- parent(Z,Y), descendant(X,Y). (R5_b)
Il applique la règle (R5_b) (de la même manière que dans l'étape 1.1), en substituant les variables.
Ce qui donne un nouveau but :
parent(paul, Y), descendant(dana,Y) (B2)
Ce dernier but étant composé de 2 buts, Prolog tente alors de les satisfaire dans l'ordre :
parent(paul, Y) (B2_1)
descendant(dana,Y) (B2_2)
2.1. la première clause dans le fichier qui correspond à (B2_1) est :
parent(paul, cloe)
Pour satisfaire (B2_1), l'unification force l'instanciation de Y par 'cloe' dans (B2).
parent(paul, cloe), descendant(dana,cloe)
La première partie de ce but est satisfaite, donc le nouveau but de vient :
descendant(dana,cloe) (B3)
2.1.1. La première clause correspondante à (B3) est (R5_a), Prolog l'applique et le nouveau but
devient :
parent(cloe,dana) (B3')
Or aucune clause ne correspond à ce but,
(B3') échoue, et Prolog revient en arrière et cherche une autre alternative pour satisfaire (B3).
2.1.2. La deuxième clause correspondante à (B3) est (R5_b), en l'appliquant on obtient le
nouveau but :
parent(cloe,Y), descendant(dana,Y) (B3")
Or la première partie de ce but échoue, du fait qu'aucune clause d'en-tête 'parent' ne
correspond à ce but.
Donc (B3") échoue, et par suite (B3) échoue.
Par suite, Prolog revient à (B2) et cherche une autre alternative pour satisfaire (B2_1).
2.2. La deuxième clause correspondante à (B2_1) est :
parent(paul, marc)
Ce qui donne après substitution des variables comme dans 2.1, un nouveau but :
descendant(dana,marc) (B4)
La règle (R5_a) est applicable, puisqu'elle correspond au but, ce qui donne un nouveau but :
parent(cloe,dana) (B4')
A. EL HARRAJ
12
descendant(dana,paul)
R5_a
R5_b
X = dana X = dana
Z = paul Z = paul
parent(paul,dana) parent(paul,Y)
descendant(dana,Y)
No
parent(paul,cloe) parent(paul,marc)
Y = cloe Y = marc
descendant(dana,cloe) descendant(dana,marc)
R5_a
R5_b
R5_a
X = dana X = dana X = dana
Z = cloe Z = cloe Z = marc
parent(cloe,dana) parent(cloe,Y) parent(marc,dana)
descendant(Y,dana)
No No Yes
• Le but du nœud racine est satisfait si au moins un nœud est libellé par 'Yes'.
• Durant la recherche, Prolog entre dans toutes le branches et si une branche échoue, Prolog
retourne en arrière : on dit que Prolog effectue un 'backtrack'.
A. EL HARRAJ
13
Données en Prolog
• La programmation en Prolog suit le principe de la programmation déclarative. Par ailleurs, il
n'effectue aucune différence entre les données du programme et le programme lui-même.
• La syntaxe du Prolog est relativement simple, il ne se base que sur la notion de 'terme'.
Ainsi, les clauses d'un programme qui représentent un fait, une règle, une question (ou contenant
des données) sont constituées de termes.
• Un terme est soit un terme simple ou un terme composé :
− Un terme simple : qui est une constante ou une variable.
− Un terme composé ou structure : formé par un foncteur et des arguments qui sont
aussi des termes
1. Constantes
• Une constante est un atome ou un nombre.
1.1. Nombres
• Un nombre est un entier ou un réel :
25, -110, 9.1, 0.1e+03, …
1.2. Atome
• On appelle atome l'une des données suivantes :
− Une chaine de caractères contenant des caractères alphanumériques et le trait de
soulignement '_' et commençant par une lettre minuscule :
parent, grand_parent, henri4, …
− Une chaine formée par des caractères spéciaux :
+, >, =, :-, …
− Une chaine de caractères entre apostrophes :
'Grand parent', 'Henri IV', …
2. Variables
• L'identificateur d'une variable est une chaine de caractères formée par des alphanumériques et le
tiret de soulignement :
X, Qui, U_01, _, _Var, _var, __
On appelle variable isolée (ou 'singleton variable'), toute variable qui apparait une seule fois dans
une clause. Une telle variable peut être remplacée par un tiret de soulignement '_', dit variable
anonyme.
Prenons par exemple, le prédicat 'a_un_enfant', défini par :
𝑋𝑋 𝑎𝑎 𝑢𝑢𝑢𝑢 𝑒𝑒𝑒𝑒𝑒𝑒𝑒𝑒𝑒𝑒𝑒𝑒 SI ∃ 𝑌𝑌 ∶ 𝑋𝑋 𝑒𝑒𝑒𝑒𝑒𝑒 𝑢𝑢𝑢𝑢 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝 𝑑𝑑𝑑𝑑 𝑌𝑌
ce qui donne en Prolog :
a_un_enfant(X) :- parent(X,Y).
Dans cette clause, la valeur de Y n'a aucune importance, il est par suite inutile de la nommée. En
Prolog on remplace de telle variable par la variable anonyme '_', ce qui donne :
a_un_enfant(X) :- parent(X,_).
Autre exemple : Dans le prédicat 'qq_a_un_enfant' (quelqu'un a un enfant) :
A. EL HARRAJ
14
qq_a_un_enfant :- parent(X,Y).
les deux variables sont isolées, on peut l'écrire alors comme :
qq_a_un_enfant :- parent(_,_).
Notez que cette relation est différente de :
qq_a_un_enfant :- parent(X,X).
• Dans SWI-Prolog, une erreur est signalée si une variable isolée n'est pas marquée. Pour marquer
une variable isolée, on utilise à sa place la variable anonyme, ou on lui donne un nom qui
commence par 2 tirets de soulignement '__' ou un tiret suivi d'une lettre majuscule, par exemple :
__var ou _Var
• L'utilisation de la variable anonyme dans une question, évite de faire apparaitre les valeurs
correspondantes dans la réponse du Prolog :
?- parent(X,_).
3. Structures
• Une structure ou terme composé est constituée par :
− un atome dit aussi foncteur, qui permet d'identifier la structure
− liste ordonnée de termes qui sont les arguments.
sous la forme :
nom_foncteur(arg1, arg2, …)
• Le nombre d'arguments est dit l'arité de la structure.
• Les structures définissent au fait une relation entre les arguments dont le nom est le foncteur.
Exemple :
date(1,1,2015).
point(a1,a2).
segement(point(1,2),point(1,3)).
etudiant(nom,prenom,date(j,m,a),cne).
• Une structure est généralement représentée par un arbre, dont la racine est le foncteur et les
branches sont les arguments.
etudiant
j m a
A. EL HARRAJ
15
en-tete :- corps
− L'en-tête est un terme
− ':-' est un atome
− Le corps est une suite de termes séparés par des virgules.
Opérateurs
• En Prolog, un opérateur est un atome, et une opération est un prédicat dont le foncteur est
l'opérateur :
% a+b est équivalent à +(a,b)
?- X = +(2,3).
X = 2+3.
• Par suite, toute expression est un terme qui est évalué suivant :
− L'ordre de priorité des opérateurs ou précédence des opérateurs.
− Le type des opérateurs : binaire, unaire préfixé ou unaire postfixé.
− L'associativité des opérateurs : gauche, droite ou aucune.
Par exemple, en SWI-Prolog :
+ est un opérateur binaire associatif à gauche :
2+3+5 ⟺ +(2+3, 5) ⟺ +(+(2,3), 5)
* est prioritaire sur + :
2*a+b*c ⟺ +(2*a, b*c) ⟺ +(*(2,a), *(b,c))
• En Prolog, on peut définir de nouveaux opérateurs ou redéfinir les opérateurs prédéfinis. Pour ce
on utilise la directive op/3 :
:- op(Précédence, Type, Nom)
− Nom : est un atome représentant le nom de l'opérateur.
− Précédence : est un entier, compris entre 1 et 1200, qui détermine l'ordre de priorité d'un
opérateur. La précédence la plus petite donne la priorité la plus élevée. Ainsi, la précédence
1 donne la plus haute priorité et la précédence 1200 donne la plus basse priorité.
− Type : détermine si un opérateur est infixé, préfixé ou suffixé ainsi que l'associativité de
l'opérateur.
Le tableau suivant énumère les valeurs que peut prendre l'argument Type :
Type Spécification
fx Préfixé, non associatif
xf Postfixé, non associatif
fy Préfixé, associatif
yf Postfixé, associatif
yfx Infixé, associatif à gauche
xfy Infixé, associatif à droite
xfx Infixé, non associatif
Autrement dit, le type de l'opérateur est donné par trois caractères 'f', 'x' et 'y', avec :
− Le caractère 'f' détermine la position de l'opérateur.
− 'x' représente un argument dont la précédence est strictement inférieure à celle de
l'opérateur.
− 'y' représente un argument dont la précédence est inférieure ou égale à celle de l'opérateur.
Sachant que :
A. EL HARRAJ
16
Ces clauses ont la même forme, ils contiennent les atomes suivants :
− 'aime' (verbe) : qui relie deux objets, le premier est un objet simple (nom) et le deuxième est
un objet composé (contenant 'et', des articles et des noms).
− la conjonction 'et' : qui relie deux objets contenant des articles et des noms.
− les articles (le, la, les) : applicables à un seul objet.
− des noms (salim, voyages, …).
On en déduit que les relations définies dans ces clauses sont
aime/2, et/2, le/1, la/1, les/1
et que les articles sont prioritaires sur la conjonction 'et', et que cette dernière est prioritaire sur la
relation 'aime'.
Ainsi on peut écrire ces clauses comme suit :
aime(salim, et( les(voyages), la(lecture) )).
aime(samir, et( le(chocolat), les(gateaux))).
aime(fahd, et( la(lecture) , le(cinema) )).
Les opérateurs à définir sont donc : aime, et, le, la, les.
− l'opérateur 'aime' est un opérateur binaire non associatif, puisqu'on ne peut pas écrire :
X aime A aime B
donc son type est xfx, et en lui donnant par exemple la précédence 500, sa définition sera :
:- op(500, xfx, aime).
− l'opérateur 'et' est binaire associatif. on lui donnera le type xfy et la précédence 400,
puisqu'il est prioritaire sur l'opérateur 'aime'.
:- op(400, xfy, et).
− Les opérateurs 'le', 'la' et 'les' sont des opérateurs unaires préfixés non associatifs (le le
A : n'a aucun sens), ils sont donc de type fx, on leur donne la précédence 300, puisqu'ils sont
prioritaires sur l'opérateur 'et'.
:- op(300, fx, la).
:- op(300, fx, le).
:- op(300, fx, les).
A. EL HARRAJ
17
Unification
• Dans son mécanisme de résolution Prolog utilise le principe d'unification :
Unifier deux termes consiste à les rendre égaux.
• L'unification de deux termes ne peut se faire que s'il y a une correspondance entre ces termes
(matching). Il faut donc que :
− Les deux termes soient identiques.
ou
− Les variables dans les deux termes peuvent être substituées par des objets de telle sorte que
les deux termes deviennent identiques.
• Prolog utilise l'opérateur '=' pour l'unification. Ainsi, si S et T sont deux termes :
S'il y a correspondance entre S et T, alors S = T est satisfait et unifie S et T.
Sinon S = T échoue.
Exemple :
% exemple d'unification qui réussit
?- date(Jour,10,An) = date(5, Mois, 2015).
Jour = 5
An = 2015
Mois = 10
?- a = b.
false.
ii. Si S est une variable libre (non instanciée) et T est autre qu'une variable libre, alors S=T réussit
et S est substituée par T.
?- T = a, S = T.
T = S, S = a.
A. EL HARRAJ
18
?- date(J,10,2015) = S.
S = date(J, 10, 2015).
iii. Si S et T sont tous les deux des variables libres, alors S=T réussit et S et T feront référence au
même objet dans la suite.
?- X = Y, X = a.
X = Y, Y = a.
?- date(1,2,2015)=date(X,X,2015).
false.
Exemple :
Quelle est la réponse du Prolog à la question suivante :
?- f(g(X,A,h(X,b)),Z) = f(g(A,b,Z),Y).
A. EL HARRAJ
19
Arithmétique
• Les expressions arithmétiques en Prolog sont construites à partir de nombres, de variables et
d'opérateurs arithmétiques et y sont considérées comme des structures.
• Les opérateurs de calcul prédéfinis en Prolog sont :
500 yfx + addition
500 yfx - soustraction
400 yfx * multiplication
400 yfx / division
400 yfx // division entire
400 yfx mod modulo
200 xfy ^ puissance
• Prolog définit aussi des opérateurs de comparaisons pour pouvoir comparer les valeurs des
expressions arithmétiques. Les opérateurs de comparaisons prédéfinis en Prolog sont tous de type
xfx et de précédence 700 :
X =:= Y Vrai si X et Y ont la même valeur
X =/= Y Vrai si X et Y sont différents
X < Y Vrai si X est inférieur strictement à Y
X =< Y Vrai si X est inférieur ou égal à Y
X > Y Vrai si X est supérieur strictement à Y
X >= Y Vrai si X est supérieur ou égal à Y
A. EL HARRAJ
20
Exemple : (extremum_1.pl)
Pour définir la relation
max(X, Y, Max)
où Max est le maximum de X et Y.
Nous avons :
− Si X >= Y alors Max = X.
− Si X < Y alors Max = Y.
d'où :
max(X, Y, X):-
X >= Y.
max(X, Y, Y):-
X < Y.
?- max(25,10,M).
M = 25 .
Listes
1. Définition
• Une liste en Prolog est une suite ordonnée de données entre crochets et séparées par des
virgules. Ces données peuvent être des constantes (atomes ou nombres) ou des termes composés,
par exemple :
[a,b,c,X,1,parent(f,g)]
est une liste qui contient des constantes, une variable et un terme composé.
• Comme toute autre donnée en Prolog, une liste est aussi un terme, qui est :
− soit la liste vide qui est représentée par [].
− soit une structure composée, dont le foncteur principale est '.' (le point), avec deux
arguments :
le premier argument est le premier élément de la liste dit tête (head)
le deuxième argument est la liste formée par le reste dit queue (tail).
• Ainsi, toute liste non vide est de la forme :
.(T,Q)
où T est une donnée et Q est une liste.
Exemple
?- L =.(a,.(b,.(c,[]))).
L = [a,b,c].
A. EL HARRAJ
21
?- L = [a,b|[c]].
L = [a, b, c].
% ou
?- L = [a,b,c|[]].
L = [a, b, c].
Remarque :
Le constructeur de listes [|] est le plus utilisé. Dans certaines implémentations du Prolog (comme
SWI-Prolog) l'opérateur '.' (point) n'est plus utilisé comme constructeur de listes, son utilisation
est réservé pour d'autres fins, surtout pour qualifier les champs d'une structure.
?- [a,b,c] = [T1,T2|R].
T1 = a,
T2 = b,
R = [c].
?- [X]=[1,2].
false.
?- cons(a,[b,c],L).
L = [a, b, c].
?- premier([a,b,c],X).
X = a.
?- reste([a,b,c], R).
R = [b, c].
• Le prédicat cons/3 peut être utilisé pour extraire le premier ou le reste de la liste :
?- cons(X, _, [a,b,c]).
X = a.
?- cons(_, R, [a,b,c]).
A. EL HARRAJ
22
R = [b, c].
?- longueur([a,b,c|[d]], N).
N = 4.
2.5. Appartenance
Pour que X soit un membre de la liste L, il suffit que l'une des conditions suivantes soit satisfaite :
− X est le premier de la liste L.
− X est un membre du reste de la liste.
Autrement dit :
L = [X|_] ou (L=[_|Reste] et X est un membre de Reste)
Si nous appelons ce prédicat 'membre', sa définition serait :
membre(X,[X|_]).
membre(X,[_|Reste]) :-
membre(X,Reste).
?- membre(b,[a,b,c]).
true .
?- membre(1,[a,b,c]).
false.
?- membre(X,[a,b,c]).
X = a ;
X = b ;
X = c ;
false.
2.6. Concaténation
Considérons la relation :
conc(L1, L2, L)
qui est vraie si L est composée par les éléments de L1 suivis par ceux de L2.
Pour définir cette relation, nous considérons les cas de L1 :
− Si L1 est vide alors L est égal à L2
− Si L1=[X|R1] alors L=[X|R] où R est la concaténation de R1 et L2.
ce qui donne :
conc([], L, L).
conc([X|R1], L2, [X|R]) :-
A. EL HARRAJ
23
conc(R1,L2,R).
?- conc([a,b,c],[1,2,3],L).
L = [a, b, c, 1, 2, 3].
• Cette relation peut réaliser plus que la concaténation de deux listes, par exemple :
i. Décomposition d'une liste :
?- conc(L1,L2,[a,b,c]).
L1 = [],
L2 = [a, b, c] ;
L1 = [a],
L2 = [b, c] ;
L1 = [a, b],
L2 = [c] ;
L1 = [a, b, c],
L2 = [] ;
false.
On peut, ainsi définir un prédicat qui permet de connaitre si S est une sous liste de L :
sous_liste(S,L) :-
conc(L1, L3, L),
conc(S, L2, L3).
En effet S est une sous liste de L si L peut être décomposée en trois listes L1, S et L2 (notez qu'il
s'agit d'une sous-liste et non d'un sous-ensemble, autrement dit l'ordre des éléments est pris en
compte).
ii. Chercher un motif dans la liste :
% chercher les mois avant et après mai
?- Mois = [jan,fev,mars,avr,mai,juin,jul,ao,sep,oct,nov,dec],
| conc(Avant,[mai|Apres],Mois).
Mois = [jan, fev, mars, avr, mai, juin, jul, ao, sep|...],
Avant = [jan, fev, mars, avr],
Apres = [juin, jul, ao, sep, oct, nov, dec] ;
false.
?- membre1(b,[a,b,c]).
true .
?- ajout(a,[b,c],L).
A. EL HARRAJ
24
L = [a, b, c].
suppr/3 peut être utilisée dans le sens inverse pour ajouter un élément.
?- suppr(a, L, [1,2,3]).
L = [a, 1, 2, 3] ;
L = [1, a, 2, 3] ;
L = [1, 2, a, 3] ;
L = [1, 2, 3, a] ;
false.
Nous remarquons, que Prolog nous donne toutes les possibilités d'insertion.
On peut, ainsi, définir le prédicat insert/3, qui permet d'insérer un élément n'importe où dans une
liste.
% insert(X, L, L1) : insert X dans L pour obtenir L1
insert(X, L, L1) :- suppr(X, L1, L).
Du fait que suppr(X, L, L1) échoue si X n'est pas un élément de L, on peut utiliser cette
relation pour définir une autre version de du prédicat membre/3.
membre2(X, L) :- suppr(X, L, _).
Remarque :
En SWI-Prolog 7.2.0, les chaines de caractères forment un type à part nommé string, et ne sont
pas considérées comme des listes. Cependant, on peut revenir à la configuration traditionnelle en
exécutant le but suivant :
set_prolog_flag(double_quotes, codes).
A. EL HARRAJ
25
La coupure (cut)
1. Principe
• Prolog effectue un retour en arrière (backtracking) si nécessaire pour satisfaire un but. Pour éviter
le backtracking, qui dans certain cas n'est pas nécessaire, on utilise le cut (la coupure).
Exemple :
Considérons la relation f(X,Y) définie par les trois règles suivantes :
R1 : Si X < 3 alors Y = 0.
R2 : Si 3 =< X et X < 6 alors Y = 2.
R3 : Si 6 =< X alors Y =4.
Ce qui donne en Prolog :
/* fichier : cut_1.pl */
% relation f(X,Y)
f(X,0) :- X < 3. % R1
f(X,2) :- 3 =< X, X < 6. % R2
f(X,4) :- 6 =< X. % R3
Evidemment ce but échoue, puisque la seule valeur possible pour Y pour satisfaire f(1,Y) est 0 qui
n'est pas supérieur à 2. Mais, en Prolog si une règle échoue alors il cherche les autres possibilités, qui
sont dans notre cas inutiles.
L'arbre de résolution dans ce cas est donnée par :
f(1,Y), Y > 2
R1 R2 R3
X ← 1 X ← 1 X ← 1
Y ← 0 Y ← 0 Y ← 0
1 < 3, 0 > 2 3 =< 1, 1 < 6, 2 > 2 6 =< 1, 4 > 2
0 > 2 No No
No
A. EL HARRAJ
26
Pour éviter que Prolog teste les autres règles applicables, on utilise un pseudo but représenté par un
point d'exclamation '!', qui l'informe qu'il doit arrêter la recherche des autres alternatives même si
l'un des sous buts suivants échoue.
Notre programme devient :
/* fichier : cut_2.pl */
f(X,0) :- X < 3, !. % R1
f(X,2) :- 3 =< X, X < 6. % R2
f(X,4) :- 6 =< X. % R3
De même, on remarque que si la règle R2 échoue alors il est inutile d'appliquer la règle R3. On
introduit donc un 'cut' au niveau de la règle R2, ce qui donne :
/* fichier : cut_3.pl */
% relation f(X,Y)
f(X,0) :- X < 3, !. % R1
f(X,2) :- 3 =< X, X < 6, !. % R2
f(X,4) :- 6 =< X. % R3
En fin, remarquons que certains tests dans notre programme sont inutiles. En effet, pour certaines
valeurs de X, le programme teste si X < 3 puis teste si 3 =< X, par exemple :
[trace] ?- f(7,Y).
Call: (7) f(7, _G2574) ? creep
Call: (8) 7<3 ? creep
Fail: (8) 7<3 ? creep
Redo: (7) f(7, _G2574) ? creep
Call: (8) 3=<7 ? creep % test inutile
Exit: (8) 3=<7 ? creep
Call: (8) 7<6 ? creep
Fail: (8) 7<6 ? creep
Redo: (7) f(7, _G2574) ? creep
Call: (8) 6=<7 ? creep % test inutile
Exit: (8) 6=<7 ? creep
Exit: (7) f(7, 4) ? creep
Y = 4.
Ici, le but '7<3' échoue, alors le but '3=<7' réussi et donc il est inutile de le tester. De même, le but '7<6' échoue, ce qui le
test '6=<7' inutile.
A. EL HARRAJ
27
[trace] ?- f(7,Y).
Call: (7) f(7, _G3634) ? creep
Call: (8) 7<3 ? creep
Fail: (8) 7<3 ? creep
Redo: (7) f(7, _G3634) ? creep
Call: (8) 7<6 ? creep
Fail: (8) 7<6 ? creep
Redo: (7) f(7, _G3634) ? creep
Exit: (7) f(7, 4) ? creep
Y = 4.
• D'autre part, le cut n'affecte que la règle où il est présent. Par exemple, si A, B, C,... sont des
termes tels que :
C :- P, Q, R, !, S, T, U.
...
A :- B, C, D.
Alors :
− Le cut affecte l'exécution de C : le backtracking est possible pour P, Q, R, il est aussi
possible pour S, T, U, mais n'est pas possible entre S et R.
− Le cut présent dans C n'affecte pas l'exécution de A : le backtracking est possible entre B, C et
D.
A. EL HARRAJ
28
Pour trouver le maximum par exemple de 3 et 5, Prolog effectuera des tests inutiles :
[trace] ?- max(3,5,M).
Call: (7) max(3, 5, _G2755) ? creep
Call: (8) 3>=5 ? creep
Fail: (8) 3>=5 ? creep
Redo: (7) max(3, 5, _G2755) ? creep
Call: (8) 3<5 ? creep % test inutile
Exit: (8) 3<5 ? creep
Exit: (7) max(3, 5, 5) ? creep
M = 5.
Ce qui donne :
[trace] ?- max3(3,2,M).
Call: (7) max3(3, 2, _G3324) ? creep
Call: (8) 3>=2 ? creep
Exit: (8) 3>=2 ? creep
Exit: (7) max3(3, 2, 3) ? creep
M = 3.
A. EL HARRAJ
29
En effet :
[trace] 9 ?- membre(2, [2,3,4,2,1]).
Call: (7) membre(2, [2, 3, 4, 2, 1]) ? creep
Exit: (7) membre(2, [2, 3, 4, 2, 1]) ? creep
true ; % Devrait s'arrêter ici
Redo: (7) membre(2, [2, 3, 4, 2, 1]) ? creep
Call: (8) membre(2, [3, 4, 2, 1]) ? creep
Call: (9) membre(2, [4, 2, 1]) ? creep
Call: (10) membre(2, [2, 1]) ? creep
Exit: (10) membre(2, [2, 1]) ? creep
Exit: (9) membre(2, [4, 2, 1]) ? creep
Exit: (8) membre(2, [3, 4, 2, 1]) ? creep
Exit: (7) membre(2, [2, 3, 4, 2, 1]) ? creep
true ;
Redo: (10) membre(2, [2, 1]) ? creep
Call: (11) membre(2, [1]) ? creep
Call: (12) membre(2, []) ? creep
Fail: (12) membre(2, []) ? creep
Fail: (11) membre(2, [1]) ? creep
Fail: (10) membre(2, [2, 1]) ? creep
Fail: (9) membre(2, [4, 2, 1]) ? creep
Fail: (8) membre(2, [3, 4, 2, 1]) ? creep
Fail: (7) membre(2, [2, 3, 4, 2, 1]) ? creep
false.
Ce qui donne :
[trace] ?- membre3(2, [2,3,4,2,1]).
Call: (7) membre3(2, [2, 3, 4, 2, 1]) ? creep
Exit: (7) membre3(2, [2, 3, 4, 2, 1]) ? creep
true.
A. EL HARRAJ
30
aime(sara, X) :- animal(X).
La première règle veut dire que :
Si le but serpent(X) réussi Alors
aime(sara, X) échoue (puisque fail échoue).
Ne chercher plus à satisfaire aime(sara, X) (puisqu'il y a une coupure).
• Cette méthode, nous permet de définir la relation :
different(X,Y)
qui est satisfaite si X est différent de Y.
En effet :
X est différent de Y si X = Y échoue.
ce qui donne :
different(X,Y) :- X=Y, !, fail.
different(X,Y).
• Le prédicat not peut être utilisé comme alternative dans les relations aime et different :
aime(sara,X) :-
animal(X),
not (serpent(X)).
En insérant le cut :
p:- a, !, b.
p:- c. ⇔ (a ET b) OU (NON(a) ET c) ⇒ p
Et si on change l'ordre :
p:- c.
p:- a, !, b. ⇔ c OU (a ET b) ⇒ p
A. EL HARRAJ
31
Prédicats prédéfinis
• Quelques prédicats sont prédéfinis dans le langage et permettent aux programmeurs d'utiliser des
fonctionnalités standards, comme de l'évaluation numérique, les entrée/sortie, les fonctionnalités
de l'interface graphique ou la communication avec le système d'exploitation.
Parmi ces prédicats prédéfinis, on trouve :
• Durant l'exécution d'un thread seulement deux flux sont actifs : current_input et
current_output. Au début ils correspondent à user. Pour modifier le flux courant on utilise les
prédicats prédéfinis see et tell.
A. EL HARRAJ
32
• Les prédicats :
seen et told
ferment respectivement le flux d'entrée et de sortie pour revenir aux flux standard user.
• Les prédicats standards de lecture et d'écriture sont read/1 et write/1.
Exemple :
Exemple de saisie d'une valeur au clavier.
/*
Fichier : cube.pl
lit une valeur suivi d'un point et affiche son cube
Pour arrêter le programme entrez : stop.
*/
cube :-
read(X), % lit une valeur
process(X). % exécute une procédure avec X
process(N) :-
C is N*N*N, % calcul le cube
write(C), % affiche le résultat
cube. % attend une autre entrée
• Prolog offre aussi d'autres prédicats d'entrée/sortie tels les prédicats de lecture/écriture de
caractère ou de formatage, on trouve par exemple :
nl Retour à la ligne
tab(N) N espaces
A. EL HARRAJ
33
• Dans certaines implémentations du Prolog, pour pouvoir ajouter ou supprimer une clause, il faut
déclarer le prédicat correspondant comme dynamique par la directive :
:- dynamic pred1/arité1, pred2/arité2,….
Exemple :
Considérons le programme suivant :
/* fichier : meteo.pl */
% déclaration des prédicats dynamiques
:- dynamic
soleil, pluie, brouillard.
% Définition de certaines règles
beau :-
soleil, \+ pluie.
bizzard :-
soleil, pluie.
mauvais :-
pluie, brouillard.
% temps actuel
pluie.
brouillard.
Pour ce programme, on peut changer les données du temps actuel, en ajoutant ou en retranchant
dynamiquement l'un des prédicats soleil, pluie et brouillard.
?- beau.
false.
?- mauvais.
true.
?- retract(brouillard).
true.
?- mauvais.
false.
?- assert(soleil).
true.
?- bizzard.
true.
• Pour ajouter une règle, on doit la passer à assert comme un seul argument. Pour ce, si la règle
contient il faut la mettre entre parenthèses.
Exemple :
Considérons le programme suivant :
/* fichier : plusrapide.pl */
% déclaration des prédicats dynamiques
:- dynamic
rapide/1, lent/1.
A. EL HARRAJ
34
rapide(lapin).
lent(tortue).
lent(escargot).
permet d'ajouter la règle plusrapide/2 au programme. Remarquez que la règle est écrite entre
parenthèses pour qu'elle soit considérée comme un seul argument.
On peut alors poser la question suivante :
?- plusrapide(A,B).
A = lapin,
B = tortue ;
A = lapin,
B = escargot.
• retract est non-déterministe, une seule clause avec rectract peut effacer plusieurs clauses, par
le procédé du backtracking.
Exemple :
Avec l'exemple précédent :
?- retract(lent(X)).
X = tortue ;
X = escargot.
• Une application usuelle et efficace du prédicat assert est la sauvegarde des résultats
intermédiaires en vue d'une utilisation ultérieure.
Exemple :
/* fichier : table_multi.pl */
?- produit(X,Y,15).
X = 3,
Y = 5 ;
X = 5,
Y = 3.
A. EL HARRAJ
35
?- X=5, var(X).
false.
Exemple :
On se propose de calculer le nombre d'un atome dans une liste. Pour ce, on définit le prédicat :
count(A,L,N)
qui est vrai si N est le nombre d'occurrences de l'atome A dans la liste L.
L'algorithme de ce prédicat est donné par :
− Si L = [], alors N=0 pour tout A.
− Si L = [A|R] alors N=Nr+1 où Nr est le nombre d'occurrences de A dans R.
− Si L = [X|R] avec X=A, alors N est le nombre d'occurrences de A dans R.
Ce qui donne en Prolog :
/* fichier : count.pl */
count(_, [], 0).
count(A, [A|R], N) :- !,
count(A, R, Nr),
N is Nr + 1.
count(A, [_|R], N) :-
count(A, R, N).
Dans la deuxième application de count, le résultat est erroné (on devrait avoir N=1). Ceci vient du fait
que Prolog ait instancié X et Y par a puisqu'elles sont des variables libres.
Nous devrons donc nous assurer que pour comptabiliser un élément, il faut bien qu'il soit un atome
égal à l'atome cherché. Soit :
A. EL HARRAJ
36
count1(A, [B|R], N) :-
atom(B), A = B, !,
count1(A, R, Nr),
N is Nr + 1.
count1(A, [_|R], N) :-
count1(A, R, N).
13 ?- f(a,b) \= f(a,X).
false.
?- 5 =\= 3.
true.
?- f(a,b) == f(a,b).
true.
?- f(a,b) == f(a,X).
false.
• Prolog établi un ordre entre les différents types de termes de la manière suivante :
Entre les termes de types différents l'ordre est :
Variables < Nombres < Atomes < Termes composés
Les variables sont triées par adresse.
Les nombres entiers ou réels sont comparés par valeurs
Les atomes sont comparés avec l'ordre alphabétique.
Pour les termes composés, on commence par comparer les arités, puis les foncteurs
principaux par ordre alphabétique enfin on compare les arguments un par un en
commençant par le plus à gauche.
• Pour tester cet ordre on utilise les opérateurs suivants :
@< @=< @> @>=
A. EL HARRAJ
37
?- functor(T, f, 2).
T = f(_G1460, _G1461).
arg/3 :
T est un terme, N est un entier entre 1 et l'arité du terme T.
arg(N, T, X)
ce prédicat unifie X avec le Nième argument de T.
?- arg(2, f(X, t(a), t(b)), Y).
Y = t(a).
=../3 :
=.. est un opérateur nommé 'univ'.
T =.. L Vrai si L est la liste formée par le foncteur principal de T suivi de ses
arguments
?- f(a,b,2) =.. L.
L = [f, a, b, 2].
?- a+b*c =.. L.
L = [+, a, b*c].
?- T =.. [f,a,b].
T = f(a, b).
name/2 :
name(A, L) L est la liste des codes ASCII des caractères formant l'atome A.
?- name(rectangle,L).
L = [114, 101, 99, 116, 97, 110, 103, 108, 101].
A. EL HARRAJ
38
7. Prédicats de contrôle
Les prédicats prédéfinis suivant permettent le contrôle d'exécution :
Exemple :
/* fichier : dosquares.pl */
% lit une sequence de nombres et affiche leurs carrées
% stop arrete l'execution.
dosquares :-
repeat,
read(X),
( X = stop, !
;
Y is X*X, write(Y),
fail
).
Produit aussi une liste des solutions comme bagof, sauf que la
setof(X,P,L)
liste L obtenue est triée et elle est sans doublons.
Exemple :
On considère la base de connaissances suivantes :
/* fichier : ages.pl */
age(ali,7).
age(mohamed,5).
age(zineb,8).
age(amina,5).
age(zouhair,7).
A. EL HARRAJ
39
% regroupement des enfants de même âge dans des listes triées avec setof.
?- setof(Enfant, age(Enfant,Age), L).
Age = 5,
L = [amina, mohamed] ;
Age = 7,
L = [ali, zouhair] ;
Age = 8,
L = [zineb].
• Pour collecter les informations indépendamment d'une ou plusieurs variables avec bagof ou
setof, on libère ces variables en les combinant avec le prédicat de recherche en utilisant
l'opérateur '^', comme suit :
bagof(X, X1^X2^…Xn^P, L)
donne la liste L formée par les objets X vérifiant P, indépendamment des variables X1, …, Xn.
% liste des enfants indépendamment de leur âge.
?- bagof(Enfant, Age ^ age(Enfant,Age), L).
L = [ali, mohamed, zineb, amina, zouhair].
A. EL HARRAJ