Sunteți pe pagina 1din 112

Laborator 1-2 1

INTRODUCERE ÎN LIMBAJUL PROLOG

Noţiuni teoretice

În Prolog se poate ajunge la soluţii prin inferenţă logică (deducţie logică) pornind de la
ceva cunoscut în prealabil. Tipic, un program în limbajul Prolog nu este o secvenţă de acţiuni ci o
mulţime de fapte şi reguli care împreună duc la nişte concluzii logice. Prolog este ceea ce numim un
limbaj declarativ.
Prolog lucrează cu aşa numitele predicate logice. Un predicat este o formă foarte clară şi
concisă de a exprima un fapt existent. Se utilizează o sintaxă foarte simplă, apropiată de limbajul
natural pentru a exprima un predicat logic.
Limbajul Prolog conţine un motor de inferenţă care efectuează deducţiile logice pornind de
la nişte fapte cunoscute. Prolog încearcă să deducă dacă o ipoteză este adevărată (cu alte cuvinte să
răspundă la întrebări) prin interogarea unui set de informaţii deja adevărate.
O altă caracteristică importantă a limbajului Prolog este aceea că găseşte toate soluţiile unei
probleme. Prolog nu parcurge baza de date logică pur şi simplu ci, după găsirea unei soluţii la o
problemă, poate face o revenire pentru găsirea altor soluţii.

Fapte şi reguli
Un program Prolog defineşte obiecte şi relaţii, apoi defineşte reguli bazate pe aceste relaţii.
De exemplu propoziţia:
Lui Mihai îi plac caii.
arată o relaţie dintre obiectele Mihai şi caii. O regulă care arată când propoziţia anterioară este
adevărată:
Lui Mihai îi plac caii dacă caii sunt pur-sânge.
În Prolog o relaţie între obiecte este numită predicat. În limbaj natural o relaţie între obiecte
este exprimată printr-o propoziţie. În cadrul logicii predicatelor, o relaţie este exprimată printr-o
simplă frază – un fapt – care constă dintr-un nume de relaţie şi unul sau mai multe obiecte (între
paranteze). Ca şi propoziţiile, faptele se încheie cu punct.
Exemple:
În limbaj natural: În Prolog:
Lui Bogdan îi place geometria. place(bogdan, geometria).
Luciei îi place chimia. place(lucia, chimia).
Lui Bogdan îi plac perele. place(bogdan,pere).
Frunzele sunt verzi. verde(frunze).
Trandafirul este floare. floare(trandafir).
Regulile permit deducţia unor fapte din alte fapte (inferenţa). Altfel spus, o regulă este o
concluzie care este adevărată dacă una sau mai multe concluzii sau fapte sunt adevărate.
2 Introducere în limbajul Prolog

Exemple:
În limbaj natural: În Prolog:
Luciei îi place tot ce îi place şi lui place(lucia,Ceva):-place(bogdan,
Bogdan. Ceva).
Danei îi plac toate florile. place(dana,Ceva):-floare(Ceva).

Simbolul „:-„ se poate interpreta ca „if” şi serveşte la separarea celor două părţi ale unei
reguli: antet şi corp. Se poate de asemenea interpreta o regulă ca o procedură. Cu alte cuvinte,
regulile anterioare se mai pot interpreta şi astfel: „ Pentru a demonstra că Luciei îi place ceva,
demonstrează că şi lui Bogdan îi place acelaşi lucru”, respectiv „Pentru a demonstra că Danei îi
place ceva, demonstrează că acel ceva este floare”.

Interogări
După ce s-au furnizat câteva fapte şi reguli unui program Prolog, acestuia i se pot pune
întrebări, numite interogări.

În limbaj natural o întrebare ar putea În Prolog se poate interoga:


fi:
Îi place lui Bogdan geometria? place(bogdan,geometria).
La această interogare Prolog răspunde:
yes
O altă interogare ar putea fi: În Prolog se poate interoga:
Ce îi place Luciei? place(lucia,Ce).
Se poate observa că sintaxa Prolog nu se schimbă când se pune o întrebare. O astfel de
interogare, din punct de vedere sintactic arată ca un fapt. Se remarcă faptul că al doilea obiect – Ce
– este scris cu literă mare, în timp ce primul obiect – lucia – este scris cu literă mică. Motivul este
că „lucia” este o constantă, este ceva fix, iar „Ce” este o variabilă, iar variabilele încep cu literă
mare sau cu caracterul „_”. Prolog întotdeauna va încerca să răspundă la o interogare prin
cercetarea faptelor începând de la început spre sfârşit. La întrebarea „Ce îi place Luciei”, Prolog va
răspunde:
Ce=geometria
Ce=chimia
Ce=pere
3 Solutions
Se pot forma interogări compuse pentru a afla o soluţie dacă subtestul A şi subtestul B sunt
adevărate, separând prin virgulă („,”) aceste două subteste. În acest caz se spune că s-a utilizat
„conjuncţia”. Există însă posibilitatea utilizării şi a disjuncţiei, prin separarea celor două subteste A
şi B prin caracterul „;”.

Variabile
Laborator 1-2 3
În Prolog variabilele permit scrierea de fapte, reguli şi interogări. În limbaj natural utilizăm
variabilele în propoziţii tot timpul. În limbaj natural putem face afirmaţia: „Lui Ionuţ îi place acelaşi
lucru ca şi lui Mircea.” Pentru a reprezenta o variabilă în Prolog, aceasta trebuie scrisă cu prima
literă mare sau cu liniuţa de subliniere. În exemplul următor:
place(ionuţ, Obiect)
„Obiect” este o variabilă şi nu un atom, cum este „ionuţ”.
Variabile anonime sunt utile atunci când sunt necesare numai anumite informaţii dintr-o
interogare, alte informaţii putând fi ignorate. În Prolog variabila anonimă se reprezintă prin
caracterul „_” (underline). O variabilă anonimă se poate utiliza în locul oricărei alte variabile.
Diferenţa dintre o variabilă anonimă şi o variabilă obişnuită este aceea că o variabilă anonimă nu va
primi niciodată valoare.
Se poate afirma că Prolog nu are instrucţiuni de atribuire; acest lucru diferenţiază limbajul
Prolog faţă de alte limbaje de programare. În Prolog variabilele primesc valori prin „potrivire” cu
constante în fapte sau reguli.
Până în momentul în care primeşte o valoare, variabilele sunt numite „libere”; în momentul
în care o variabilă primeşte o valoare ea devine variabilă „legată”. Ea rămâne legată atâta timp cât
este nevoie pentru a furniza o soluţie la o interogare, după care variabila respectivă devine din nou
liberă, în vederea obţinerii altor soluţii.

Predicate
Denumirea simbolică a unei relaţii este „predicat”. Obiectele care sunt legate printr-un
predicat poartă numele de „argumente”. În faptul place(bogdan,geometria), relaţia „place” este un
predicat, iar „bogdan” şi „geometria” sunt argumentele sale.
În continuare sunt prezentate exemple de predicate Prolog cu zero sau mai multe argumente.
persoana(nume, prenume, sex)
data_nasterii(nume, data)
tara(denumire,continent,populatie,suprafata)
place(symbol,symbol)
functia(real,real)
triunghi(real,real,real)
determina

Comentarii

Comentariile au următoarea sintaxă:

/*Acesta este un comentariu*/

% Acesta este tot un comentariu

/********************************************/
/* Si aceste trei linii sunt comentarii */
/********************************************/
4 Introducere în limbajul Prolog

Aplicaţii rezolvate

Aplicaţia 1
Se dă următoarea bază de cunoştinţe:
 Lui Dan îi place fotbalul.
 Anei îi place voleiul.
 Lui Bogdan îi place voleiul şi tenisul.
 Elenei îi place tenisul.
 Lui Dan îi place acel sport care îi place şi lui Bogdan.
Să se scrie un program Prolog care răspunde întrebării: “Ce sporturi îi plac lui Dan?”

Programul corespunzător problemei este:


predicates
nondeterm place(symbol,symbol)
clauses
place(dan, fotbal).
place(ana,volei).
place(bogdan,volei).
place(bogdan,tenis).
place(elena,tenis).
place(dan,Sport):-
place(bogdan, Sport).
goal
place(dan,Ce).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Ce=fotbal
Ce=volei
Ce=tenis
3 Solutions
Programul este structurat pe trei secţiuni. În prima secţiune s-a declarat predicatul place cu
două argumente de tip symbol.
În secţiunea legată de clauze sunt declarate cinci fapte şi o regulă corespunzătoare
afirmaţiilor din enunţ. Ultima clauză este o regulă, care în limbaj natural corespunde următoarei
propoziţii: “Lui Dan îi place un sport dacă şi lui Bogdan îi place acel sport”. În această regulă
antetul este place(dan,Sport) şi corpul place(bogdan,Sport).
Ultima secţiune corespunde întrebării din enunţul problemei, întrebare redată de interogarea
place(dan,Ce).
În interogarea „place(dan,Ce)” variabila Ce este o variabilă liberă, valoarea sa este
necunoscută până în momentul în care se găseşte o soluţie. În schimb argumentul dan este cunoscut.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. La prima căutare, pentru că se poate realiza o
potrivire între primul argument de la interogare cu primul argument al primului fapt, variabila liberă
Laborator 1-2 5
Ce va deveni legată de atomul fotbal, obţinându-se astfel o primă soluţie. Căutarea nu se opreşte
aici, variabila Ce devine din nou liberă şi se încearcă o nouă potrivire la următorul fapt. Se observă
că primul argument de la interogare, atomul dan, nu mai poate fi unificat cu primul argument de la
celelalte fapte din secţiunea de clauze.
O astfel de potrivire se mai realizează doar cu antetul regulii definite în secţiunea de clauze.
În acest moment variabila liberă Ce se unifică cu variabila liberă Sport, iar căutarea se va reduce
acum la îndeplinirea obiectivului din corpul acestei reguli. Astfel, parcurgând lista de clauze, se
poate observa că se realizează potrivire cu faptul al treilea şi al patrulea, obţinându-se încă două
soluţii.

Aplicaţia 2
Se dă următoarea bază de cunoştinţe:
 Anei îi place să citească.
 Adelei îi place să cânte şi să înoate.
 Lui Marius îi place să înoate.
 Lui Bogdan îi place să deseneze şi să citească.
Să se scrie un program Prolog care răspunde întrebării: “Care este persoana căreia îi place să
citească şi să deseneze?”

Programul corespunzător problemei este:


predicates
nondeterm place(symbol,symbol)
clauses
place(ana,citeste).
place(adela,canta).
place(adela,inoata).
place(marius, inoata).
place(bogdan,deseneaza).
place(bogdan,citeste).
goal
place(Persoana,citeste), place(Persoana,deseneaza).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Persoana=bogdan
1 Solution
Programul este structurat pe trei secţiuni. În prima secţiune s-a declarat predicatul place cu
două argumente de tip symbol. În secţiunea legată de clauze sunt declarate şase fapte
corespunzătoare afirmaţilor din enunţ. Ultima secţiune corespunde întrebării din enunţul problemei,
întrebare redată de interogarea compusă place(Persoana,citeste), place(Persoana,deseneze).
Prolog va rezolva această interogare căutând în lista de fapte de la început spre sfârşit,
utilizând mecanismul de căutare cu revenire. În prima parte a interogării variabila Persoana este o
variabilă liberă, valoarea sa este necunoscută până în momentul în care se găseşte o soluţie. În
schimb argumentul citeste este cunoscut. La prima căutare, pentru că se poate realiza o potrivire
între al doilea argument de la interogare cu primul argument al primului fapt, variabila liberă
Persoana va deveni legată de atomul ana. A doua parte a interogării devine: place(ana,deseneaza).
6 Introducere în limbajul Prolog
Negăsindu-se un fapt care să corespundă celei de a doua părţi a interogării, căutarea se încheie cu
eşec la acest pas.
Căutarea nu se opreşte aici, variabila Persoana devine din nou liberă şi se încearcă o nouă
potrivire la următorul fapt. Se observă că al doilea argument din prima parte a interogării, atomul
citeste, poate fi unificat cu al doilea argument de la ultimul fapt din secţiunea de clauze. În acest
moment variabila liberă Persoana va deveni legată de atomul bogdan, iar căutarea se va reduce
acum la îndeplinirea părţii a doua a interogării care devine: place(bogdan,deseneaza). Astfel,
parcurgând lista de clauze, se poate observa că se realizează potrivire cu penultimul fapt, obţinându-
se astfel o soluţie.

Aplicaţia 3
Se dă următoarea bază de cunoştinţe:
 Elena este femeie.
 Roxana este femeie.
 Ioana este femeie.
 Ioana este părintele lui Paul.
 Paul este părintele Roxanei.
 Mihai este părintele Ioanei.
 Elena este părintele lui Ionuţ.
Să se scrie un program Prolog care răspunde întrebării: “Cine este mamă?”

Programul corespunzător problemei este:


predicates
nondeterm femeie(symbol)
nondeterm parinte(symbol,symbol)
nondeterm mama(symbol)
clauses
femeie(elena).
femeie(roxana).
femeie(ioana).
parinte(ioana,paul).
parinte(paul,roxana).
parinte(mihai,ioana).
parinte(elena,ionut).
mama(Persoana):-
femeie(Persoana),
parinte(Persoana,_).
goal
mama(Mama).
Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Mama=elena
Mama=ioana
2 Solutions
Programul este structurat pe trei secţiuni. În prima secţiune s-au declarat trei predicate:
predicatul parinte cu două argumente de tip symbol şi predicatele femeie şi mama cu un argument
Laborator 1-2 7
de tip symbol. În secţiunea legată de clauze este definit predicatul femeie prin trei fapte, predicatul
parinte prin patru fapte şi predicatul mama printr-o regulă. În definiţia regulii este utilizată variabila
anonimă, pentru că al doilea subobiectiv al regulii furnizează soluţii privitoare la ce persoane sunt
părinţii cuiva, dar nu interesează ai cui părinţi sunt aceştia.
Ultima secţiune corespunde întrebării din enunţul problemei, întrebare redată de interogarea
mama(Mama). În interogare variabila Mama este o variabilă liberă, valoarea sa este necunoscută
până în momentul în care se găseşte o soluţie.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Pentru că predicatul din interogare este definit
printr-o regulă, căutarea soluţiilor se va reduce la îndeplinirea celor două subobiective ale regulii.
La prima căutare variabila liberă Mama se va lega de atomul din prima definiţie a
predicatului femeie. Astfel, al doilea subobiectiv al regulii devine parinte(elena, _). Pentru că al
doilea argument al acestui subobiectiv este variabilă anonimă, se va căuta în secţiunea unde este
definit predicatul parinte dacă există un fapt al cărui prin argument să fie atomul elena. Pentru că
există un fapt cu această proprietate, se obţine o primă soluţie.
În continuare variabila Mama redevine o variabilă liberă şi va fi legată de atomul roxana.
Astfel, al doilea subobiectiv al regulii devine parinte(roxana, _). Pentru că nu există nici un fapt în
definiţia predicatului parinte al cărui prin argument să fie atomul roxana, căutarea se încheie cu
eşec la acest pas.
Variabila Mama redevine o variabilă liberă şi va fi legată de atomul ioana, iar al doilea
subobiectiv al regulii devine parinte(ioana, _). Pentru că primul fapt din definiţia predicatului
parinte are ca prim argument atomul ioana, căutarea se încheie cu obţinerea unei noi soluţii la acest
pas.
Variabila Mama redevine o variabilă liberă şi, deoarece nu mai există o altă posibilitate de a
deveni legată de un alt atom, căutarea se încheie cu obţinerea a două soluţii.

Aplicaţia 4
Se dă următoarea bază de cunoştinţe:
 Ioan este bărbat.
 Mihai este bărbat.
 Bogdan este bărbat.
 Dan este bărbat.
 Ioan este părintele Anei.
 Elena este părintele Anei.
 Ana este părintele Roxanei.
 Bogdan este părintele lui Paul.
 Dan este părintele Mihaelei.
 Mihaela este părintele lui Bogdan.
Să se scrie un program Prolog care răspunde întrebării: “Cine este bunic?”

Programul corespunzător problemei este:


predicates
nondeterm barbat(symbol)
nondeterm parinte(symbol,symbol)
nondeterm bunic(symbol)
clauses
barbat(ioan).
8 Introducere în limbajul Prolog
barbat(mihai).
barbat(bogdan).
barbat(dan).
parinte(ioan,ana).
parinte(elena,ana).
parinte(ana,roxana).
parinte(bogdan,paul).
parinte(dan,mihaela).
parinte(mihaela,bogdan).
bunic(Bunic):-
barbat(Bunic),
parinte(Bunic,X),
parinte(X,_).
goal
bunic(Bunic).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Bunic=ioan
Bunic=dan
2 Solutions
Programul este structurat pe trei secţiuni. În prima secţiune s-au declarat trei predicate:
predicatul parinte cu două argumente de tip symbol şi predicatele barbat şi bunic cu un argument de
tip symbol. În secţiunea legată de clauze este definit predicatul barbat prin patru fapte, predicatul
parinte prin şase fapte şi predicatul bunic printr-o regulă. În definiţia regulii este utilizată variabila
anonimă, pentru că al treilea subobiectiv al regulii furnizează soluţii privitoare la ce persoane sunt
părinţii cuiva, dar nu interesează ai cui părinţi sunt aceştia. Ultima secţiune corespunde întrebării
din enunţul problemei, întrebare redată de interogarea bunic(Bunic). În interogare variabila Bunic
este o variabilă liberă, valoarea sa este necunoscută până în momentul în care se găseşte o soluţie.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Pentru că predicatul din interogare este definit
printr-o regulă, căutarea soluţiilor se va reduce la îndeplinirea celor trei subobiective ale regulii.
La prima căutare variabila liberă Bunic se va lega de atomul din prima definiţie a
predicatului barbat. Astfel, al doilea subobiectiv al regulii devine parinte(ioan,X), unde X este o
variabilă liberă. Se va căuta în secţiunea unde este definit predicatul parinte dacă există un fapt al
cărui prim argument să fie atomul ioan. Pentru că există un fapt cu această proprietate, variabila
liberă va deveni legată de atomul ana. Al treilea subobiectiv al regulii devine parinte(ana, _). Se va
căuta în secţiunea unde este definit predicatul parinte dacă există un fapt al cărui prin argument să
fie atomul ana. Pentru că există un fapt cu această proprietate, se obţine o primă soluţie. Se revine
la al doilea obiect al interogării, iar variabila X devine independentă. Deoarece nu mai există o altă
posibilitate pentru variabila X de a deveni legată de un alt atom, se revine la primul obiectiv şi
variabila Bunic devine liberă.
În continuare variabila Bunic va fi legată de atomul mihai. Astfel, al doilea subobiectiv al
regulii devine parinte(mihai,X). Pentru că nu există nici un fapt în definiţia predicatului parinte al
cărui prin argument să fie atomul mihai, căutarea se încheie cu eşec la acest pas. Variabila Bunic
Laborator 1-2 9
redevine o variabilă liberă şi va fi legată de atomul bogdan, iar al doilea subobiectiv al regulii
devine parinte(bogdan,X). Se va căuta în secţiunea unde este definit predicatul parinte dacă există
un fapt al cărui prin argument să fie atomul bogdan. Pentru că există un fapt cu această proprietate,
variabila liberă va deveni legată de atomul paul. Al treilea subobiectiv al regulii devine
parinte(paul, _). Se va căuta în secţiunea unde este definit predicatul parinte dacă există un fapt al
cărui prin argument să fie atomul paul. Pentru că nu există un fapt cu această proprietate, căutarea
se încheie cu eşec. Se revine la al doilea obiect al interogării, iar variabila X devine independentă.
Deoarece nu mai există o altă posibilitate pentru variabila X de a deveni legată de un alt atom, se
revine la primul obiectiv şi variabila Bunic devine liberă.
Variabila liberă Bunic se va lega acum de atomul dan. Astfel, al doilea subobiectiv al regulii
devine parinte(dan,X), unde X este o variabilă liberă. Se va căuta în secţiunea unde este definit
predicatul parinte dacă există un fapt al cărui prin argument să fie atomul dan. Pentru că există un
fapt cu această proprietate, variabila liberă va deveni legată de atomul mihaela. Al treilea
subobiectiv al regulii devine parinte(mihaela, _). Se va căuta în secţiunea unde este definit
predicatul parinte dacă există un fapt al cărui prin argument să fie atomul mihaela. Pentru că există
un fapt cu această proprietate, se obţine încă o soluţie. Se revine la al doilea obiect al interogării, iar
variabila X devine independentă. Deoarece nu mai există o altă posibilitate pentru variabila X de a
deveni legată de un alt atom, se revine la primul obiectiv. Variabila Bunic redevine o variabilă
liberă şi, pentru că nu mai există o altă posibilitate de a deveni legată de un alt atom, căutarea se
încheie cu obţinerea a două soluţii.

Aplicaţia 5
Se dă următoarea bază de cunoştinţe:
 Maria este mama lui Dan.
 Mihai este fratele Mariei.
 Ion este tatăl Mariei.
 Ana este mama lui Mihai.
 George este fiul lui Mihai.
Să se scrie un program Prolog care răspunde întrebării: “Cum se numesc bunicii lui George?”

Programul corespunzător problemei este:


predicates
nondeterm parinte(symbol,symbol)
nondeterm frate(symbol,symbol)
nondeterm bunic(symbol,symbol)
clauses
parinte(maria,dan).
parinte(ion,maria).
parinte(ana,mihai).
parinte(mihai,george).
frate(mihai,maria).
bunic(X,Y):-
parinte(Z,Y), parinte(X,Z).
bunic(X,Y):-
parinte(T,Y), frate(T,Z),
parinte(X,Z).
goal
10 Introducere în limbajul Prolog
bunic(Cine,george).

Incercati sa modificati programul pentru a se obtine raspunsul la intrebarea “Cine sunt


bunicii lui Dan?”

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Cine=ana
Cine=ion
2 Solutions
Programul este structurat pe trei secţiuni. În prima secţiune s-au declarat trei predicate:
parinte, frate şi bunic cu două argumente de tip symbol. În secţiunea legată de clauze este definit
predicatul parinte prin patru fapte, predicatul frate printr-un fapt şi predicatul bunic prin două
reguli. Ultima secţiune corespunde întrebării din enunţul problemei, întrebare redată de interogarea
bunic(Cine,george). În interogare variabila Cine este o variabilă liberă, valoarea sa este necunoscută
până în momentul în care se găseşte o soluţie. În schimb argumentul george este cunoscut.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Pentru că predicatul din interogare este definit
prin două reguli, căutarea soluţiilor se va reduce la îndeplinirea subobiectivelor acestor reguli.
Se încearcă îndeplinirea celor două obiective din definiţia primei reguli. Variabila liberă Y se
va lega de atomul george ce reprezintă al doilea argument al predicatului din interogare, iar
variabila liberă Cine se va lega de variabila liberă X. Astfel primul subobiectiv al acestei reguli
devine parinte(Z,george) şi se va urmări îndeplinirea acestuia. Se observă că se poate realiza
potrivire între acest subobiectiv şi ultimul fapt din secvenţa de program unde este definit predicatul
parinte, variabila liberă Z devenind legată de atomul mihai. Al doilea subobiectiv al regulii devine
parinte(Cine,mihai). Pentru că în definiţia predicatului parinte există un fapt care are ca al doilea
argument atomul mihai, variabila liberă Cine devine legată de atomul ana, obţinându-se o primă
soluţie.
Variabila X revine o variabilă liberă şi, deoarece nu se mai poate realiza o altă potrivire, se
revine la primul subobiectiv, iar variabila Z va fi din nou liberă.
Pentru că nu se mai poate realiza o altă potrivire, se trece la a doua regulă ce defineşte
predicatul parinte şi se încearcă îndeplinirea celor trei subobiective ale acestei reguli. Variabila
liberă Y se va lega de atomul george ce reprezintă al doilea argument al predicatului din interogare,
iar variabila liberă Cine se va lega de variabila liberă X. Astfel primul subobiectiv al acestei reguli
devine parinte(T,george) şi se va urmări îndeplinirea acestuia. Se observă că se poate realiza
potrivire între acest subobiectiv şi ultimul fapt din secvenţa de program unde s-a definit predicatul
parinte, variabila liberă T devenind legată de atomul mihai. Al doilea obiectiv al regulii devine
frate(mihai,Z). Predicatul frate fiind definit printr-un singur fapt, se observă că se poate realiză
potrivire, variabila Z devenind legată de atomul maria. Al treilea subobiectiv al regulii devine
parinte(Cine,maria). Pentru că în definiţia predicatului parinte există un fapt care are ca al doilea
argument atomul maria, variabila liberă Cine devine legată de atomul ion, obţinându-se încă o
soluţie.
Variabilele Cine, Z şi T redevin variabile libere şi, pentru că nu mai există alte posibilităţi de
a deveni legate, căutarea se încheie cu obţinerea a două soluţii.
Laborator 1-2 11
Aplicaţii propuse
1. Se dă următoarea bază de cunoştinţe.
 Paul este bărbat.
 Vasile este bărbat.
 Mihai este bărbat.
 Ioana este părintele lui Paul.
 Paul este părintele Roxanei.
 Mihai este părintele Ioanei.
 Elena este părintele lui Ionuţ.
Să se scrie un program Prolog care răspunde întrebării: “Cine este tată?”
2. Se dă următoarea bază de cunoştinţe.
 Ioana este femeie.
 Mihaela este femeie.
 Elena este femeie.
 Daniela este femeie.
 Bianca este femeie.
 Ioana este părintele Anei.
 Elena este părintele Ioanei.
 Ana este părintele Roxanei.
 Bianca este părintele lui Paul.
 Daniela este părintele Mihaelei.
 Mihaela este părintele Biancăi.
Să se scrie un program Prolog care răspunde întrebării: “Cine este bunică?”
Laborator 3 1

STRUCTURA UNUI PROGRAM PROLOG

Noţiuni teoretice

Secţiunea „DOMAINS”
Secţiunea domains permite declararea domeniilor (tipurilor) argumentelor predicatelor.
Există două clase de tipuri, tipul elementar şi tipul complex, care la rândul lui se subîmparte în: tipul
compus şi tipul listă.

Tipul standard
Tipul elementar poate fi standard sau definit de utilizator. Tabelul 1 descrie tipurile standard
cele mai des utilizate în programe implementate în limbajul Prolog. Uneori este util să se declare un
domeniu atunci când se doreşte clarificarea unei porţiuni din secţiunea predicates. Prin declararea
unui domeniu propriu, cu un nume personalizat, se pot da nume sugestive, mai uşor şi mai simplu,
argumentelor predicatului respectiv.

Cuvânt Semnificaţie Exemple


rezervat
byte Număr întreg pe 8 biţi fără semn, 0, 56,234
cuprins între 0  255
short Număr întreg pe 16 biţi, independent +34,88,332
de platformă, cuprins între -32768 
32767
ushort Număr întreg pe 16 biţi fără semn, 0,32,987
independent de platformă, cuprins
între 0  65535
integer Număr întreg cu semn având +45,78,986
dimensiunea dependentă de
arhitectura calculatorului
long Număr întreg lung pe 32 biţi, -5550,21, 198700
independent de platformă
ulong Număr întreg lung pe 32 biţi fără 0,5678, 200000
semn, independent de platformă
real Număr real reprezentat pe 64 de biţi 5.5, 0.007, -11.897
char Caracter încadrat între apostrofuri ‘a’, ‘A’, ‘0’, ‘*’
symbol Şir de caractere în care primul caracter numar, nuMar,
este literă mică numar_1
string Şir de caractere cuprinse între “numar”, “numar”
ghilimele
Tabel 1 Tipuri standard în Prolog
2 Structura unui program Prolog
Tipul compus şi functori
Obiectele compuse ne permit tratarea informaţiilor compuse ca un singur item, astfel încât
să poată fi extrase uşor componentele acestora. Obiectele compuse sunt alcătuite dintr-un functor şi
o listă de obiecte aparţinând functorului respectiv:
nume=functor(obiect1, obiect2, ..., obiectN)
Un obiect compus poate fi unificat cu o variabilă simplă sau cu un alt obiect compus.
Obiectele compuse pot fi privite şi tratate ca un singur obiect în clauzele Prolog.

Expresii aritmetice
Expresiile aritmetice constă din operanzi (numere şi variabile), operatori (+,-,*,/, div şi mod)
şi paranteze. Simbolurile din partea dreaptă a semnului = (care este predicatul =) de mai jos
formează o expresie aritmetică:
A=1+6/(11+3)*Z
Numerele hexazecimale se reprezintă prin precedarea lor cu semnul $:
$FFF=4095
86=$4A+12
Valoarea unei expresii poate fi calculată dacă toate variabile sunt legate la momentul
evaluării. Calculele se fac într-o anumită ordine determinată de prioritatea operatorilor aritmetici:
operatorii cu cea mai mare prioritate sunt evaluaţi primii.

Operaţii
Tipul rezultatului unei operaţii aritmetice este precizat în tabelul 2.2.

Operand 1 Operator Operand 2 Rezultat


întreg +,-,* întreg întreg
real +,-,* întreg real
întreg +,-,* real real
real +,-,* real real
întreg, real / întreg, real real
întreg div întreg întreg
întreg mod întreg întreg
Tabelul 2.2 Operaţii aritmetice

Comparaţii
Limbajul Prolog poate compara expresii aritmetice, caractere, string-uri şi simboluri. Este
utilizată notaţia infix, ceea ce înseamnă că operatorii sunt plasaţi între operanzi ( de exemplu, X <
4) şi nu înaintea lor (de exemplu, <(X,4)).
Lista operatorilor permişi în Prolog este următoarea:
< mai mic
<= mai mic sau egal
> mai mare
>= mai mare sau egal
= egal
<>, >< diferit
Laborator 3 3

Funcţii şi predicate predefinite

Denumire Tip Semnificaţie


abs(X) funcţie întoarce valoarea absolută a lui X
sin(X) funcţie întoace sinusul unghiului X, dat în radiani
cos(X) funcţie întoace cosinusul unghiului X, dat în radiani
tan(X) funcţie întoace tangenta unghiului X, dat în radiani
arctan(X) funcţie întoace arctangenta valorii reale de care este leagat X
exp(X) funcţie întoace e la puterea X
ln(X) funcţie întoace logaritm natural din X
log(X) funcţie întoace logaritm în baza 10 din X
sqrt(X) funcţie întoace rădăcina pătrată a lui X
random(X) predicat leagă X de o valoare reală aleatoare, 0<=X<1
random(X,Y) predicat leagă Y de o valoare întreagă aleatoare, 0<=Y<X
round(X) funcţie întoace valoarea rotunjită a lui X
trunc(X) funcţie întoace valoarea trunchiată a lui X
Tabelul 2.3 Funcţii şi predicate predefinite

Egalitate şi predicatul egal


În Prolog propoziţii precum N=N1-2 indică fie o relaţie între trei obiecte (N,N1,2) fie o
relaţie între două obiecte ( N şi valoarea lui N1-2). Dacă N este variabilă independentă, propoziţia
este satisfăcută legând pe N de rezultatul evaluării expresiei N1-2. Aceasta corespunde unei
instrucţiuni de atribuire din limbajele de programare procedurale. Dacă N este o variabilă legată,
atunci propoziţia este satisfăcută dacă relaţia de egalitate are loc. În ambele situaţii variabila N1
trebuie să fie legată.
OBSERVAŢIE:
O propoziţie de forma N=N-1 va eşua întotdeauna.

Secţiunea „CONSTANTS”
Secţiunea constants se utilizează pentru declararea de constante simbolice în Prolog. O
constantă simbolică se declară astfel: <identificator> = <macro definiţie>
Exemple:
CONSTANTS
zero = 0
pi = 3.1415927
Există câteva restricţii în utilizarea constantelor simbolice:
 Definirea unei constante nu poate face o referire la ea însăşi.
 Sistemul nu face distincţie între literele mari şi literele mici într-o declaraţie de constante.
Totuşi trebuie evitată utilizarea literelor mari ca primă literă într-o secţiune clauses pentru a nu se
confunda cu variabilele.
 Pot fi declarate mai multe constante într-un program, însă constantele trebuie declarate
înainte de utilizarea lor.
 Declaraţiile de constante devin efective din punctul în care sunt făcute şi rămân valabile
în orice fişier inclus după declarare.
4 Structura unui program Prolog
 Un identificator constantă trebuie declarat o singură dată.

Secţiunea „CLAUSES”
Clauzele (faptele şi regulile) pentru un predicat trebuie amplasate împreună în secţiunea
clauses. O secvenţă de fapte şi reguli care definesc un predicat poartă numele de procedură.
Atunci când răspunde la o interogare, Prolog va începe cu începutul secţiunii clauses
căutând o potrivire cu toate faptele şi regulile din această secţiune.
În Prolog o regulă se foloseşte atunci când un fapt depinde de un alt fapt sau grup de fapte.
O regulă are un antet şi un corp cu următoarea sintaxă:
ANTET : - <subobiectiv 1>, < subobiectiv 2>, ..., < subobiectiv n>.
Corpul unei reguli este format din unul sau mai multe subobiective. Subobiectivele se separă
prin virgulă, specificând conjuncţia. Fiecare subobiectiv este un apel la un alt predicat Prolog care
se poate încheia cu succes sau cu eşec. Dacă toate subobiectivele care alcătuiesc corpul unei reguli
se încheie cu succes, atunci regula se încheie cu succes, iar dacă cel puţin un subobiectiv eşuează,
atunci şi regula eşuează.
Pentru a avea succes la o regulă, Prolog trebuie să satisfacă toate subobiectivele sale. Când
un subobiectiv eşuează, Prolog se întoarce înapoi la subobiectivele anterioare, apoi continuă cu
valori schimbate ale variabilelor. Această tehnică este denumită backtracking.

Secţiunea „PREDICATES”
Atunci când se defineşte un predicat în secţiunea clauses a unui program Prolog, trebuie în
prealabil declarat în secţiunea predicates prin care se comunică programului Prolog despre ce este
vorba. Există şi predicate predefinite în Prolog care nu trebuie redeclarate. De asemenea vor trebui
precizate domeniile argumentelor predicatului. Declararea unui predicat se face după sintaxa:
nume_predicat(tip_argument1, tip_argument2, ... )
Domeniile utilizate în declararea unui argument sunt fie domenii standard, fie domenii care
au fost declarate în secţiunea domains.
Numele predicatelor trebuie să înceapă cu literă, urmată apoi de o secvenţă de litere, cifre şi
eventual caracterul _ . Nu are importanţă dacă literele utilizate sunt litere mari sau mici dar este
recomandabil ca prima literă să fie literă mică, deoarece alte versiuni ale limbajului Prolog nu
acceptă literele mari ca primă literă în numele unui predicat. Numele unui predicat nu poate depăşi
250 de caractere.
Aritatea unui predicat este dată de numărul argumentelor sale. Pot exista două predicate cu
acelaşi nume, dar cu arităţi diferite. Aceste predicate trebuie grupate împreună în secţiunile
predicates şi clauses.

Predicate de interacţiune
Prologul, ca limbaj de programare rafinat, oferă posibilitatea interacţiunii unui program cu
echipamentul periferic al calculatorului, cu ecranul şi cu utilizatorul. Aceasta se realizează cu
ajutorul unor predicate predefinite. Cele mai utilizate predicate predefinite de ieşire sunt write şi
writef.
Forma generală a predicatului write este:
write(arg1, arg2,…, argn)
Laborator 3 5
unde arg1, arg2,…, argn sunt obiecte elementare sau variabile. În cazul în care argk este o variabilă,
ea trebuie să fie legată în momentul execuţiei predicatului write. Predicatul write reuşeşte
întotdeauna şi are ca efect afişarea pe ecran a valorilor legate de argumentele sale. Pentru trecerea la
“o linie nouă” se poate utiliza predicatul nl (new line) sau secvenţa de caractere ”\n”.
Predicatul writef se utilizează în cazul transmiterii cu format de ieşire a datelor. Forma sa
este:
writef(şir_format,arg1, arg2,…, argn)
unde şir_format este un şir de caractere care indică formatul utilizat pentru afişarea pe ecran a
valorilor legate de arg1, arg2,…, argn. În general şirul şir_format conţine:
 caractere care se copiază ca atare pe ecran;
 grupuri de specificatori de forma % -<dimens>.<prec>tip (pentru fiecare argument din
listă trebuie să existe un specificator).
Semnificaţia caracterelor dintr-un specificator este următoarea:
 % marchează începutul specificatorului.
 - determină alinierea la stânga şi completarea cu spaţii la dreapta. Prezenţa acestui
caracter este opţională. Implicit: aliniere la dreapta şi completare la stânga cu spaţii sau cu 0.
 dimens specifică dimensiunea minimă a câmpului afişat, nu se efectuează trunchierea
valorii afişate.
 prec este opţional şi arată precizia numărului în virgulă mobilă sau lungimea maximă a
numărului de caractere care se tipăresc în cazul obiectului de tip string.
 tip (opţional) specifică forma în care este afişat argumentul în virgulă mobilă; poate avea
valorile f, e, g cu următoarele semnificaţii:
 f indică virgula mobilă;
 e indică scriere exponenţială;
 g indică scrierea cu format scurt.
Există patru predicate predefinite de intrare: readln, readchar, readint şi readreal.
Predicatul readln permite citirea unui obiect de tip symbol sau string, şi are forma
readln(X)
unde X este o variabilă, care în urma introducerii textului devine legată la valoarea obţinută în urma
citirii.
Predicatul readchar permite citirea unui singur caracter, şi are forma
readchar(X)
unde X este o variabilă care în urma execuţiei va fi legată la caracterul introdus.
Predicatul readint are forma
readint(X)
şi permite citirea unui număr întreg care va fi legat la variabila X.
Predicatul readreal are forma
readreal(X)
şi permite citirea unui număr real care va fi legat la variabila X.

Secţiunea „GOAL”
În esenţă, această secţiune este identică cu antetul unei reguli. Sunt 2 diferenţe între
secţiunea goal şi o regulă:
 cuvântul cheie goal nu este urmat de „: -„;
6 Structura unui program Prolog
 Prolog execută automat interogarea când programul rulează.
În timp ce programul rulează, se încearcă satisfacerea corpului regulii din secţiunea goal.
Dacă toate subobiectivele din secţiunea goal sunt terminate cu succes, programul se încheie cu
success. Dacă, în timp ce programul rulează, unul dintre subobiectivele din interogare se termină cu
eşec, întregul program se termină cu eşec.

Aplicaţii rezolvate
Aplicaţia 1
Se dă următoarea bază de cunoştinţe:
 Vlad este fratele Luciei.
 Mihaela este mama lui Vlad.
 Dana este sora lui Vlad.
 Mihaela locuieşte împreună cu copiii ei.
 Mihaela locuieşte în Deva.
Să se scrie un program Prolog care răspunde întrebării: “Unde locuieşte Lucia?”

Programul corespunzător problemei este:


domains
nume,localitate=symbol
predicates
nondeterm frate(nume,nume)
nondeterm mama(nume,nume)
nondeterm loc(nume,localitate)
clauses
frate(vlad,lucia).
frate(vlad,dana).
mama(mihaela,vlad).
loc(mihaela,deva).
loc(X,Y):-
mama(mihaela,X),
loc(mihaela,Y).
loc(X,Y):-
frate(Z,X),
mama(mihaela,Z),
loc(mihaela,Y).
goal
loc(lucia,Localitate).

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


Localitate=deva
1 Solution
Programul este structurat pe patru secţiuni. În prima secțiune sunt declarate două domenii,
nume, localitate de tip symbol, deci sunt tipuri definite de utilizator. În acest caz, oricine utilizează
programul scris, poate să cunoască foarte bine care sunt argumentele fiecărui predicat.
Laborator 3 7
În a doua secţiune s-au declarat trei predicate: mama, frate cu două argumente de tip nume,
respectiv loc cu două argumente, unul de tip nume, iar altul de tip localitate. În secţiunea legată de
clauze este definit predicatul frate prin două fapte, predicatul mama printr-un fapt şi predicatul loc
printr-un fapt şi două reguli. Ultima secţiune corespunde întrebării din enunţul problemei, întrebare
redată de interogarea loc(lucia,Localitate). În interogare variabila Localitate este o variabilă liberă,
valoarea sa este necunoscută până în momentul în care se găseşte o soluţie. În schimb argumentul
lucia este cunoscut.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Pentru că predicatul din interogare este definit
prin două reguli, căutarea soluţiilor se va reduce la îndeplinirea subobiectivelor acestor regulii.
Neputându-se realiza potrivire cu faptul din definiţia predicatului loc, se încearcă
îndeplinirea celor două obiective din definiţia primei reguli. Variabila liberă X se va lega de atomul
lucia ce reprezintă primul argument al predicatului din interogare, iar variabila liberă Localitate se
va lega de variabila liberă Y. Astfel primul subobiectiv al acestei reguli devine mama(mihaela,lucia)
şi se observă că nu poate fi îndeplinit.
Se trece la a doua regulă ce defineşte predicatul loc şi se încearcă îndeplinirea celor trei
subobiective ale acestei reguli. Variabila liberă X se va lega de atomul lucia ce reprezintă primul
argument al predicatului din interogare, iar variabila liberă Localitate se va lega de variabila liberă
Y. Astfel primul subobiectiv al acestei reguli devine frate(Z,lucia) şi se va urmări îndeplinirea
acestuia. Se observă că se poate realiza potrivire între acest subobiectiv şi primul fapt din secvenţa
de program unde s-a definit predicatul frate, variabila liberă Z devenind legată de atomul vlad. Al
doilea obiectiv al regulii devine mama(mihaela,vlad). Predicatul mama fiind definit printr-un
singur fapt, se observă că acest subobiectiv este îndeplinit. Al treilea subobiectiv al regulii devine
loc(mihaela,Localitate). Pentru că în definiţia predicatului loc există un fapt care are ca prim
argument atomul mihaela, variabila liberă Localitate devine legată de atomul deva, obţinându-se o
soluţie.

Aplicaţia 2
Se dă următoarea bază de cunoştinţe:
 Toţi prietenii Luciei având vârsta de peste 10 ani merg la şcoală.
 Marius este fratele Alexandrei.
 Toţi fraţii Alexandrei sunt născuţi înaintea anului 2005.
 Lucia este prietenă cu toţi fraţii Alexandrei.
 Acum este anul 2016.
Să se scrie un program Prolog care răspunde întrebării: “Merge Marius la şcoală?”

Programul corespunzător problemei este:


domains
nume=symbol
an=ushort
predicates
nondeterm prieten(nume,nume)
nondeterm frate(nume,nume)
nondeterm nascut(nume,an)
nondeterm prezent(an)
nondeterm scoala(nume)
clauses
8 Structura unui program Prolog
frate(alexandra,marius).
nascut(X,2005):-
frate(alexandra,X).
prieten(lucia,X):-
frate(alexandra,X).
prezent(2016).
scoala(X):-
prieten(lucia,X),
nascut(X,N),
prezent(An),
An-N>=10.
goal
scoala(marius).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
yes
Programul este structurat pe patru secţiuni. În prima secțiune sunt declarate două domenii:
nume de tip symbol și an de tip ushort, deci sunt tipuri definite de utilizator. În acest caz, oricine
utilizează programul scris, poate să cunoască foarte bine care sunt argumentele fiecărui predicat.
În a doua secţiune s-au declarat cinci predicate: frate, născut, prieten, prezent şi scoala cu
unul sau două argumente de tip nume, respectiv an. În secţiunea legată de clauze sunt definite
predicatele frate şi prezent prin câte un fapt, iar predicatele nascut, prieten şi scoala prin câte o
regulă. Ultima secţiune corespunde întrebării din enunţul problemei, întrebare redată de interogarea
scoala(marius).
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Pentru că predicatul din interogare este definit
printr-o regulă, căutarea soluţiilor se va reduce la îndeplinirea celor patru subobiective ale acestei
reguli.
Variabila liberă X se va lega de atomul marius ce reprezintă argumentul predicatului din
interogare. Astfel primul subobiectiv al acestei reguli devine prieten(lucia,marius) şi se va urmări
îndeplinirea acestuia. Predicatul prieten fiind definit printr-o regulă, verificarea se reduce la
îndeplinirea subobiectivului frate(alexandra,marius). În secţiunea de clauze unde este definit
predicatul frate se găseşte acest fapt, deci primul subobiectiv din corpul regulii corespunzătoare
predicatului din interogare este îndeplinit.
Al doilea subobiectiv devine nascut(marius,N). Predicatul nascut fiind definit printr-o
regulă, subobiectivul ce urmează a fi îndeplinit devine frate(alexandra,marius), iar variabila liberă
N devine legată de atomul 2005. Predicatul frate fiind definit printr-un singur fapt, se observă că
acest subobiectiv este îndeplinit. Al treilea subobiectiv duce la legarea variabilei libere An de
atomul 2016.
Ultimul obiectiv devine 2016-2005>=10 şi, fiind adevărat, căutarea se încheie cu succes.

Aplicaţia 3
Se dă următoarea bază de cunoştinţe:
 Maria, Ana, Elena și Marius sunt persoane.
 Într-un salon auto există o mașină roșie și una galbenă.
Laborator 3 9
 Mariei îi place mașina roșie.
 Anei îi place pizza.
 Elenei și lui Marius le place tenisul.
 Pizza picanta, mașina galbenă și mașina roșie sunt de vânzare.
Să se scrie un program Prolog care răspunde întrebării: “Cine poate să cumpere ceva și ce este tip
este acel ceva?”

domains
nume,obiect,model=symbol
predicates
nondeterm poate_cumpara(nume,obiect,model)
nondeterm persoana(nume)
nondeterm tip(obiect,model)
nondeterm place(nume,obiect,model)
nondeterm de_vanzare(obiect,model)
clauses
poate_cumpara(X,Y,Z):-
persoana(X),
tip(Y,Z),
place(X,Y,Z),
de_vanzare(Y,Z).

persoana(maria).
persoana(ana).
persoana(elena).
persoana(marius).

tip(masina,galbena).
tip(masina,rosie).
tip(pizza,picanta).

place(maria,masina, rosie).
place(ana, pizza,_).
place(elena, tenis,_).
place(marius, tenis,_).

de_vanzare(pizza,picanta).
de_vanzare(masina,galbena).
de_vanzare(masina,rosie).

goal
poate_cumpara(Cine,Ce,Tip).

Cerinte

1. Testaţi programul cu următoarele teste:


10 Structura unui program Prolog
poate_cumpara(ana,Ce,Model).
poate_cumpara(maria,Ce,_).
poate_cumpara(Cine,masina,_).

2. Adăugaţi şi alte fapte şi eventual reguli acestui program (de exemplu sa se afiseze
persoanele care detin un anumit obiect si pe care il pot vinde daca acel obiect este bun).

Aplicaţia 4
Să se implementeze un program Prolog care determină impozitul plătit de un salariat al cărui
salariu se cunoaşte.

Programul corespunzător problemei este:


domains
nume=string
salariu=ushort
impozit=real
predicates
calcul(nume,salariu,impozit)
rezolva
clauses
calcul(_,S,I):-
I=S*0.16.
rezolva:-
write("Nume salariat: "),
readln(N),
write("Salariu="),
readint(S),
calcul(N,S,I),
write("Impozitul platit de salariatul ",N," este "),
writef("%5.2f",I),
nl.
goal
rezolva.

Analiza programului:
Dacă, la o testare, numele salariatului este Ionescu Mihai, iar salariul acestuia este 1000,
atunci în fereastra de ieşire se va afişa:
Nume salariat: Ionescu Mihai
Salariu=1000
Impozitul platit de salariatul Ionescu Mihai este 160.00
yes
Programul este structurat pe patru secţiuni.
În prima secţiune de definesc trei domenii standard cu nume personalizate pentru că se
doreşte clarificarea rolului fiecărui argument din predicatul calcul declarat în secţiunea următoare.
În a doua secţiune s-au declarat două predicate: predicatul calcul cu trei argumente de tip
nume, salariu, respectiv impozit şi predicatul rezolva fără argumente.
Laborator 3 11
În secţiunea legată de clauze sunt definite cele două predicate, fiecare prin intermediul unei
reguli.
În prima regulă se utilizează variabila anonimă deoarece modul de calcul al impozitului nu
depinde de numele salariatului. Deoarece variabila I utilizată în această regulă este liberă, corpul
acestei reguli corespunde unei instrucţiuni de atribuire din limbajele procedurale.
Corpul celei de-a doua reguli utilizează predicate predefinite de interacţiune. Pentru citirea
datelor se utilizează predicatul readln pentru citirea numelui salariatului şi predicatul readint pentru
citirea salariului. Pentru afişarea rezultatului se utilizează atât predicatul write, cât şi predicatul
writef . Prin utilizarea ultimului predicat amintit se precizează un format de afişare pentru rezultat.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea rezolva.

Aplicaţia 5
Să se implementeze un program Prolog care determină volumul unui paralelipiped
dreptunghic când se introduc de la intrarea standard, lungimea, lăţimea şi înălţimea acestuia.

Programul corespunzător problemei este:


domains
lungime=real
latime=real
inaltime=real
volum=real
paralelipiped=dimensiuni(lungime,latime,inaltime)
predicates
calcul(paralelipiped,volum)
citeste(paralelipiped)
rezolva
clauses
calcul(dimensiuni(Lungime,Latime,Inaltime),Volum):-
Volum=Lungime*Latime*Inaltime.
citeste(dimensiuni(Lungime,Latime,Inaltime)):-
write("Lungime= "),
readreal(Lungime),
write("Latime="),
readreal(Latime),
write("Inaltime="),
readreal(Inaltime).
rezolva:-
citeste(Paralelipiped),
calcul(Paralelipiped, Volum),
writef("Volumul paralelipipedului este %8.3f",Volum),
nl.
goal
rezolva.

Analiza programului:
12 Structura unui program Prolog
Dacă de la tastatură se vor introduce următoarele valori: 10 pentru lungime, 8 pentru lăţime
şi 5 pentru înălţime, atunci în fereastra de ieşire se va afişa:
Lungime=10
Lăţime=8
Înălţime=5
Volumul paralelipipedului este 400.000
yes
Programul este structurat pe patru secţiuni.
În prima secţiune de definesc patru domenii standard cu nume personalizate pentru că se
doreşte clarificarea rolului fiecărui argument din predicatele declarate în secţiunea următoare. Tot în
această secţiune se declară şi un domeniu compus care permite tratarea informaţiilor despre un
paralelipiped dreptunghic ca un singur item, astfel încât să poată fi extrase uşor componentele
acestuia.
În a doua secţiune s-au declarat trei predicate: predicatul calcul cu două argumente de tip
paralelipiped, respectiv volum, predicatul citeste cu un argument de tip paralelipiped şi predicatul
rezolva fără argumente.
În secţiunea legată de clauze sunt definite cele trei predicate, fiecare prin intermediul unei
reguli.
Deoarece variabila Volum utilizată în prima regulă este liberă, corpul acestei reguli
corespunde unei instrucţiuni de atribuire din limbajele procedurale, atribuire utilizată pentru
calculul volumului paralelipipedului.
Corpul celei de-a doua reguli utilizează predicate predefinite de interacţiune. Pentru citirea
datelor se utilizează predicatul readreal pentru citirea dimensiunilor paralelipipedului, aceste valori
fiind de tip real.
În ultima regulă, pentru afişarea rezultatului se utilizează predicatul writef prin intermediul
căruia se precizează un format de afişare pentru rezultat.
În primele două reguli, pentru că se doreşte accesul la componentele obiectului de tip
paralelipiped dreptunghic, se utilizează functorul acestui domeniu compus. În schimb, în primele
două subobiective ale ultimei reguli, nemaifiind necesare informaţiile despre dimensiunile
paralelipipedului dreptunghi, pentru argumentele de tip compus se utilizează variabile.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea rezolva.

Aplicaţii propuse
1. Se dă următoarea bază de cunoştinţe.
 Mircea este tatăl Danei.
 Mihaela este sora lui Mircea.
 Gheorghe este tatăl Mihaelei.
 Elena este mama lui Mircea.
 Laura este fiica Mihaelei.
Să se scrie un program Prolog care răspunde întrebării: “Cum se numesc bunicii Laurei?”

2. Se dă următoarea bază de cunoştinţe.


 Doi copii pot juca într-un meci de tenis dacă au aceeași vârstă.
 Petru este un copil ce are 9 ani.
 Paul este un copil ce are 10 ani.
Laborator 3 13
 Andrei este un copil ce are 9 ani.
 Marius este un copil ce are 9 ani.
Dacă se consideră că se vor organiza cate 2 jocuri pentru fiecare pereche de jucători, să se scrie
un program Prolog care afișează toate perechile de copii care pot juca într-un turneu de tenis.

3. Se dă următoarea bază de cunoştinţe.


 Toţi elevii cu vârsta de peste 15 ani de la colegiul de informatică merg în excursie.
 Ioan este tatăl lui Ovidiu.
 Toţi fraţii lui Silviu sunt elevi la colegiul de informatică.
 Silviu este băiatul lui Ioan.
 Copiii lui Ioan care sunt elevi la colegiul de informatică au cel puţin 15 ani.
 Acum este anul 2015.
Să se scrie un program Prolog care răspunde întrebării: “Va merge Ovidiu în excursie?”

4. Să se implementeze un program Prolog care determină aria unui dreptunghi când


lungimea şi lăţimea acestuia se introduc de la intrarea standard.

5. Să se implementeze un program Prolog care determină maximul a două numere citite de


la intrarea standard.

6. Să se implementeze un program Prolog care determină media unui elev la o disciplină


când se citesc, de la intrarea standard, notele obţinute la aceea disciplină (nota
finala=(2*nota examen+nota laborator)/3).
Laborator 4 1

UNIFICARE ŞI BACKTRACKING

Noţiuni teoretice

Unificare
Procesul de căutare care încearcă să găsească o potrivire a unui obiectiv cu o clauză din
secţiunea clauses poartă denumirea de unificare, ceea ce presupune găsirea unei potriviri între
structurile de date utilizate în apelul obiectivului şi cele din clauzele date. În Prolog, unificarea
implementează câteva dintre procedeele cunoscute din alte limbaje de programare cum ar fi:
atribuirea, transmiterea parametrilor unei proceduri, selecţie case, construcţia unor structuri de date,
etc.
Când Prolog încearcă să satisfacă un obiectiv, trebuie testată fiecare clauză din program
corespunzătoare predicatelor ce compun obiectivul. Programul va căuta o potrivire de la începutul
programului şi până la sfârşit. Când o clauză se potriveşte cu un obiectiv, Prolog caută să atribuie
valori pentru variabilele libere astfel încât clauza şi obiectivul să fie identice. Se spune că obiectivul
se unifică cu clauza.

Backtracking
În mai multe situaţii când se dezvoltă probleme reale trebuie urmată o cale până la concluzia
sa logică. Dacă, pentru calea aleasă nu se ajunge la răspunsul dorit, atunci trebuie aleasă o altă cale.
Prolog utilizează metoda numită „revenire înapoi şi încercare din nou” (backing – up – and – trying
- again). Într-un program Prolog este posibil să se obţină o soluţie prin alegerea unei alternative din
două posibile. Se pune un indicator la punctul de ramificare (cunoscut sub numele de backtracking
point) şi se selectează primul subobiectiv. Dacă acesta eşuază, Prolog va reveni înapoi (backtrack)
la punctul respectiv şi va încerca subobiectivul alternativ.
Reguli specifice acestei metode:
 Când Prolog începe satisfacerea unui obiectiv, începe căutarea unei posibile unificări de
la începutul programului.
 Când se face un apel, de asemenea Prolog va începe căutarea de la început spre sfârşit.
 Dacă un apel a găsit o potrivire, el se va încheia cu success, şi se va încerca în continuare
satisfacerea următorului subobiectiv.
 Odată ce o variabilă a fost legată, într-un program Prolog, singura modalitate de a deveni
liberă este prin backtracking.
 Subobiectivele trebuie satisfăcute în ordine, de la început spre sfârşit.
 Clauzele - predicat sunt testate în ordinea în care apar în program, de sus în jos.
 Când un subobiectiv găseşte o potrivire a antetului unei reguli, în continuare trebuie
satisfăcut corpul regulii respective. Corpul regulii va constitui un nou set de subobiective care
trebuie satisfăcute.
 Un obiectiv este satisfăcut când este găsită o potrivire pentru toate faptele de la
extremităţile arborelui corespunzător acestui obiectiv.
Cu ajutorul tehnicii backtracking, Prolog poate găsi toate soluţiile unui probleme, nu doar pe
prima.
2 Unificare şi backtracking

Controlul căutării soluţiilor


Mecanismul backtracking implementat de către Prolog poate conduce la căutări care nu sunt
necesare; din acest motiv poate apărea o ineficienţă în căutarea soluţiilor. De exemplu sunt situaţii
în care se doreşte aflarea unei singure soluţii, pentru o anumită problemă particulară; în alte situaţii,
dimpotrivă, ar putea fi necesar ca mecanismul de căutare să fie forţat să continue căutarea chiar
dacă un anumit obiectiv particular a fost îndeplinit.
Prolog furnizează două predicate pentru controlul căutării:
 predicatul fail care se utilizează când se doreşte forţarea mecanismului backtracking;
 predicatul cut care se simbolizează prin ! şi se utilizează când se doreşte prevenirea
mecanismului backtracking.

Utilizarea predicatului fail


La apelul acestui predicat, Prolog începe căutarea prin backtracking (forţat, de la început).
Acest lucru poat fi necesar în anumite situaţii pentru găsirea unor soluţii alternative. Predicatul fail,
forţează apariţia unui eşec, şi, în consecinţă, încurajează mecanismul backtracking (revenire înapoi
şi încercare din nou – backing up and trying again). Efectul predicatului fail corespunde efectului
comparaţiei 2 = 3 sau oricărui alt predicat imposibil.
Predicatul fail nu poate fi niciodată satisfăcut, astfel încât Prolog este obligat să revină
înapoi şi va reveni în punctul în care se găsesc mai multe soluţii. Când are loc revenirea înapoi,
Prolog revine la ultimul apel care poate produce mai multe soluţii. Un astfel de apel este numit
nedeterminist. Un apel nedeterminist poate produce mai multe soluţii, în timp ce un apel determinist
produce o singură soluţie.
Predicatul fail nu poate oferi noi soluţii, astfel încât Prolog trebuie să revină înapoi, în acest
caz la primul subobiectiv al regulei.
După un predicat fail în corpul unei reguli nu se mai pune alt subobiectiv deoarece dacă
oricum fail eşuează, nu se mai face nici un apel la vreun subobiectiv plasat după fail.

Utilizarea predicatului cut


Predicatul cut este utilizat în Prolog pentru a preveni backtracking, el este scris ca un semn
de exclamare (!). Efectul acestui predicat este următorul: este imposibil să se revină prin
backtracking peste un cut.
Predicatul cut se pune într-o clauză în acelaşi fel în care se pune un subobiectiv. După
procesarea corpului unei reguli, apelul predicatului cut se va finaliza cu succes imediat, şi se va
apela următorul subobiectiv (dacă acesta există). După ce procesarea predicatului cut a fost
încheiată, este imposibilă revenirea la subobiectivele care sunt plasate înaintea predicatului cut.
Există două modalităţi de utilizare a predicatului cut:
 Atunci când se cunosc anticipat posibilităţile care nu vor conduce niciodată la soluţii
care au sens şi ar fi o pierdere inutilă de timp şi de spaţiu de memorie să se caute soluţii alternative.
Programele rezultate vor rula mai repede şi vor utiliza mai puţină memorie. Acest tip de predicat se
mai numeşte „cut verde”(green cut). Cut verde este folosit doar pentru optimizare, eliminarea lui nu
strica logica programului, clauzele predicatului pot fi rescrise si in alta ordine.
 Când logica unui program cere utilizarea unui predicat cut pentru a preveni luarea în
considerare a unor subobiective alternative, atunci predicatul se numeşte “cut roşu”(red cut). Cut
roșu se folosește practic când nu este vorba de un cut verde, eliminarea lui sau schimbarea ordinii
clauzelor va schimba logica programului.
Dacă fail se întâlneste după predicatul cut, nu se mai face backtracking.
Laborator 4 3
Exemplu de cut verde si cut rosu:
Se consideră exemplul de definire a predicatului de aflare a minimului dintre două numere,
în următoarele două variante:

min1(X, Y, X) :- X =< Y, !. % cut verde


min1(X, Y, Y) :- X > Y.
min2(X, Y, X) :- X =< Y, !. % cut roşu
min2(X, Y, Y).

În definiţia predicatului min1 se utilizează un cut verde; el este pus pentru creşterea
eficienţei programului, dar ordinea clauzelor de definire a lui min1 poate fi schimbată fără nici un
efect asupra rezultatului programului. În cazul predicatului min2 se utilizează un cut roşu,
asemănător structurii daca_atunci_altfel. Dacă se schimbă ordinea clauzelor de definire a
predicatului min2:

min2(X, Y, Y).
min2(X, Y, X) :- X =< Y, !.

rezultatul programului va fi evident incorect pentru valori X < Y.


Într-un program Prolog, dacă se întâlnește predicatul !, se va gasi prima soluție pentru un
predicat, apoi se vor ignora definițiile din program ale aceluiași predicat.

Combinaţia !, fail
Combinaţia !, fail este deseori utilizată în Prolog şi are rolul de negaţie. Se mai spune că
limbajul Prolog modelează negaţia ca eşec al satisfacerii unui scop (negaţia ca insucces), aceasta
fiind de fapt o particularizare a ipotezei lumii închise. Combinaţia !, fail este echivalentă cu un
predicat standard existent în Prolog, predicatul not. Predicatul not admite ca argument un predicat
Prolog şi reuşeşte dacă predicatul argument eşuează. Dacă se întâlnește combinația !, fail,, nu se
mai face backtracking.
Exemplu: enunţul "Un individ este rău dacă nu este bun." se poate exprima astfel:
predicates
nondeterm bun (symbol)
nondeterm rau(symbol)
clauses
bun(gelu).
bun(vlad).
bun(mihai).
rau(X) :- bun(X), !, fail.
rau(_).

Pentru urmatoarele teste („goal”) se va furniza în fereastra de ieşire următorul rezultat:


goal
rau(gelu).
no
rau(mihai).
no
rau(petru).
4 Unificare şi backtracking
yes

Aplicaţii rezolvate
Aplicaţia 1
Se dă următoarea bază de cunoştinţe:
 Slavici a scris „Mara” care are 280 de pagini.
 Sadoveanu a scris „Neamul Şoimăreştilor”care are 300 de pagini .
 Creanga a scris „Amintiri din copilărie” care are 100 de pagini.
 O carte este roman dacă are cel puţin 250 de pagini.
Să se scrie un program Prolog care răspunde întrebării: “Care, dintre cărţile menționate, sunt
romane?”

Programul corespunzător problemei este:


domains
titlu,autor = symbol
pagini = ushort
predicates
carte(titlu, pagini)
nondeterm scrisa_de(autor, titlu)
nondeterm roman(titlu)
clauses
scrisa_de(slavici,"Mara").
scrisa_de(creanga, "Amintiri din copilarie").
scrisa_de(sadoveanu, "Neamul Soimarestilor").
carte("Neamul Soimarestilor", 300).
carte("Amintiri din copilarie", 100).
carte("Mara",280).
roman(Titlu):-
scrisa_de(_, Titlu), carte(Titlu, NrPagini), NrPagini >=250.
goal
roman(X).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
X=Mara
X=Neamul Soimarestilor
2 Solutions
Programul este structurat pe patru secţiuni.
În prima secţiune de definesc trei domenii standard cu nume personalizate pentru că se
doreşte clarificarea rolului fiecărui argument din predicatele declarate în secţiunea următoare.
În a doua secţiune s-au declarat trei predicate: predicatul carte cu două argumente de tip
titlu, respectiv pagini, predicatul scrisa_de cu două argumente de tip autor, respectiv titlu şi
predicatul roman cu un argument de tip titlu.
În secţiunea legată de clauze sunt definite cele trei predicate, primele două prin intermediul
faptelor, iar ultimul prin intermediul unei reguli.
Laborator 4 5
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
roman(X). În interogarea „roman(X)” variabila X este o variabilă liberă, valoarea sa este
necunoscută până în momentul în care se găseşte o soluţie.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Predicatul roman fiind definit prin intermediul
unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli. Primul
subobiectiv corespunde predicatului scrisa_de, iar variabila liberă Titlu va deveni legată de atomul
"Mara", primul argument al predicatului din subobiectiv fiind înlocuit cu variabila anonimă,
deoarece în determinarea acelor cărţi care sunt romane nu ne interesează numele autorului. Această
unificare reprezintă un prim punct de ramificare.
Al doilea subobiectiv al regulii devine carte("Mara",NrPagini). Se va căuta în secţiunea
unde este definit predicatul carte dacă există un fapt al cărui prim argument să fie atomul "Mara".
Se va găsi potrivire la cel de-al treilea fapt din definiţia predicatului carte, variabila liberă NrPagini
devenind legată de atomul 280. Această unificare reprezintă al doilea punct de ramificare.
Al treilea subobiectiv al regulii devine 280>=250, subobiectiv încheiat cu succes. Astfel s-a
obţinut o primă soluţie.
Căutarea nu se opreşte aici, revenindu-se la al doilea punct de ramificare variabila NrPagini
redevine liberă şi se încearcă o nouă potrivire la următorul fapt. Se observă că primul argument de
la subobiectiv, atomul "Mara", nu mai poate fi unificat cu primul argument de la celelalte fapte din
secţiunea de clauze.
Revenindu-se la primul punct de ramificare, variabila Titlu redevine liberă şi se încearcă o
nouă potrivire la următorul fapt corespunzător predicatului scrisa_de. Variabila Titlu devine legată
de atomul "Amintiri din copilarie", iar al doilea subobiectiv al regulii devine carte("Amintiri din
copilarie",NrPagini). Se va căuta în secţiunea unde este definit predicatul carte dacă există un fapt
al cărui prim argument să fie atomul "Amintiri din copilarie". Se va găsi potrivire la cel de-al doilea
fapt din definiţia predicatului carte, variabila liberă NrPagini devenind legată de atomul 100. Al
treilea subobiectiv al regulii devine 100>=250, subobiectiv încheiat cu eşec.
Căutarea nu se opreşte aici, revenindu-se la al doilea punct de ramificare variabila NrPagini
redevine liberă şi se încearcă o nouă potrivire la următorul fapt. Se observă că primul argument de
la subobiectiv, atomul "Amintiri din copilarie", nu mai poate fi unificat cu primul argument de la
celelalte fapte din secţiunea de clauze.
Revenindu-se la primul punct de ramificare, variabila Titlu redevine liberă şi se încearcă o
nouă potrivire la următorul fapt corespunzător predicatului scrisa_de. Variabila Titlu devine legată
de atomul "Neamul Soimarestilor", iar al doilea subobiectiv al regulii devine carte("Neamul
Soimarestilor",NrPagini). Se va căuta în secţiunea unde este definit predicatul carte dacă există un
fapt al cărui prim argument să fie atomul "Neamul Soimarestilor". Se va găsi potrivire la primul
fapt din definiţia predicatului carte, variabila liberă NrPagini devenind legată de atomul 300. Al
treilea subobiectiv al regulii devine 300>=250, subobiectiv încheiat cu succes. Astfel se obţine o
nouă soluţie.
Mecanismul de căutare cu revenire se încheie odată cu revenirea la primul punct de
ramificare, deoarece pentru variabila liberă Titlu nu se mai poate realiza nicio potrivire.

Aplicaţia 2
Se dă următoarea bază de cunoştinţe:
 Mamiferele şi peştii sunt animale.
 Zebra si foca sunt mamifere.
 Rechinul și păstrăvul sunt peşti.
6 Unificare şi backtracking
 Rechinul şi foca trăiesc în apă.
 Zebra şi foca trăiesc pe pamânt.
Să se scrie un program Prolog care răspunde întrebării: “Cine înoată?”

Programul corespunzător problemei este:


predicates
nondeterm tip(symbol, symbol)
nondeterm este_un(symbol, symbol)
traieste(symbol, symbol)
nondeterm inoata(symbol)
clauses
tip(mamifer,animal).
tip(peste,animal).
este_un(zebra,mamifer).
este_un(rechin,peste).
este_un(pastrav,peste).
este_un(foca,mamifer).
traieste(zebra,pe_pamant).
traieste(foca,pe_pamant).
traieste(foca,in_apa).
traieste(rechin,in_apa).
inoata(Y):-
tip(X,animal),
este_un(Y,X),
traieste(Y,in_apa).
goal
inoata(Cine).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Cine=rechin
Cine=foca
2 Solutions
Programul este structurat pe trei secţiuni.
În prima secţiune s-au declarat patru predicate: predicatul tip cu două argumente de tip
symbol, predicatul este_un cu două argumente de tip symbol, predicatul traieste cu două argumente
de tip symbol şi predicatul inoata cu un argument de tip symbol.
În secţiunea legată de clauze sunt definite cele patru predicate, primele trei prin intermediul
faptelor, iar ultimul prin intermediul unei reguli.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
inoata(Cine). În interogarea „inoata(Cine)” variabila Cine este o variabilă liberă, valoarea sa este
necunoscută până în momentul în care se găseşte o soluţie.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Predicatul inoata fiind definit prin intermediul
unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli. Primul
Laborator 4 7
subobiectiv corespunde predicatului tip, iar variabila liberă X va deveni legată de atomul mamifer.
Această unificare reprezintă un prim punct de ramificare.
Al doilea subobiectiv al regulii devine este_un(Y,mamifer). Se va căuta în secţiunea unde
este definit predicatul este_un dacă există un fapt pentru care al doilea argument să fie atomul
mamifer. Se va găsi potrivire la primul fapt din definiţia predicatului este_un, variabila liberă Y
devenind legată de atomul zebra. Această unificare reprezintă al doilea punct de ramificare.
Al treilea subobiectiv al regulii devine traieste(zebra,in_apa), iar în continuare se va căuta
în secţiunea unde este definit predicatul traieste dacă există un fapt care să coincidă cu
subobiectivul anterior precizat. Negăsindu-se un astfel de fapt, căutarea se încheie cu eşec.
Căutarea nu se opreşte aici, revenindu-se la al doilea punct de ramificare variabila Y
redevine liberă şi se încearcă o nouă potrivire la următorul fapt. Se observă că al doilea argument de
la subobiectiv, atomul mamifer, nu mai poate fi unificat cu al doilea argument de la celelalte fapte
corespunzătoare predicatului este_un din secţiunea de clauze.
Revenindu-se la primul punct de ramificare, variabila X redevine liberă şi se încearcă o nouă
potrivire la următorul fapt corespunzător predicatului tip. Variabila X devine legată de atomul peste,
iar al doilea subobiectiv al regulii devine este_un(Y,peste). Se va căuta în secţiunea unde este definit
predicatul este_un dacă există un fapt pentru care al doilea argument să fie atomul peste. Se va găsi
potrivire la cel de-al doilea fapt din definiţia predicatului este_un, variabila liberă Y devenind legată
de atomul rechin.
Al treilea subobiectiv al regulii devine traieste(rechin,in_apa), iar în continuare se va căuta
în secţiunea unde este definit predicatul traieste dacă există un fapt care să coincidă cu
subobiectivul anterior precizat. Găsindu-se un astfel de fapt, căutarea se încheie cu succes,
obţinându-se o primă soluţie.
Căutarea nu se opreşte aici, revenindu-se la al doilea punct de ramificare variabila Y
redevine liberă şi se încearcă o nouă potrivire la următorul fapt. Se va găsi potrivire la cel de-al
treilea fapt din definiţia predicatului este_un, variabila liberă Y devenind legată de atomul pastrav.
Al treilea subobiectiv al regulii devine traieste(pastrav,in_apa), iar în continuare se va căuta
în secţiunea unde este definit predicatul traieste dacă există un fapt care să coincidă cu
subobiectivul anterior precizat. Negăsindu-se un astfel de fapt, căutarea se încheie cu eşec.
Variabila Y redevine liberă, iar ăn continuare se va găsi potrivire la ultimul fapt din definiţia
predicatului este_un, variabila liberă Y devenind legată de atomul foca.
Al treilea subobiectiv al regulii devine traieste(foca,in_apa), iar în continuare se va căuta în
secţiunea unde este definit predicatul traieste dacă există un fapt care să coincidă cu subobiectivul
anterior precizat. Găsindu-se un astfel de fapt, căutarea se încheie cu succes, obţinându-se o nouă
soluţie.
Variabila Y redevine liberă, dar pentru că nu mai există nicio o definiţie a predicatului
este_un, se revine la primul punct de ramificare. Mecanismul de căutare cu revenire se încheie
odată cu revenirea la primul punct de ramificare, deoarece pentru variabila liberă X nu se mai poate
realiza nicio potrivire.

Aplicaţia 3
Se dă următoarea bază de cunoştinţe:
 Preţul maşinii fiat de culoare verde este 24000.
 Preţul maşinii audi de culoare albă este 28000.
 Preţul maşinii renault de culoare gri este 15000.
 Preţul maşinii mercedes de culoare negru este 24000.
 Preţul maşinii mercedes de culoare roşie este 26000.
8 Unificare şi backtracking
 Preţul maşinii porsche de culoare roşie este 24000.
 Preţul maşinii skoda de culoare albă este 19000.
 Preţul maşinii citroen de culoare galbenă este 27000.
 Preţul maşinii citroen de culoare verde este 22000.
 Culorile roşu şi galben sunt vesele.
 Culorile negru şi gri sunt sobre.
 Culoarea albă este clasică.
 Culoarea verde este ţipătoare.
 O maşină va fi cumparată dacă culoarea sa este veselă şi preţul este mai mare decât
25000.
Să se scrie un program Prolog care răspunde întrebării: “Care este maşina ce va fi cumpărată
prima?”

Programul corespunzător problemei este:


predicates
nondeterm cumpara_masina(symbol,symbol)
nondeterm masina(symbol,symbol,integer)
culoare(symbol,symbol)
clauses
cumpara_masina(Model,Culoare):-
masina(Model,Culoare,Pret),
culoare(Culoare,vesela),!,
Pret > 25000.
masina(fiat,verde,24000).
masina(audi,alb,28000).
masina(mercedes,rosu,26000).
masina(mercedes,negru,24000).
masina(renault,gri,15000).
masina(porsche,rosu,24000).
masina(skoda,alb,19000).
masina(citroen,galben,27000).
masina(citroen,verde,22000).
culoare(rosu,vesela).
culoare(negru,sobra).
culoare(verde,tipatoare).
culoare(alb,clasic).
culoare(gri,sobra).
culoare(galben,vesela).
goal
cumpara_masina(Model,Culoare).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Model=mercedes, Culoare=rosu
1 Solution
Programul este structurat pe trei secţiuni.
Laborator 4 9
În prima secţiune s-au declarat trei predicate: predicatul cumpara_masina cu două
argumente de tip symbol, predicatul masina cu două argumente de tip symbol şi un argument de tip
integer şi predicatul culoare cu două argumente de tip symbol.
În secţiunea legată de clauze sunt definite cele trei predicate, primul prin intermediul unei
reguli, iar celelalte prin intermediul faptelor.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
cumpara_masina(Model,Culoare). În interogarea „cumpara_masina (Model,Culoare)” variabilele
Model şi Culoare sunt variabile libere, valorile lor fiind necunoscute până în momentul în care se
găseşte o soluţie.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Predicatul cumpara_masina fiind definit prin
intermediul unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli.
Primul subobiectiv corespunde predicatului masina, iar variabilele libere Model, Culoare, Pret vor
deveni legate de atomii fiat, verde şi, respectiv, 25000. Această unificare reprezintă un prim punct
de ramificare.
Al doilea subobiectiv al regulii devine culoare(verde,vesela). Se va căuta în secţiunea unde
este definit predicatul culoare dacă există un fapt care să coincidă cu subobiectivul anterior
precizat. Negăsindu-se un astfel de fapt, căutarea se încheie cu eşec.
Căutarea nu se opreşte aici, revenindu-se la punctul de ramificare. Variabilele Model,
Culoare, Pret redevin libere şi se încearcă o nouă potrivire la următorul fapt. Variabilele libere
Model, Culoare, Pret vor deveni legate de atomii audi, alb şi, repsectiv, 28000.
Al doilea subobiectiv al regulii devine culoare(alb,vesela). Se va căuta în secţiunea unde
este definit predicatul culoare dacă există un fapt care să coincidă cu subobiectivul anterior
precizat. Negăsindu-se un astfel de fapt, căutarea se încheie cu eşec.
Căutarea nu se opreşte şi se revine la punctul de ramificare. Variabilele Model, Culoare,
Pret redevin libere şi se încearcă o nouă potrivire la următorul fapt. Variabilele libere Model,
Culoare, Pret vor deveni legate de atomii mercedes, rosu şi, repsectiv, 26000.
Al doilea subobiectiv al regulii devine culoare(rosu,vesela). Se va căuta în secţiunea unde
este definit predicatul culoare dacă există un fapt care să coincidă cu subobiectivul anterior
precizat. Găsindu-se un astfel de fapt, se ajunge la predicatul cut şi se trece peste acesta de la
stânga la dreapta. În continuare se verifică dacă ultimul subobiectiv este îndeplinit. Această
verificare se încheie cu succes, obţinându-se o soluţie. Pentru că s-a utilizat predicatul cut,
mecanismul de căutare cu revenire este stopat.

Aplicaţia 4
Să se implementeze un program Prolog pentru a aranja un turneu între jucătorii în vârsta de
nouă ani ai clubului respectiv. Se vor organiza două jocuri pentru fiecare pereche de jucători.
Scopul va fi să se găsească toate perechile posibile pe care le putem face cu jucătorii în vârsta de 9
ani.

Programul corespunzător problemei este:


domains
copil=symbol
varsta=byte
predicates
nondeterm jucator(copil, varsta)
perechi
10 Unificare şi backtracking
clauses
jucator(marius,9).
jucator(paul,10).
jucator(cristian,9).
jucator(sorin,9).
perechi:-
jucator(Copil1, 9),
jucator(Copil2, 9),
Copil1<>Copil2,
write("Pereche: (",Copil1,",",Copil2,")"),
nl, fail.
perechi.
goal
perechi.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Pereche: (marius,cristian)
Pereche: (marius,sorin)
Pereche: (cristian,marius)
Pereche: (cristian,sorin)
Pereche: (sorin,marius)
Pereche: (sorin,cristian)
yes
Programul este structurat pe patru secţiuni.
În prima secţiune de definesc două domenii standard cu nume personalizate pentru că se
doreşte clarificarea rolului fiecărui argument din predicatele declarate în secţiunea următoare.
În a doua secţiune s-au declarat două predicate: predicatul jucator cu două argumente de tip
copil, respectiv varsta şi predicatul perechi fără argumente.
În secţiunea legată de clauze sunt definite cele două predicate, primul prin intermediul
faptelor, iar al doilea prin intermediul unei reguli şi a unui fapt.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
perechi.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Predicatul perechi fiind definit prin intermediul
unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli. Primele două
subobiective corespund predicatului jucator. După ce variabilele libere Copil1 şi Copil2 devin
legate, iar al treilea obiectiv al regulii este îndeplinit, predicatul predefinit write va afişa numele
celor 2 copii ce vor forma o pereche, obţinându-se o soluţie. Regula se încheie cu predicatul fail,
prin intermediul căruia se presupune că a eşuat căutarea şi se forţează mecanismul de bactracking.
Datorită prezenţei ultimului fapt din secţiunea de clauze, afişarea perechilor ce se pot forma
se încheie cu succes. Pentru ca rezultatul ce se afişează în fereastra de ieşire să nu se încheie cu
mesajul no se utilizează ultimul fapt din definiţia predicatului perechi. Astfel rezultatul afişat în
fereastra de ieşire se va încheia cu mesajul yes.
Laborator 4 11
Aplicaţia 5
Daniel, Eduard, Paul si Maria, fiecare cunosc câte un limbaj de programare diferit si fiecare
este specializat într-un domeniu diferit. Se cunosc următoarele:
 Cel ce programează în Java e specialist în biologie.
 Eduard nu știe nici Prolog nici HTML, nici matematică.
 Daniel nu cunoaște nici Prolog, nici HTML, nici matematică .
 Specialistul în chimie nu programează în HTML.
 Paul nu e specialist în fizică si nu programează în Prolog.
 Al patrulea limbaj de programare este CPP.
În ce limbaj si în ce domeniu e specializat fiecare?

Programul corespunzător problemei este:

predicates
persoana(symbol)
nondeterm limbaj(symbol)
nondeterm domeniu(symbol)
nondeterm reguli(symbol,symbol,symbol)
nondeterm rezolva(symbol,symbol,symbol)
diferit(symbol,symbol,symbol,symbol)
nondeterm solutie(symbol,symbol,symbol,symbol,symbol,symbol,symbol,symbol)

clauses
%fapte
persoana(daniel).
persoana(eduard).
persoana(paul).
persoana(maria).

limbaj(html).
limbaj(cpp).
limbaj(java).
limbaj(prolog).

domeniu(mate).
domeniu(fizica).
domeniu(chimie).
domeniu(bio).

%reguli
reguli(_,Limbajul,Domeniul):-Limbajul=java,!,Domeniul=bio.
reguli(Cineva,Limbajul,Domeniul):-
Cineva=eduard,!,Limbajul<>"prolog",Limbajul<>"html",Domeniul<>"mate".
reguli(Cineva,Limbajul,Domeniul):-
Cineva=daniel,!,Limbajul<>"prolog",Limbajul<>"html",Domeniul<>"mate".
reguli(_,Limbajul,Domeniul):-Limbajul<>"html",!,Domeniul=chimie.
12 Unificare şi backtracking
reguli(Cineva,Limbajul,Domeniul):-
Cineva=paul,!,Limbajul<>"prolog",Domeniul<>"fizica".

rezolva(X,Y,Z):-
persoana(X),
limbaj(Y),
domeniu(Z),
reguli(X,Y,Z).

diferit(X1,X2,X3,X4):-X1<>X2,X1<>X3,X1<>X4,X2<>X3,X2<>X4,X3<>X4.
solutie(Y1,Z1,Y2,Z2,Y3,Z3,Y4,Z4):-
rezolva(daniel,Y1,Z1),
rezolva(eduard,Y2,Z2),
rezolva(paul,Y3,Z3),
rezolva(maria,Y4,Z4),
diferit(Y1,Y2,Y3,Y4),
diferit(Z1,Z2,Z3,Z4).
goal
solutie(Y1,Z1,Y2,Z2,Y3,Z3,Y4,Z4),
write("\nDaniel ",Y1," ",Z1),
write("\nEduard ",Y2," ",Z2),
write("\nPaul ",Y3," ",Z3),
write("\nMaria ",Y4," ",Z4),nl,fail.

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


Daniel java bio
Eduard cpp fizica
Paul html mate
Maria prolog chimie

Daniel cpp fizica


Eduard java bio
Paul html mate
Maria prolog chimie
No Solution

Aplicaţia 6

Se dă următoarea bază de cunoştinţe:


 Un student este bursier dacă are media mai mare sau egală cu 8.50 şi nu este restanţier.
 Lucia are media 8.50.
 Sebastian are media 7.00.
 Ioana are media 8.70.
 Lucia şi Sebastian sunt restanţieri.
Să se scrie un program Prolog care răspunde întrebării: “Cine este bursier?”

Programul corespunzător problemei este:


Laborator 4 13
domains
nume = symbol
media = real
predicates
nondeterm bursier(nume)
nondeterm student(nume, media)
restantier(nume)
clauses
bursier(Nume):-
student(Nume, Media),
Media>=8.5,
not(restantier(Nume)).
student("Lucia", 8.5).
student("Sebastian", 7.0).
student("Ioana", 8.7).
restantier("Lucia").
restantier("Sebastian").
goal
bursier(Cine).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Cine=Ioana
1 Solution
Programul este structurat pe patru secţiuni. În prima secţiune de definesc două domenii
standard cu nume personalizate pentru că se doreşte clarificarea rolului fiecărui argument din
predicatele declarate în secţiunea următoare.
În a doua secţiune s-au declarat trei predicate: predicatul bursier cu un argument de tip
nume, predicatul student cu două argumente de tip nume, respectiv media şi predicatul restantier cu
un argument de tip nume.
În secţiunea legată de clauze sunt definite cele trei predicate, primul prin intermediul unei
reguli, iar celelalte prin intermediul faptelor.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
bursier(Cine). În interogarea „bursier(Cine)” variabila Cine este o variabilă liberă, valoarea sa este
necunoscută până în momentul în care se găseşte o soluţie.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Predicatul bursier fiind definit prin intermediul
unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli. Primul
subobiectiv corespunde predicatului student, iar variabilele libere Nume şi Media vor deveni legate
de atomii "Lucia", respectiv 8.5. Această unificare reprezintă un prim punct de ramificare.
Al doilea subobiectiv al regulii devine 8.5>=8.5, subobiectiv încheiat cu succes. Al treilea
subobiectiv este definit prin intermediul predicatelor not şi restantier. Acesta se va încheia cu eşec
pentru că în secţiunea de clauze există faptul restantier(Lucia).
Căutarea nu se opreşte aici, revenindu-se la punctul de ramificare. Variabilele Nume şi
Media redevin libere şi se încearcă o nouă potrivire la următorul fapt. Variabila Nume devine legată
14 Unificare şi backtracking
de atomul "Petre" şi variabila Media de atomul 7.0, iar al doilea subobiectiv al regulii devine
7.0>=8.5. Acesta se încheie cu eşec şi se revine la punctul de ramificare.
Variabilele Nume şi Media redevin libere şi se încearcă o nouă potrivire la următorul fapt.
Variabila Nume devine legată de atomul "Ioana" şi variabila Media de atomul 8.7, iar al doilea
subobiectiv al regulii devine 8.7>=8.5 şi se încheie cu succes. Al treilea subobiectiv se va încheia
cu succes pentru că în secţiunea de clauze nu există faptul restantier(Ioana). Astfel se obţine o
primă soluţie, apoi se revine la punctul de ramaficare.
Mecanismul de căutare cu revenire se încheie odată cu revenirea la primul punct de
ramificare, deoarece pentru variabilele libere Nume şi Media nu se mai poate realiza nicio potrivire.

Aplicaţii propuse
1. Să se scrie un program care să îi găsească Anei un partener la bal. Ea doreşte sa meargă
cu un bărbat care este nefumător sau vegetarian. Pentru aceasta se dispune de o bază de date cu
informaţii despre câţiva bărbaţii:
 Gelu este barbat.
 Bogdan este barbat.
 Denis este barbat si este fumator.
 Dan este fumator.
 Gelu este vegetarian.

2. Se dau următoarele enunţuri:


 Oricine poate citi este literat.
 Delfinii nu sunt literaţi.
 Anumiţi delfini sunt inteligenţi.
Să se demonstreze în Prolog că: “Există fiinţe inteligente care nu pot citi”.

3. Se dau următoarele enunţuri:


 Fiecare om îi este devotat cuiva.
 Oamenii încearcă să asasineze dictatorii faţă de care nu sunt devotaţi.
 Toţi pompeienii erau romani.
 Cezar era dictator.
 Fiecare roman fie îl ura pe Cezar, fie îi era devotat.
 Marcus era pompeian.
 Marcus era om.
 Marcus a încercat să îl asasineze pe Cezar.
Să se demonstreze în Prolog că:
1) Marcus nu îi era devotat lui Cezar.
2) Marcus îl ura pe Cezar.
Laborator 5 1

OBIECTE ŞI DOMENII COMPUSE

Prezentare teoretică

Obiectele compuse ne permit tratarea informaţiilor compuse ca un singur item, astfel încât
să poată fi extrase uşor componentele acestora. Obiectele compuse sunt alcătuite dintr-un functor şi
o listă de obiecte aparţinând functorului respectiv:
nume=functor(obiect1, obiect2, ..., obiectN)

Unificarea obiectelor compuse


Un obiect compus poate fi unificat cu o variabilă simplă sau cu un alt obiect compus.
Obiectele compuse pot fi privite şi tratate ca un singur obiect în clauzele Prolog. O caracteristică
importantă a obiectelor compuse este aceea că permite atribuirea unui grup de valori ca un unic
argument.

Declararea domeniilor pentru obiectele mixte compuse


Sintaxa declarării domeniilor compuse este următoarea:
domeniu =alternativa1(D, D, ...);
alternativa2(D, D, ...);
...
unde alternativa1 şi alternativa2 sunt diferiţi functori. Notaţia (D, D, ...) reprezintă o listă de nume
de domenii care pot fi declarate în altă parte sau pot fi domenii standard (ca de exemplu symbol,
integer, real, etc). Alternativele sunt separate prin caracterul ;.

Domenii compuse pe mai multe nivele


În limbajul Prolog se pot declara domenii de obiecte compuse pe mai multe nivele.
De exemplu, în declaraţia domeniului:
carte(titlu, autor)
al doilea argument al domeniului carte este autor, care poate fi declarat ca symbol. Dar acest al
doilea argument, astfel declarat nu poate conţine decât numele autorului, nu şi prenumele.
Pentru a elimina acest neajuns se poate declara domeniul autor ca mai jos:
autor = autor(nume, prenume)
şi vom avea următoarele declaraţii de domenii:
DOMAINS
articole = carte(titlu, autor); .. /* primul nivel */
autor = autor(nume, prenume) /* al doilea nivel */
titlu, nume, prenume = symbol /* al treilea nivel */
Când se utilizează obiecte compuse pe diferite nivele, în această manieră, se obţine un
arbore:
2 Obiecte şi domenii compuse
carte
/ \
titlu autor
/ \
nume prenume
O declaraţie de domeniu poate descrie la un moment dat un singur nivel, nu întreg arborele
de nivele. De exemplu declaraţia următoare nu este corectă:
carte = carte(titlu,autor(nume,prenume)) /* Incorect */

Aplicaţii rezolvate

Aplicaţia 1
Cunoscându-se o bază de cunoştinţe cu datele despre mai multe ţări: denumire, suprafaţă şi
populaţie, să se scrie un program Prolog care afişează lista ţărilor cu densitatea mai mare decât 100.

Programul corespunzător problemei este:


domains
tara=date_tara(denumire,suprafata,populatia)
denumire=string
suprafata, populatia=ulong
densitate=real
predicates
nondeterm lista(tara)
scrie_tara(tara,densitate)
nondeterm determina
clauses
lista(date_tara("Anglia",130395,53012456)). % suprafata este data in km^2
lista(date_tara("Austria",83879,8032926)).
lista(date_tara("Belgia",30528,11071483)).
lista(date_tara("Danemarca",43094,5580516)).
lista(date_tara("Finlanda",338432,5421827)).
lista(date_tara("Franta",674843,66600000)).
lista(date_tara("Germania",357121,80399300)).
lista(date_tara("Italia",301340,60776531)).
lista(date_tara("Norvegia",385252,5009150)).
lista(date_tara("Romania",238391,21790479)).
lista(date_tara("Spania",504645,47129783)).
lista(date_tara("Suedia",449964,9573466)).
scrie_tara(date_tara(T,_,_),D):-
write(T," are o densitate de "),
writef("%6.2f",D),
write(" loc/km^2"),nl.
determina:-
lista(date_tara(T,S,P)),
D=P/S,
D>100,
Laborator 5 3
scrie_tara(date_tara(T,S,P),D),
fail.
determina.
goal
determina.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Anglia are o densitate de 406.55 loc/km^2
Belgia are o densitate de 362.67 loc/km^2
Danemarca are o densitate de 129.50 loc/km^2
Germania are o densitate de 225.13 loc/km^2
Italia are o densitate de 201.69 loc/km^2
yes
Programul este structurat pe patru secţiuni.
În prima secţiune se defineşte domeniul compus tara prin intermediul functorului date_tara
grupând informaţii de trei tipuri diferite. Se mai definesc două domenii standard cu nume
personalizate pentru că se doreşte clarificarea rolului fiecărui argument din predicatele declarate în
secţiunea următoare.
În a doua secţiune s-au declarat trei predicate: predicatul nedeterminist lista cu un argument
de tip tara, predicatul scrie_tara cu două argumente de tip tara, respectiv densitate şi predicatul
nedeterminist determina fără argumente.
În secţiunea legată de clauze sunt definite cele trei predicate, primul prin intermediul
faptelor, al doilea prin intemediul unei reguli, iar ultimul prin intermediul unei reguli şi a unui fapt.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
determina.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Predicatul determina, fiind definit prin
intermediul unei reguli, apoi a unui fapt, se va încerca satisfacerea tuturor subobiectivelor din
corpul acestei reguli. Primul subobiectiv corespunde predicatului lista, iar variabilele libere T, S şi P
vor deveni legate de atomii "Anglia", 130395, respectiv 53012456. Al doilea subobiectiv duce la
legarea variabilei libere D de valoarea 406.55 prin evaluarea expresiei D=P/S, când variabile P şi S
sunt legate de atomii anterior precizaţi. Al treilea subobiectiv devine 406.55 >100 şi se încheie cu
succes. Următorul subobiectiv este determinat de predicatul scrie_tara definit prin intermediul unei
reguli. Corpul acestei reguli este definit prin intermediul predicatelor predefinite write şi writef care
se utilizează cu scopul de a afişa la ieşirea standard denumirea şi densitatea ţării.
Chiar dacă al patrulea subobiectiv al regulii determina s-a încheiat cu succes, prin utilizarea
predicatului fail se forţează căutarea şi se reia procesului accesând următorul fapt din definiţia
predicatului lista.
Mecanismul de căutare cu revenire se încheie odată cu parcurgerea întregii liste de fapte
corespunzătoare predicatului lista, dar, datorită prezenţei predicatului fail, căutarea se încheie cu
eşec chiar dacă s-au obţinut mai multe soluţii. Pentru ca rezultatul ce se afişează în fereastra de
ieşire să nu se încheie cu mesajul no se utilizează ultimul fapt din definiţia predicatului determină.
Astfel rezultatul afişat în fereastra de ieşire se va încheia cu mesajul yes.
4 Obiecte şi domenii compuse

Aplicaţia 2
Cunoscându-se o bază de date cu informaţiile despre sateliţii planetelor din sistemul solar, să
se scrie un program Prolog care determină sateliţii planetei Saturn de dimensiune mai mare decât o
valoare dată.

Programul corespunzător problemei este:


domains
satelit=date_satelit(denumire, diametru)
denumire, planeta = symbol
diametru = real
predicates
nondeterm lista(satelit,planeta)
scrie_satelit(satelit)
nondeterm determina
clauses
lista(date_satelit("Phobos",15),"Marte").
lista(date_satelit("Deimos",28),"Marte").
lista(date_satelit("Mimas",397.2),"Saturn").
lista(date_satelit("Enceladus",504),"Saturn").
lista(date_satelit("Rhea",1528.8),"Saturn").
lista(date_satelit("Europa",3121),"Jupiter").
lista(date_satelit("Callisto",4900),"Jupiter").
lista(date_satelit("Ariel",1058),"Uranus").
lista(date_satelit("Miranda",472),"Uranus").
lista(date_satelit("Umbriel",1170),"Uranus").
scrie_satelit(date_satelit(Nume,D)):-
write("Satelitul ",Nume, " avand diametrul de aproximativ ", D, " km"),
nl.
determina:-
write("Diametru minim="),
readreal(Dim),
write("Satelitii planetei Saturn de diametru mai mare decat ", Dim, " sunt: "),
nl,
lista(date_satelit(Care,D),"Saturn"),
D>Dim,
scrie_satelit(date_satelit(Care,D)),
fail.
determina.
goal
determina.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Diametru minim=500
Satelitii planetei saturn de diametru mai mare decat 500 sunt:
Satelitul Enceladus avand diametrul de aproximativ 504 km
Laborator 5 5
Satelitul Rhea avand diametrul de aproximativ 1528 km
yes
Programul este structurat pe patru secţiuni.
În prima secţiune se defineşte domeniul compus satelit prin intermediul functorului
date_satelit grupând informaţii de două tipuri diferite. Se mai definesc două domenii standard cu
nume personalizate pentru că se doreşte clarificarea rolului fiecărui argument din predicatele
declarate în secţiunea următoare.
În a doua secţiune s-au declarat trei predicate: predicatul nedeterminist lista cu două
argumente de tip satelit, respectiv planeta, predicatul scrie_satelit cu un argument de tip satelit şi
predicatul nedeterminist determina fără argumente.
În secţiunea legată de clauze sunt definite cele trei predicate, primul prin intermediul
faptelor, al doilea prin intemediul unei reguli, iar ultimul prin intermediul unei reguli şi a unui fapt.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
determina.
Prolog va rezolva această interogare căutând în lista de fapte şi reguli de la început spre
sfârşit, utilizând mecanismul de căutare cu revenire. Predicatul determina, fiind definit prin
intermediul unei reguli, apoi a unui fapt, se va încerca satisfacerea tuturor subobiectivelor din
corpul acestei reguli. Primul subobiectiv duce la afişarea la ieşirea standard a unui mesaj care
precizează că se doreşte introducerea unei valori ce va reprezenta limita inferioară pentru diametrul
sateliţilor. Al doilea subobiectiv utilizează predicatul predefinit readreal care permite citirea valorii
introduse (aceasta fiind 500 în cazul prezentat ca exemplu). Următorul subobiectiv duce la afişarea
unui mesaj.
Subobiectul aflat în continuare corespunde predicatului lista, iar variabilele libere Care şi D
vor deveni legate de atomii "Mimas", respectiv 397, deoarece acest fapt este primul din definiţia
predicatului lista pentru care al doilea argument este atomul "Saturn" după cu se precizează în
interogare.
Următorul subobiectiv devine 397>500 şi se încheie cu eşec.
Variabilele Care şi D redevin libere şi se vor lega de atomii din următorul fapt pentru care al
doilea argument este atomul "Saturn", deci de atomii "Enceladus", respectiv 504. Următorul
subobiectiv devine 504>500 şi se încheie cu succes.
Următorul subobiectiv este determinat de predicatul scrie_satelit definit prin intermediul
unei reguli. Corpul acestei reguli este definit prin intermediul predicatului predefinit write care se
utilizează cu scopul de a afişa la ieşirea standard denumirea şi diametrul satelitului.
Chiar dacă penultimul subobiectiv al regulii determina s-a încheiat cu succes, prin utilizarea
predicatului fail se forţează căutarea şi se reia procesului accesând următorul fapt din definiţia
predicatului lista.
Mecanismul de căutare cu revenire se încheie odată cu parcurgerea întregii liste de fapte
corespunzătoare predicatului lista, dar, datorită prezenţei predicatului fail, căutarea se încheie cu
eşec chiar dacă s-au obţinut mai multe soluţii. Pentru ca rezultatul ce se afişează în fereastra de
ieşire să nu se încheie cu mesajul no se utilizează ultimul fapt din definiţia predicatului determina.
Astfel rezultatul afişat în fereastra de ieşire se va încheia cu mesajul yes.

Aplicaţia 3
Se consideră cunoscută o listă cu rezultatele atleţilor înscrişi la un concurs de sărituri în
lungime. Să se scrie un program Prolog care determină lista atleţilor care vor intra în finală, ştiind
că fiecare atlet trebuie să aibă, la preselecţie, cel puţin o săritură peste o valoare minimă dată şi să
nu fie descalificat.
6 Obiecte şi domenii compuse

Programul corespunzător problemei este:


domains
atlet=date_atlet(nume, sarituri)
sarituri=date_sarituri(saritura1, saritura2, saritura3)
nume = symbol
saritura1, saritura2, saritura3 = real
predicates
nondeterm lista(atlet)
maxim(sarituri,real)
scrie_atlet(atlet)
descalificat(nume)
nondeterm finala
clauses
lista(date_atlet("Bogdan", date_sarituri(7.23,7.33,6.77))).
lista(date_atlet("Vlad", date_sarituri(7.43,7.33,8.17))).
lista(date_atlet("Ionut", date_sarituri(6.83,8.13,7.12))).
lista(date_atlet("Cristian", date_sarituri(8.23,7.93,8.17))).
lista(date_atlet("Marius", date_sarituri(7.53,6.93,7.87))).
lista(date_atlet("Ovidiu", date_sarituri(7.83,7.03,8.10))).
lista(date_atlet("Silviu", date_sarituri(8.03,7.93,8.17))).
maxim(date_sarituri(S1,S2,S3),S1):-
S1>S2,S1>S3,!.
maxim(date_sarituri(S1,S2,S3),S2):-
S2>S1,S2>S3,!.
maxim(date_sarituri(S1,S2,S3),S3):-
S3>S1,S3>S2.
scrie_atlet(date_atlet(Nume, date_sarituri(S1,S2,S3))):-
write(Nume, " Lungimile sariturilor sunt ", S1,"; ",S2,"; ",S3),
nl.
descalificat("Cristian").
descalificat("Ovidiu").
finala:-
write("Saritura minima="),
readreal(Min),
write("Atletii care vor intra in finala sunt:"),
nl,
lista(date_atlet(Care,S)),
maxim(S,M),
M>Min,
not(descalificat(Care)),
scrie_atlet(date_atlet(Care,S)),
fail.
finala.
goal
finala.
Laborator 5 7
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Saritura minima=7.9
Atletii care vor intra in finala sunt:
Vlad Lungimile sariturilor sunt 7.43; 7.33; 8.17
Ionut Lungimile sariturilor sunt 6.83; 8.13; 7.12
Silviu Lungimile sariturilor sunt 8.03; 7.93; 8.17
yes

Aplicaţia 4
Cunoscând o listă cu numerele de telefon a unor persoane, să se implementeze un program
Prolog care va afişa toate persoanele născute în luna curentă.
Obs: Pentru a se obține luna curenta a sistemului de operare, se utilizează predicatul
predefinit din prolog, date(year,month,day). Lunile sunt numerotate de la 1 la 12, iar zilele sunt
numerotate de la 1 la 31.

Programul corespunzător problemei este:


domains
nume = persoana(prenumele,numele)
data_nastere = dn(zi,luna,an)
nr_telefon = symbol
numele,prenumele,luna=symbol
zi,an=ushort
predicates
nondeterm lista_telefoane(nume,nr_telefon,data_nastere)
conversie_luna(luna,integer)
verifica(integer,data_nastere)
scrie_persoana(nume)
afla_luna
clauses
lista_telefoane(persoana(ion, popa), "0256-718463", dn(3,ianuarie, 1955)).
lista_telefoane(persoana(monica, pop), "0254-718400", dn(5, februarie, 1985)).
lista_telefoane(persoana(radu, olaru), "0255-125653", dn(3, martie, 1935)).
lista_telefoane(persoana(pavel, ionescu), "0767-522223", dn(29,aprilie, 1951)).
lista_telefoane(persoana(cristi, adam), "0255-141212", dn(12, mai, 1962)).
lista_telefoane(persoana(iustin, radu), "0733-128400", dn(17, iunie, 1980)).
lista_telefoane(persoana(ana, faur), "0766-528463", dn(20, iunie, 1986)).
lista_telefoane(persoana(bogdan, radu), "0255-535653", dn(16, iulie, 1981)).
lista_telefoane(persoana(raluca, faur), "0767-782223", dn(10, august, 1981)).
lista_telefoane(persoana(cristina, lupu), "0728-328400", dn(25, septembrie,1981)).
lista_telefoane(persoana(lia, popescu), "0732-368400", dn(26, octombrie, 1952)).
lista_telefoane(persoana(mirela, popescu),"0255-121212", dn(29, octombrie, 1984)).
lista_telefoane(persoana(adam, faur), "0744-152223", dn(15, noiembrie, 1987)).
lista_telefoane(persoana(eugenia,nicula),"0722-878400",dn(31, decembrie, 1981)).
conversie_luna(ianuarie, 1).
conversie_luna(februarie, 2).
conversie_luna(martie, 3).
8 Obiecte şi domenii compuse
conversie_luna(aprilie, 4).
conversie_luna(mai, 5).
conversie_luna(iunie, 6).
conversie_luna(iulie, 7).
conversie_luna(august, 8).
conversie_luna(septembrie, 9).
conversie_luna(octombrie, 10).
conversie_luna(noiembrie, 11).
conversie_luna(decembrie, 12).
verifica(L,dn(_,Luna,_)):-
conversie_luna(Luna,Luna1),
L = Luna1.
scrie_persoana(persoana(Prenume,Nume)):-
write(" ",Prenume,"\t\t ",Nume),nl.
afla_luna:-
write("Lista cu datele de nastere din aceasta luna"),nl,
write("Prenume\t\t Nume\n"),nl,
date(_, Luna_curenta, _), %predicatul predefinit date(year,month,day)
lista_telefoane(Persoana, _, Data),
verifica(Luna_curenta, Data),
scrie_persoana(Persoana),
fail.
afla_luna.
goal
afla_luna.

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


Lista cu datele de nastere din aceasta luna
Prenume Nume
lia popescu
mirela popescu
yes

Aplicatie: Modificati programul astfel incat sa se afiseze doar persoanele care astazi isi
sarbatoresc ziua de nastere.

Aplicaţia 5
Se dă următoarea bază de cunoştinţe:
 Cartea „Limbajul C” scrisă de Liviu Negrescu şi editată la Editura Albastră se găseşte în
biblioteca Oanei.
 Cartea „Java – de la 0 la expert” scrisă de Ştefan Tănasă şi editată la Editura Polirom se
găseşte în biblioteca Oanei şi a lui Mihai.
 Cartea „Geometrie” scrisă de Traian Lalescu şi editată la Editura Didactica se găseşte în
biblioteca Mirabelei şi a lui Mircea.
 Cartea „Geometrie” scrisă de Traian Lalescu şi editată la Editura All se găseşte în
biblioteca Ioanei şi a Rodicăi.
Laborator 5 9
 Mirabela, Ioana şi Mircea deţin filmul „Titanic” cu durata de 187 de minute ecranizat în
anul 1997.
 Mihai, Rodica şi Sorina deţin filmul „Pe aripile vântului” cu durata de 224 de minute
ecranizat în anul 1939.
 Sorina şi Mircea deţin melodia „Lady in red” compusă şi interpretată de Elton John.
 Ioana, Rodica, Oana şi Mircea deţin melodia „Love me tender” compusă şi interpretată
de Elvis Presley.
Să se scrie un program Prolog care, prin utilizarea unui domeniu compus mixt, răspunde întrebării:
“Cine deţine cartea „Geometrie” scrisă de Traian Lalescu, filmul „Titanic” ecranizat în anul 1997 şi
melodia „Love me tender” compusă şi interpretată de Elvis Presley?”

Programul corespunzător problemei este:


domains
articol = carte(titlu, autor, editura);
film(titlu,durata,an_ecranizare);
melodie(titlu,interpret,compozitor)
autor, interpret, compozitor = persoana(prenume, nume)
titlu, nume, prenume, editura = symbol
durata, an_ecranizare = integer
predicates
nondeterm detine(prenume,articol)
clauses
detine(Cineva,carte("Limbajul C",persoana("Liviu","Negrescu"),"Albastra")):-
Cineva="Oana".

detine(Cineva,carte("Java - de la 0 la expert",persoana("Stefan","Tanasa"),"Polirom")):-
Cineva="Oana";
Cineva="Mihai".

detine(Cineva,carte("Geometrie", persoana("Traian","Lalescu"),"Didactica")):-
Cineva="Mirabela";
Cineva="Mircea".

detine(Cineva,carte("Geometrie", persoana("Traian","Lalescu"),"All")):-
Cineva="Ioana";
Cineva="Rodica".

detine(Cineva,film("Titanic",187,1997)):-
Cineva="Mirabela";
Cineva="Ioana";
Cineva="Mircea".

detine(Cineva,film("Pe aripile vantului",224,1939)):-


Cineva="Mihai";
Cineva="Rodica";
Cineva="Sorina".
10 Obiecte şi domenii compuse
detine(Cineva,melodie("Lady in red",persoana("John","Elton"),persoana("John","Elton"))):-
Cineva="Sorina";
Cineva="Mircea".

detine(Cineva,melodie("Love me tender",
persoana("Elvis","Presley"),persoana("Elvis","Presley"))):-
Cineva="Ioana";
Cineva="Rodica";
Cineva="Oana";
Cineva="Mircea".

goal
detine(Cine,carte("Geometrie",persoana("Traian","Lalescu"),_)),
detine(Cine,film("Titanic",187,1997)),
detine(Cine,melodie("Love me tender",persoana("Elvis","Presley"),_)).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Cine=Ioana
Cine=Mircea
2 Solutions

Se poate observa că s-au definit domenii compuse pe diferite nivele, în această manieră
obţinându-se un arbore, ca în exemplu de mai jos:
carte
/ | \
titlu autor editura
/ \
prenume nume

Aplicaţii propuse
1. Cunoscându-se o listă cu informaţiile despre mai multe persoane: nume, prenume, ziua şi
luna naşterii, să se scrie un program Prolog care afişează numele presoanelor născute într-o anumită
zodie.
2. Dându-se informaţii despre mai mulți domnitori, să se scrie un program Prolog care afişează
lista domnitorilor dintr-o perioadă specificată. Pentru fiecare domnitor se cunoaşte: numele, ţara de
domnie, anul începutului domniei şi anul finalului de domnie.
Laborator 6 1

APLICAȚII SUPLIMENTARE
UNIFICARE ȘI BACKTRACKING

Aplicaţia 1
Se dau următoarele enunţuri:
1. Oamenii pot să citească.
2. Oricine poate citi este literat.
3. Delfinii nu sunt literaţi.
4. Anumiţi delfini sunt inteligenţi.

Să se scrie un program Prolog care răspunde întrebării: “Există ființe inteligente care nu pot
citi?”

Aplicaţia 2
Se dă următoarea bază de cunoştinţe:
1. Leul minte luni, marţi şi miercuri şi spune adevărul în toate celelalte zile.
2. Unicornul minte miercuri, vineri şi sâmbătă şi spune adevărul în toate celelalte zile.
3. Astăzi Leul spune: "Ieri a fost una din zilele în care eu mint."
4. Tot astăzi Unicornul spune: "Ieri a fost una din zilele în care eu mint."

Să se scrie un program Prolog care răspunde întrebării: “Ce zi este astăzi?”

Aplicaţia 3
Se dau următoarele enunţuri:
1. Fiecare om îi este devotat cuiva.
2. Oamenii încearcă să asasineze dictatorii faţă de care nu sunt devotaţi.
3. Toţi pompeienii erau romani.
4. Cezar era dictator.
5. Fiecare roman fie îl ura pe Cezar, fie îi era devotat.
6. Marcus era pompeian.
7. Marcus era om.
8. Marcus a încercat să îl asasineze pe Cezar.

Să se scrie un program Prolog care răspunde întrebărilor:


1. Îi era devotat Marcus lui Cezar?
2. Îl ura Marcus pe Cezar?
2 Aplicatii suplimentare

RECURSIVITATE ŞI PROCESE REPETITIVE


Aplicaţia 4
Se cunosc următoarele:
1. Dacă oraşul X este legat de oraşul Y prin drumul D şi pot circula biciclete pe
drumul D, atunci se poate merge de la X la Y.
2. Dacă oraşul X este legat de oraşul Y prin drumul D, atunci şi oraşul Y este legat
de oraşul X prin drumul D.
3. Dacă se poate merge de la X la Y şi de la Y la Z, atunci se poate merge de la X
la Z.
4. Oraşul a este legat de oraşul b prin drumul d1.
5. Oraşul b este legat de oraşul c prin drumul d2.
6. Oraşul a este legat de oraşul c prin drumul d3.
7. Pe drumul d1 pot circula biciclete.
8. Pe drumul d2 pot circula biciclete.

Să se scrie un program Prolog care răspunde întrebării: “ Se poate merge de la oraşul a la


oraşul c?” (răspuns: da).

Înlocuiți ipoteza 8 cu “ Pot circula biciclete fie pe drumul d2, fie pe drumul d3, dar nu pe
ambele în acelaşi timp.
Să se demonstreze aceeaşi concluzie.
Laborator 6-7 1

RECURSIVITATE ŞI PROCESE REPETITIVE

Noţiuni teoretice - recursivitate şi procese repetitive

În Prolog nu există instrucţiuni care specifică direct un ciclu. Prolog admite două tipuri de
procese repetitive – backtracking şi recursivitate.
Această aparentă lipsă a instrucţiunilor ciclice nu restricţionează limbajul Prolog. De fapt
Prolog recunoaşte un caz special de recursivitate – numită tail recursion – şi o compilează
transformând-o într-o buclă iterativă în limbaj maşină. Aceasta înseamnă că programul va fi scris
recursiv, însă codul compilat este la fel de eficient ca şi când ar fi scris în Pascal sau C iterativ.
Atunci când o procedură revine înapoi, prin mecanismul backtracking, acest lucru se face cu
scopul de a găsi o soluţie nouă la un obiectiv. Se face această revenire prin retragerea la cel mai
recent subobiectiv care mai are alternative ce nu au fost luate în considerare; se ia în considerare
această nouă alternativă, apoi se continuă mai departe. Se poate exploata mecanismul backtracking
pentru a obţine procese repetitive, după cum s-a exemplificat şi anterior.

Implementarea mecanismului backtracking cu cicluri


Backtracking este un mecanism utilizat pentru a produce soluţii alternative. Dar chiar dacă
obiectivul testat nu are soluţii alternative, se poate utiliza mecanismul de căutare cu revenire pentru
a introduce cicluri. Se pot defini două clauze ale aceluiaşi predicat astfel:
repeta.
repeta :- repeta.
Scopul acestui predicat este de a permite mecanismul backtracking la nesfârşit.

Proceduri recursive
O altă modalitate de a genera procese repetitive este prin utilizarea recursivităţii. O
procedură este recursivă dacă ea se apelează pe ea însăşi. Într-o astfel de procedură contoarele,
sumele, precum şi alte rezultate imediate pot fi transmise de la o iteraţie la următoarea ca
argumente.
Recursivitatea are trei mari avantaje:
 Permite exprimarea unui algoritm cu ajutorul recursivităţii, care nu se poate exprima în alt
mod.
 Din punct de vedere logic este mai simplă decât iteraţia.
 Se utilizează pe scară largă în procesarea listelor.
Recursivitatea este o cale naturală de a descrie orice problemă care conţine în ea însăşi o altă
problemă de acelaşi tip.
Din punct de vedere logic, algoritmii recursivi seamănă cu deducţiile din inducţia
matematică.
2 Recursivitate şi procese repetitive

Optimizarea recursivităţii
Recursivitatea are un mare neajuns: consumă memorie foarte multă. La apelul unei
proceduri recursive starea apelului curent trebuie salvată astfel încât să poată fi reluată după
încheierea apelului următor. Acest lucru înseamnă că, dacă procedura se autoapelează de 100 de ori,
trebuie salvate 100 de stări diferite. Aceste salvări se fac într-o zonă de memorie numită stivă, care
este limitată. Se pune problema ce ar trebui făcut pentru a se evita acest consum de memorie.
Există o situaţie specială în care o procedură se poate autoapela fără a se stoca starea
execuţiei sale. Ce se întâmplă dacă un apel de procedură nu este urmat de o revenire, după
încheierea procedurii. Să presupunem că se ajunge la ultimul pas din setul de operaţii al unei
proceduri, care este precedat chiar de un apel recursiv de procedură. După încheierea ultimei
proceduri apelate, această procedură nu mai are nimic de făcut. Aceasta înseamnă că nu trebuie
salvată starea execuţiei, deoarece informaţia nu mai este necesară. După ce procedura apelată se
încheie pentru ultima dată, controlul poate fi trecut oriunde se doreşte.
De exemplu, să presupunem că procedura A apelează B şi B apelează C. După ce apelează
procedura C, să presupunem că B nu mai are altceva de făcut. Astfel, în loc să se salveze starea
curentă a apelului procedurii C din procedura B, se poate înlocui vechea stare (care nu mai este
necesară) cu starea curentă a procedurii C. După încheierea procedurii C se va considera că ea a fost
apelată direct din A.
Să presupunem acum, că procedura B se autoapelează pe ea însăşi şi nu apelează o altă
procedură C. Să presupunem că acesta este ultimul apel efectuat. Nu se vor mai salva stările
intermediare ale apelurilor efectuate, ci doar starea primului apel. Acest tip de recursivitate se
numeşte recursivitate optimizată după ultimul apel.
În Prolog o procedură apelează o altă procedură ca un ultim pas al său dacă:
 Apelul este ultimul subobiectiv al clauzei.
 Nu sunt puncte de revenire amplasate anterior în clauză.

Aplicaţii rezolvate

Aplicaţia 1
Să se implementeze un program Prolog care calculează recursiv neoptimizat factorial dintr-
un număr natural.

Programul corespunzător problemei este:


predicates
factorial(unsigned,ulong)
rezolva
clauses
factorial(0,1):-!.
factorial(N,F):-
M=N-1,
factorial(M,F1),
F=F1*N.
rezolva:-
write("N="),
readint(N),
Laborator 6-7 3
factorial(N,F),
write(N,"!=",F),nl.
goal
rezolva.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
N=4
4!=24
yes
Programul este structurat pe trei secţiuni. În prima secţiune s-a declarat predicatul factorial
cu două argumente de tip unsigned, respectiv ulong şi predicatul rezolva fără argumente. În
secţiunea legată de clauze sunt definite aceste două predicate prin intermediul regulilor.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea
rezolva.
Prolog va rezolva această interogare căutând în lista de reguli de la început spre sfârşit,
utilizând mecanismul de căutare cu revenire. Deoarece predicatul rezolva este definit prin
intermediul unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli.
Primul subobiectiv duce la afişarea la ieşirea standard a unui mesaj care precizează că se doreşte
introducerea unei valori ce va reprezenta numărul natural pentru care se doreşte calculul
factorialului. Al doilea subobiectiv utilizează predicatul predefinit readint care permite citirea
valorii introduse (aceasta fiind 4 în cazul prezentat ca exemplu).
Următorul subobiectiv apelează procedura ce defineşte predicatul factorial, primul argument
al acestui predicat fiind dat de variabila legată N. Se încearcă potrivirea valorii variabilei legate N
cu primul atom din prima definiţie a predicatului. Dacă valoarea variabilei N este diferită de 0,
căutarea se încheie cu eşec şi se trece la următoarea definiţie. Primul subobiectiv al regulii
determină valoarea variabilei M, iar apoi se autoapelează predicatul factorial având primul
argument dat de variabila legată M. În urma acestui apel se va determina M! şi va fi returnat prin
variabila F1. Ultimul subobiectiv duce la determinarea lui N!. Apelul recursiv are loc până când
variabila legată corespunzătoare primului argument al predicatului factorial coincide cu valoarea 0.
La fiecare apel recursiv starea apelului curent va fi salvată astfel încât să poată fi reluată
după încheierea apelului următor. În cazul în care valoarea citită pentru N este 6, vor avea loc 4
autoapeluri prezentate în figura 5.1, deci vor fi salvate 4 stări diferite.

Fig. 5.1. Apelurile predicatului factorial

Ultimul subobiectiv al predicatului rezolva utilizează predicatul predefinit write pentru a


afişa rezultatul obţinut.

Aplicaţia 2
Să se implementeze un program Prolog care calculează recursiv optimizat factorial dintr-un
număr natural.
4 Recursivitate şi procese repetitive

Programul corespunzător problemei este:


predicates
factorial(unsigned,long)
factorial(unsigned,long,unsigned,long)
rezolva
clauses
factorial(N,Factorial):-
factorial(N,Factorial,0,1).
factorial(N,Factorial,N,Factorial):-!.
factorial(N,Factorial,I,F):-
NoulI=I+1,
NoulF=F*NoulI,
factorial(N,Factorial,NoulI,NoulF).
rezolva:-
write("N="),
readint(N),
factorial(N,F),
write(N,"!=",F),nl.
goal
rezolva.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
N=6
6!=720
yes
Programul este structurat pe trei secţiuni. În prima secţiune s-a declarat predicatul factorial
cu două argumente de tip unsigned, respectiv ulong, predicatul factorial cu patru argumente de tip
unsigned, respectiv ulong şi predicatul rezolva fără argumente. În secţiunea legată de clauze sunt
definite aceste trei predicate prin intermediul regulilor. Ultima secţiune corespunde cerinţei din
enunţul problemei, întrebare redată de interogarea rezolva.
Prolog va rezolva această interogare căutând în lista de reguli de la început spre sfârşit,
utilizând mecanismul de căutare cu revenire. Deoarece predicatul rezolva este definit prin
intermediul unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli.
Primul subobiectiv duce la afişarea la ieşirea standard a unui mesaj care precizează că se doreşte
introducerea unei valori ce va reprezenta numărul natural pentru care se doreşte calculul
factorialului. Al doilea subobiectiv utilizează predicatul predefinit readint care permite citirea
valorii introduse (aceasta fiind 6 în cazul prezentat ca exemplu).
Următorul subobiectiv apelează procedura ce defineşte predicatul factorial cu două
argumente, primul argument al acestui predicat fiind dat de variabila legată N. Acesta este definit
printr-o regulă cu un singur subobiectiv dat de predicatul factorial cu patru argumente, ultimile
două argumente primind valorile implicite 0 şi 1 (deoarece 0!=1).
În continuare se verifică dacă primul argument şi al treilea argument al predicatului factorial
sunt egale. În acest caz se încheie recursivitatea şi se unifică variabila liberă dată de al doilea
argument cu valoarea ultimului argument. Altfel se trece la a doua definiţie a acestui predicat.
Laborator 6-7 5
Primul subobiectiv al regulii determină valorile variabilelor NoulI şi NoulF, iar apoi se
autoapelează predicatul factorial având ca ultime două argumente variabilele legate anterior
determinate. Deoarece autoapelul reprezintă ultimul subobiectiv al regulii, nu mai este necesar
păstrarea stărilor intermediare ale apelurilor efectuate. Astfel s-a definit predicatul factorial recursiv
optimizat.
Ultimul subobiectiv al predicatului rezolva utilizează predicatul predefinit write pentru a
afişa rezultatul obţinut.

Aplicaţia 3
Se consideră că există 3 suporturi de discuri, respectiv trei discuri diferite ca mărime
(diametru). Discurile sunt aranjate ca în figura de mai jos. Mutați discurile de pe suportul nr. 1 (cel
din stânga), pe suportul nr. 3 (cel din dreapta). Discurile pot fi mutate prin intermediul suportului nr.
2 (cel din mijloc) și pot fi așezate mai multe discuri unul peste altul. Nu puteți avea un disc de
mărime mai mare peste unul de mărime mai mică. Rezolvați această problemă (problema turnurilor
din Hanoi cu n discuri).

Programul corespunzător problemei este:

DOMAINS
suport =stanga;mijloc;dreapta
nrDiscuri=unsigned

PREDICATES
rezolva
hanoi(nrDiscuri,suport,suport,suport)

CLAUSES
hanoi(1,A,_,C):-
write("Muta discul de pe suportul ", A, " pe suportul ", C),nl,!.
hanoi(N,A,B,C):-
N1=N-1,
hanoi(N1,A,C,B),
write("Muta discul de pe suportul ", A, " pe suportul ", C),nl,
hanoi(N1,B,A,C).
rezolva:-
write("N="),
readint(N),
%Nr=N,
hanoi(N,stanga,mijloc,dreapta).
6 Recursivitate şi procese repetitive
goal
rezolva.
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:

N=3
Muta discul de pe suportul stanga pe suportul dreapta
Muta discul de pe suportul stanga pe suportul mijloc
Muta discul de pe suportul dreapta pe suportul mijloc
Muta discul de pe suportul stanga pe suportul dreapta
Muta discul de pe suportul mijloc pe suportul stanga
Muta discul de pe suportul mijloc pe suportul dreapta
Muta discul de pe suportul stanga pe suportul dreapta
yes

hanoi(3,A,B,C) N1=1
N1=2 hanoi(1,A,B,C) => scrie A->C (1)
hanoi(2,A,C,B) scrie A->B (2)
scrie A->C (4) hanoi(1,C,A,B) => scrie C->B (3)
hanoi(2,B,A,C) N1=1
hanoi(1,B,C,A) => scrie B->A (5)
scrie B->C (6)
hanoi(1,A,B,C) => scrie A->C (7)

Aplicaţia 4
Se cunosc următoarele:
1. Dacă oraşul X este legat de oraşul Y prin drumul D şi pot circula biciclete pe
drumul D, atunci se poate merge de la X la Y.
2. Dacă oraşul X este legat de oraşul Y prin drumul D, atunci şi oraşul Y este legat
de oraşul X prin drumul D.
3. Dacă se poate merge de la X la Y şi de la Y la Z, atunci se poate merge de la X
la Z.
4. Oraşul a este legat de oraşul b prin drumul d1.
5. Oraşul b este legat de oraşul c prin drumul d2.
6. Oraşul a este legat de oraşul c prin drumul d3.
7. Pe drumul d1 pot circula biciclete.
8. Pe drumul d2 pot circula biciclete.

Să se scrie un program Prolog care răspunde întrebării: “ Se poate merge de la oraşul a la


oraşul c?” (răspuns: da).
Laborator 6-7 7

Programul corespunzător problemei este:


domains
oras,drum=symbol

predicates
nondeterm oras(oras)
nondeterm legat(oras,oras,drum)
nondeterm legat1(oras,oras,drum)
nondeterm se_poate_merge(oras,oras)
circula_biciclete(drum)
clauses

oras(a). %ip 4
oras(b). %ip 4
oras(c). %ip 5

circula_biciclete(d1). %ip 7
circula_biciclete(d2). %ip 8
legat(a,b,d1). %ip 4
legat(b,c,d2). % ip 5
legat(a,c,d3). %ip 6
legat1(Y,X,D):-
oras(X),
oras(Y),
legat(X,Y,D). %ip 2*/
se_poate_merge(X,Y):-write("1"),
oras(X),
oras(Y),
legat(X,Y,D),
circula_biciclete(D). %ip 1
se_poate_merge(X,Y):-write("2"),
oras(X),
oras(Y),
legat1(X,Y,D),
circula_biciclete(D). %ip 1- simetrica X,Y=> legat si de la Y=>X
se_poate_merge(X,Z):-write("3"),
se_poate_merge(X,Y),
se_poate_merge(Y,Z). %ip 3

goal
se_poate_merge(a,c). %intrebarea
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
yes

Înlocuiți ipoteza 8 cu “ Pot circula biciclete fie pe drumul d2, fie pe drumul d3, dar nu pe
ambele în acelaşi timp.
8 Recursivitate şi procese repetitive
Să se demonstreze aceeaşi concluzie.

Aplicația 5
Se dă următoarea problemă: o maimuţă se găseşte la uşa unei camere şi o banană se află
agăţată de plafon în centrul camerei. Lângă fereastra camerei se află o cutie pe care maimuţa o
poate folosi pentru a se urca pe ea şi a ajunge la banană. Maimuţa ştie să facă următoarele mişcări:
să meargă pe sol, să se urce pe cutie, să deplaseze cutia dacă este lângă cutie, să apuce banana dacă
este pe cutie şi cutia este în centrul camerei. Se cere să se scrie un program Prolog care să descrie
această problema şi care să poată răspunde dacă, dintr-o configuraţie iniţială specificată prin poziţia
maimuţei şi a cubului, maimuţa poate apuca banana după execuţia unei secvenţe de mişcări.
Reprezentarea universului problemei în Prolog este specificată după cum urmează.

Starea iniţială este:


1. Maimuţa este la uşă.
2. Maimuţa este pe sol.
3. Cutia este la fereastră.
4. Maimuţa nu are banana.

În Prolog poate fi reprezentată această stare prin următorul predicat:

stare(pozitie_maimuta, loc_maimuta, pozitie_cutie, are_sau_nu_banana)

pozitie_maimuta: la usa, la fereastra, la centru


loc_maimuta: pe sol, pe cutie
pozitie_cutie: la usa, la fereastra, la centru
are_sau_nu_banana: are banana, nu are banana

Starea finală este aceea în care maimuţa are banana.

stare(_,_,_, are _banana)

Mişcările cunoscute de maimuţă, deci operatorii de tranziţie dintr-o stare în alta, sunt:
1. Prinde banana. prinde
2. Urcă pe cutie. urcă
3. Mută cutia. mută(Poziţia1, Poziţia2)
4. Merge (maimuţa se deplasează pe sol). merge(Poziţia1, Poziţia2)

Dintr-o anumită stare numai anumite mişcări sunt permise. De exemplu, maimuţa nu poate
apuca banana decât dacă este urcată pe cutie şi cutia este în centrul camerei, adică sub banană.
Mişcările permise pot fi reprezentate în Prolog prin predicatul deplasare cu trei argumente:

deplasare(Stare1, Mişcare, Stare2)

Stare1 = unde se afla inițial maimuța


Stare2 = unde ajunge maimuța după ce face o mișcare
Mișcare = prinde banana, urcă pe cutie, merge, mută cutia
Laborator 6-7 9
Programul corespunzător problemei este:
domains
miscare=prinde;urca;muta(pozitie,pozitie);merge(pozitie,pozitie)
pozitie=la_centru;la_fereastra;la_usa
loc_maimuta=pe_sol;pe_cutie
are_sau_nu_banana=are_banana;nu_are_banana
stare=stare(pozitie,loc_maimuta,pozitie,are_sau_nu_banana)

predicates
nondeterm deplasare(stare,miscare,stare)
nondeterm poate_lua(stare)
clauses
deplasare(stare(la_centru, pe_cutie, la_centru, nu_are_banana),prinde, stare(la_centru, pe_cutie,
la_centru, are_banana)).
deplasare(stare(la_centru, pe_sol, la_centru, nu_are_banana),urca, stare(la_centru, pe_cutie,
la_centru, nu_are_banana)).
deplasare(stare(Pozitie1, pe_sol, Pozitie1, Nu_are), muta(Pozitie1, la_centru), stare(la_centru,
pe_sol, la_centru, Nu_are)).
deplasare(stare(Pozitie1, pe_sol, Pozitie2, Nu_are), merge(Pozitie1, Pozitie2), stare(Pozitie2,
pe_sol, Pozitie2, Nu_are)).

poate_lua(stare( _ , _ , _ , are_banana)).
poate_lua(Stare1) :- deplasare(Stare1, _, Stare2), poate_lua(Stare2).

goal
poate_lua(stare(la_usa, pe_sol, la_fereastra, nu_are_banana)).

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


yes

Cerințe:
1. Testati toate variantele posibile, pentru a vedea dacă maimuța poate lua banana.
2. Adăugați o nouă mișcare pentru maimuță, prin care se poate da jos de pe cutie, dacă
maimuța este pe cutie și nu are banana, iar cutia nu este la centru. Testați pentru a vedea
dacă poate lua banana dacă este pe cutie, iar cutia este la fereastră. Afișați mișcările pe care
le face maimuța pentru a lua banana.
Laborator 8 1

LISTE ÎN PROLOG

Noţiuni teoretice

În Prolog, o listă este un obiect care conţine în interiorul său un număr arbitrar de alte
obiecte. O listă corespunde unui tablou din alte limbaje de programare, dar spre deosebire de
acestea, o listă nu presupune declararea prealabilă a numărului de componente.
O listă care conţine numerele 1,2 şi 3 este scrisă astfel:
[ 1, 2, 3 ]
Fiecare item din listă este denumit element.

Declararea listelor
Se poate declara un domeniu pentru o listă de întregi, ca în exemplul următor:
DOMAINS
listaIntregi = integer*
Caracterul * desemnează o listă. Elementele unei liste pot fi de orice tip, chiar şi o altă listă.
Elementele listei însă trebuie să fie toate de acelaşi tip. În plus, trebuie să existe şi o declarare a
domeniului elementelor listei ca în exemplul următor:
DOMAINS
listaElemente = element*
element = ....
În această declaraţie element trebuie să aparţină aceluiaşi tip (de exemplu: integer, real sau
symbol) . Următorul exemplu este incorect:
listaElemente = element*
element = integer; real; symbol /* incorect */
Pentru a crea o listă care combină elemente de tip întreg, real şi şir de caractere se poate
defini un singur domeniu, utilizând functori. De exemplu:
listaElemente = element*
element = i(integer); r(real); s(symbol) /* functorii sunt i, r, si s */

Structura unei liste


O listă este o structură recursivă de elemente. Ea este formată din două părţi: capul listei şi
coada listei. Coada listei este la rândul său o listă, iar capul listei este un singur element.
De exemplu:
 Capul listei [a, b, c] este a
 Coada listei [a, b, c] este [b, c]
Pentru o listă cu un singur element:
2 Liste în Prolog
 Capul listei [c] este c
 Coada listei [c] este []
Lista vidă nu poate fi separată în cap şi coadă. Din punct de vedere conceptual, o listă are o
structură arborescentă. Structura listei [a, b, c, d] este:
lista
/ \
a lista
/ \
b lista
/ \
c lista
/ \
d []
În continuare, o listă formată dintr-un singur element, ca de exemplu [a], este reprezentată
astfel:
lista
/ \
a []

Prelucrarea listelor
Prolog furnizează o modalitate de scriere a listelor astfel încât capul şi coada listei să fie
evidenţiate explicit. Se utilizează ca separator între capul listei şi coada listei caracterul |, astfel
[a, b, c] este echivalent cu [a|[b, c]]
şi aşa mai departe,
[a|[b, c]] este echivalent cu [a|[b|[c]]]
care este echivalent cu [a|[b|[c|[]]]]. Se pot utiliza ambele tipuri de separatori în cadrul aceleiaşi
liste.

Utilizarea listelor
Datorită faptului că listele sunt structuri de date recursive este necesar să se utilizeze
algoritmi recursivi pentru prelucrarea lor. Cea mai utilizată modalitate de a prelucra o listă este de a
începe parcurgerea acesteia de la început (de la cap) şi de a efectua o operaţie cu fiecare element din
listă până se ajunge la sfârşitul ei.
Un astfel de algoritm în mod uzual utilizează două clauze. Una dintre clauze precizează ce
se efectuează cu o listă obişnuită, iar cealaltă clauză precizează ce operaţii se efectuează cu lista
vidă.

Aplicaţii rezolvate

Aplicaţia 1
Să se implementeze un program Prolog care afişează conţinutul unei liste de numere întregi.
Laborator 8 3
Programul corespunzător problemei este:
domains
lista = integer*
predicates
scrie_lista(lista)
clauses
scrie_lista([]).
scrie_lista([H|T]):-
write(H),nl,
scrie_lista(T).
goal
scrie_lista([1,2,3,4]).

Sau

domains
lista = integer*
predicates
scrie_lista(lista)
clauses
scrie_lista(T):-write(T),nl.
goal
scrie_lista([1,2,3,4]).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
1
2
3
4
yes
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru afişarea conţinutului listei se declară predicatul scrie_lista cu un argument de
tip lista.
În secţiunea clauses se defineşte acest predicat printr-un fapt şi o regulă. Faptul corespunde
cazului unei liste vide. Primul subobiectiv din corpul regulii corespunzătoare celei de-a doua
definiţii permite afişarea valorii din capul listei. Prin intermediul ultimului subobiectiv se
autoapelează acest predicat având ca parametru lista ce reprezintă coadă pentru lista curentă. Acest
apel se va repeta până ce coada listei curente este vidă.

Aplicaţia 2
Să se implementeze un program Prolog care determină lungimea unei liste folosind un
predicat recursiv neoptimizat.

Programul corespunzător problemei este:


domains
lista = integer*
4 Liste în Prolog
predicates
lungime(lista,integer)
clauses
lungime([], 0).
lungime([_|T],L):-
lungime(T,LungimeCoada),
L = LungimeCoada + 1.
goal
lungime([2,4,6,8,10],Lungime).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Lungime=5
1 Solution
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru determinarea numărului de elemente din listă se declară predicatul lungime
cu un argument de tip lista şi un argument de tip integer.
În secţiunea clauses se defineşte acest predicat printr-un fapt şi o regulă. Faptul corespunde
cazului unei liste vide, lungimea unei astfel de liste fiind 0. Primul subobiectiv din corpul regulii
corespunzătoare celei de-a doua definiţii autoapelează acest predicat având ca parametru lista ce
reprezintă coadă pentru lista curentă. Acest apel se va repeta până ce coada listei curente este vidă.
Prin intermediul ultimului subobiectiv se determină lungimea listei curente adunând la
lungimea cozii valoarea 1 corespunzătoare elementului din capul listei. Pentru că autoapelul
predicatului recursiv nu reprezintă ultimul subobiectiv din corpul regulii, definiţia acestuia este
recursivă neoptimizată.

Aplicaţia 3
Să se implementeze un program Prolog care determină lungimea unei liste folosind un
predicat recursiv optimizat.

Programul corespunzător problemei este:


domains
lista = integer*
predicates
lungime(lista,integer,integer)
clauses
lungime([], Rezultat, Rezultat).
lungime([_|T],Rezultat,Contor):-
ContorNou = Contor + 1,
lungime(T, Rezultat, ContorNou).
goal
lungime([1, 3, 5, 7], Lungime, 0).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Lungime=4
Laborator 8 5
1 Solution
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru determinarea numărului de elemente din listă se declară predicatul lungime
cu un argument de tip lista şi două argumente de tip integer.
În secţiunea clauses se defineşte acest predicat printr-un fapt şi o regulă. Faptul corespunde
cazului unei cozii vide, când variabila liberă dată de al doilea argument se va lega de valoarea
specificată de al treilea argument.
Primul subobiectiv din corpul regulii corespunzătoare celei de-a doua definiţii modifică
valoarea ultimului argument prin incrementare cu 1, valoare corespunzătoare elementului din capul
listei.
Prin intermediul ultimului subobiectiv se autoapelează acest predicat având ca prim
parametru lista ce reprezintă coadă pentru lista curentă. Acest apel se va repeta până ce coada listei
curente este vidă.
Pentru că autoapelul predicatului recursiv reprezintă ultimul subobiectiv din corpul regulii,
definiţia acestuia este recursivă optimizată.

Aplicaţia 4
Să se implementeze un program Prolog care permite determinarea produsului elementelor
dintr-o listă de numere întregi, folosind un predicat recursiv neoptimizat.

Programul corespunzător problemei este:


domains
lista = integer*
predicates
scrie_lista(lista)
produs(lista,integer)
rezolva
clauses
produs([],1).
produs([Cap|Rest],P):-
produs(Rest,P1),
P=Cap*P1.
scrie_lista([]).
scrie_lista([H|T]):-
write(H),nl,
scrie_lista(T).
rezolva:-
produs([1,2,3,4],P),
scrie_lista([1,2,3,4]),
write("Produsul este ",P),nl.
goal
rezolva.

Aplicaţia 5
Să se implementeze un program Prolog care permite determinarea produsului elementelor
dintr-o listă de numere întregi, folosind un predicat recursiv optimizat.
6 Liste în Prolog
Programul corespunzător problemei este:
domains
lista = integer*
predicates
scrie_lista(lista)
produs(lista,integer,integer)
rezolva
clauses
produs([],P,P).
produs([Cap|Rest],P,Intermediar):-
I1=Intermediar*Cap,
produs(Rest,P,I1).
scrie_lista([]).
scrie_lista([H|T]):-
write(H),nl,
scrie_lista(T).
rezolva:-
produs([1,2,3,4],P,1),
scrie_lista([1,2,3,4]),
write("Produsul este ",P),nl.
goal
rezolva.

Aplicaţia 6
Să se implementeze un program Prolog care permite modificarea unei liste prin
incrementarea fiecărei valori din listă.

Programul corespunzător problemei este:


domains
lista = integer*
predicates
adauga_1(lista,lista)
clauses
adauga_1([], []).
adauga_1([Cap|Coada],[Cap1|Coada1]):-
Cap1= Cap+1,
adauga_1(Coada,Coada1).
goal
adauga_1([1,2,3,4,5],L).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
L=[2,3,4,5,6]
1 Solution
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru determinarea listei ale cărei elemente sunt obţinute prin incrementarea cu 1 a
valorilor din prima listă se declară predicatul adauga_1 cu două argumente de tip lista.
Laborator 8 7
În secţiunea clauses se defineşte acest predicat printr-un fapt şi o regulă. Faptul corespunde
cazului unei cozii vide, lista rezultată va fi tot o listă vidă.
Primul subobiectiv din corpul regulii corespunzătoare celei de-a doua definiţii determină
valoarea din capul noii liste prin incrementare cu 1 a valorii din capul listei iniţiale.
Prin intermediul ultimului subobiectiv se autoapelează acest predicat având ca prim
argument coada listei cunoscute, iar ca al doilea argument o variabilă liberă utilizată pentru
memorarea cozii listei ce se va determina în apelurile următoare. Acest apel se va repeta până ce
coada listei curente este vidă.

Aplicaţia 7
Să se implementeze un program Prolog care verifică dacă un element aparţine unei liste.

Programul corespunzător problemei este:


domains
listanume= nume*
nume = symbol
predicates
nondeterm apartine(nume, listanume)
clauses
apartine(Nume, [Nume|_]).
apartine(Nume, [_|T]):-
apartine(Nume,T).
goal
apartine("Dan",["Ana","Maria","Dan","Ioan"]).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
yes
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
şiruri de caractere. Pentru a verifica dacă un element aparţine unei liste se declară predicatul
apartine cu un argument de tip nume (symbol) şi un argument de tip listanume.
În secţiunea clauses se defineşte acest predicat printr-un fapt şi o regulă. Faptul corespunde
cazului când elementul căutat se găseşte în capul listei curente, căutarea încheindu-se cu succes. A
doua definiţie tratează cazul când elementul căutat nu se găseşte în capul listei curente, căutarea
continuând cu elementele din coada listei. Această căutare se realizează prin autoapelul predicatului
apartine.

Aplicaţia 8
Să se implementeze un program Prolog care permite adăugarea unui element la sfârşitul unei
liste.

Programul corespunzător problemei este:


domains
lista=element*
element=symbol
predicates
adaugare(element,lista,lista)
8 Liste în Prolog
clauses
adaugare(E,[],[E]).
adaugare(E,[A|L],[A|X]):-
adaugare(E,L,X).
goal
adaugare("Pisica",["Cocos","Oaie","Caine"],L).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
L=["Cocos","Oaie","Caine","Pisica"]
1 Solution

În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru a adăuga un element la sfârşitul unei liste se declară predicatul adaugare cu
un argument de tip element şi două argumente de tip lista.
În secţiunea clauses se defineşte acest predicat printr-un fapt şi o regulă. Faptul corespunde
cazului când lista este vidă, lista finală fiind formată doar din elementul ce se adaugă.
Lista obţinută în urma inserării va avea în capul listei acelaşi element ca şi elementul din
capul listei curente. Elementul dat se va insera în coada listei curente prin autoapelul predicatului
adaugare în a doua definiţie a acestuia.

Aplicaţia 9
Să se implementeze un program Prolog care permite concatenarea a doua liste.

Programul corespunzător problemei este:


domains
lista=element*
element=symbol
predicates
nondeterm concatenare(lista,lista,lista)
scrie_lista(lista)
nondeterm rezolva
clauses
concatenare([],L,L).
concatenare([H|L1],L2,[H|L3]):-concatenare(L1,L2,L3).
scrie_lista([]).
scrie_lista([H|T]):-
write(H),nl,
scrie_lista(T).

rezolva:-
L1=["Denis","Marius","Claudiu"],
L2=["Maria","Elena","Ramona","Claudia","Alexia"],
concatenare(L1,L2,Rez),
scrie_lista(Rez).

goal
Laborator 8 9
rezolva.

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


Denis
Marius
Claudiu
Maria
Elena
Ramona
Claudia
Alexia
yes

Aplicaţia 10
Considerând ca exista o lista cu mai multe nume, să se implementeze un program Prolog
care permite adăugarea in fata fiecărui nume, apelativul dl sau dna in funcție de sexul pe care il are
fiecare.

Programul corespunzător problemei este:


domains
lista=nume*
nume,apelativ =string
sex=char
predicates
nondeterm adaugare(lista,lista)
nondeterm determinareApelativ(sex,apelativ)
scrie_lista(lista)
nondeterm rezolva
clauses

adaugare([],[]).
adaugare([Nume|Rest],[NumeFrumos|Coada]):-
nl,write(Nume," este de sex feminin sau masculin? f/m "),
readchar(C),
determinareApelativ(C,Apelativ),
concat(Apelativ,Nume,NumeFrumos),
adaugare(Rest,Coada).

determinareApelativ(Sex,Apelativ):-
Sex='f',
Apelativ="Dna ".
determinareApelativ(Sex,Apelativ):-
Sex='m',
Apelativ="Dl ".
scrie_lista([]).
scrie_lista([H|T]):-
nl,write(H),
10 Liste în Prolog
scrie_lista(T).

rezolva:-
L=["Maria","Elena","Eduard","Denis"],
adaugare(L,L1),
scrie_lista(L1),nl.
goal
rezolva.

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


Maria este de sex feminin sau masculin? (f/m)
Elena este de sex feminin sau masculin? (f/m)
Eduard este de sex feminin sau masculin? (f/m)
Denis este de sex feminin sau masculin? (f/m)

Dna Maria
Dna Elena
Dl Eduard
Dl Denis
yes

Aplicatie propusa
Considerând ca exista o lista cu numele unor persoane, sa se determine de cate ori apare in
lista un anumit nume introdus de la tastatura.

LISTE – partea a doua

Aplicaţii rezolvate

Aplicaţia 1
Să se implementeze un program Prolog care permite eliminarea valorilor negative dintr-o
listă de numere întregi.

Programul corespunzător problemei este:


domains
lista = integer*
predicates
elimina_negative(lista, lista)
clauses
elimina_negative([], []).
elimina_negative([H|T],CoadaPrelucrata):-
H < 0,!,
elimina_negative(T, CoadaPrelucrata).
elimina_negative([H|T],[H|CoadaPrelucrata]):-
elimina_negative(T, CoadaPrelucrata).
goal
Laborator 8 11
elimina_negative([10, -45, 24, 68, -56, 11], X).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
X=[10,24,68,11]
1 Solution
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru determinarea listei ale cărei elemente sunt obţinute prin eliminarea valorilor
negative din prima listă se declară predicatul elimina_negative cu două argumente de tip lista.
În secţiunea clauses se defineşte acest predicat printr-un fapt şi două reguli. Faptul
corespunde cazului unei cozii vide, lista rezultată fiind tot o listă vidă.
Primul subobiectiv din corpul primei regulii verifică dacă elementul din capul listei curente
este strict negativ. Dacă acest subobiectiv este îndeplinit, ultimul subobiectiv autoapelează acest
predicat având ca prim argument coada listei cunoscute, iar ca al doilea argument o variabilă liberă
utilizată pentru memorarea cozii listei ce se va determina în apelurile următoare.
Obiectivul din corpul celei de-a doua reguli tratează cazul când elementul din capul listei
este pozitiv, acest element fiind transmis ca şi cap şi pentru lista rezultantă. Iar apoi se autoapelează
acest predicat având ca prim argument coada listei cunoscute, iar ca al doilea argument o variabilă
liberă utilizată pentru memorarea cozii listei ce se va determina în apelurile următoare.

Aplicaţia 2
Să se implementeze un program Prolog care calculează media aritmetică a elementelor unei
liste de numere reale.

Programul corespunzător problemei este:


domains
lista=real*
predicates
media_real(lista,real,real,integer)
rezolva
clauses
media_real([],0,0,0):-!.
media_real([],M,S,N):-
M=S/N.
media_real([H|C],M,S,N):-
S1=S+H,
N1=N+1,
media_real(C,M,S1,N1).
rezolva:-
media_real([20,5,-15,35,10],M,0,0),
write("Media=",M,"\n").
goal
rezolva.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Media=11
12 Liste în Prolog
yes
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere reale. Pentru a determina media aritmetică a elementelor din listă se declară predicatul
media cu un argument de tip lista, două argumente de tip real şi un argument de tip integer şi
predicatul rezolva fără argumente.
Predicatul media se defineşte prin două reguli. Prima definiţie corespunde cazului când lista
este vidă, media aritmetică determinându-se din suma valorilor dată de al treilea argument şi
numărul acestora specificat de ultimul argument. Cea de-a doua definiţie a acestui predicat
determină suma şi numărul elementelor parcurse până în acel moment, calculul rezumându-se în
continuare la parcurgerea elementelor din coada listei prin autoapel.
Predicatul rezolva este definit printr-o regulă, primul obiectiv apelând predicatul media
având argumentele unu, trei şi patru variabile legate. Al doilea obiectiv permite afişarea rezultatului
prin utilizarea predicatului de interacţiune write.

Aplicaţia 3
Să se implementeze un program Prolog care sortează crescător o listă de numere întregi.
OBS: Sortare prin inserţie constă în eliminarea unui element din listă, sortarea listei
obţinute şi în final reinserarea elementului în listă astfel încât lista să rămână
sortată. Predicatul sortare(L,L1), definit mai jos, determină sortarea listei L prin
inserţie şi legarea variabilei L1 la lista sortată obţinută. Pentru definirea
acestui predicat se utilizează următorul predicat auxiliar: inserare(A,L,L1) cu
semnificaţia L este lista sortată obţinută prin inserarea elementului A în lista
sortată L1.

Programul corespunzător problemei este:


domains
lista=element*
element=integer
predicates
sortare(lista,lista)
inserare(element,lista,lista)
clauses
sortare([],[]):-!.
sortare([A|L],L1):-
sortare(L,L2),inserare(A,L2,L1).
inserare(A,[],[A]):-!.
inserare(A,[B|L],[A|[B|L]]):-
A<B,!.
inserare(A,[B|L],[B|L1]):-
inserare(A,L,L1).
goal
sortare([-4, 345, -23, 2, 12],L).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
L=[-23,-4,2,12,345]
Laborator 8 13
1 Solution
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru a sorta crescător elementele listei se declară predicatul inserare cu un
argument de tip element şi două argumente de tip lista şi predicatul sortare cu două argumente de
tip lista.
Predicatul inserare se defineşte prin trei reguli ce vor permite inserarea unui număr în listă
astfel încât după inserare lista să rămână sortată. Prima definiţie tratează cazul când lista este vidă,
în urma inserării obţinându-se o listă formată dintr-un singur element. Primul subobiectiv din cea
de-a doua regulă tratează cazul când numărul din capul listei curente este mai mic decât numărul ce
se doreşte a fi inserat. În acest caz se construieşte o nouă listă ce va avea în cap numărul ce se
inserează, iar coadă va fi lista curentă. Dacă primul subobiectiv din cea de-a doua regulă nu este
îndeplinit, atunci, conform ultimei reguli, se va încerca inserarea în coada listei curente.
Predicatul sortare se defineşte prin două reguli. Prima definiţie corespunde cazului când
lista este vidă. Cea de-a doua definiţie a acestui predicat permite sortarea elementelor din coada
listei curente, iar apoi utilizează predicatul inserare pentru a adăuga elementul din capul listei în
lista obţinută prin sortarea elementelor din coadă.

Aplicaţia 4
Să se implementeze un program Prolog care verifică dacă elementele listei sunt distincte.

Programul corespunzător problemei este:


domains
element=integer
lista=element*
predicates
distincte(lista)
apartine(element,lista)
clauses
distincte([]):-!.
distincte([A|C]):-
not(apartine(A,C)),
distincte(C).
apartine(A, [A|_]):-!.
apartine(A, [_|C]):-
apartine(A,C).
goal
distincte([1,2,3,4,2,6,7]).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
no
În prima secţiune a programului se defineşte tipul de dată lista corespunzător unei liste de
numere întregi. Pentru a verifica dacă elementele listei sunt distincte se declară predicatul apartine
cu un argument de tip element şi un argument de tip lista şi predicatul distincte cu un argument de
tip lista.
Definiţia predicatului apartine coincide cu cea din aplicaţia 6. Predicatul distincte se
defineşte prin două reguli. Prima definiţie corespunde cazului când lista este vidă. Cea de-a doua
14 Liste în Prolog
definiţie a acestui predicat verifică dacă elementul din capul listei curente nu aparţine cozii acestei
liste. În caz afirmativ se va verifica dacă elementele din coada listei sunt distincte. Procedeul se
repetă până coada listei curente devine vidă.

Aplicaţia 5
Fie o baza de date cu drumuri între orase de forma drum(oras1,oras2).
Predicatul traseu(X, Y, T) este adevărat dacă se poate ajunge de la oraşul X la oraşul Y,
calculând si traseul T între cele două oraşe. Drumurile sunt bidirecţionale (dacă exista un drum de
la X la Y, atunci există implicit un drum de la Y la X).
De exemplu:
drum(calan, hunedoara).
drum(calan, simeria).
drum(santuhalm, simeria).
drum(santuhalm, hunedoara).
drum(deva, santuhalm).

Programul corespunzător problemei este:


domains
listaOrase=oras*
oras=symbol

predicates
nondeterm drum(oras,oras) %exista drum de la un oras la alt oras
nondeterm traseu(oras,oras) % predicatul prin care se dau 2 orase pt a vedea traseul
nondeterm det_traseu(oras,oras,listaOrase)%predicatul care determina traseul prin care se
ajunge de la un oras la alt oras
apartine(oras,listaOrase)%pt a vedea daca un oras a fost scris deja in lista de orase pt
determinarea traseului de la un oras la altul
nondeterm rezolva %predicatul care executa cateva teste (pt mai multe orase se determina
traseul pt fiecare).

clauses
drum(calan, hunedoara).
drum(calan, simeria).
drum(santuhalm, simeria).
drum(santuhalm, hunedoara).
drum(deva, santuhalm).

apartine(X, [X|_]) :- !.
apartine(X, [_|T]):-apartine(X, T).

traseu(Sursa, Destinatie) :- write("x "),


det_traseu(Sursa, Destinatie, [Destinatie]).

det_traseu(Sursa, Sursa, Traseu) :- write("1 "),nl,


write(Traseu), nl.
Laborator 8 15
det_traseu(Sursa, Oras, Traseu) :- write("2 "),drum(OrasInt, Oras),
not(apartine(OrasInt, Traseu)),
det_traseu(Sursa,OrasInt, [OrasInt | Traseu]).

det_traseu(Sursa, Oras, Traseu) :- write("3 "),drum(Oras, OrasInt),


not(apartine(OrasInt, Traseu)),
det_traseu(Sursa,OrasInt, [OrasInt | Traseu]).

det_traseu( _ , _ , _ ) :- nl,write("Nu exista traseu."),nl.

% cateva teste
rezolva :-
traseu(calan, deva),nl,
traseu(deva, calan),nl,
traseu(calan, hunedoara),nl,
traseu(hunedoara, calan),nl,
traseu(simeria, craiova).
goal
rezolva.

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


x232321
["calan","simeria","santuhalm","deva"]

x23221
["deva","santuhalm","hunedoara","calan"]

x21
["calan","hunedoara"]

x231
["hunedoara","calan"]

x23
Nu exista traseu.
yes

LISTE PARTEA a 3 a

Aplicaţii rezolvate

Aplicaţia 1
Să se implementeze un program Prolog care permite citirea a n numere şi memorarea lor
într-o listă.

Programul corespunzător problemei este:


16 Liste în Prolog
domains
lista=element*
element=integer

predicates
adaugare_element(lista,lista,integer,integer)
creare_lista(lista,integer)
initializare(lista)

clauses
initializare([]).
creare_lista(L,N):-
initializare(L1),
adaugare_element(L1,L,N,0).
adaugare_element(L,L,N,N):-!.
adaugare_element(L1,L,N,I):-
write("Numarul = "),
readint(X),!,
I1=I+1,
adaugare_element([X|L1],L,N,I1).

goal
write("Numar elemente="),
readint(N),
creare_lista(L,N).

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Numar elemente=5
Numarul=33
Numarul=-11
Numarul=22
Numarul=-100
Numarul=50
N=5, L=[50,-100,22,-11,33]
1 Solution

Aplicaţia 2
Următorul exemplu ilustrează o aplicaţie de tip sistem expert pentru a diagnostica o
defecţiune într-un circuit electric, de exemplu dintr-o locuinţă. Se presupune că în circuit se pot
conecta diverse dispozitive (becuri, reşouri, etc). Toate aceste dispozitive pot fi conectate prin
intermediul unor siguranţe (pot fi mai multe dispozitive pe aceeaşi siguranţă). Sistemul permite
identificarea dispozitivelor defecte, a siguranţelor arse, etc.

Sunt definite următoarele reguli:


defect(tip_element) care apreciază că un element este defect dacă un element nu
funcţionează deşi este conectat la o siguranţă, iar acea siguranţă este intacta;
Laborator 8 17
intact(siguranta) care apreciază că o siguranţă este intactă dacă siguranța nu este arsa;
ars(siguranta) care apreciază că o siguranţă este arsă atunci când la ea sunt conectate cel
puţin 2 elemente diferite, iar acestea nu funcţionează sau daca este conectat un singur element, iar
acesta nu functioneaza.
De exemplu, pentru un circuit simplu ca în figura următoare:

Programul corespunzător problemei este:


domains
denumire=symbol
putere=integer
imax=integer
tip_element=bec(denumire,putere);resou(denumire,imax,putere)
lista_elemente=tip_element*
siguranta=symbol
numar_elemente_lista=integer

predicates
%numara cate elemente sunt conectate la fiecare siguranta
nondeterm numaraTotal(siguranta,lista_elemente,numar_elemente_lista)
%la o siguranta sunt conectate o lista de elemente
nondeterm lista_siguranta(siguranta,lista_elemente)
%obtinerea pe rand a elementelor din lista, in a doua lista se obtine o lista ce contine
elementele din coada listei initiale
nondeterm obtinere_element(tip_element, lista_elemente,lista_elemente)
%un anumit element functioneaza
nondeterm functioneaza(tip_element)
%un anumit element este defect
nondeterm defect(tip_element)
%o anumita siguranta este intacta
nondeterm intact(siguranta)
18 Liste în Prolog
%o anumita siguranta este arsa
nondeterm ars(siguranta)
nondeterm rezolva

clauses
defect(Dispozitiv):-
lista_siguranta(Siguranta,Lista_Dispozitive),
obtinere_element(Dispozitiv,Lista_Dispozitive,_),
not(functioneaza(Dispozitiv)),
intact(Siguranta).

intact(Siguranta):- lista_siguranta(Siguranta,_), not(ars(Siguranta)).

%def 1-Daca nu mai exista elemente in lista de dispozitive, atunci a doua lista va fi lista vida
si nu se mai face backtracking
obtinere_element(Nume,[Nume|[]],[]):-!.
%def 2-obtinerea pe rand a elementului din capul listei
obtinere_element(Nume, [Nume|Coada],Coada).
%def 3-trecerea la alt element din lista de dispozitive
obtinere_element(Nume, [_|Coada],L):-obtinere_element(Nume,Coada,L).

%daca la siguranta curenta nu este conectat decat un dispozitiv, atunci se verifica daca acest
element nu functioneaza.
ars(Siguranta):-write("1 "),lista_siguranta(Siguranta,Lista_Dispozitive),
numaraTotal(Siguranta,Lista_Dispozitive,N),
N=1,
obtinere_element(Dispozitiv,Lista_Dispozitive,_),
not(functioneaza(Dispozitiv)).
%daca sunt mai multe dispozitive conectate la o siguranta, se cauta in lista 2 dintre acestea
care sa nu functioneze
ars(Siguranta):- write("2 "),lista_siguranta(Siguranta,Lista_Dispozitive),
obtinere_element(Dispozitiv1,Lista_Dispozitive,Lista),
not(functioneaza(Dispozitiv1)),
obtinere_element(Dispozitiv2,Lista,_),
not(functioneaza(Dispozitiv2)).

numaraTotal(_,[],0):-!.
numaraTotal(Siguranta,[_|Coada],N):-numaraTotal(Siguranta,Coada,N1),
N=N1+1.

functioneaza(bec(bec1,100)).
functioneaza(bec(bec2,60)).
functioneaza(bec(bec3,60)).
lista_siguranta(siguranta1,[bec(bec1,100),bec(bec2,60),resou(resou1,10,400)]).
lista_siguranta(siguranta2,[bec(bec3,60),bec(bec4,60)]).
Laborator 8 19
rezolva:-defect(Dispozitiv),
write(" ------ ",Dispozitiv),nl,
fail.

goal
rezolva.

Testul „goal” va furniza în fereastra de ieşire următorul rezultat:


resou("resou1",10,400)
bec("bec4",200)
no

Cerinte:
1. Fie sistemul expert pentru determinarea cauzei apariţiei unor defecte într-un circuit
electric. Modificaţi acest sistem astfel încât să se specifice elementele conectate la o
siguranţă prin intermediul listelor. Denumirea sigurantei va fi introdusa de la intrarea
standard.

2. a) Completaţi sistemul ca in figura de mai jos, astfel încât circuitul electric să mai
conţină următoarele elemente:

Este cunoscut faptul că, în plus faţă de situaţia precedentă, mai funcţionează frigiderul
zanussi. Pentru frigider se cunosc: denumirea,capacitatea si puterea. Determinaţi elementele de
circuit ce sunt defecte şi siguranţele ce sunt arse.
20 Liste în Prolog
b) Mai adăugaţi în circuit o maşină de spălat (se cunoaste denumirea, capacitatea,
categoria – A, A++, etc) şi un cuptor cu microunde (se cunoaste denumirea si puterea)
conectate ambele pe o a patra siguranţă. Presupunem că se ştie că funcţionează doar
maşina de spălat.

Aplicatii propuse
1. Considerând numele mai multor persoane, existente intr-o lista statica, sa se afișeze care
este numele ultimei persoane din lista.

2. Considerând numele a n persoane, sa se afișeze care este ultimul nume introdus in lista
(de la intrarea standard).
Laborator 9 1

Secţiunile facts în Prolog

În acest capitol se descrie cum poate fi declarată secţiunea facts şi cum se poate modifica conţinutul acestei
secţiuni.
O secţiune facts este compusă din fapte care pot fi adăugate sau şterse direct în/din program în timpul
execuţiei acestuia. Se declară predicatele prin descrierea faptelor în secţiunea facts a programului, apoi se
pot utilize aceste predicate în aceeaşi manieră ca şi cele declarate în secţiunea predicates.
Se utilizează predicatele assert, asserta, assertz pentru a adăuga noi fapte în secţiunea facts şi predicatele
retract şi retractall pentru a şterge fapte din secţiunea facts. Se poate modifica secţiunea facts prin retragerea
unui fapt urmată apoi de declararea unei noi versiuni a aceluiaşi fapt (sau a unuia diferit). Predicatul consult
citeşte faptele dintr-un fişier şi le declară în secţiunea de fapte interne şi le salvează apoi pe toate într-un
fişier.
Visual Prolog tratează faptele care aparţin secţiunii facts diferit faţă de modul în care sunt tratate predicatele.
Faptele din secţiunea facts sunt salvate în tabele, care sunt uşor de modificat, în timp ce predicatele normale
sunt compilate în binar pentru a avea viteză maximă.

Declararea sectiunii facts

Cuvântul cheie facts (sau database) marchează începutul unei secvenţe de declaraţii de predicate care
descriu secţiunea facts.
Se pot adăuga fapte – nu şi reguli – la o secţiune facts de la tastatură cu asserta şi assertz. Sau, prin apelul
predicatului standard consult regăsi faptele noi prin citirea dintr-un fişier. Secţiunea facts are sintaxa
prezentată în următorul exemplu:
DOMAINS
nume, adresa= string
varsta = integer
sex= masculin; feminin

FACTS
persoana(nume, adresa, varsta, sex)

PREDICATES
masculin(nume, adresa, varsta)
feminin(nume, adresa, varsta)
copil(nume, varsta, sex)

CLAUSES
masculin(Nume, Adresa, Varsta) :-
persoana(Nume, Adresa, Varsta, masculin).

În acest exemplu se poate utiliza predicatul “persoana” la fel ca şi alte predicate. Singura
diferenţă este că se pot introduce şi şterge fapte pentru predicatul “persoana” în timpul execuţiei
programului.
Există două restricţii în secţiunea “facts”:
1) Nu se pot introduce reguli
2 Liste în Prolog
2) Faptele nu pot avea variabile libere.

Există posibilitatea de a avea câteva secţiuni facts, dar, în acest caz trebuie denumite în mod explicit fiecare
dintre acestea.

FACTS - mydatabase
primaRelatie(integer)
aDouaRelatie(real, string)
aTreiaRelatie(string)
/* etc. */

Această declaraţie creează o secţiune facts cu numele mydatabase. Dacă nu se furnizează un nume
pentru o bază de date de fapte se va utilize numele standard dbasedom .
Numele predicatelor într-o secţiune facts trebuie să fie unice în cadrul unui modul (fişier sursă), nu
se poate utilize acelaşi nume în două secţiuni facts diferite. Numele de predicate dintr-un fişier sursă
sunt private în cadrul modulului corespunzător fişierului sursă respective aşa că nu vor exista
conflicte de nume cu predicate declarate cu aceleaşi nume din alte module.

Utilizarea secţiunilor facts


Deoarece Visual Prolog reprezintă o secţiune relaţională de fapte ca o colecţie de fapte, ea se poate utilize ca
un limbaj de interogare pentru baze de date. Algoritmul de unificare din Visual Prolog selectează automat
fapte cu valoarea corectă pentru argumentele cunoscute şi atribuie valori oricăror argumente necunoscute,
utilizând algoritmul backtracking, pentru a da soluţii oricărui obiectiv.

Accesarea secţiunii facts


Predicatele aparţinând unei secţiuni facts sunt accesate la fel ca şi alte predicate. Singura diferenţă vizibilă
este declararea lor în secţiuni diferite facts şi respective predicates. Luăm în considerare următorul exemplu:
DOMAINS
nume = string
sex = char

FACTS
persoana(nume,sex)

CLAUSES
persoana("Elena",'F').
persoana("Maria",'F').
persoana("Sorina",'F').
persoana("Petre",'M').

se poate apela predicatul persoana cu obiectivul persoana(Nume,'F')pentru a găsi toate femeile sau cu
obiectivul persoana("Maria",'F')pentru a verifica dacă există o persoană de sex feminine cu numele Maria
în secţiunea facts a programului.
Trebuie făcută precizarea că, predicatele din secţiunea facts sunt, prin natura lor nedeterministe. Deoarece se
pot adăuga oricând fapte în timpul execuţiei programului, compilatorul trebuie să presupună că oricând se
Laborator 9 3
pot găsi soluţii alternative la un obiectiv în timpul mecanismului backtracking. Dacă există vreun predicat în
secţiunea facts care nu va avea mai mult de un fapt, puteţi preciza acest lucru prin directive determ în cadrul
declarării acestui predicat:
FACTS
determ nume_predicat(integer)

Tentativa de a adăuga mai multe fapte corespunzătoare acestui predicat va conduce la obţinerea unui mesaj
de eroare.

Actualizarea secţiunii FACTS

Faptele pentru baza de date de predicate se pot specifica la compilare în secţiunea clauses. La
execuţie faptele se pot adăuga sau şterge. Faptele specificate la compilare în secţiunea CLAUSES
se pot de asemenea şterge.
Predicatele standard pentru baze de date Visual Prolog sunt: assert, asserta, assertz, retract,
retractall, consult şi save şi au unul sau două argumente. Al doilea argument este opţional şi este
numele secţiunii facts. Notaţia "/1" şi "/2"utilizată după fiecare nume de predicat specifică
numărul de argumente . Comentariile, ca de exemplu /* (i) */ şi /* (o,i) */ arată modelul acestui
predicat.

Adăugarea de fapte în timpul execuţiei


Adăugarea de fapte în timpul execuţiei se face utilizând predicatele: assert, asserta şi
assertz sau prin încărcarea unui fişier utilizând consult.
Există trei predicate pentru a adăuga fapte în timpul execuţiei:
asserta(< fapt > ) /* (i) */

asserta(< fapt > , nume_sectiune_facts) /* (i, i) */

assertz(< fapt > ) /* (i) */

assertz(< fapt > , nume_sectiune_facts) /* (i, i) */

assert(< fapt > ) /* (i) */

assert(< fapt > , nume_sectiune_facts) /* (i, i) */

asserta declară un nou fapt în secţiunea facts înaintea faptelor existente pentru predicatul respective iar
assertz declară un nou fapt după faptele existente pentru predicatul respectiv. Predicatul assert se comportă
ca şi assertz.
La declararea predicatelor se cunoaşte întotdeauna care este secţiunea facts în care se va insera faptul
respective deoarece numele de predicate sunt unice în cadrul unui modul. Se poate însă utiliza cel de-al
doilea argument opţional pentru a specifica în mod explicit numele secţiunii.
4 Liste în Prolog
Primul dintre exemplele următoare inserează un fapt pentru predicatul persoana, fapt referitor la Sorina, după
toate faptele care sunt declarate în secţiunea facts. Al doilea exemplu inserează un fapt despre Mihai înaintea
tuturor faptelor. Al treilea exemplu inserează un fapt despre John după toate faptele place din secţiunea
bazaDeDatePlace, în timp ce al patrulea fapt despre Simona în aceeaşi secţiune facts, înaintea tuturor faptelor
place.
assertz(persoana("Sorina", "Hunedoara", 35)).
asserta(persoana("Mihai", "Deva", 26)).
assertz(place("John", "bani"), bazaDeDatePlace).
asserta(place("Simona", "munca de birou"), bazaDeDatePlace).

După aceste apeluri secţiunea facts arată astfel:


/* Sectiunea facts implicita -- dbasedom */
persoana("Mihai", "Deva", 26).
/* ... alte fapte referitoare la predicatul persoana ... */
persoana("Sorina", "Hunedoara", 35).

/* Sectiunea de fapte -- bazaDeDatePlace */


place("Simona", "munca de birou").
/* ... alte fapte despre predicatul place ... */
place("John", "bani").

Trebuie avut grijă la declararea faptelor în cadrul unei secţiuni facts, astfel încât un fapt să nu apară de mai
multe ori cu aceeaşi declaraţie. Un test de unicitate se poate face în felul următor:
FACTS - persoane
persoana(string,string)

PREDICATES
uassert(persoane)

CLAUSES
uassert(persoana(Nume,Adresa)):-
persoana(Nume,Adresa), ! ; % sau
assert(persoana(Nume,Adresa)).

Încărcarea faptelor dintr-un fişier în timpul execuţiei


consult citeşte în fişierele cu numele numeFisier care conţine fapte declarate în secţiunea facts şi
declară aceste fapte la sfârşitul secţiunii facts corespunzătoare. Predicatul consult are două
argumente:
consult(numeFisier) /* (i) */
consult(numeFisier, numeBazaDeDate) /* (i, i) */

Spre deosebire de predicatul assertz dacă se apelează consult cu un singur argument (fără numele secţiunii
facts), se vor citi doar faptele care sunt declarate în secţiunea facts cu numele implicit dbasedom.
Dacă se aplează consult cu două argumente (numele fişierului şi numele secţiunii facts), se vor consulta doar
faptele din secţiunea facts respectivă (cu numele precizat în al doilea argument). Dacă fişierul conţine şi
altceva în afara faptelor aparţinând respectivei secţiuni facts, consult va returna un mesaj se eroare când
ajunge la acea parte din fişier.
De reţinut că predicatul consult citeşte un fapt odată; dacă fişierul are 10 fapte şi cel de-al şaptelea are erori
de sintaxă, predicatul consult va insera primele şase fapte în secţiunea facts apoi va emite un mesaj se eroare.
Laborator 9 5
Predicatul consult este capabil să citească un fişier în acelaşi format în care este generat de către predicatul
save . Nu se pot utiliza :
Caractere majuscule, cu excepţia celor dintre ghilimele
Spaţii cu excepţia celor dintre ghilimele
Comentarii
Linii vide
Simboluri fără ghilimele

Ştergerea faptelor în timpul execuţiei


Predicatul retract unifică faptele şi le şterge din secţiunea facts. El are următoarea sintaxă:
retract(<fapt> [, numeBazaDeDate]) /* (i, i) */

predicatul retract va şterge primul fapt din secţiunea facts care se potriveşte cu <fapt>. Retr ager ea
(şt er ger ea ) faptelor din s ecţ iu nea facts se face la fel cu accesarea lor. În afară de situaţia în care
predicatul accesat de retract a fost declarat în secţiunea facts ca deterministic, retract este nedeterministic
şi , în timpu l meca nis mu lu i backtr acking, el va şterge, pe rand toate faptele rămase care se
potrivesc primului argument, unul câte unul. După ce toate faptele care se potrivesc au fost şterse, predicatul
retract se va termina cu eşec.
Să presupunem că există într-un program următoarea secţiune facts:
DATABASE
persoana(string, string, integer)

FACTS - bazaDeDatePlace
place(string, string)
displace(string, string)

CLAUSES
persoana("Florin", "Petrosani", 35).
persoana("Florin", "Deva", 37).
persoana("Michael", "Hunedoara", 26).

place("John", "bani").
place("Jane", "bani").
place("Chris", "ciocolata").
place("John", "cafea").

displace("Florin", "cafea").
displace("Michael", "bere").

Programul poate fi testat cu următoarele obiective:


retract(persoana("Florin", _, _)). /* 1 */
retract(place(_, "cafea")). /* 2 */
retract(place(_, "bani"), bazaDeDatePlace). /* 3 */
retract(persoana("Florin", _, _), bazaDeDatePlace) /* 4 */
6 Liste în Prolog
Primul obiectiv retrage (şterge) primul fapt persoana despre Florin din secţiunea facts cu nume implicit
dbasedom . Al doilea obiecti va şterge primul fapt care se potriveşte cu place(X, "cafea") din secţiunea
facts cu numele bazaDeDatePlace. În ambele obiective Prolog ştie care sunt secţiunile facts din care se face
ştergerea predicatelor respective deoarece numele predicatelor sunt unice: predicatul persoana este declarat
doar în secţiunea implicită iar predicatul place este definit doar în secţiunea facts cu numele
bazaDeDatePlace .
Cel de-al treilea şi al patrulea obiectiv ilustrează cum poate fi utilizat al doilea argument al predicatului
retract. Al treilea obiectiv se încheie cu success, prin retragerea primului fapt care se potriveşte cu obiectivul
place(_, "bani")din secţiunea cu numele bazaDeDatePlace, dar al patrulea obiectiv nu se poate compila
deoarece nu sunt declarate predicate cu numele persoana în secţiunea facts bazaDeDatePlace. Mesajul de
eroare pe care-l dă compilatorul este:
506 Type error: The functor does not belong to the domain.

Următorul exemplu ilustrează cum se pot obţine valori de la predicatul retract.


GOAL
retract(persoana(Name, Varsta)),
write(Name, ", ", Varsta),
fail.

Dacă se utilizează pentru al doilea argument al predicatului retract numele unei secţiuni facts, nu mai este
necesară specificarea faptului care se va retrage. În acest caz retract va retrage toate predicatele pe care le
găseşte în secţiunea facts specificată. Următorul exemplu ilustrează observaţia anterioară:
GOAL
retract(X, bazaDeDate),
write(X),
fail.

Ştergerea simultană a mai multor fapte


retractall şterge toate faptele care se potrivesc cu primul său argument r<fapt> din secţiunea facts şi are
următoarea sintaxă
retractall(<fapt> [, numeBazaDeDate])

retractall se comportă ca şi cum ar fi definit prin


retractall(X):- retract(X), fail.
retractall(_).

Dar este mult mai rapid decât secvenţa anterioară


Predicatul retractall are success exact o singură dată şi nu se pot obţine valori de ieşire de la acest predicat.
Acest lucru înseamnă că, la fel ca şi în cazul predicatului not trebuie utilizată variabila anonimă pentru
variabilele libere.
La fel ca şi în cazul predicatelor assert şi retract se poate utiliza al doilea argument . Şi, la fel ca în cazul
predicatului retract se poate apela retractall cu variabila anonimă pentru a şterge toate faptele dintr-o
secţiune
Următorul obiectiv şterge toate faptele corespunzătoare persoanelor de sex masculine dintr-o secţiune facts.
retractall(persoana(_, _, _, masculin)).

Următorul obiectiv şterge toate faptele din secţiunea mydatabase


Laborator 9 7
retractall(_, mydatabase).

Cuvinte cheie utilizate în secţiunea facts


Faptele se pot decalra utilizând câteva cuvinte cheie:
NONDETERM precizează că pot exista oricâte instanţe a unui fapt. Aceasta este opţiunea implicită
DETERM precizează că nu poate exista decât o singură instanţă a unui fapt
GLOBAL precizează că secţiunea facts este globală în cadrul proiectului
SINGLE precizează că ar trebui să existe doar o instanţă a faptului fact_N. Fact_N este functorul
pentru faptele (predicatele) aparţinând acestei secţiuni facts.
NOCOPY precizează că, aceste date nu sunt copiate din stiva globală a Visual Prolog, Visual Prolog
Global Stack (GStack). În mod normal, când se apelează un fapt pentru a se potrivi o
variabilă cu un string sau cu un obiect compus, şirul sau obiectul respectiv sunt copiate în
Gstack.

Observaţii

Fapte declarate cu cuvântul cheie nondeterm.


Cuvântul cheie nondeterm este modul implicit de declarare a faptelor (predicatelor database) declarate în
secţiunile facts. Dacă nici unul dintre cuvintele cheie determ sau single nu este specificat la declarare,
compilatorul va aplica cuvântul cheie nondeterm. În mod normal, prin natura lor, predicatele dintr-o bază de
date sunt nedeterministe. Deoarece faptele se pot adăuga în timpul execuţiei oricând, compilatorul trebuie să
presupună întotdeauna că este posibil să găsească soluţii alternative prin mecanismul backtracking la orice
predicat.
Dacă totuşi există un predicat într-o bază de date căruia nu-i va corespunde decât un singur fapt, se va utiliza
unul dintre cuvintele cheie determ sau single la declararea lui.

Fapte declarate cu cuvântul cheie determ.

Cuvântul cheie determ precizează că baza de date de fapte va conţine o singură instanţă a predicatului astfel
declarat fact_N(...). Astfel, dacă se va încerca declararea unui al doilea fapt care corespunde acestui predicat
motorul de inferenţă Visual Prolog va genera o eroare la execuţie (1041 Assert to a fact declared as determ,
but fact already exists).
Dacă se precede un fapt cu cuvântul cheie determ compilatorul va putea produce un cod mai eficient şi nu se
vor mai obţine mesajele de eroare de avertizare.
În particular atunci când se retrage (şterge) un fapt care a fost declarat ca determinist, apelul retract/1 şi
retract/2predicatelor va fi deterministic. Astfel, dacă se ştie faptul că în orice moment secţiunea facts nu
conţine mai mult de un fapt contor() se poate scrie:
8 Liste în Prolog
FACTS
determ contor(integer ValoareContor)
GOAL
...
retract(contor(ContorCurent)),
/* aici Prolog nu va pune un punct de revenire - backtracking point */
Contor= ContorCurent + 1,
assert(contor(Contor)),
...
în loc de
FACTS
contor(integer ValoareContor)
PREDICATES
determ my_retract(dbasedom)
CLAUSES
my_retract(X): - retract(X),!. % predicat deterministic
GOAL
...
my_retract(contor(ContorCurent)),
/* aici Prolog nu va pune un punct de revenire - backtracking point */
Contor= ContorCurent + 1,
asserta(contor(Contor)),
...

Fapte declarate cu cuvântul cheie single.


Cuvântul cheie single utilizat ca prefix în declararea unui fapt fact_N precizează că numai o instanţă a
faptului respective trebuie să existe:
Faptele declarate cu single trebuie să fie deja cunoscute când se testează un obiectiv în secţiunea Goal; de
aceea faptele declarate single trebuie iniţializate într-o secţiune clauses în codul sursă al programului. De
exemplu:

FACTS
single singleFact(STRING, STRING)
CLAUSES
singleFact("","").

Faptele declarate single nu se pot retrage (şterge). Dacă se face o tentativă de retragere a unui astfel de fapt
se va obţine un mesaj de eroare 249 "Attempt to retract a fact declared as single". Deoarece există o singură
instanţă a unui astfel de fapt, apelul său nu se va termina cu eşec dacă se apelează cu argumente libere. De
exemplu apelul
singleFact(X,Y),

nu se va termina cu eşec dacă X şi Y sunt variabile libere. De aceea este convenabil să se utilizeze fapte
single în predicatele assert, asserta, assertz şi consult aplicate unui fact single. Aceasta înseamnă că assert va
modifica instanţa existentă a faptului respectiv.
Prin precedarea unui fapt cu cuvântul cheie single compilatorul va produce un cod mai optimizat pentru
accesarea şi actualizarea unui fapt. De exemplu, pentru predicatul assert aplicat unui fapt precedat cu single
copilatorul va genera un cod care lucrează mult mai eficient decât o pereche de predicat retract şi assert
aplicate unui fapt declarat cu determ.
Laborator 9 9
Salvarea unei baze de date de fapte în timpul execuţiei

Predicatul save salvează faptele date într-o secţiune facts într-un fişier. Acest predicat are unul sau două
argumente:
save(fileName) /* (i) */
save(fileName, databaseName) /* (i, i) */

Dacă se utilizează predicatul save cu un singur argument se vor salva faptele din baza de date implicită cu
numele dbasedom în fişierul specificat.
Dacă se utilizează predicatul save cu două argumente se va salva baza de date cu numele specificat în fişierul
specificat.

Exemple

1. Acest exemplu prezintă un minisistem expert pentru clasificare. Avantajul important al utilizării secţiuni
facts din acest exemplu este că se pot adăuga cunoştinţe şi se pot şterge în timpul execuţiei.
DOMAINS

obiect = string

utils = util*

util = string

FACTS

este_un(obiect, obiect, utils)

tipul_lui(obiect, obiect, utils)

false(util)

PREDICATES

nondeterm run(obiect)

nondeterm intreaba(utils)

actualizeaza

CLAUSES

run(Item):-

este_un(X, Item, Lista),

intreaba(Lista),

tipul_lui(RASPUNS, X, Lista2),

intreaba(Lista2),

write( Item," de care aveti nevoie este un ", Raspuns),nl.

run(_):-

write("Acest program nu are suficiente "),

write("date pentru a trage o concluzie. "),

nl.
10 Liste în Prolog
intreaba([]).

intreaba([H|T]):-

not(false(H)),

write("Va ajuta acest obiect pentru "),

write(H," (introduceti y/n)"),

readchar(Raspuns), nl, Raspuns='y',

intreaba(T).

intreaba([H|_]):-

assertz(false(H)), fail.

este_un(limbaj, obiectul, ["comunica"]).

este_un(ciocan, obiectul, ["construi o casa", "repara o soba", "sparge o nuca"]).

este_un(masina_cusut, obiectul, ["croi imbracaminte", "repara o panza"]).

este_un(plug, obiectul, ["ara terenul", "cultiva pamantul"]).

tipul_lui(engleza, limbaj, ["comunica cu alti oameni"]).

tipul_lui(prolog, limbaj, ["comunica cu un computer"]).

actualizeaza:-

retractall(tipul_lui(prolog, limbaj, ["comunica cu un computer"])),

asserta(tipul_lui("PDC Prolog", limbaj,

["comunica cu un computer personal"])),

asserta(tipul_lui(prolog, limbaj,

["comunica cu un mainframe computer"])).

goal

run(obiectul).

Următoarea bază de date se poate adăuga utilizând asserta sau assertz, predicatele sau prin consultare
dintr-un fişier cu ajutorul predicatului consult. În acest exemplu ele sunt prezentate în secţiunea clauses.
este_un(limbaj, obiectul, ["comunica"]).

este_un(ciocan, obiectul, ["construi o casa", "repara o soba", "sparge o nuca"]).

este_un(masina_cusut, obiectul, ["croi imbracaminte", "repara o panza"]).

este_un(plug, obiectul, ["ara terenul", "cultiva pamantul"]).

tipul_lui(engleza, limbaj, ["comunica cu alti oameni"]).

tipul_lui(prolog, limbaj, ["comunica cu un computer"]).

Testaţi apoi programul cu următorul obiectiv:


actualizeaza, run(tool).

Se poate salva întreaga bază de date de fapte într-un fişier text ca în exemplul următor:
save("date.dba")
Laborator 9 11
Se poate citi ulterior acest fişier prin utilizarea predictului consult:
consult("date.dba")

2. Se pot manipula faptele prin descrierea predicatelor bazei de date (fapte declarate în secţiunea facts) cu
toate că ele erau termini.
Când se declară o secţiune facts, Visual Prolog va genera intern o definiţie de domeniu corespunzătoare
declaraţiei faptelor. Considerăm următorul exemplu:
FACTS - dba1
persoana(nume, nrtel)
oras(cnr, cnume)

Pe baza acestor declaraţii, Visual Prolog generează intern domeniul dba1:


DOMAINS
dba1 = persoana(name, nrtel) ; oras(cnr, cnume)

Domeniul dba1 se poate utilize ca orice alt domeniu predefinit.


Clase și obiecte
Visual Prolog conține un mecanism puternic care îmbină programarea logică și programarea orientate pe obiecte.
Pentru ca un program să fie orientat obiect trebuie să ofere: încapsulare, clase, moștenire.

Încapsulare
Importanța încapsulării și modularității sunt bine cunoscute. Obiectele încapsulate pot ajuta la construirea de
programe structurate și mai ușor de citit, deoarece obiectele sunt tratate ca niște cutii negre.

Obiecte și clase
Modul în care datele sunt stocate în limbajele de programare tradiționale este de obicei dificil de înțeles și nu este
potrivit pentru modelarea unui concept din lumea reală. Obiectele sunt mult mai ușor de utilizat, deoarece sunt mai
aproape de modul în care oamenii înțeleg obiectele din lumea reală și, de fapt, un instrument de modelare în sine.
Obiectul este o structură de date mult mai complexă decât listele. Un obiect este la nivel de bază o declarație de
date coerente. Această declarație poate conține și predicate, care lucrează pe aceste date. În terminologia OOP se
numesc metode. Fiecare tip de clasă reprezintă un set unic de obiecte și operațiile (metodele) disponibile pentru a
crea, manipula și distruge astfel de obiecte.
O clasă este o definiție a unui obiect. O instanță este o apariție reală a acestui obiect. În mod normal, puteți defini
cât de multe instanțe doriți de la o clasă.
Exemple
class automobil
proprietar string
marca string
endclass

Actual instance 1
proprietar Maria
marca Peugeot
End

Actual instance 2
proprietar Paul
marca Rolls Royce
End

Moștenire
OOP este un instrument puternic de modelare. Obiectele pot fi definite pe nivelul de abstractizare care se potrivește
cel mai bine. Un obiect poate moșteni date și metode de la obiecte la niveluri superioare. Obiectele sunt astfel o
modalitate ușoară de a face programe foarte modulare.

Clase Prolog
Definirea unei clase în Visual Prolog necesită două elemente: o declarație de clasă și o implementare de clasă.
Declarația de clasă specifică interfața cu clasa, ceea ce se poate vedea din exterior. Implementarea clasei conține
clauzele Prolog pentru definirea funcționalității actuale a clasei.

Declarația interfeței cu o clasă și definiția reală a clauzelor pentru o clasă sunt separate. Declarațiile de clasă vor fi
adesea plasate în fișiere antet care pot fi incluse în locurile care utilizează clasa.
Declararea claselor
Sintaxa declarării unei clase:
CLASS class-name [: parentclass-list ]
PREDICATES
predicatedeclaration-list

FACTS
factdeclaration-list

ENDCLASS

Lista de clase opționale parentclass-list specifică clasa părinte sau clasele din care class-name va deriva (sau
moșteni) predicate și fapte (metode și obiecte). Dacă sunt specificate clase părinte, class-name este denumită clasă
derivată.

Implementarea claselor
Sintaxa implementării unei clase:
IMPLEMENT class-name [: parentclass-list ]
PREDICATES
predicatedeclaration-list

FACTS
factdeclaration-list

CLAUSES
Clause-list

ENDCLASS

Definirea clauzelor pentru o clasă se face într-o secțiune care începe cu cuvântul cheie IMPLEMENT și se termină
cu cuvântul cheie ENDCLASS. În cadrul implementării clasei pot fi mai multe secțiuni pentru predicate, fapte și
clauze.
Dacă secțiunile Predicates și Facts nu sunt precedate de cuvântul cheie STATIC, declarațiile funcționează ca și cele
indicate în declarația de clasă, ceea ce înseamnă că faptele vor aparține instanței, iar predicatele vor purta pointerul
obiect invizibil. Cu toate acestea, lucrurile declarate în implementare vor fi în întregime private pentru
implementarea clasei.
Rețineți, de asemenea, că este posibilă moștenirea clasei în implementare, încapsulând astfel detalii despre
implementarea clasei.

Instanțele unei clase


O nouă instanță pentru o clasă se face cu un apel al operatorului new pentru acea clasă. New va returna o referință
la instanță, care poate fi apoi utilizată pentru a efectua operații asupra obiectului.
Exemple:
CLASS counter
PREDICATES
inc()
dec()
INTEGER getval()
ENDCLASS
IMPLEMENT counter
FACTS
single count(INTEGER)

CLAUSES
count(0).

inc:-
count(X),
X1=X+1,
assert(count(X1)).

dec:-
count(X),
X1=X-1,
assert(count(X1)).

getval(VAL):-
count(Val).
ENDCLASS

GOAL
O = counter::new,
Initial = O:getval(),
O:inc,
NewVal = O:getval(),
O:delete.

Distrugerea obiectelor
Obiectele se pot distruge explicit cu delete
O = customers::new,
O:change_account,
O:delete.

Ștergerea unui obiect va duce la retragerea automată a tuturor faptelor din baza de date pentru acea instanță

Domenii pentru clase


Declarația unei clase generează un domeniu cu numele clasei. Acest domeniu poate fi folosit pentru a declara
parametrii pentru predicate care ar trebui să se ocupe de o referință la obiect.

CLASS customer
.....
ENDCLASS

PREDICATES
p(customer)

Transmiterea unui obiect printr-un parametru înseamnă doar transmiterea unui pointer la obiect ca în stilul normal
de programare OOP

Subclase și moștenire
Într-o clasă, atât predicatele clasei părinte, cât și predicatele globale pot fi redefinite. De exemplu, predicatele
globale, pot fi suprascrise în interiorul unei clase.
Când se folosește clasa derivată, este posibil să se utilizeze predicate și fapte atât din clasa părinte, cât și din clasa
derivată, totuși, clasa derivată ar putea alege să redefinească unele predicate sau fapte.
CLASS person
FACTS
name( STRING )
father( person )
mother( person )

PREDICATES
write_info()

ENDCLASS

CLASS employe : person


FACTS
company(STRING Name)

PREDICATES
write_info()

ENDCLASS

IMPLEMENT person
CLAUSES
write_info():-
name(X),write("Name=",X),nl,fail.
write_info():-
father(F),write("Father:\n"),
F:person::write_info(),fail.
write_info():-
mother(M),write("Mother:\n"),
M:person::write_info(),fail.
write_info().
ENDCLASS

IMPLEMENT employe
CLAUSES
write_info():-
this(O),
O:person::write_info(),fail.
write_info():-
company(X),write("Company=",X),nl,fail.
write_info().
ENDCLASS
GOAL
F = person::new(),
assert(F:name("Arne")),
O = employe::new(),
assert(O:name("Leo")),
assert(O:father(F)),
assert(O:company("PDC")),
O:write_info(),
O:delete().
Sintaxa formală de utilizare a unui membru a unui obiect este;
[ObjectVariable:] [name_of_class:] name_of_member[( list_of_arguments ) ]

Obiectul poate fi omis în interiorul implementării unei clase sau la apelul membrilor care au fost declarați statici.
Acesta va fi considerat ca un apel al membrului corespunzător al acelei clase (sau a părintelui său) în cazul în care
există. În caz contrar, dacă nu există un membru cu nume dat, acesta va fi considerat ca un apel la predicatul cu
același nume care trebuie declarat anterior în unele secțiuni PREDICATES sau DATABASE.
Numele membrilor pot fi redefinite în ierarhia claselor.

Predicate Virtuale
În Visual Prolog toate predicatele claselor sunt echivalente metodelor virtuale C ++. Metodele virtuale permit
claselor derivate să implementeze diferite versiuni ale unei metode dintr-o clasă părinte. Puteți declara o metodă
într-o clasă părinte și apoi o puteți redefini în orice clasă derivată.
Presupunem că o clasă părinte P conține o persoană who_am_i și o clasă D, derivată din P, are definiții pentru
predicatul who_am_i. Dacă who_am_i este apelat pentru un obiect din D, apelul este D: who_am_i, chiar dacă
accesul este prin intermediul unei referințe la P. De exemplu:
CLASS P
PREDICATES
test
who_am_i()
ENDCLASS

CLASS D : P
PREDICATES
who_am_i()
ENDCLASS

IMPLEMENT P
CLAUSES
test:-who_am_i().
who_am_i():-
write("I am of class P\n").
ENDCLASS

IMPLEMENT D
CLAUSES
who_am_i():-
write("I am of class D\n").
ENDCLASS

GOAL
O = D::new,
O:test,
O:delete.

Ieșirea programului va fi:


I am of class D

Rețineți că, dacă definiți un predicat cu același nume într-o subclasă cu domenii sau număr de argumente diferite,
Prolog va trata acest lucru ca o altă declarație și nu va funcționa ca un predicat virtual.
Fapte și predicate statice
Este posibil să se declare predicatele sau faptele ca fiind statice, ceea ce înseamnă că faptele nu sunt generate pentru
fiecare instanță, ci există o singură versiune pentru întreaga clasă. Acest lucru este util, de exemplu, pentru a număra
numărul de instanțe pentru o clasă..
Exemple:
CLASS Count
PREDICATES
procedure new( )
ENDCLASS
IMPLEMENT Count
STATIC FACTS
single countInstance( INTEGER )

CLAUSES
CountInstance( 0 ).

new( ):-
countInstance( Num ),
NumNext = Num +1,
assert( countInstance( NumNext ) ),
writef( "Count = %d\n", NumNext ).

ENDCLASS

GOAL
NewObject = count::new(),
NewObject1 = count::new().

The output of this program will be:


Count = 1
Count = 2
1 Solution

Class Scopes
Domeniul unui predicat este definit ca zona în care îl puteți accesa. Numele predefinite și faptele pot fi redefinite
în ierarhia de clasă. Pentru a face referire la nume din domeniul menționat anterior, poate fi utilizată notația
clasa::nume ().
CLASS parent
PREDICATES
p(INTEGER)
ENDCLASS

CLASS child : parent


PREDICATES
p(STRING, INTEGER)
ENDCLASS

% IMPLEMENTATION …..

GOAL
O = child::new,
O : parent:p(99) % Access the definition in parent

O altă utilizare a domeniului explicit este utilizarea claselor cu predicate statice și fapte statice ca pachete, cum ar
fi un sistem de module:
Exemple:
ILIST = INTEGER*

CLASS list
static PREDICATES
append(ILIST, ILIST, ILIST) - (i,i,o)
ILIST gen(INTEGER)
ENDCLASS

IMPLEMENT list
CLAUSES
append([],L,L).
append([H|L1],L2,[H|L3]):-
append(L1,L2,L3).
gen(0,[]):-!.
gen(N,[N|L]):-
N1=N-1,
L = gen(N1).
ENDCLASS

GOAL
L1 = list::gen(3),
L2 = list::gen(5),
list::append(L1,L2,L3).

Constructori și Destructori
Sistemul Visual Prolog va aloca și inițializa memoria în timpul creării unui obiect. Cu toate acestea, ar putea exista
totuși dorința de a specifica modul în care un obiect este creat, de exemplu inițializarea faptelor cu valori speciale,
crearea unei ferestre pe ecran sau deschiderea unui fișier. În același mod, un programator ar putea dori să controleze
modul în care un obiect este șters, de exemplu închiderea ferestrelor sau a fișierelor. Definițiile definite de utilizator
pentru crearea obiectelor șterse se numesc constructori și destructori. Un constructor este realizat prin definirea
predicatului new pentru un obiect și un destructor se face prin definirea predicatului delete pentru un obiect. În
clauzele pentru predicatul nou, este posibil să se facă referire la constructorii clasei de bază în funcție de
baseclass::new.
CLASS mywind : wind
PREDICATES
new(INFO,Color)
ENDCLASS

IMPLEMENT mywind
CLAUSES
new(Info,Color):-
wind::new(... , ...,Color),
assert(info(INFO)).
ENDCLASS

GOAL
O = mywind::new(“info”,blue),
.....
O:delete.

Observație! Orice constructor ai clasei de bază trebuie apelat înainte de a face orice referință la obiect, compilatorul
va verifica acest lucru.
Constructorii și destructorii nu au voie să eșueze, sunt implicit declarați ca proceduri.
Dacă un constructor sau un destructor iese cu eroare de execuție, starea obiectului este nedefinită.
new va returna automat o referință la instanța creată.

Referința This
All the non-static predicates of an object have an invisible (to the programmer) extra parameter, which is a pointer
to the object.
Toate predicatele non-statice ale unui obiect au un parametru invizibil (pentru programator), care este un pointer
spre obiectul însuși.
Într-o clauză ca:
IMPLEMENT x
CLAUSES
inc:-
count(X),
X1=X+1,
assert(count(X1)).
ENDCLASS

Obiectul este complet invizibil. Dacă este necesar să se facă referire la obiectul însuși, de exemplu, pentru a accesa
un predicat într-o clasă moștenită, este posibil să se utilizeze predicatul this. Predicatul permite ca o instanță să
aibă acces la orice predicat membru, care este definit, în clasa corespunzătoare sau în clasa părinte Sintaxa pentru
a face apel la acest predicat este:
this ( name_of_variable )

Utilizarea predicatului este permisă numai în predicate, care au fost declarate ca nefiind statice. Acest predicat are
un singur parametru de ieșire.
De exemplu:
IMPLEMENT x

CLAUSES
inc:-
this(Object),
Object:x::count(X),
X1=X+1,
assert(count(X1)).
ENDCLASS

Acest cod este identic din punct de vedere funcțional codul de mai sus, cu singura diferență, că se creează un pointer
la acest obiect. Acesta poate fi transmis ca parametru altor predicate.

Clase Abstracte
O clasă abstractă este o definiție de clasă fără o implementare, și este moștenită de subclase. Scopul unei clase
abstracte este de a avea o declarație a unor predicate care ulterior vor fi implementate în clasele derivate și care
funcționează cu o specializare diferită a clasei mai generale. O clasă abstractă este definită de cuvântul cheie
ABSTRACT. În cazul în care o clasă abstractă moștenește unele clase de bază, acestea trebuie de asemenea
declarate abstracte.
De exemplu, dacă doriți să creați un browser care poate funcționa pe mai multe tipuri diferite de date, veți deschide
browser-ul prin transmiterea acestuia într-un obiect pe care îl poate apela pentru a obține datele și pentru a vă
deplasa înainte sau înapoi. Utilizând o clasă abstractă, browser-ul știe la care predicate poate efectua apeluri.
ABSTRACT CLASS browseinterface
PREDICATES
STRING get_Current()
next()
prev()
ENDCLASS

CLASS dbinterface : browseinterface


PREDICATES
new(DB_SELECTOR,CHAIN)
STRING get_Current()
next()
prev()
ENDCLASS

CLASS fileinterface : browseinterface


STRING get_Current()
next()
prev()
ENDCLASS

CLASS browser
PREDICATES
new(browseinterface)
ENDCLASS

Protectejarea faptelor și predicatelor


Este posibil să declarați dacă puteți accesa faptele sau predicatele din afara clasei. În mod implicit, toate faptele și
predicatele sunt publice, ceea ce înseamnă că pot fi apelate de oricare alte predicate.
Drepturile de acces implicite pot fi modificate prin precizarea unei declarații de fapte sau predicate cu ajutorul
cuvintelor cheie: protected. Protejat înseamnă că toate clasele derivate din clasă au permisiunea de a accesa faptul
sau predicatul, dar faptul sau predicatul nu pot fi accesate din afara clasei.
Un exemplu de utilizare a predicatelor protejate :
CLASS window
PROTECTED PREDICATES
onUpdate(RCT)
onCreate(LONG)
ENDCLASS

Controlul accesului la clasele derivate


O problemă importantă în construirea corecta a ierarhiilor obiectelor, astfel încât să puteți reutiliza cât mai mult
cod posibil, este moștenirea. Puteți defini metode la un nivel, iar acestea pot fi reutilizate la niveluri inferioare.
Dacă o clasă moștenește de la alte clase, spunem că această clasă este o clasă derivată. Când declarați o clasă D
derivată, listați clasele părinte P1, P2 .. într-o listă de clase părinte delimitate prin virgulă:
CLASS D : P1, P2 ...

D moștenește toate faptele și predicatele clasei părinte. Elementele redefinite pot fi accesate prin suprascriere, dacă
este necesar.
Dacă două sau mai multe predicate dintr-o ierarhie de clase sunt numite la fel, spunem că acest nume este
supraîncărcat. Apoi, trebuie să luăm în considerare domeniul de aplicabilitate al numelui, definit aici ca zona în
care fiecare predicat este valabil. Orice utilizare a numelui pentru orice membru al unei clase trebuie să fie
neechivocă (la o aproximare a supraîncărcării).
Toate predicatele din declarația de clasă, cu excepția new și delete, sunt virtuale. Toate predicatele declarate în
implementare devin non-virtuale.
Laborator 11 1

LUCRARE DE LABORATOR NR. 13

CITIRE/SCRIERE. SISTEMUL DE FIŞIERE

Noţiuni teoretice

Citirea și scrierea

Scrierea
Visual Prolog are 3 predicate pentru scriere . Aceste predicate sunt write, nl și writef.

write/* și nl
Predicatul write poate fi apelat cu un număr arbitrar de argumente:
write(Param1, Param2, Param3, ..., ParamN)
/* (i, i, i, ..., i) */

Aceste argumente pot fi constante din domeniile standard sau pot fi variabile. Dacă acestea sunt variabile, ele
trebuie să fie parametri de intrare.
Predicatul standard nl (pt new line) este utilizat frecvent împreună cu write; el generează o linie nouă pe
ecran. De exemplu, următoarele sub-goals:
student(STUDENTUL, CL),
write(STUDENTUL," este în anul ",CL),
nl,
write("-----------------------------").

Pot avea ca efect:


Elena Popa este in anul 3
--------------------------

În timp ce:
....,
write("List1= ", L1, ", List2= ", L2 ).

Poate afișa:
List1= [pasare,caine,pisica], List2= [1,2,3]

Rețineți că, în ceea ce privește șirurile de caractere, backslash (\) este un caracter de tip escape. Pentru a scrie
acest caracter el trebuie dublat
Caractere de control
'n' newline si carriage return
't' tab
'r' carriage return

Exemple de programe ce utilizează predicatul write:


2 Fisiere
DOMAINS
integerlist = integer*
namelist = symbol*

PREDICATES
writelist(integerlist)
writelist(namelist).

CLAUSES
writelist([]).
writelist([H|T]):-
write(H, " "),
writelist(T).

Testați cu:

GOAL
writelist([1, 2, 3, 4]).

Următorul exemplu, scrie elementele dintr-o listă cu cel mult cinci elemente pe linie.
DOMAINS
integerlist = integer*

PREDICATES
writelist(integerlist)
write5(integerlist,integer)

CLAUSES
writelist(NL):-
nl,
write5(NL,0),nl.
write5(TL,5):-!,
nl,
write5(TL, 0).
write5([H|T],N):-!,
write(H," "),
N1=N+1,
write5(T,N1).
write5([],_).

Testați cu:

GOAL
writelist([2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22]).

În mod frecvent, este posibil să doriți un predicat care să fie afișat într-o formă mai ușor de citit. Programul
următor afișează un obiect compus cum ar fi:
plus(mult(x, number(99)), mult(number(3), x))

în forma:
x*99+3*x
Laborator 11 3
DOMAINS
expr = number(integer); x; log(expr);
plus(expr, expr); mult(expr, expr)
PREDICATES
writeExp(expr)
CLAUSES
writeExp(x):-write('x').
writeExp(number(No)):-write(No).
writeExp(log(Expr)):-
write("log("), writeExp(Expr), write(')').
writeExp(plus(U1, U2)):-
writeExp(U1),write('+'),writeExp(U2).
writeExp(mult(U1,U2)):-
writeExp(U1),write('*'),writeExp(U2).
GOAL
writeExp(plus(mult(x, number(99)), mult(number(3), x))).

writef/*
Predicatul writef produce ieșiri cu format:
writef(FormatString, Arg1, Arg2, Arg3, ...,ArgN)
/* (i, i, i, i, ..., i) */

Arg1 … ArgN trebuie să fie constante sau variabile libere aparținând domeniilor standard; nu este posibilă
formatarea domeniilor compuse. Șirul de formatare conține caractere obișnuite și specificatori de format;
Caracterele obișnuite sunt tipărite fără modificări, iar specificatorii de format au forma:
%-m.pf

Exemple
1.
GOAL
A = unu,
B = 330.12,
C = 4.3333375,
D = "unu doi trei",
writef("A = '%-7' \nB = '%8.1e'\n",A,B),
writef("A = '%' \nB = '%8.4e'\n",A,B),nl,
writef("C = '%-7.7g' \nD = '%7.7'\n",C,D),
writef("C = '%-7.0f' \nD = '%0'\n",C,D),
writef("char: %c, decimal: %d, octal: %o, hex: %x",'a','a','a','a').

2.
DATABASE
person(string,integer,real)

CLAUSES
person("Petre Adamescu",20,11111.111).
person("Maria Suciu",32,33333.333).
person("Corina Lupu",28,66666.666).

GOAL
% Nume aliniere la stanga pe 15 pozitii
% Varsta aliniere la dreapta 2 pozitii
% Venit aliniere la dreapta 9 pozitii cu 2 zecimale
person(N, A, I),
writef("Name= %-15, Age= %2, Income= $%9.2f \n",N,A,I),
fail
;
true.
4 Fisiere

Citirea
Prologul vizual include câteva predicate standard pentru citire. Cele 4 predicate de bază sunt: readln,
readchar, readint, readreal. In plus readterm pentru orice termen inclusive obiecte compuse. Aceste
predicate pot fi redirecționate să citească din fișiere
Un alt predicat mai specializat care aparține categoriei de citire este file_str pentru citirea unui întreg fișier
text într-un șir.

readln/1
readln citește o linie de text; format:
readln(Line) /* (o) */

Domeniul pentru variabila Line va fi un șir. Înainte de a apela readln, variabila Line trebuie să fie liberă.
Readln citește până la 254 caractere de la tastatură, până la 64K de la alte dispozitive. Dacă apăsați Esc în
timpul intrării de la tastatură, citirea va eșua.

readint/1, readreal/1, și readchar/1


readint citește un întreg
readint(X) /* (o) */

Domeniul pentru variabila X trebuie să fie de tip integer și X trebuie să fie liberă înainte de apel. readint va
citi un intreg (probabil tastatura) pana la apasarea tastei Enter. Dacă linia citită nu corespunde sintaxei
obișnuite pentru numere întregi, readint eșuează și Visual Prolog invocă mecanismul backtracking. Dacă
apăsați Esc în timpul intrării de la tastatură, citirea va eșua.
readreal citește un număr real. readreal utilizează următorul format:
readreal(X) /* (o) */

Domeniul pentru variabila X trebuie să fie de tip real și X trebuie să fie liberă înainte de apel. readreal va
citi un număr real pana la apasarea tastei Enter. Dacă linia citită nu corespunde sintaxei obișnuite pentru
numere reale, readreal eșuează și Visual Prolog invocă mecanismul backtracking. Dacă apăsați Esc în timpul
intrării de la tastatură, citirea va eșua.
readchar citește un singur caracter si are formatul:
readchar(CharParam) /* (o) */

CharParam trebuie să fie o variabilă liberă înainte de apel. Dacă fluxul curent de intrare este tastatura,
readchar va aștepta tastarea a unui singur caracter. Dacă apăsați Esc în timpul intrării de pe tastatură,
readchar va eșua.

readterm/2
readterm citește un termen compus și îl transformă într-un obiect; format:
readterm(DomainName, Term) /* (i, i) */

readterm se apelează cu două argumente: un nume de domeniu și un termen. readterm citește o


linie și o convertește la un obiect al domeniului dat. Dacă linia nu corespunde cu formatul
obiectului, citirea dă o eroare. Poate fi utilizat readtermerror (predicat standard) pentru tratarea
erorii.
Laborator 11 5
Prelucrare de fișiere in Prolog

La fel ca în limbajele de tip procedural şi în Prolog putem utiliza fişiere pentru prelucrarea
informaţiilor. Pentru recunoaşterea unui fişier, pe lângă numele lui, mai este necesar un nume
simbolic ce se defineşte în secţiunea domains sub forma următoare:
domains
file=nume_simbolic
unde nume_simbolic este un şir de caractere alfabetice, cifre sau caracterul underline şi trebuie să
înceapă cu literă mică.
Dispozitivele standard de intrare/ieşire (tastatura, ecranul, imprimanta) sunt tratate ca fişiere
având nume simbolice standard şi nu trebuie declarate în secţiunea domains.
Dispovitiv Tip Nume simbolic
tastatura intrare keyboard
ecranul ieşire screen
imprimanta ieşire printer

Predicate pentru redirectarea intrării/ieşirii


În Prolog întâlnim predicatul readdevice pentru redirectarea intrării şi writedevice pentru
redirectarea ieşirii. Ele au ca argument numele simbolic al fişierului la care se referă.
readdevice(nume_simbolic)
writedevice(nume_simbolic)
Predicatul writedevice controlează destinaţia ieşirilor prin intermediul predicatelor write şi
writef, iar readdevice controlează intrările prin predicatele de citire (read, readln, readint, etc).
Implicit, Prolog consideră ca intrare tastatura, iar ca ieşire ecranul.

Deschiderea şi închiderea fişierelor


Înainte de a fi utilizat, un fişier trebuie să fie deschis relativ la operaţiile de intrare/ieşire.
Fişierele standard de intrare/ieşire (tastatura, ecranul şi imprimanta) sunt, implicit, deschise.
Predicatul openwrite se utilizează pentru deschiderea unui fişier în vederea scrierii de
informaţii, şi are forma:
openwrite(nume_simbolic,nume_fisier)
Dacă fişierul nu există pe disc, atunci îl creează cu numele dat în nume_fisier, care este de
tip string, şi-l deschide în scriere, iar dacă fişierul este deja creat, îl şterge, deschizându-l ca un nou
fişier. Imediat ce un fişier a fost deschis, programatorul trebuie să redirecteze ieşirea către el.
Numărul maxim de fişiere deschise simultan este limitat de sistemul de operare.
După terminarea prelucrărilor, este necesară închiderea fişierului, operaţie ce se efectuează
cu predicatul:
closefile(nume_simbolic)

Predicatul openappend se utilizează pentru adăugarea de informaţii la sfârşitul unui fişier, şi


are forma:
openappend(nume_simbolic,nume_fisier)
Spre deosebire de predicatul openwrite, dacă fişierul este deja creat, nu-l şterge, ci,
dimpotrivă, scrie la sfârşit.
6 Fisiere
Predicatul openread se utilizează pentru citirea informaţiilor dintr-un fişier creat de
utilizatori. Implicit, Prolog recunoaşte pentru citire tastatura. Predicatul are forma:
openread(nume_simbolic,nume_fisier)
Predicatul openmodify deschide un fişier în acces direct atât pentru citire cât şi pentru
scriere, şi are forma:
openmodify(nume_simbolic, nume_fisier)

Funcţii caracteristice fişierelor


Predicatul eof testează sfârşitul de fişier şi este adevărat dacă s-a ajuns la sfârşitul unui fişier
şi fals în caz contrar. Are forma:
eof(nume_simbolic)
Se poate accesa orice poziţie într-un fişier cu acces direct, prin utilizarea predicatului
standard filepos. Odată poziţia identificată, se poate citi sau scrie aici utilizând predicatele standard
de intrare sau ieşire. Forma generală a predicatului este:
filepos(nume_simbolic, pozitia, mod_acces)
unde mod_acces indică modul în care se identifică poziţia în fişier, şi anume:
0 - în raport cu începutul fişierului;
1 - în raport cu poziţia curentă;
2 - în raport cu sfârşitul fişierului.
Pentru a specifica tipul fişierului (text sau binar) se poate folosi predicatul filemode, având
următoarea sintaxă:
filemode(NumeSimbolic, Mod)
Dacă variabila Mod este legată de valoarea 0 atunci fişierul este text, iar dacă variabila Mod
este legată de valoarea 1 atunci fişierul este binar.

Aplicaţii rezolvate

Aplicaţia 1
Să se implementeze un program Prolog care permite memorarea într-un fişier a a divizorilor
proprii ai unui număr natural citit de la intrarea standard.

Programul corespunzător problemei este:


domains
file=fisier
predicates
divizor
divizor(unsigned,unsigned)
verifica(unsigned,unsigned)
deschidere(string)
clauses
divizor:-
write("N="),readint(N),
deschidere("D:\divizori.txt"),
writedevice(fisier),
write("\nDivizorii proprii ai numarului ",N," sunt:"),
divizor(N,1),
Laborator 11 7
closefile(fisier),
writedevice(screen).
divizor(N,I):-
I>=N div 2,!.
divizor(N,I):-
NoulI=I+1,
verifica(N,NoulI).
verifica(N,I):-
N mod I=0,!,
write("\n",I),
divizor(N,I).
verifica(N,I):-
divizor(N,I).
deschidere(X):-
existfile(X),!,
openappend(fisier,X).
deschidere(X):-
openwrite(fisier,X).
goal
divizor.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
N=24
yes
Dacă fişierul „divizori.txt” nu există, conţinutul său este următorul:
Divizorii proprii ai numarului 24 sunt:
2
3
4
6
8
12
În secţiunea domains se declară numele simbolic pentru recunoaşterea fişierului text în care
se vor memora divizorii proprii ai numărului natural introdus de la intrarea standard. În secţiunea
următoare se declară patru predicate, dintre care predicatul divizor este fără argumente, predicatele
divizor şi verifica au două argumente de tip unsigned şi predicatul deschidere primeşte un argument
de tip string.
În secţiunea legată de clauze sunt definite aceste patru predicate prin intermediul regulilor.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea divizor.
Prolog va rezolva această interogare căutând în lista de reguli de la început spre sfârşit,
utilizând mecanismul de căutare cu revenire. Deoarece predicatul divizor fără argumente este definit
prin intermediul unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei
reguli.

Primul subobiectiv duce la afişarea la ieşirea standard a unui mesaj care precizează că se
doreşte introducerea unei valori ce va reprezenta numărul pentru care se doreşte determinarea sumei
divizorilor proprii. Al doilea subobiectiv utilizează predicatul predefinit readint care permite citirea
valorii introduse.
Următorul subobiectiv apelează procedura ce defineşte predicatul deschidere, astfel fiind
deschis fişierul pentru scriere. Apoi are loc redirectarea ieşirii spre fişierul anterior deschis şi
8 Fisiere
salvarea unui şir de caractere constant. În continuare se apelează procedura ce defineşte predicatul
divizor cu două argumente, al doilea argument fiind legat de valoarea 1. În prima definiţie a acestui
predicat se verifică dacă valoarea celui de-al doilea argument este mai mare decât jumătate din
valoarea primului argument. În acest caz se încheie căutarea, prin utilizarea predicatului cut. Altfel
se trece la a doua definiţie a acestui predicat. Primul subobiectiv al regulii determină valoarea
variabilei NoulI, iar apoi se apelează predicatul verifica având ca al doilea argument variabila legată
anterior determinată.
Primul subobiectiv din prima regulă din definiţia predicatului verifica testează dacă valoarea
dată de primul argument se divide prin valoarea dată de al doilea argument. În caz afirmativ se
memorează în fişier divizorul propriu găsit şi se apelează predicatul divizor având ca ultim
argument variabila anterior salvată. Altfel se trece la a doua definiţie a predicatului verifica care va
apela predicatul divizor fără a salva în fişier.
Penultimul subobiectiv al predicatului fără argumente divizor permite închiderea fişierul,
apoi ultimul subobiectiv redirectează ieşirea spre ieşirea standard.
Predicatul deschidere, definit prin două reguli, primeşte ca şi argument un şir de caractere ce
reprezintă numele fişierului utilizat. În corpul primei reguli se verifică dacă fişierul cu numele
introdus există, caz în care fişierul este deschis pentru adăugare la final. A doua definiţie, apelată
când nu este îndeplinit primul subobiectiv al primei reguli, crează fişierul şi îl deschide pentru
scriere.

Aplicaţia 2
Să se implementeze un program Prolog pentru memorarea într-un fişier a numerelor strict
pozitive dintr-o listă de numere întregi introduse de la intrarea standard.

Programul corespunzător problemei este:


domains
element=integer
lista=element*
file=fisier
predicates
adaugare_element(lista,lista,integer,integer)
creare_lista(lista,integer)
rezolva
pozitive(lista)
clauses
rezolva:-
write("Numarul de elemente="), readint(N),
creare_lista(L,N),
openwrite(fisier,"D:\pozitive.txt"),
writedevice(fisier),
write("Lista valorilor pozitive:"),
pozitive(L),
closefile(fisier),
writedevice(screen).
creare_lista(L,N):-
adaugare_element([],L,N,0).
adaugare_element(L,L,N,N):-!.
adaugare_element(L1,L,N,I):-
write("Numarul = "),
readint(X),!,
I1=I+1,
Laborator 11 9
adaugare_element([X|L1],L,N,I1).
pozitive([]):-!.
pozitive([X|L]):-
X>0,!,
write(" ",X),
pozitive(L).
pozitive([_|L]):-
pozitive(L).
goal
rezolva.

Analiza programului:
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Numarul de elemente=8
Numarul = 11
Numarul = -13
Numarul = 18
Numarul = -1
Numarul = 23
Numarul = -37
Numarul = 44
Numarul = -3
yes
Conţinutul fişierul pozitive.txt este următorul:
Lista valorilor pozitive: 44 23 18 11
În prima secţiune a programului se declară numele simbolic pentru recunoaşterea fişierului
în care se vor memora valorile strict pozitive şi tipul de dată lista corespunzător unei liste de
numere întregi.
În secţiunea următoare se declară patru predicate, dintre care predicatul rezolva este fără
argumente, predicatul creare_lista cu un argument de tip lista şi un argument de tip integer,
predicatul adaugare_element cu două argumente de tip lista şi două argumente de tip integer şi
predicatul pozitive cu un argument de tip lista.
În secţiunea legată de clauze sunt definite aceste patru predicate prin intermediul regulilor.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea rezolva.
Prolog va rezolva această interogare căutând în lista de reguli de la început spre sfârşit,
utilizând mecanismul de căutare cu revenire. Deoarece predicatul rezolva este definit prin
intermediul unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli.
Primul subobiectiv duce la afişarea la ieşirea standard a unui mesaj care precizează că se doreşte
introducerea unei valori ce va reprezenta numărul de elemente din lista de numere întregi. Al doilea
subobiectiv utilizează predicatul predefinit readint care permite citirea valorii introduse. În
continuare se apelează procedura de defineşte predicatul creare_lista ce va permite citirea de la
intrarea standard a celor N valori întregi.

Următorul subobiectiv apelează predicatul predefinit openwrite, astfel fiind deschis fişierul
„pozitive.txt” pentru scriere. Apoi are loc redirectarea ieşirii spre fişierul anterior deschis şi salvarea
unui şir de caractere constant. În continuare se apelează procedura ce defineşte predicatul pozitive.
Prima definiţia a predicatului pozitive tratează cazul când lista transmisă ca argument este
vidă. În a doua definiţie a acestui predicat se verifică dacă valoarea elementului din capul listei este
strict pozitivă. În caz afirmativ se memorează în fişier numărul strict pozitiv şi se apelează
10 Fisiere
predicatul pozitive având ca argument coada listei curente. Altfel se trece la a treia definiţie a
predicatului pozitive care va apela predicatul pozitive fără a salva în fişier.
Penultimul subobiectiv al predicatului rezolva permite închiderea fişierul, apoi ultimul
subobiectiv redirectează ieşirea spre ieşirea standard.

Aplicaţia 3
Se citesc de la intrarea standard numere reale până la apariţia valorii 0. Să se implementeze
un program Prolog care salvează într-un fişier toate tripletele de numere citite consecutiv pentru
care al doilea număr din triplet este media aritmetică a celorlalte două.

Programul corespunzător problemei este:


domains
element=real
lista=element*
file=fisier
predicates
adaugare_element(lista,lista)
creare_lista(lista)
zero(real,lista,lista)
rezolva
triplete(lista)
clauses
rezolva:-
creare_lista(L),
openwrite(fisier,"D:\triplete.txt"),
writedevice(fisier),
write("Tripletele de valori consecutive:"),
triplete(L),
closefile(fisier),
writedevice(screen).
creare_lista(L):-
adaugare_element([],L).
adaugare_element(L1,L):-
write("Numarul = "),
readreal(X),!,
zero(X,L1,L).
zero(X,L1,L):-
X<>0,!,
adaugare_element([X|L1],L).
zero(_,L,L).
triplete([_|[_|[]]]):-!.
triplete([X1|[X2|[X3|L]]]):-
X2=(X1+X3)/2,!,
write("\n(",X1,",",X2,",",X3,")"),
triplete([X2|[X3|L]]).
triplete([_|L]):-
triplete(L).
goal
rezolva.

Analiza programului:
Laborator 11 11
Testul „goal” va furniza în fereastra de ieşire următorul rezultat:
Numarul = 5
Numarul = 10
Numarul = 15
Numarul = 20
Numarul = 4
Numarul = 8
Numarul = 12
Numarul = 16
Numarul = 0
yes
Conţinutul fişierul triplete.txt este următorul:
Tripletele de valori consecutive:
(16,12,8)
(12,8,4)
(20,15,10)
(15,10,5)
În prima secţiune a programului se declară numele simbolic pentru recunoaşterea fişierului
în care se vor memora tripletele de numere consecutive şi tipul de dată lista corespunzător unei liste
de numere reale.
În secţiunea următoare se declară cinci predicate, dintre care predicatul rezolva este fără
argumente, predicatul creare_lista cu un argument de tip lista, predicatul adaugare_element cu
două argumente de tip lista, predicatul zero cu un argument de tip real şi două argumente de tip
lista şi predicatul triplete cu un argument de tip lista.
În secţiunea legată de clauze sunt definite aceste cinci predicate prin intermediul regulilor.
Ultima secţiune corespunde cerinţei din enunţul problemei, întrebare redată de interogarea rezolva.
Prolog va rezolva această interogare căutând în lista de reguli de la început spre sfârşit,
utilizând mecanismul de căutare cu revenire. Deoarece predicatul rezolva este definit prin
intermediul unei reguli, se va încerca satisfacerea tuturor subobiectivelor din corpul acestei reguli.
Primul subobiectiv apelează procedura ce defineşte predicatul creare_lista ce va permite citirea de
la intrarea standard a valorilor reale până la apariţia valorii 0. Următorul subobiectiv apelează
predicatul predefinit openwrite, astfel fiind deschis fişierul „triplete.txt” pentru scriere. Apoi are loc
redirectarea ieşirii spre fişierul anterior deschis şi salvarea unui şir de caractere constant. În
continuare se apelează procedura ce defineşte predicatul triplete.
Prima definiţia a predicatului triplete tratează cazul când lista transmisă ca argument conţine
mai puţin de trei valori, caz în care nu se mai poate forma un triplet, iar căutarea se opreşte. În a
doua definiţie a acestui predicat se verifică dacă al doilea element din listă este egal cu media
aritmetică dintre primul şi al treilea element din listă. În caz afirmativ se memorează în fişier
tripletul şi se apelează predicatul triplete având ca argument coada listei curente. Altfel se trece la a
treia definiţie a predicatului triplete care va apela predicatul triplete fără a salva în fişier.
Penultimul subobiectiv al predicatului rezolva permite închiderea fişierul, apoi ultimul
subobiectiv redirectează ieşirea spre ieşirea standard.

Aplicaţii propuse

1. Să se implementeze un program Prolog pentru memorarea într-un fişier a numerelor prime


dintr-o listă de numere naturale introduse de la intrarea standard.
12 Fisiere
2. Se citesc de la intrarea standard numere întregi până la apariţia valorii 0. Să se implementeze
un program Prolog care salvează într-un fişier toate perechile de numere citite consecutiv
pentru care un număr din pereche este opusul celuilalt.
3. Se citeşte un text de la intrarea standard. Să se implementeze un program Prolog care
codifică textul dublând fiecare vocală şi îl salvează într-un fişier.

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