Sunteți pe pagina 1din 145

A. M. Florea

B. Dorohonceanu

Programare în Prolog pentru Inteligenţă Artificială

C. Frâncu

UNIVERSITATEA “POLITEHNICA” BUCUREŞTI

1997

Partea I

Limbajul Prolog

Începutul programării logice poate fi atribuit lui R. Kowalski şi A. Colmerauer şi se situează la începutul anilor '70. Kowalski a plecat de la o formulă logică de tipul:

S 1 S 2

S n S

care are, în logica cu predicate de ordinul întâi semnificaţia declarativă conform căreia

şi S n sunt fiecare adevărate atunci şi S este

S 1 S 2

adevărat, şi a propus o interpretare procedurală asociată. Conform acestei interpretări,

formula de mai sus poate fi scrisă sub forma:

S n implică S, adică dacă S 1 şi S 2

S dacă S 1 şi S 2

şi S n

şi poate fi executată ca o procedură a unui limbaj de programare recursiv, unde S este

antetul procedurii şi S 1 , S 2 ,

logică, a unei astfel de formule, formula poate fi interpretată procedural astfel: pentru a

executa S se execută S 1 şi S 2

S n corpul acesteia. Deci, pe lângă interpretarea declarativă,

şi S n .

În aceeaşi perioadă, A. Colmerauer şi colectivul lui de cercetare de la Universitatea din Marsilia au dezvoltat un limbaj de implementare a acestei abordări, pe care l-au denumit Prolog, abreviere de la "Programmation et Logique". De atunci şi până în prezent, limbajul Prolog s-a impus ca cel mai important limbaj de programare logică şi s-au dezvoltat numeroase implementări, atât ale unor interpretoare cât şi ale unor compilatoare ale limbajului.

Limbajul Prolog este un limbaj declarativ susţinut de o componentă procedurală. Spre deosebire de limbajele procedurale, cum ar fi C sau Pascal, în care rezolvarea problemei este specificată printr-o serie de paşi de execuţie sau acţiuni, într-un limbaj declarativ problema este specificată prin descrierea universului problemei şi a relaţiilor sau funcţiilor existente între obiecte din acest univers. Exemple de astfel de limbaje sunt cele funcţionale, de exemplu Lisp, Scheme, ML, şi cele logice, de exemplu Prolog.

Deşi iniţial a fost gândit pentru un set restrâns de probleme, Prolog a devenit cu timpul un limbaj de uz general, fiind o unealtă importantă în aplicaţiile de inteligenţă artificială [CM84, Bra88, SS86]. Pentru multe probleme, un program Prolog are cam de 10 ori mai puţine linii decât echivalentul lui în Pascal.

În 1983, cercetătorii din Japonia au publicat un plan ambiţios de creare a unor calculatoare de generaţia a 5-a pentru care Prolog era limbajul de asamblare. Planul nu a

2

reuşit, dar acest proiect a marcat o dezvoltare deosebită a interpretoarelor şi compilatoarelor de Prolog, precum şi o creştere mare a numărului de programatori în acest limbaj.

Multe clase de probleme poate fi rezolvate în Prolog, existând anumite categorii care sunt rezolvabile mult mai uşor în Prolog decât în orice alt limbaj procedural. Astfel de probleme sunt în principal cele dedicate prelucrării simbolice sau care necesită un proces de căutare a soluţiei într-un spaţiu posibil de transformări ale problemei.

Prezentarea limbajului Prolog ce urmează este în principal orientată pe descrierea limbajului Prolog standard. Exemplele de programe din această parte cât şi din partea a doua sunt rulate utilizând interpretorul ARITY Prolog pe microcalculatoare de tip IBM/PC, mediul de programare ARITY Prolog şi particularităţile lui sintactice fiind prezentate în partea a doua.

1 Entităţile limbajului Prolog

Limbajul Prolog este un limbaj logic, descriptiv, care permite specificarea problemei de rezolvat în termenii unor fapte cunoscute despre obiectele universului problemei şi a relaţiilor existente între aceste obiecte. Execuţia unui program Prolog constă în deducerea implicaţiilor acestor fapte şi relaţii, programul definind astfel o mulţime de consecinţe ce reprezintă înţelesul sau semnificaţia declarativă a programului.

Un program Prolog conţine următoarele entităţi:

fapte despre obiecte şi relaţiile existente între aceste obiecte;

reguli despre obiecte şi relaţiile dintre ele, care permit deducerea (inferarea) de noi fapte pe baza celor cunoscute;

întrebări, numite şi scopuri, despre obiecte şi relaţiile dintre ele, la care programul răspunde pe baza faptelor şi regulilor existente.

1.1

Fapte

Faptele sunt predicate de ordinul întâi de aritate n considerate adevărate. Ele stabilesc relaţii între obiectele universului problemei. Numărul de argumente ale faptelor este dat de aritatea (numărul de argumente) corespunzătoare a predicatelor.

Exemple:

3

Fapt:

Aritate:

papagal(coco).

1

iubeşte(mihai, maria).

2

iubeşte(mihai, ana).

2

frumoasă(ana).

1

bun(gelu).

1

deplasează(cub, camera1, camera2).

3

Interpretarea particulară a predicatului şi a argumentelor acestuia depinde de programator. Ordinea argumentelor, odată fixată, este importantă şi trebuie păstrată la orice altă utilizare a faptului, cu aceeaşi semnificaţie. Mulţimea faptelor unui program Prolog formează baza de cunoştinţe Prolog. Se va vedea mai târziu că în baza de cunoştinte a unui program Prolog sunt incluse şi regulile Prolog.

1.2 Scopuri

Obţinerea consecinţelor sau a rezultatului unui program Prolog se face prin fixarea unor scopuri care pot fi adevărate sau false, în funcţie de conţinutul bazei de cunoştinţe Prolog. Scopurile sunt predicate pentru care se doreşte aflarea valorii de adevăr în contextul faptelor existente în baza de cunoştinţe. Cum scopurile pot fi văzute ca întrebări, rezultatul unui program Prolog este răspunsul la o întrebare (sau la o conjuncţie de întrebări). Acest răspuns poate fi afirmativ, yes, sau negativ, no. Se va vedea mai târziu că programul Prolog, în cazul unui răspuns afirmativ la o întrebare, poate furniza şi alte informaţii din baza de cunoştinţe.

Exemplu

Considerând baza de cunoştinţe specificată anterior, se pot pune diverse întrebări, cum

ar fi:

?- iubeste(mihai, maria). yes ?- papagal(coco). yes ?- papagal(mihai). no ?- inalt(gelu). no

1.3 Variabile

deoarece acest fapt există în baza de cunoştinţe

deoarece acest fapt nu există în baza de cunoştinţe

În exemplele prezentate până acum, argumentele faptelor şi întrebărilor au fost obiecte particulare, numite şi constante sau atomi simbolici. Predicatele Prolog, ca orice predicate în logica cu predicate de ordinul I, admit ca argumente şi obiecte generice numite variabile. În Prolog, prin convenţie, numele argumentelor variabile începe cu literă iar numele constantelor simbolice începe cu literă mică. O variabilă poate fi instanţiată (legată) dacă

4

există un obiect asociat acestei variabile, sau neinstanţiată (liberă) dacă nu se ştie încă ce obiect va desemna variabila.

La fixarea unui scop Prolog care conţine variabile, acestea sunt neinstanţiate iar sistemul încearcă satisfacerea acestui scop căutând printre faptele din baza de cunoştinţe un fapt care poate identifica cu scopul, printr-o instanţiere adecvată a variabilelor din scopul dat. Este vorba de fapt de un proces de unificare a predicatului scop cu unul din predicatele fapte existente în baza de cunoştinţe.

La încercarea de satisfacere a scopului, căutarea se face întotdeauna pornind de la începutul bazei de cunoştinţe. Dacă se întâlneşte un fapt cu un simbol predicativ identic cu cel al scopului, variabilele din scop se instanţiază conform algoritmului de unificare şi valorile variabilelor astfel obţinute sunt afişate ca răspuns la satisfacerea acestui scop.

Exemple:

?- papagal(CineEste).

CineEste = coco

?- deplaseaza(Ce, DeUnde, Unde).

Ce = cub, DeUnde = camera1, Unde = camera2

?-

deplaseaza(Ce, Aici, Aici).

no

Cum se comportă sistemul Prolog în cazul în care există mai multe fapte în baza de cunoştinţe care unifică cu întrebarea pusă? În acest caz există mai multe răspunsuri la întrebare, corespunzând mai multor soluţii ale scopului fixat. Prima soluţie este dată de prima unificare şi există atâtea soluţii câte unificări diferite există. La realizarea primei unificări se marchează faptul care a unificat şi care reprezintă prima soluţie. La obţinerea următoarei soluţii, căutarea este reluată de la marcaj în jos în baza de cunoştinţe. Obţinerea primei soluţii este de obicei numită satisfacerea scopului iar obţinerea altor soluţii, resatisfacerea scopului. La satisfacera unui scop căutarea se face întotdeauna de la începutul bazei de cunoştinţe. La resatisfacerea unui scop, căutarea se face începând de la marcajul stabilit de satisfacerea anterioară a acelui scop.

Sistemul Prolog, fiind un sistem interactiv, permite utilizatorului obţinerea fie a primului răspuns, fie a tuturor răspunsurilor. În cazul în care, după afişarea tuturor răspunsurilor, un scop nu mai poate fi resatisfăcut, sistemul răspunde no.

Exemple:

?-

iubeste(mihai, X).

X

= maria;

tastând caracterul “;” şi Enter, cerem o nouă soluţie

X

= ana;

no

?-

iubeste(Cine, PeCine).

5

Cine = mihai, PeCine = maria; Cine = mihai, PeCine = ana; no

Există deci două soluţii pentru scopul iubeste(mihai, X) şi tot două soluţii pentru scopul iubeste(Cine, PeCine), considerând tot baza de cunoştinţe prezentată în secţiunea

1.1.

1.4

Reguli

O regulă Prolog exprimă un fapt care depinde de alte fapte şi este de forma:

S :- S 1 , S 2 , …S n .

cu semnificaţia prezentată la începutul acestui capitol. Fiecare S i , i = 1,n şi S au forma faptelor Prolog, deci sunt predicate, cu argumente constante, variabile sau structuri. Faptul S care defineşte regula, se numeşte antet de regulă, iar S 1 , S 2 , …S n formează corpul regulii şi reprezintă conjuncţia de scopuri care trebuie satisfăcute pentru ca antetul regulii să fie satisfăcut.

Fie următoarea bază de cunoştinţe Prolog:

frumoasa(ana).

% 1

bun(vlad).

% 2

cunoaste(vlad, maria).

% 3

cunoaste(vlad, ana).

% 4

iubeste(mihai, maria).

% 5

iubeste(X, Y) :- bun(X), cunoaste(X, Y), frumoasa(Y).

% 6

Se observă că enunţul (6) defineşte o regulă Prolog; relaţia iubeste(Cine, PeCine), fiind definită atât printr-un fapt (5) cât şi printr-o regulă (6).

În condiţiile existenţei regulilor în baza de cunoştinţe Prolog, satisfacerea unui scop se face printr-un procedeu similar cu cel prezentat în Secţiunea 1.2, dar unificarea scopului se încearcă atât cu fapte din baza de cunoştinţe, cât şi cu antetul regulilor din bază. La unificarea unui scop cu antetul unei reguli, pentru a putea satisface acest scop trebuie satisfăcută regula. Aceasta revine la a satisface toate faptele din corpul regulii, deci conjuncţia de scopuri. Scopurile din corpul regulii devin subscopuri a căror satisfacere se va încerca printr-un mecanism similar cu cel al satisfacerii scopului iniţial.

Pentru baza de cunoştinţe descrisă mai sus, satisfacerea scopului

?- iubeste(vlad, ana).

6

se va face în următorul mod. Scopul unifică cu antetul regulii (6) şi duce la instanţierea variabilelor din regula (6): X = vlad şi Y = ana. Pentru ca acest scop să fie îndeplinit, trebuie îndeplinită regula, deci fiecare subscop din corpul acesteia. Aceasta revine la îndeplinirea scopurilor bun(vlad), care reuşeşte prin unificare cu faptul (2), cunoaste(vlad, ana), care reuşeşte prin unificare cu faptul (4), şi a scopului frumoasa(ana), care reuşeşte prin unificare cu faptul (1). În consecinţă, regula a fost îndeplinită, deci şi întrebarea iniţială este adevarată, iar sistemul răspunde yes.

Ce se întîmplă dacă se pune întrebarea:

?- iubeste(X, Y).

Prima soluţie a acestui scop este dată de unificarea cu faptul (5), iar răspunsul este:

X = mihai, Y = maria

Sistemul Prolog va pune un marcaj în dreptul faptului (5) care a satisfăcut scopul. Următoarea soluţie a scopului iubeste(X, Y) se obţine începând căutarea de la acest marcaj în continuare în baza de cunoştinţe. Scopul unifică cu antetul regulii (6) şi se vor fixa trei noi subscopuri de îndeplinit, bun(X), cunoaste(X, Y) şi frumoasa(Y). Scopul bun(X) este satisfăcut de faptul (2) şi variabila X este instanţiată cu valoarea vlad, X = vlad . Se încearcă acum satisfacerea scopului cunoaste(vlad, Y), care este satisfăcut de faptul (3) şi determină instanţierea Y = maria. Se introduce în baza de cunoştinţe un marcaj asociat scopului cunoaste(vlad, Y), care a fost satisfăcut de faptul (3). Se încearcă apoi satisfacerea scopului frumoasa(maria). Acesta eşuează. În acest moment sistemul intră într-un proces de backtracking în care se încearcă resatisfacerea scopului anterior satisfăcut, cunoaste(vlad, Y), în speranţa că o noua soluţie a acestui scop va putea satisface şi scopul curent care a eşuat, frumoasa(Y). Resatisfacerea scopului cunoaste(vlad, Y) se face pornind căutarea de la marcajul asociat scopului în jos, deci de la faptul (3) în jos. O nouă soluţie (resatisfacere) a scopului cunoaste(vlad, Y) este dată de faptul (4) care determină instanţierea Y = ana. În acest moment se încearcă satisfacerea scopului frumoasa(ana). Cum este vorba de un nou scop, căutarea se face de la începutul bazei de cunoştinţe şi scopul frumoasa(ana) este satisfăcut de faptul (1). În consecinţă a doua soluţie a scopului iubeste(X, Y) este obţinută şi sistemul răspunde:

X = vlad, Y = ana

urmând un mecanism de backtracking, descris intuitiv în figura 1 prin prezentarea arborilor de deducţie construiţi de sistemul Prolog.

La încercarea de resatisfacere a scopului iubeste(X, Y), printr-un mecanism similar, se observă că nu mai există alte solutii. În concluzie, fiind dată baza de fapte şi reguli Prolog anterioară, comportarea sistemului Prolog este:

?- iubeste(X, Y).

7

X = mihai, Y = maria; X = vlad, Y = ana; no iubeste(X,Y) %5
X
= mihai, Y = maria;
X
= vlad, Y = ana;
no
iubeste(X,Y)
%5 iubeste(mihai,maria)
SUCCES
solutie 1: X=mihai, Y=maria
iubeste(X,Y)
bun(X)
cunoaste(X,Y)
%6 frumoasa(Y)
%2 bun(vlad)
cunoaste(vlad,Y)
frumoasa(maria)
%1 frumoasa(ana)
SUCCES
%3 cunoaste(vlad,maria)
%4 cunoaste(vlad,ana) INSUCCES
SUCCES
SUCCES
SUCCES

solutie 2: X=vlad, Y=ana

Figura 1. Mecanismul de satisfacere a scopurilor în Prolog

Observaţii:

La satisfacerea unei conjuncţii de scopuri în Prolog, se încearcă satisfacerea fiecărui scop pe rând, de la stânga la dreapta. Prima satisfacere a unui scop determină plasarea unui marcaj în baza de cunoştinţe în dreptul faptului sau regulii care a determinat satisfacerea scopului.

Dacă un scop nu poate fi satisfăcut (eşuează), sistemul Prolog se întoarce şi încearcă resatisfacerea scopului din stânga, pornind căutarea în baza de cunoştinţe de la marcaj în jos. Înainte de resatisfacerea unui scop se elimină toate instanţierile de variabile determinate de ultima satisfacere a acestuia. Dacă cel mai din stânga scop din conjuncţia de scopuri nu poate fi satisfăcut, întreaga conjuncţie de scopuri eşuează.

8

Această comportare a sistemului Prolog în care se încearcă în mod repetat satisfacerea şi resatisfacerea scopurilor din conjuncţiile de scopuri se numeşte backtracking.

În secţiunea 4 se va discuta pe larg structura de control a sistemului Prolog, mecanismul fundamental de backtracking şi modurile în care se poate modifica parţial acest mecanism.

1.5 Un program Prolog simplu

Simplitatea şi expresivitatea limbajului Prolog poate fi pusă în evidentă de următorul exemplu de descriere a unui circuit logic "poartă ŞI". Se consideră poarta ŞI ca fiind construită prin conectarea unei "porţi ŞI-NU" cu un inversor. Întregul circuit este definit de relaţia:

poarta_si(Intrare1, Intrare2, Iesire)

pe baza relaţiilor

poarta_si_nu(Intrare1, Intrare2, Iesire) inversor(Intrare, Iesire).

În figura 2 este prezentată schema porţii ŞI în care se observă că inversorul este construit dintr-un tranzistor cu sursa conectată la masă şi un rezistor cu un capăt conectat la alimentare. Poarta tranzistorului este intrarea inversorului, în timp ce celălalt capăt al rezistorului trebuie conectat la colectorul tranzistorului care formează ieşirea inversorului.

n1 n2 n3 n4 n5 Figura 2. Un circuit logic poartă ŞI
n1
n2
n3
n4
n5
Figura 2. Un circuit logic poartă ŞI

Variabilele comune între predicate sunt utilizate pentru a specifica legăturile comune.

rezistor(alimentare, n1). rezistor(alimentare, n2).

9

tranzistor(n2, masa, n1). tranzistor(n2, masa, n1). tranzistor(n3, n4, n2). tranzistor(n5, masa, n4). inversor(Intrare, Iesire) :- tranzistor(Intrare, masa, Iesire), rezistor(alimentare, Iesire). poarta_si_nu(Intrare1, Intrare2, Iesire) :- tranzistor(Intrare1, X, Iesire), tranzistor(Intrare2, masa, X), rezistor(alimentare, Iesire). poarta_si(Intrare1, Intrare2, Iesire) :- poarta_si_nu(Intrare1, Intrare2, X), inversor(X, Iesire).

Pentru întrebarea

?- poarta_si(In1, In2, Iesire). In1 = n3, In2= n5, Iesire = n1

răspunsul sistemului Prolog confirmă faptul că circuitul descris este o poartă ŞI, identificând intrările şi ieşirile corespunzătoare.

2 Sintaxa limbajului Prolog

Aşa cum s-a arătat în secţiunea anterioară, un program Prolog este format din fapte, reguli şi întrebări, acestea fiind construite pe baza predicatelor definite de utilizator sau predefinite. În orice sistem Prolog există o mulţime de predicate predefinite, unele dintre acestea fiind predicate standard, iar altele depinzând de implementare. Argumentele predicatelor Prolog, prin analogie cu logica predicatelor de ordinul I, se numesc termeni, şi pot fi constante, variabile sau structuri.

2.1

Constante

Constantele definesc obiecte specifice, particulare, sau relaţii particulare. Există două tipuri de constante: atomi şi numere. Atomii sunt constante simbolice care încep, de obicei, cu o literă şi pot conţine litere, cifre şi caracterul “_”. Există şi alte caractere ce pot forma atomi speciali, care au o semnificaţie aparte în limbaj. Atomii pot desemna:

obiecte constante care sunt argumentele predicatelor, de exemplu atomii mihai şi maria în faptul iubeste(mihai, maria);

predicate Prolog, fie cele definite de utilizator, fie cele predefinite în sistem; de exemplu atomul iubeste în faptul iubeste(mihai, maria);

10

atomi speciali, de exemplu atomii “:-şi “?- ;

diverse alte reguli de construcţie sintactică a atomilor depind de implementare.

Numerele pot fi întregi sau reale; sintaxa particulară acceptată cât şi domeniile de definiţie depinzând de implementare. Detalii despre formatul numerelor şi a altor tipuri de constante din mediul ARITY Prolog sunt descrise în partea a doua.

2.2 Variabile

Variabilele sunt, din punct de vedere sintactic, tot atomi, dar ele au o semnificaţie specială, aşa cum s-a arătat în Secţiunea 1.3. Spre deosebire de regulile generale admise pentru construcţia atomilor, numele unei variabile poate începe şi cu simbolul “_”, ceea ce indică o variabilă anonimă. Utilizarea unei variabile anonime semnifică faptul că nu interesează valoarea la care se va instanţia acea variabilă.

De exemplu, interogarea ?- iubeste( _, maria). semnifică faptul că se întreabă dacă există cineva care o iubeşte pe Maria, dar nu interesează cine anume. Limbajul Prolog face distincţia între litere mari şi litere mici (este case sensitive). Se reaminteşte că, din punctul de vedere al convenţiei Prolog, numele oricărei variabile trebuie să înceapă fie cu literă mare, fie cu “_”.

2.3 Structuri

O structură Prolog este un obiect ce desemneaza o colecţie de obiecte corelate logic, care formează componentele structurii. Un exemplu este structura asociată obiectului carte, care este formată din componentele: titlu carte, autor, şi an apariţie. Un fapt ce referă relaţia de posedare a unei cărţi de Prolog de către Mihai poate fi exprimat astfel:

poseda(mihai, carte(prolog, clocksin, 1981)).

unde carte(prolog, clocksin, 1981) este o structură cu numele carte şi cu trei componente:

prolog, clocksin şi 1981. Se admit şi structuri imbricate, de exemplu:

poseda(mihai, carte(prolog, autori(clocksin, mellish), 1981)).

unde predicatul poseda are două argumente: primul argument este constanta mihai, iar cel

de-al doilea este structura carte(prolog structura autori(clocksin, mellish).

cu două componente, a doua componentă fiind

),

În Prolog, o structură se defineşte prin specificarea:

(1)

numelui structurii ( functorul structurii);

(2)

elementelor structurii (componentele structurii).

Structurile Prolog pot fi utilizate pentru reprezentarea structurilor de date, de exemplu liste sau arbori. În Secţiunea 2.6 se vor prezenta structuri Prolog predefinite care permit

11

lucrul cu liste. Un arbore binar poate fi reprezentat în Prolog tot printr-o structură, de exemplu:

arb(barbu, arb(ada, vid, vid), vid).

reprezintă un arbore binar cu cheia din rădăcina barbu, cu subarborele drept vid şi cu subarborele stâng format dintr-un singur nod cu cheia ada.

Observaţii:

Sintaxa structurilor este aceeaşi cu cea a faptelor Prolog. Un predicat Prolog poate fi văzut ca o structură a cărui functor este numele predicatului, iar argumentele acestuia reprezintă componentele structurii.

Considerarea predicatelor Prolog ca structuri prezintă un interes deosebit; atât datele cât şi programele în Prolog au aceeaşi formă, ceea ce facilitează tratarea uniformă şi sinteza dinamică de programe. În Secţiunea 4.3 se vor prezenta predicate predefinite

2.4

în Prolog care permit sinteza şi execuţia dinamică a programelor Prolog.

Operatori

Uneori este convenabil să se scrie anumiţi functori (nume de structuri sau predicate) în formă infixată. Aceasta este o formă sintactică ce măreşte claritatea programului, cum ar fi cazul operatorilor aritmetici sau al operatorilor relaţionali.

Limbajul Prolog oferă o mulţime de operatori, unii care se regăsesc în aproape orice implementare, iar alţii care sunt specifici unei versiuni particulare de implementare a limbajului. În continuare se vor prezenta o parte dintre operatorii din prima categorie.

(1) Operatori aritmetici

Operatorii aritmetici binari, cum ar fi +, -, *, /, pot fi scrişi în Prolog în notaţie infixată; de exemplu:

1 + 2*(X * Y) / Z

Aceasta sintaxa este de fapt o rescriere infixată a formei prefixate a structurilor echivalente:

+(1, 2) / (*(X, Y), Z)

Este important de reţinut că operatorii aritmetici sunt o rescriere infixată a unor structuri deoarce valoarea expresiei astfel definită nu este calculată. Evaluarea expresiei se face la cerere în cazul în care se foloseste operatorul predefinit infixat is, de exemplu:

X is 1 + 2.

va avea ca efect instanţierea variabilei X la valoarea 3.

(2) Operatori relaţionali

12

Operatorii relaţionali sunt predicate predefinite infixate. Un astfel de operator este operatorul de egalitate =. Predicatul (operatorul) de egalitate funcţionează ca şi cum ar fi definit prin următorul fapt:

X = X.

iar încercarea de a satisface un scop de tipul X = Y se face prin încercarea de a unifica X cu Y. Din aceasta cauză, dându-se un scop de tipul X = Y, regulile de decizie care indică dacă scopul se îndeplineşte sau nu sunt următoarele:

Dacă X este variabilă neinstanţiată, iar Y este instanţiată la orice obiect Prolog, atunci scopul reuşeşte. Ca efect lateral, X se va instanţia la aceeaşi valoare cu cea a lui Y. De exemplu:

?- carte(barbu, poezii) = X.

este un scop care reuşeşte şi X se instanţiază la carte(barbu, poezii).

Dacă atât X cât şi Y sunt variabile neinstanţiate, scopul X = Y reuşeşte, variabila X este legată la Y şi reciproc. Aceasta înseamnă că ori de câte ori una dintre cele două variabile se instanţiază la o anumită valoare, cealaltă variabila se va instanţia la aceeaşi valoare.

Atomii şi numerele sunt întotdeauna egali cu ei înşişi. De exemplu, următoarele scopuri au rezultatul marcat drept comentariu:

mihai = mihai mare = mic 102 = 102 1010 = 1011

% este satisfăcut % eşuează % reuşeşte % eşuează

Două structuri sunt egale dacă au acelaşi functor, acelaşi număr de componente şi fiecare componentă dintr-o structură este egală cu componenta corespunzătoare din cealaltă structură. De exemplu, scopul:

autor(barbilian, ciclu(uvedenrode, poezie(riga_crypto))) = autor(X, ciclu(uvedenrode, poezie(Y)))

este satisfăcut, iar ca efect lateral se fac instanţierile:

X = barbilian şi Y = riga_crypto.

Operatorul de inegalitate \= se defineşte ca un predicat opus celui de egalitate. Scopul X \= Y reuşeşte dacă scopul X = Y nu este satisfăcut şi eşuează dacă X = Y reuşeşte. În plus, există predicatele relaţionale de inegalitate definite prin operatorii infixaţi >, <, =<, >=, cu semnificaţii evidente.

13

Un operator interesant este =:=. El face numai evaluare aritmetică şi nici o instanţiere. Exemplu:

?- 1 + 2 =:= 2 + 1. yes ?- 1 + 2 = 2 + 1. no

Predicatul = = testează echivalenţa a două variabile. El consideră cele două variabile egale doar dacă ele sunt deja partajate. X = = Y reuşeşte ori de câte ori X = Y reuşeşte, dar reciproca este falsă:

?- X = = X.

X=_23

%variabilă neinstanţiată

?- X= =Y. no ?- X=Y, X= =Y. X=_23, Y=_23

% X şi Z variabile neinstanţiate partajate

În cele mai multe implementări Prolog există predefinit operatorul de obţinere a modulului unui număr, mod; scopul

X is 5 mod 3.

reuşeşte şi X este instanţiat la 2.

Comentariile dintr-un program Prolog sunt precedate de caracterul “%”.

Exemple:

1. Presupunând că nu există operatorul predefinit mod, se poate scrie un predicat Prolog cu efect similar. O definiţie posibilă a predicatului modulo(X, Y, Z), cu semnificaţia argumentelor Z = X mod Y , presupunând X, Y > 0, este:

% modulo(X, Y, Z)

modulo(X, Y, X) :- X < Y. modulo(X, Y, Z) :- X >= Y, X1 is X - Y, modulo(X1, Y, Z).

2. Plecând de la predicatul modulo definit anterior, se poate defini predicatul de calcul

al celui mai mare divizor comun al două numere, conform algoritmului lui Euclid,

presupunând X>0, Y>0, astfel:

% cmmdc(X, Y, C)

cmmdc(X, 0, X). cmmdc(X, Y, C) :- modulo(X, Y, Z), cmmdc(Y, Z, C).

La întrebarea

14

?- cmmdc(15, 25, C).

C=5

răspunsul sistemului este corect. În cazul în care se încearcă obţinerea unor noi soluţii (pentru semnificaţia cmmdc acest lucru este irelevant, dar interesează din punctul de vedere al funcţionării sistemului Prolog) se observă ca sistemul intră într-o buclă infinită datorita imposibilităţii resatisfacerii scopului modulo(X, Y, Z) pentru Y=0. Dacă la definiţia predicatului modulo se adaugă faptul:

modulo(X, 0, X).

atunci predicatul modulo(X, Y, Z) va genera la fiecare resatisfacere aceeaşi soluţie, respectiv solutia corectă, la infinit. Cititorul este sfătuit să traseze execuţia predicatului cmmdc în ambele variante de implementare a predicatului modulo.

3. Calculul ridicării unui număr natural la o putere naturală se poate face definind următorul predicat:

% expo(N, X, XlaN) expo( _ , 0, 0). expo(0, _ , 1). expo(N, X, Exp) :- N > 0, N1 is N - 1, expo(N1, X, Z), Exp is Z * X.

2.5 Operatori definiţi de utilizator

Limbajul Prolog permite definirea de noi operatori de către programator prin introducerea în program a unor clauze de formă specială, numite directive. Directivele acţionează ca o definire de noi operatori ai limbajului în care se specifică numele, precedenţa şi tipul (infixat, prefixat sau postfixat) operatorului. Se reaminteşte faptul că orice operator Prolog este de fapt o structură care are ca functor numele operatorului şi ca argumente argumentele operatorului, dar este scris într-o sintaxă convenabilă. La definirea de noi operatori în Prolog, se creează de fapt noi structuri cărora le este permisă o sintaxă specială, conform definiţiei corespunzătoare a operatorului. Din acest motiv, nu se asociază nici o operaţie operatorilor definiţi de programator. Operatorii noi definiţi sunt utilizaţi ca functori numai pentru a combina obiecte în structuri şi nu pentru a executa acţiuni asupra datelor, deşi denumirea de operator ar putea sugera o astfel de acţiune.

De exemplu, în loc de a utiliza structura:

are(coco, pene)

se poate defini un nou operator are

:- op(600, xfx, are).

caz în care este legală expresia

15

coco are pene.

Definirea de noi operatori se face cu ajutorul directivei:

:- op(precedenţă-operator, tip-operator, nume-operator).

Operatorii sunt atomi iar precedenta lor trebuie să fie o valoare întreagă într-un anumit interval şi corelată cu precedenţa operatorilor predefiniţi în limbaj. Tipul operatorilor fixează caracterul infixat, prefixat sau postfixat al operatorului şi precedenţa operanzilor săi. Tipul operatorului se defineşte utilizând una din următoarele forme standard:

(1)

operatori infixaţi:

xfx xfy yfx

(2)

operatori prefixaţi:

fx

fy

(3)

operatori postfixaţi:

xf

yf

unde f reprezintă operatorul, iar x şi y operanzii săi. Utilizarea simbolului x sau a simbolului y depinde de precedenţa operandului faţă de operator. Precedenţa operanzilor se defineşte astfel:

un argument între paranteze sau un argument nestructurat are precedenţa 0;

un argument de tip structură are precedenţa egală cu cea a functorului operator.

Observaţie: În ARITY Prolog, cu cât valoare-precedenţă-operator este mai mare, cu atât operatorul are o precedenţă mai mică şi invers.

Semnificaţiile lui x şi y în stabilirea tipului operatorului sunt următoarele:

x reprezintă un argument (operand) cu precedenţă strict mai mică decât cea a functorului (operatorului) f

precedenţa( x ) < precedenţa( f )

y reprezintă un argument (operand) cu precedenţă mai mică sau egală cu cea a functorului (operandului) f

precedenţa( y ) precedenţa( f )

Aceste reguli ajută la eliminarea ambiguităţii operatorilor multipli cu aceeaşi precedenţă. De exemplu, operatorul predefinit în Prolog - (minus) este definit din punct de vedere al tipului ca yfx, ceea ce înseamnă că structura a - b - c este interpretată ca (a - b)-c şi nu ca a-(b - c) . Dacă acest operator ar fi fost definit ca xfy, atunci interpretarea structurii a - b - c ar fi fost a-(b - c) .

Numele operatorului poate fi orice atom Prolog care nu este deja definit în Prolog. Se poate folosi şi o listă de atomi, dacă se definesc mai mulţi operatori cu aceeaşi precedenţă şi acelaşi tip.

Exemple:

16

:- op(100, xfx, [este, are]). :- op(100, xf, zboara). coco are pene. coco zboara. coco este papagal. bozo este pinguin. ?- Cine are pene. Cine = coco ?- Cine zboara. Cine = coco ?- Cine este Ce. Cine = coco, Ce = papagal; Cine = bozo, Ce = pinguin; no

În condiţiile în care se adaugă la baza de cunoştinţe anterioară şi definiţia operatorilor daca, atunci şi si

:- op(870, fx, daca). :- op(880, xfx, atunci). :- op(880, xfy, si).

următoarea structură este validă în Prolog:

2.6

daca Animalul are pene şi Animalul zboara atunci Animalul este pasare.

Liste

O listă este o structură de date ce reprezintă o secvenţă ordonată de zero sau mai multe elemente. O listă poate fi definită recursiv astfel:

(1)

lista vidă (lista cu 0 elemente) este o listă

(2)

o listă este o structură cu două componente: primul element din listă (capul listei) şi restul listei (lista formată din urmatoarele elemente din lista).

Sfârşitul unei liste este de obicei reprezentat ca lista vidă.

În Prolog structura de listă este reprezentată printr-o structură standard, predefinită, al cărei functor este caracterul “.şi are două componente: primul element al listei şi restul listei. Lista vidă este reprezentată prin atomul special [ ]. De exemplu, o listă cu un singur element a se reprezintă în Prolog, prin notaţie prefixată astfel:

.(a, [ ])

având sintaxa in ARITY Prolog

17

'.'(a, [ ])

iar o listă cu trei elemene, a, b, c, se reprezintă ca:

.(a, . (b, . (c, [ ]))) cu sintaxa în ARITY Prolog

'.'(a, '. '(b, '. '(c, [ ]))).

Deoarece structura de listă este foarte des utilizată în Prolog, limbajul oferă o sintaxă alternativă pentru descrierea listelor, formată din elementele listei separate de virgulă şi încadrate de paranteze drepte. De exemplu, cele două liste anterioare pot fi exprimate astfel:

[a]

[a, b, c]

Această sintaxă a listelor este generală şi valabilă în orice implementare Prolog. O operaţie frecventă asupra listelor este obţinerea primului element dintr-o listă şi a restului listei, deci a celor două componente ale structurii de listă. Aceasta operaţie este realizată în Prolog de operatorul de scindare a listelor “|” scris sub următoarea formă:

[Prim | Rest]

Variabila Prim stă pe postul primului element din listă, iar variabila Rest pe postul listei care conţine toate elementele din listă cu excepţia primului. Acest operator poate fi aplicat pe orice listă care conţine cel puţin un element. Dacă lista conţine exact un element, Rest va reprezenta lista vidă. Încercarea de identificare a structurii [Prim | Rest] cu o listă vidă duce la eşec. Mergând mai departe, se pot obţine chiar primele elemente ale listei şi restul listei. Iată câteva echivalenţe:

[a, b, c] = [a | [b, c] ] = [a, b | [c] ] = [a, b, c | [ ] ] = [a | [b | [c] ] ] = [a | [b | [c | [ ] ] ] ].

În Prolog elementele unei liste pot fi atomi, numere, liste şi în general orice structuri. În consecinţă se pot construi liste de liste.

Exemple:

1. Se poate defini următoarea structură de listă:

[carte(barbu, poezii), carte(clocksin, prolog)]

2. Considerând următoarele fapte existente în baza de cunoştinţe Prolog

pred([1, 2, 3, 4]). pred([coco, sta, pe, [masa, alba]]).

se pot pune următoarele întrebări obţinând răspunsurile specificate:

?- pred([Prim | Rest]). Prim = 1, Rest = [2, 3, 4]; Prim = coco, Rest = [sta, pe, [masa, alba]]; no ?- pred([ _, _ , _ , [ _ | Rest]])

18

Rest = [alba]

3. Un predicat util în multe aplicaţii este cel care testează apartenenţa unui element la o listă şi care se defineşte astfel:

% membru(Element, Lista)

membru(Element, [Element | _ ]). membru(Element, [ _ | RestulListei]) :- membru(Element, RestListei).

Funcţionarea acestui scurt program Prolog poate fi urmărită cerând răspunsul sistemului la următoarele scopuri:

?-

membru(b, [a, b, c]).

%1

yes

?-

membru(X, [a, b, c]).

%2

X

= a;

X

= b;

X

= c;

no

?-

membru(b, [a, X, b]).

%3

X

= b;

X

= _0038;

no

Deci pentru cazul în care primul argument este o variabilă (%2) există trei soluţii posibile ale scopului membru(Element, Lista). Dacă lista conţine o variabilă (%3) există două soluţii pentru forma listei ([a, b, b] sau [a, _ , b]).

4. Un alt predicat util este cel de concatenare a două liste L1 şi L2, rezultatul concatenării obţinându-se în lista L3, iniţial neinstanţiată.

% conc(L1, L2, L3)

conc([], L2, L2).

conc([Prim1|Rest1], Lista2, [Prim1|Rest3]) :- conc(Rest1, Lista2, Rest3).% lista rezultată este primul element

% lista vidă concatenată cu L2 este L2.

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

L3 = [a, b, c, d, e];

% al sublistei curente din L1 concatenat

% cu lista întoarsă de apelul recursiv.

no

?-

conc([a, b], [c, d, e], L3).

L3 = [a, b, c, d, e].

yes

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

L1 = [a, b];

% “.şi Enter când nu căutăm alte soluţii

19

no

?- conc([a, b], L2, [a, b, c, d, e]). L2 = [c, d, e]; no ?- conc(L1, L2, [a, b]). L1 = [ ], L2 = [a, b]; L1 = [a], L2 = [b]; L1 = [a, b], L2 = [ ]; no

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

5. Următorul exemplu defineşte predicatul de testare a existenţei unei sortări în ordine crescătoare a elementelor unei liste de întregi. Predicatul reuşeşte dacă elementele listei sunt sortate crescător şi eşuează în caz contrar.

% sortcresc(Lista) sortcresc([ ]). sortcresc([ _ ]).

sortcresc([X, Y | Rest]) :- X = < Y, sortcresc([Y | Rest]). ?- sortcresc([1, 2, 3, 4]).

yes ?- sortcresc([1, 3, 5, 4]). no ?- sortcresc([ ]). yes

% lista vidă se consideră sortată % lista cu un singur element este sortată

Dacă se consideră că lista vidă nu este o listă sortată crescător atunci se poate elimina faptul:

sortcresc([ ]).

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

Observaţii:

Exemplul 3 pune în evidenţă o facilitate deosebit de interesantă a limbajului Prolog, respectiv puterea generativă a limbajului. Predicatul membru poate fi utilizat atât pentru testarea apartenenţei unui element la o listă, cât şi pentru generarea, pe rând,

20

a elementelor unei liste prin resatisfacere succesivă. În anumite contexte de utilizare această facilitate poate fi folositoare, iar în altele ea poate genera efecte nedorite atunci când predicatul membru este utilizat în definirea altor predicate, aşa cum se va arăta în partea a doua a lucrării.

Aceeaşi capacitate generativă a limbajului Prolog poate fi observată şi în Exemplul 4 unde în funcţie de combinaţiile de argumente instanţiate şi neinstanţiate, predicatul conc poate produce rezultate relativ diferite.

La definirea unui predicat p care va fi utilizat în definirea altor predicate trebuie întotdeauna să se analizeze numărul de soluţii şi soluţiile posibile ale predicatului p. Acest lucru este necesar deoarece dacă p apare într-o conjuncţie de scopuri p 1 ,…, p i-1 , p ,p i+1 ,…, p n şi unul dintre scopurile p i+1 ,…, p n eşuează, mecanismul de backtracking din Prolog va încerca resatisfacerea scopului p. Numărul de soluţii şi soluţiile scopului p influenţează astfel, în mod evident, numărul de soluţii şi soluţiile conjuncţiei de scopuri p 1 ,…, p i-1 , p ,p i+1 ,…, p n . Exemple în acest sens şi modalităţi de reducere a numărului total de soluţii ale unui corp vor fi prezentate în Secţiunea 4.2.

3 Limbajul Prolog şi logica cu predicate de ordinul I

Limbajul Prolog este un limbaj de programare logică. Deşi conceput iniţial pentru dezvoltarea unui interpretor de limbaj natural, limbajul s-a impus ca o soluţie practică de construire a unui demonstrator automat de teoreme folosind rezoluţia. Demonstrarea teoremelor prin metoda rezoluţiei necesită ca axiomele şi teorema să fie exprimate în forma clauzală, adică o disjuncţie de literali, unde un literal este un predicat sau un predicat negat. Pentru detalii despre noţiunea de clauză şi modul de transformare a unei formule din logica cu predicate de ordinul I în formă clauzală se poate consulta [Flo93]. Sintaxa şi semantica limbajului Prolog permit utilizarea numai a unei anumite forme clauzale a formulelor bine formate: clauze Horn distincte.

Deoarece faptele şi regulile Prolog sunt în formă clauzală, forma particulară a clauzelor fiind clauze Horn distincte, ele se mai numesc şi clauze Prolog.

Definiţie. Se numeşte clauză Horn o clauză care conţine cel mult un literal pozitiv. O clauză Horn poate avea una din următoarele patru forme:

(1)

o

clauză

unitară pozitivă formată dintr-un singur literal pozitiv (literal

nenegat);

(2)

o clauză negativă formată numai din literali negaţi;

(3)

o clauză formată dintr-un literal pozitiv şi cel puţin un literal negativ, numită şi clauză Horn mixtă;

21

(4)

clauză vidă.

Definiţie. Se numeşte clauză Horn distinctă o clauză care are exact un literal pozitiv, ea fiind fie o clauză unitară pozitivă, fie o clauză Horn mixtă.

Clauzele Horn unitare pozitive se reprezintă în Prolog prin fapte, iar clauzele Horn mixte prin reguli. O clauză Horn mixtă de forma:

S 1 ∨ ∼S 2 ∨ ∼S n S

se exprimă în Prolog prin regula:

S :- S 1 , S 2 , …S n .

Semnificaţia intuitivă a unei reguli Prolog are un corespondent clar în logica cu predicate de ordinul I dacă se ţine cont de faptul că o clauză Horn mixtă poate proveni din următoarea formulă bine formată:

S 1 S 2 S n S

Variabilele din clauzele distincte se transformă în variabile Prolog, constantele din aceste formule în constante Prolog, iar funcţiile pot fi asimilate cu structuri Prolog. Deci argumentele unui predicat Prolog au forma termenilor din calculul cu predicate de ordinul I.

Exemple:

1. Fie următoarele enunţuri: Orice sportiv este puternic. Oricine este inteligent şi puternic va reuşi în viaţă. Oricine este puternic va reuşi în viaţă sau va ajunge bătăuş. Există un sportiv inteligent. Gelu este sportiv. Exprimând enunţurile în logica cu predicate de ordinul I se obţin următoarele formule bine formate:

A1.

(x) (sportiv(x) puternic(x))

A2.

(x) (inteligent(x) puternic(x) reuşeşte(x))

A3.

(x) (puternic(x) (reuşeşte(x) bătăuş(x)))

A4.

(x) (sportiv(x) inteligent(x))

A5.

Sportiv(gelu)

Axiomele se transformă în forma clauzală şi se obţin următoarele clauze:

C1.

sportiv(x) puternic(x)

C2.

inteligent(x) ~ puternic(x) reuseste(x)

C3.

~ puternic(x) reuseste(x) bataus(x)

C4.

sportiv(a)

C4'. inteligent(a)

C5.

sportiv(gelu)

22

Clauzele C1, C2, C4, C4' şi C5 pot fi transformate în Prolog deoarece sunt clauze Horn distincte, dar clauza C3 nu poate fi transformată în Prolog. Programul Prolog care se obţine prin transformarea acestor clauze este următorul:

puternic(X) :- sportiv(X). reuseste(X) :- inteligent(X), puternic(X). sportiv(a). inteligent(a). sportiv(gelu).

2. Fie următoarele enunţuri:

(a)

Orice număr raţional este un număr real.

(b)

Există un număr prim.

(c)

Pentru fiecare număr x există un număr y astfel încât x < y .

Dacă se notează cu prim(x) “x este număr prim”, cu rational(x) ”x este număr raţional”, cu real(x) “x este număr real” şi cu mai_mic(x, y) “x este mai mic decât y“, reprezentarea sub formă de formule bine formate în calculul cu predicate de ordinul I este următoarea:

A1.

(x) (raţional(x) real(x))

A2.

(x) prim(x)

A3.

(x) (y) mai_mic(x, y)

Reprezentarea în formă clauzală este:

C1.

~ rational(x) real(x)

C2.

prim(a)

C3.

mai_mic(x, mai_mare(x))

unde mai_mare(x) este funcţia Skolem care înlocuieşte variabila y cuantificată existenţial. Forma Prolog echivalentă a acestor clauze este:

real(X) :- rational(X). prim(a). mai_mic(X, mai_mare(X)).

unde mai_mare(x) este o structură Prolog.

Este evident că nu orice axiomă poate fi transformată în Prolog şi că, dintr-un anumit punct de vedere, puterea expresivă a limbajului este inferioară celei a logicii cu predicate de ordinul I. Pe de altă parte, limbajul Prolog oferă o mulţime de predicate de ordinul II, adică predicate care acceptă ca argumente alte predicate Prolog, care nu sunt permise în logica cu predicate de ordinul I. Acest lucru oferă limbajului Prolog o putere de calcul superioară

23

celei din logica clasică. Uneori, aceste predicate de ordinul II existente în Prolog pot fi folosite pentru a modela versiuni de programe Prolog echivalente cu o mulţime de axiome care nu au o reprezentare în clauze Horn distincte.

Se propune cititorului, după parcurgerea secţiunii următoare, să revină la Exemplul 1 şi să încerce găsirea unei forme Prolog relativ echivalente (cu un efect similar) cu clauza

C3.

Limbajul Prolog demonstrează scopuri (teoreme) prin metoda respingerii rezolutive [Flo93]. Strategia rezolutivă utilizată este strategia de intrare liniară, strategie care nu este în general completă dar este completă pentru clauze Horn. Această strategie este deosebit de eficientă din punct de vedere al implementării şi jutifică astfel forma restricţionată a clauzelor Prolog.

4 Structura de control a limbajului Prolog

În această secţiune se prezintă în detaliu structura de control specifică sistemului Prolog. Spre deosebire de limbajele de programare clasice, în care programul defineşte integral structura de control şi fluxul de prelucrări de date, în Prolog există un mecanism de control predefinit.

4.1 Semnificaţia declarativă şi procedurală a programelor Prolog

Semnificaţia declarativă a unui program Prolog se referă la interpretarea strict logică a clauzelor acelui program, rezultatul programului fiind reprezentat de toate consecinţele logice ale acestuia. Semnificaţia declarativă determină dacă un scop este adevărat (poate fi satisfăcut) şi, în acest caz, pentru ce instanţe de variabile este adevărat scopul. Se reaminteşte că o instanţă a unei clauze este clauza de bază (clauza fără variabile) obţinută prin instanţierea variabilelor din clauza iniţială. În aceste condiţii semnificaţia declarativă a unui program Prolog se poate defini precum urmează:

Definiţie. Un scop S este adevărat într-un program Prolog, adică poate fi satisfăcut sau derivă logic din program, dacă şi numai dacă:

1. există o clauză C a programului;

2. există o instanţă I a clauzei C astfel încât:

2.1. antetul lui I să fie identic cu cel al lui S;

2.2. toate scopurile din corpul lui I sunt adevărate, deci pot fi satisfăcute.

Observaţii:

În definiţia de mai sus clauzele referă atât fapte cât şi reguli Prolog. Antetul unei clauze este antetul regulii dacă clauza este o regulă Prolog (clauză Horn mixtă) şi

24

este chiar faptul dacă clauza este un fapt Prolog (clauză unitară pozitivă). Corpul unui fapt este considerat vid şi un fapt este un scop care se îndeplineşte întotdeauna.

În cazul în care întrebarea pusă sistemului Prolog este o conjuncţie de scopuri, definiţia anterioară se aplică fiecărui scop din conjuncţie.

Semnificaţia procedurală a unui program Prolog se referă la modul în care sistemul încearcă satisfacerea scopurilor, deci la strategia de control utilizată. Diferenţa dintre semnificaţia declarativă şi semnificaţia procedurală este aceea că cea de a doua defineşte, pe lânga relaţiile logice specificate de program, şi ordinea de satisfacere a scopurilor şi subscopurilor. În prima secţiune a acestui capitol s-a făcut o prezentare informală a modalităţii procedurale de satisfacere a scopurilor în Prolog. În continuare se rafinează această comportare. Din punct de vedere procedural, un program Prolog poate fi descris de schema bloc prezentată