Sunteți pe pagina 1din 6

Capitolul 3

Prelucrarea listelor în Prolog


Structura de date listă este cea mai frecvent utilizată structură de date în
programele Prolog. Acest capitol prezintă în detaliu lucrul cu liste în Prolog.

3.1 Predicate de prelucrare a listelor


Cele mai frecvente predicate utilizate în prelucrarea listelor sunt cel de apartenenţă
a unui element la o listă şi concatenarea a două liste. Aceste predicate sunt
predefinite in SWI-Prolog: member(Element, Lista) si append(Lista1, Lista2,
ListaRezultat).
În continuare, vom defini două predicate cu efect similar cu cele două
predicate predefinite amintite mai sus.
Predicatul membru se defineşte astfel:
% membru(Element, Lista)
membru(Elem, [Elem|_]).
membru(Elem, [_|Rest]) :- membru(Elem, Rest).
Pentru subcapitolul 3.2 vom folosi o versiune de membru puţin modificată:
membru1(Elem, [Elem|_]) :- !.
membru1(Elem, [_|Rest]) :- membru1(Elem, Rest).
Operatorul cut împiedică resatisfacerea scopului, în condiţiile în care elementul
căutat apare de mai multe ori in listă.
Predicatul conc se defineşte astfel:
% conc(Lista1, Lista2, ListaRezultat)
conc([], L2, L2).
conc([Prim1|Rest1], Lista2, [Prim1|Rest3]) :- conc(Rest1, Lista2, Rest3).
?- conc([a, b], [c, d, e], L3).
L3 = [a, b, c, d, e];
No
?- conc([a, b], [c, d, e], L3).
L3 = [a, b, c, d, e] % Enter când nu căutăm alte soluţii
Yes
?- conc(L1, [c, d, e], [a, b, c, d, e]).
L1 = [a, b];
No
48 CAPITOLUL 3

?- conc([a, b], L2, [a, b, c, d, e]).


L2 = [c, d, e];
No

?- conc(L1, L2, [a, b]).


L1 = [ ],
L2 = [a, b];

L1 = [a],
L2 = [b];

L1 = [a, b],
L2 = [ ];
No

Se observă că, pentru cazul în care predicatul de concatenare are un singur


argument neinstanţiat, există o singură soluţie, iar pentru cazul în care primele
două argumente sunt neinstanţiate (variabile), se obţin mai multe soluţii,
corespunzătoare tuturor variantelor de liste, care prin concatenare generează cea de
a treia listă.

În continuare, se prezintă o serie de alte predicate utile în prelucrarea listelor.


Eliminarea unui obiect dintr-o listă. Să scriem un predicat, care elimină
un obiect dintr-o listă. Astfel, elim(a, [a, b, c], L) va returna în L lista [b, c].
Implementarea în Prolog este:
% elim(+El,+Lista,-ListaRez)
elim(X, [X | Rest], Rest).
elim(X, [Y | Rest], [Y | Rest1]) :- elim(X, Rest, Rest1).
Conform acestei implementări, elim nu va elimina decât o apariţie a
elementului căutat. Astfel, eliminarea lui a din lista [a,b,a,c], va genera două
soluţii posibile:
?- elim(a, [a, b, a, c], L).
L = [b, a, c];
L = [a, b, c];
No

Dar este posibilă şi întrebarea “Ce liste din care se elimina a dau ca rezultat lista
[b, c]?”:
?- elim(a, L, [b, c]).
PRELUCRAREA LISTELOR ÎN PROLOG 49

L = [a, b, c];
L = [b, a, c];
L = [b, c, a];
No

Incluziunea listelor. Fie un predicat, care este adevărat, dacă o listă este
sublista alteia.
De exemplu,
sublist([c, d, e], [a, b, c, d, e, f]) este adevărat, iar
sublist([b, c, e], [a, b, c, d, e, f]) este fals. Ne putem folosi de predicatul
deja scris append. O listă S este sublistă a listei L, dacă:
1) Există o descompunere a lui L în L1 şi L2 şi
2) Există o descompunere a lui L2 în S si L3.
Implementare:
% sublist(+SubLista,+Lista)
sublist(S, L) :- append(L1, L2, L), append(S, L3, L2).
Această implementare are un mic defect: afişează lista vidă de mai multe ori.
Încercaţi să aflaţi de ce. O variantă care elimină acest defect este subset:
subset([], _).
subset([X | Rest], L) :- member(X, L), subset(Rest, L).
In plus, pentru subset nu conteaza ordinea elementelor din L.

Liniarizarea listelor. Vom scrie predicatul liniar(ListaListe, Lista), unde


ListaListe este o listă de elemente, care pot fi la rândul lor liste, iar în Lista se
construieşte liniarizarea listei ListaListe:
% liniar(+Lista,ListaLiniarizata)
liniar([] , []).
liniar([[ ] | Rest], Rez) :- liniar(Rest, Rez).
liniar([X | Rest], [X | Rez]) :- X \= [], X \= [ _ | _ ], liniar(Rest, Rez).
liniar([[X | Rest] | RestList], Rez) :- liniar([X, Rest | RestList], Rez).
Un exemplu de execuţie este:
?- liniar([1, 2, [3, 4], [5, [6, 7], [[8], 9]]], L).
L = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Yes
50 CAPITOLUL 3

3.2 Predicate care utilizează liste


În acest subcapitol prezentăm câteva predicate care utilizează liste.
Mulţimi. Mulţimile pot fi reprezentate în Prolog ca liste. Predicatul
multime(L, M) transformă lista L în mulţimea M.
multime([], []).
multime([X | Rest], Rez) :- membru1(X, Rest), multime(Rest, Rez).
multime([X | Rest], [X | Rez]) :- not(membru1(X, Rest)),
multime(Rest, Rez).
Predicatul de definire a intersecţiei a două liste, prezentat în capitolul 4, se
poate aplica şi pentru obţinerea intersecţiei a două mulţimi. Prezentăm în
continuare, predicatul de determinare a reuniunii a două mulţimi.
% reun(+L1,+L2,-L)
reun([],L,L).
reun([X | Rest], L, Rez) :-membru1(X,L), reun(Rest,L,Rez).
reun([X | Rest], L, [X | Rez]) :-not(membru1(X,L)), reun(Rest,L,Rez).

Problema drumurilor. Fie o bază de date cu drumuri între oraşe, de forma


drum(oras1, oras2):
drum(bucuresti, ploiesti).
drum(bucuresti, cheia).
drum(cheia, brasov).
drum(brasov, bucuresti).
drum(cheia, sinaia).
drum(ploiesti, sinaia).
drum(ploiesti, brasov).
Predicatul traseu(X, Y, T) este adevărat dacă se poate ajunge de la oraşul X
la oraşul Y, calculând şi traseul T între cele două oraşe. Drumurile sunt
bidirecţionale (dacă exista drum de la X la Y, atunci există drum de la Y la X).
membru2(X, [Y | T]) :- X == Y, ! ; membru2(X, T).
traseu(Y, X) :- traseu(X, Y, [X]).
traseu(Y, Y, Traseu) :- write(Traseu), nl.
traseu(X, Y, Traseu) :-
(drum(X, Z) ; drum(Z, X)), not(membru2(Z, Traseu)),
traseu(Z, Y, [Z | Traseu]).
traseu( _ , _ , _ ) :- write('Nu exista traseu.'), nl.
test :- traseu(bucuresti, sinaia), traseu(sinaia, bucuresti),
traseu(bucuresti, ploiesti), traseu(ploiesti, bucuresti),
traseu(cheia, craiova).
PRELUCRAREA LISTELOR ÎN PROLOG 51

?- test.
[bucuresti, brasov, cheia, sinaia]
[sinaia, ploiesti, bucuresti]
[bucuresti, brasov, cheia, sinaia, ploiesti]
[ploiesti, bucuresti]
Nu exista traseu.
Yes

Descompunerea unui număr în factori primi. Predicatul descomp(N,


Lista) primeşte un număr întreg N şi întoarce o listă a factorilor primi ai numărului
N; de exemplu: descomp(12, [2, 2, 3]) este adevărat.
% descomp(+N,-L)
descomp(N, L) :- factp(N, L, 2).
factp(1, [ ], _ ).
factp(N, [Divizor | Lista], Divizor) :-
N > 1, 0 is N mod Divizor, N1 is N // Divizor,
factp(N1, Lista, Divizor).
factp(N,Lista,Divizor) :-
N > 1, not(0 is N mod Divizor), D1 is Divizor + 1,
factp(N, Lista, D1).

Verificare palindrom. Predicatul palindrom(Lista) verifică dacă o listă


este palindrom. Un palindrom este o secvenţă care, dacă este parcursă de la stânga
la dreapta sau de la dreapta la stânga, este identică; de exemplu: [a, b, c, b, a] sau
[a, b, c, c, b, a].
% Idee: o lista este palindrom daca este egala cu inversa ei.
palindrom(L) :- reverse(L, [], L).
reverse([], Acc, Acc).
reverse([X | Rest], Acc, L) :- reverse(Rest, [X | Acc], L).

Verificare listă sortată. Predicatul sortcresc verifică dacă o listă de


numere întregi este sortată crescător.
% sortcresc(Lista)
sortcresc([ ]). % lista vida se considera sortata
sortcresc([ _ ]). % lista cu un singur element este sortata
sortcresc([X, Y | Rest]) :- X =< Y, sortcresc([Y | Rest]).

?- sortcresc([1, 2, 3, 4]).
Yes
52 CAPITOLUL 3

?- sortcresc([1, 3, 5, 4]).
No
?- sortcresc([ ]).
Yes

Dacă se consideră că lista vidă nu este o listă sortată crescător atunci se


poate elimina faptul:
sortcresc([ ]).

din definiţia predicatului sortcresc, comportarea lui pentru liste diferite de lista
vidă rămânând aceeaşi.

3.3 Exerciţii propuse


EP1. Aflaţi care este defectul predicatului sublist.
EP2. Folosind predicatul elim, puneţi întrebarea: “Ce elemente se pot elimina din
[a, b, a, c] şi ce listă rezultă în cazul fiecărei eliminări?”
EP3. Să se definească şi să se exemplifice cu câte două exemple în Prolog,
următoarele predicate de prelucrare a listelor:
1) invers(Lista, ListaInversata) - inversează elementele unei liste. Să
se scrie două variante ale predicatului de inversare a unei liste: o
variantă în care lista inversată este calculată pe ramura de revenire
din recursivitate şi o variantă în care lista inversată este calculată pe
ramura de avans în recursivitate.
2) reun(Lista1, Lista2, ListaRez) - produce ListaRez, care conţine
reuniunea elementelor din Lista1 şi din Lista2. Pe baza predicatului
mulţime din secţiunea 3.2, se va da o implementere alternativă a
predicatului reun.
EP4. Să se scrie predicatul Prolog substitutie(X, Y, L1, L2), unde L2 este
rezultatul substituirii tuturor apariţiilor lui X din lista L1 cu Y, producând lista L2.
Ex: substitutie(a, x, [a, [b,a,] c], L2) va produce: L2 = [x, [b, x], c].
EP5. Să se scrie predicatul imparte(L, L1, L2) care împarte lista L în două
subliste L1 şi L2, care au un număr de elemente aproximativ egal, fără a calcula
lungimea listei L.
Ex: imparte([a, b, c, d, e], L1, L2) va produce: L2 = [a, b, c] şi
L3 = [d, e].

S-ar putea să vă placă și