listes.pl : test de prolog / P. TRAU


/* listes.pl : test de prolog /P. TRAU janvier 2001 */
/* on trouvera ici 5 paragraphes :
    - les prédicats listes prédéfinis (en SWI Prolog)
    - des règles générales sur les listes
    - l'application des listes aux ensembles
    - listes ordonnées (avec relation d'ordre, ce qui permet par exemple de trier)
    - listes d'entiers
*/

/* prédicats prédéfinis (d'apres help(3-24) *************************
 *
 * member(Elem,Liste) dit si Elem appartient à la Liste, ou en ressort les éléments
 * append(Liste1,Liste2,ListeResult) concatène deux listes (ou coupe si les premières sont libres et result lié)
 * delete(ListeAvecDesX,X,ListeResultSansAucunX) enlève tous les X d'une liste
 * select(ListeAvecAuMoinsUnX,X,ListeResultAvecUnXDeMoins) enlève UN seul X de la liste 
 *   (faux s'il y en avait aucun). Peut aussi servir à insérer X si 1er arg libre et dernier lié
 * reverse(Liste,ListeDansLOrdreInverse)
 * last(DernierElemDeLaListe,Liste)
 * nth1(CombienTieme,Liste,ElemDeLaListe) le 1er est noté 1
 * nth0(CombienTieme,Liste,ElemDeLaListe) le 1er est noté 0
 * length(Liste,NbDElementsDeLaListe)
 * sort(ListeMelangee,ListeTriee) les doublons ne sont pris qu'une fois
 * msort(ListeMelangee,ListeTriee) les doublons sont gardés
 *
 */

/********************************************************************
 * règles générales sur listes                                      */ 

/* member(Elem,Liste) est prédéfini et fait la même chose 
 * Si Elem est libre, rendra successivement tous les éléments de la liste */
membre(Elem,[Elem|_]).
membre(Elem,[_|Suivants]):-membre(Elem,Suivants).

/* étendons le à plusieurs membres (pas nécessairement contigus)
 * membres(PetiteListe,GrandeListe) : les deux listes doivent être liées ! */
membres([Elem|AutresElem],Liste):-membre(Elem,Liste),membres(AutresElem,Liste).
membres([],_).

/* append est prédéfini et fait la même chose. Mais j'aime bien tout réécrire
 * ceci permet de coller deux listes l'une derrière l'autre, en une troisième.
 * un but rigolo : colle(X,Y,[a,b,c]). (il y a en tout 4 solutions) */
colle([X|SuiteDesX],ListeDesY,[X|SuiteDesXpuisLesY]) :- colle(SuiteDesX,ListeDesY,SuiteDesXpuisLesY).
colle([],Liste,Liste).

/* colle peut également servir à isoler le dernier d'une liste, et récupérer 
 * la liste restante : */
coupe_dernier(ListeRestante,Dernier,ListeInitiale):-colle(ListeRestante,[Dernier],ListeInitiale).
/* ou vérifier si une sous-liste est au début ou la fin d'une liste : */
debut(SousListe,Liste):-colle(SousListe,_,Liste). 
final(SousListe,Liste):-colle(_,SousListe,Liste). 

/* pour ajouter un élément en tête de liste
 * append([X],Liste,ListeResult) peut être utilisé aussi */
ajoute(X,Liste,[X|Liste]). /* le troisième arg est la liste augmentée de X  */

/* ma version de reverse qui inverse l'ordre */
inverse([Prem|Reste],Resultat) :- inverse(Reste,Tampon),colle(Tampon,[Prem],Resultat).
inverse([],[]).

/* dit si tous les éléments de la liste sont différents */
tous_differents([]). %ou même tous_differents([_]). 
tous_differents([T|Q]):- not(membre(T,Q)),tous_differents(Q).

/* celui-là rend une liste (plus courte) où tous sont différents */
virer_doublons([Tete|Queue],ResultSansDoublons):-
	membre(Tete,Queue),virer_doublons(Queue,ResultSansDoublons).
virer_doublons([Tete|Queue],[Tete|ResultSansDoublons]):-
	not(membre(Tete,Queue)),virer_doublons(Queue,ResultSansDoublons).
virer_doublons([],[]).

/* supprime(ElemXASupprimer,ListeInitiale,ListeSansX)
 * si l'elem à supprimer n'était déja pas présent, la liste est retournée telle quelle  */
supprime(X,[X|ResteListeInitiale],ListeSansX) :- 
	supprime(X,ResteListeInitiale,ListeSansX).
supprime(X,[PasX|ResteListeInitiale],[PasX|ListeSansX]) :- 
	X\=PasX,supprime(X,ResteListeInitiale,ListeSansX).
supprime(_,[],[]).

/* supprime_plusieurs(ListeElemASupprimer,ListeInitiale,ListeRacourcie) */
supprime_plusieurs([Prem|Autres],ListeInitiale,ListeRacourcie) :- 
	supprime(Prem,ListeInitiale,ListeUnPeuRacourcie),
	supprime_plusieurs(Autres,ListeUnPeuRacourcie,ListeRacourcie).
supprime_plusieurs([],ListeInitiale,ListeInitiale).

/* permutation(ListeInitiale,LesMemesDansUnAutreOrdre).
 * si le deuxière arg est libre, donne toutes les permutations possibles.
 * Attention la listeinitale est aussi une des solutions */
permutation(ListeInitiale,[T|Result]):-select(ListeInitiale,T,Tmp),permutation(Tmp,Result).
permutation([],[]).

/* write écrit : [a, b, c, d], writef écrit : abcd et aff_liste écrit : [ a b c d ] */
aff_elements_de_liste([T|Q]) :- write(T), write(' '),aff_elements_de_liste(Q).
aff_elements_de_liste([]).
aff_liste(L) :- write('[ '),aff_elements_de_liste(L),write(']'),nl.

/*************************************************************************
 * version ensembles                                                     */

appartient_a(X,Ensemble):-membre(X,Ensemble).

inclus(PetitEns,GrandEns):-membres(PetitEns,GrandEns).

/* union existe déjà et fait pareil */
unir(E1,E2,E1uE2):-colle(E1,E2,Temp),virer_doublons(Temp,E1uE2).

complement(Ens,Espace,Cmplt):-supprime_plusieurs(Ens,Espace,Cmplt).

inter([T1|Q1],E2,[T1|Result]):-appartient_a(T1,E2),inter(Q1,E2,Result).
inter([T1|Q1],E2,Result):-not(appartient_a(T1,E2)),inter(Q1,E2,Result).
inter([],_,[]).

/*************************************************************************
 * cas des listes ordonnées (avec les opérateurs =< et >)                */

/* je définis la relation d'ordre (si on veut traiter autre chose que des entiers) */
superieur(X,Y):-X>Y.
inferieur_ou_egal(X,Y):-not(superieur(X,Y)). /* ou alors =< (et pas <=) */

/* vérifie si la liste est triée */
est_trie([_]).
est_trie([X,Y|Suite]):- inferieur_ou_egal(X,Y),est_trie([Y|Suite]).

/* une solution pour trier est de tester toutes les permutations et de ne garder
 * que celles bien triées (évidement pas très efficace comme solution, 
 * ne pas dépasser une dizaine d'éléments). */
tri_debile(Liste,Resultat):-permutation(Liste,Resultat),est_trie(Resultat).

/* autre tri : par insertion : je sors le 1er, je trie le reste (récursivement)
 * puis je l'insère au bon endroit (devant le 1er plus grand).
 * Avant tout, je définis : insertion(Elem,SuiteOrdonnée,SuiteOrdonnéeAvecElem) */
insertion(Elem,[Prem|Suite],[Elem,Prem|Suite]):-inferieur_ou_egal(Elem,Prem).
insertion(Elem,[Prem|Suite],[Prem|Resultat]):-superieur(Elem,Prem),insertion(Elem,Suite,Resultat).
insertion(Elem,[],[Elem]).
tri_insertion([Prem|Suite],Resultat):-tri_insertion(Suite,Tmp),insertion(Prem,Tmp,Resultat).
tri_insertion([],[]).

/* trier la liste : je prends le premier, je coupe la liste en ceux plus petits et ceux
 * plus grands que je trie, puis je recolle les deux. (algorithme QuickSort) 
 * Les 3 premières règles (clauses) sont pour couper.
 * exemple de test : trier([1,4,6,2,8,4,3,6,1,0,9],X). */
couper_liste([Prem|Reste],Seuil,[Prem|ListeDesPetits],ListeDesGrands) :-
  	inferieur_ou_egal(Prem,Seuil),
        couper_liste(Reste,Seuil,ListeDesPetits,ListeDesGrands).
couper_liste([Prem|Reste],Seuil,ListeDesPetits,[Prem|ListeDesGrands]) :-
  	superieur(Prem,Seuil),
	couper_liste(Reste,Seuil,ListeDesPetits,ListeDesGrands).
couper_liste([],_,[],[]).
trier([Prem|Reste],Resultat) :-
	couper_liste(Reste,Prem,ListeDesPetits,ListeDesGrands),
	trier(ListeDesPetits,PetitsEtTries),
        trier(ListeDesGrands,GrandsEtTries),
	colle(PetitsEtTries,[Prem|GrandsEtTries],Resultat).
trier([],[]).

/* fusionner deux listes déja triées par ordre croissant */
fusion([T1|Q1],[T2|Q2],[T1|Qres]):-inferieur_ou_egal(T1,T2),fusion(Q1,[T2|Q2],Qres). 
fusion([T1|Q1],[T2|Q2],[T2|Qres]):-superieur(T1,T2),fusion([T1|Q1],Q2,Qres).
/* cas ci-dessous inutile si j'ai prévu l'égalité plus haut ( =< au lieu de < )
 * mais l'écriture me plaisait (tête à deux éléments)
 * fusion([Tidem|Q1],[Tidem|Q2],[Tidem,Tidem|Qres]):-fusion(Q1,Q2,Qres).  */
fusion([],L2,L2).
fusion(L1,[],L1).

/* rechercher le plus grand : max(Liste,X) */
max([Prem|Suite],X) :- max(Suite,X),superieur(X,Prem).
max([Prem|Suite],Prem) :- max(Suite,X),not(superieur(X,Prem)).
max([X],X).

min([Prem|Suite],X) :- min(Suite,X),not(superieur(X,Prem)).
min([Prem|Suite],Prem) :- min(Suite,X),superieur(X,Prem).
min([X],X).

/***************************************************************************************
 * cas particulier des listes d'entiers                                                */

/* pour commencer, rappel sur les opérateurs arithmétiques :
 * attention :  bien faire la différence entre is, = et =:=
 * - is calcule la valeur à droite (toujours liée) et instancie le résultat à la variable (libre)
 * à gauche,  ou la compare à la val liée de gauche. par ex X is 10+15 met 25 dans X, ou dit True
 * si X vaut 25 (parce que avant on a dit X is 30-5 ou même X=25)
 * - = recopie l'équation avec les variables instanciées : Y=10,X=Y+15,write(X). affiche 10+15
 * mais on pourra toujours tester X par =:= (ou même is, si il est à droite du is).
 * Au moins un des arguments doit être lié.
 * - =:= évalue à droite et à gauche et regarde si les résultats sont égaux (les deux arguments doivent être liés)
 * les opérateurs de comparaison (qui évaluent à droite et à gauche) sont  : 
 *  =:=  =\=  <  >  =<  >= , les autres opérateurs sont + - * / mod ** , sin,cos,log .... (voir help(3-22).) */

/* evalue([1,2,3,4],X) retourne X=1234. */
evalue(ListeChiffres,Resultat) :-
	append(TousSaufDernier,[Dernier],ListeChiffres),
	evalue(TousSaufDernier,X),
	Resultat is Dernier + 10*X.
evalue([X],X).

somme([T|Q],Result):-somme(Q,Tmp),Result is T+Tmp.
somme([],0).


Patrick TRAU, ULP - IPST janvier 2002

retour au cours Prolog    retour