7. Exercices corrigés et raisonnements
7.1 Quatre problèmes d’unification
a. f(a,[Z|R]) = f(X,[1,2,3])
Les deux termes ont le même foncteur f/2. Le premier argument impose X=a; le second impose une tête 1 et une queue [2,3]. Résultat : X=a, Z=1, R=[2,3].
b. [L|R] = f(X,g(h(y)))
La structure d’une liste non vide est incompatible avec celle du terme f/2. Résultat : faux, indépendamment de la possibilité de remplacer les arguments internes.
c. L=[V1/O1,V2/O2,X], V1=V2, V2=3, X=1/x, O2=o
Les variables V1 et V2 sont liées entre elles, puis à 3; cette liaison se propage aux deux positions de L. X=1/x reste un terme symbolique, sans division; O2=o précise sa position correspondante. Résultat :
Code (text) · sélectionner pour copier
L = [3/O1, 3/o, 1/x]
V1 = V2 = 3
X = 1/x
O2 = o
O1 reste libre
d. g(x,h(y,59)) = [X|Y]
Même obstacle structurel que pour b : faux. Unifier ne consiste pas à distribuer arbitrairement les composants d’un terme dans une liste.
7.2 Intersection de deux listes
Contrat : inters(L1,L2,LR) produit les éléments de L1 présents dans L2. Exemple officiel actuel :
Code (prolog) · sélectionner pour copier
?- inters([a,12,f,5],[w,a,5,34,12,e],X).
X = [a,12,5].
La vidéo signale une erreur dans une ancienne version de cet exemple; le PDF téléchargé le 16 octobre donne le résultat corrigé ci-dessus.
Le résultat est toujours une liste. Sans élément commun, il faut [], et non 0; deux listes identiques peuvent donner la liste entière. Les positions des éléments dans les deux listes n’ont pas à correspondre : 12 est commun même s’il n’est pas au même indice. On parcourt L1 et, pour chaque tête, on vérifie son appartenance à toute L2.
Code (prolog) · sélectionner pour copier
inters([],L2,[]).
inters([X|L1],L2,[X|LR]) :-
member(X,L2), inters(L1,L2,LR).
inters([X|L1],L2,LR) :-
not(member(X,L2)), inters(L1,L2,LR).
Trois cas : la première liste est vide; sa tête appartient à L2 et est gardée; ou sa tête n’y appartient pas et est ignorée. Dans les deux cas non vides, poursuivre sur la queue de L1. La deuxième liste sert de référence et reste inchangée.
Trace de l’exemple : garder a, garder 12, rejeter f, garder 5, puis terminer sur le vide. L’ordre du résultat suit donc L1.
Variante par si-alors-sinon, également fournie :
Code (prolog) · sélectionner pour copier
inters([],L2,[]).
inters([X|L1],L2,LR) :-
( member(X,L2)
-> LR=[X|LRbis], inters(L1,L2,LRbis)
; inters(L1,L2,LR)
).
Ces deux définitions sont des alternatives, à tester séparément sous ce nom, plutôt que des clauses à cumuler. Le test décide si la tête doit apparaître dans le résultat. Les appels récursifs traitent la queue dans les deux branches.
Sur les données fixées des exercices, le mécanisme est direct. Pour comprendre ses limites : member/2 unifie et peut donc lier des variables libres; not(...) vérifie l’échec au moment de l’appel. Des doublons de L1 sont conservés; des doublons de L2 peuvent produire plusieurs preuves dans la première version, tandis que -> s’engage sur le premier succès de son test. Ce code ne doit pas être assimilé sans réserve à une intersection ensembliste générale dans tous les modes d’appel.
7.3 Nombre d’occurrences
Contrat : nb_occ(L,X,C) associe C au nombre d’occurrences de X dans L. Cette fois, le résultat est un nombre :
Code (prolog) · sélectionner pour copier
?- nb_occ([a,a,f,d,a],a,N).
N = 3.
Code (prolog) · sélectionner pour copier
nb_occ([],X,0).
nb_occ([X|R],X,C) :- nb_occ(R,X,D), C is D+1.
nb_occ([Y|R],X,C) :- not(X=Y), nb_occ(R,X,C).
Le vide contient zéro occurrence. Si la tête s’unifie à X, compter la queue et ajouter 1. Sinon, compter la queue sans incrément. Pour l’exemple, les contributions sont 1+1+0+0+1=3.
La variable D doit avoir reçu le compte récursif avant C is D+1. Les deux cas de tête sont exclusifs sur les éléments fixés de l’exemple. Avec des variables libres, la clause positive peut créer des liaisons, et not(X=Y) n’est pas une contrainte persistante de différence. Le contrat d’usage doit donc être clair.
7.4 Répartir positifs et négatifs
Contrat : sup_inf(L,S,I) conserve dans S les éléments strictement positifs de L et dans I les strictement négatifs. Les zéros n’apparaissent dans aucune des deux listes.
Code (prolog) · sélectionner pour copier
?- sup_inf([2,12,0,-3,6,-5],S,I).
S = [2,12,6], I = [-3,-5].
Corrigé officiel :
Code (prolog) · sélectionner pour copier
sup_inf([],[],[]).
sup_inf([N|R],S,I) :- N=0, sup_inf(R,S,I).
sup_inf([N|R],[N|S],I) :- N>0, sup_inf(R,S,I).
sup_inf([N|R],S,[N|I]) :- N<0, sup_inf(R,S,I).
Le vide donne deux résultats vides. Un zéro est ignoré; un positif devient tête de S; un négatif devient tête de I. Ignorer zéro ne signifie pas arrêter la récursion : il faut encore examiner tous les éléments de la queue. L’ordre relatif des positifs et celui des négatifs sont conservés. La liste d’entrée n’est pas modifiée en place.
Le code imprimé utilise N=0, unification avec le terme entier 0. Pour les entiers des exemples, cela convient. Un flottant 0.0 n’est toutefois pas le même terme que 0 et n’est ni strictement positif ni strictement négatif : cette version ne couvre donc pas tous les nombres possibles. Une variante destinée à des nombres déjà instanciés peut employer N =:= 0 pour comparer la valeur arithmétique. Cette variante doit rester distinguée de la ligne effectivement donnée dans le corrigé.