Inteligenta artificiala- Teorie

Post on 15-Jun-2015

2.090 views 27 download

Tags:

Transcript of Inteligenta artificiala- Teorie

Introducere in programarea Introducere in programarea nonprocedurala (PNP)

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Motivatia necesitatii PNP

• In cadrul paradigmelor de programare un locaparte il ocupa programarea nonprocedurala.

• Programarea nonprocedurala cuprinde:▫ Programarea logica▫ Programarea functionala.▫ Programarea functionala.

• Programarea nonprocedurala mai poartanumele de programare declarativa.

Plasarea PNP

Programare

Nonprocedurala

ProceduralaOrientata pe

obiecteConcurenta ...

FunctionalaLogica

Avantajele programelor PNP

Sunt orientate catre implementari logice.Sunt orientate catre implementari logice.Sunt extrem de potrivite pentru sistemele expert

apartinand domeniului inteligentei artificiale.Sunt usor de analizat, transformat, verificat,

intretinut.In general, sunt eficiente si competitive inIn general, sunt eficiente si competitive in

comparatie cu programele nedeclarative.Programul este mai degraba “intrebat” decat

executat.

Dezavantajeleprogramelor PNP

Nu sunt usor implementabile si utilizabile pentrualgoritmii matematici de cautare si optimizare dincadrul inteligentei artificiale.

Nu sunt cele mai potrivite pentru exprimareaalgoritmilor folositi in industrie.algoritmilor folositi in industrie.

Au mecanisme mai putin directe decat ale celornedeclarative si sunt considerate de multe ori mai“neprietenoase”.

Programare LOGICA versus programare FUNCTIONALA. Schema

Programare logica Programare functionala

• Adunarea a doua numere • Adunarea a doua numere

Out DirectionatDirectionat

aduna

In In In/Out

aduna

In In

NedirectionatNedirectionat

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Adunarea a doua numere • Adunarea a doua numere

aduna

3 4 X =

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Adunarea a doua numere • Adunarea a doua numere

7

aduna

3 4 X = 7

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Scrierea simbolica

Programare logica Programare functionala

• Adunarea a doua numere

aduna In × In × In/Out

aduna(3, 4, X)

• Adunarea a doua numere

aduna(In, In) Out

aduna(3, 4)

X = 7 7

Programare LOGICA versus programare FUNCTIONALA. Schema

Programare logica Programare functionala

• Patratul sumei a doua numere

patrat

In In/Out

• Patratul sumei a doua numereOut

patratIn

Out

aduna

In In In/Out

aduna

In In

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Patratul sumei a doua numere

patrat

• Patratul sumei a doua numere

patrat

aduna

3 4

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Patratul sumei a doua numere

patrat

• Patratul sumei a doua numere

patrat

7

aduna

3 4 X = 7

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Patratul sumei a doua numere

patrat

Y = 49

• Patratul sumei a doua numere49

patrat

7

aduna

3 4 X = 7

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Scrierea simbolica

Programare logica Programare functionala

• Patratul sumei a doua numere

aduna(3, 4, X), patrat(X, Y)

X = 7 patrat(7, Y)

• Patratul sumei a doua numere

patrat(aduna(3, 4))

patrat(7)

X = 7 Y = 49 49

Programare LOGICA versus programare FUNCTIONALA. Schema

Programare logica Programare functionala

• Testarea egalitatii

succes/esec

• Testarea egalitatiisucces/esec

=

In Out

aduna

In In In

aduna

In In

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Testarea egalitatii • Testarea egalitatii

=

7

aduna

3 4 7

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Testarea egalitatii

succes

• Testarea egalitatii

=

7 7

aduna

3 4 7

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Schema. Exemplu

Programare logica Programare functionala

• Testarea egalitatii

succes

• Testarea egalitatiisucces

=

7 7

aduna

3 4 7

aduna

3 4

Programare LOGICA versus programare FUNCTIONALA. Scrierea simbolica

Programare logica Programare functionala

• Testarea egalitatii

aduna(3, 4, 7)

succes

• Testarea egalitatii

7 = aduna(3, 4)

7 = 7

succes

Scopurile acestui curs

Programare logica Programare functionala

• Notiuni avansate alelimbajului PROLOG.

• Implementari pretabileprogramarii logice.

• Notiuni de baza ale limbajuluiLISP.

• Implementari pretabileprogramarii functionale.

• Intelegerea particularitatilorprogramarii logice.

• Observarea diferentelor fata deprogramarea functionala.

• Intelegerea particularitatilorprogramarii functionale.

• Observarea diferentelor fata deprogramarea logica.

Consecintele acestui curs• Cunostinte elementare asupra programarii

neprocedurale.neprocedurale.• Intelegerea diferentelor fata de programarea

nedeclarativa.• Observarea tipurilor de probleme care sunt

pretabile programarii neprocedurale si a celorcare sunt potrivite programarii nedeclarative.

• Obtinerea unei note bune la examen• Obtinerea unei note bune la examen▫ Proba practica (proba laborator) 50% Dezvoltarea unui program in Prolog Dezvoltarea unui program in Lisp

▫ Test grila (proba scrisa) 50%

PROLOG

• Numele este o abreviere pentru PROgrammation enLOGique.LOGique.

• Prima versiune a fost creata in 1972 de catre Alain Colmerauersi Philippe Roussel, bazandu-se pe interpretarea clauzelorHorn data de catre Robert Kowalski.

• Vom folosi versiunea Swi-Prolog.

• Bibliografie: • Bibliografie: ▫ Leon Sterling, Ehud Shapiro, The Art of Prolog, Second Edition:

Advanced Programming Techniques (Logic Programming), MIT Press, 1994.

▫ Internet.

LISP• Numele provine de la LISt Processing.

• Codul LISP se scrie sub forma listelor parantetizate (sau s -expresii).

• Prima versiune a aparut in 1958 si a fost dezvoltata de catreSteve Russell, Timothy P. Hart si Mike Levin, bazandu-se pecalculul lambda al lui Alonzo Church.

• Vom folosi versiunea CLISP.• Vom folosi versiunea CLISP.

• Bibliografie:▫ Stuart C.Shapiro, Common Lisp: An Interactive Approach,

Computer Science Press, 1992.▫ Internet.

I know what you did last summer… term

• A trecut mult de anul trecut…

• Nu prea mai tinem minte de atunci…

• Prolog, am mai facut noi Prolog?...

• A, Prolog, limbajul acela urat…• A, Prolog, limbajul acela urat…

• Sa recapitulam asadar notiunile de baza dinProlog.

Ce trebuie sa ne amintim din Prolog?

• Fisierul Prolog• Descrierea relatiilor• Descrierea relatiilor

▫ Fapte▫ Reguli

• Numere, atomi, variabile, operatii• Unificare• Recursivitate• Recursivitate• Liste

▫ Accesare elemente▫ Unificare

Fisierul Prolog• Se deschide programul Notepad.• Se acceseaza File / Save As.• Se acceseaza File / Save As.• In cadrul optiunii File Name se va scrie numele

fisierului, urmat de extensia .pl:▫ nume.pl, de exemplu.

• In final, in campul Save as Type se completeaza cuAll Files.

• Se face dublu-click pe fisier:• Se face dublu-click pe fisier:▫ Directorul curent devine cel in care se afla fisierul▫ Fisierul este compilat

• Daca se mai opereaza modificari asupra sa,compilarea se face prin [nume].

Fara extensia .pl!Fara extensia .pl!

Descrierea relatiilor in Prolog

• Faptele – ceea ce stim despre problema:▫ nume_relatie(arg1, arg2, …, argN).

In Prolog, orice

predicat se incheie cu

punct!

In Prolog, orice

predicat se incheie cu

punct!

Numele relatiilor incep cu litera

mica!

▫ nume_relatie(arg1, arg2, …, argN).

▫ nume_relatie este numele relaţiei (alpredicatului) iar arg1, arg2, … - argumentele.

▫ N reprezintă aritatea predicatului nume_relatie.

punct!punct!

▫ Exemplu: frate(dan, maria).

▫ Un predicat de aritate 0 se poate defini simplu:nume_predicat.

Descrierea relatiilor in Prolog

• Regulile (legaturi logice) – definesc predicate pebaza altor predicatebaza altor predicate▫ nume_relatie(arg1, …, argN) :-

nume_relatie_1(…), …, nume_relatie_M(…).• Două predicate care au acelaşi nume, dar un

număr diferit de argumente se consideră că suntpredicate diferite.

• Fiecare predicat dintr-un program este definitde existenţa uneia sau mai multor clauze.

• Clauzele care definesc acelasi predicat se aflauna lângă alta în fişierul sursă.

Tipuri de termeni in Prolog

• Argumentele unui predicat Prolog se numesc termeni si pot avea urmatoarele tipuri:termeni si pot avea urmatoarele tipuri:

▫ Numere pozitive sau negative.

▫ Atomii – text care începe cu literă mică.

▫ Variabilele – încep cu literă mare sau underline ▫ Variabilele – încep cu literă mare sau underline (_).

Tipuri de termeni in Prolog• Numerele:

▫ -12, 7.1, 24 ▫ -12, 7.1, 24

• Atomii:▫ Sunt alcatuiti de obicei din litere şi cifre, având

primul caracter o literă mică. ▫ salut, douaCuvinteAlaturate, un_atom, a2 sunt ▫ salut, douaCuvinteAlaturate, un_atom, a2 sunt

atomi▫ nu-este-atom, 5nu, _faraunderline, Literamare nu

sunt atomi▫ ’acesta-este-atom’, ’inca un atom’, ’Atom’ - atomi

Tipuri de termeni in Prolog• Variabilele:

▫ Sunt asemănatoare atomilor, cu excepţia că ele ▫ Sunt asemănatoare atomilor, cu excepţia că ele încep cu literă mare sau cu underline (_).

▫ Variabila, _variabila, Alta_vaRiabila2 sunt variabile.

• Variabila anonima:▫ Sunt variabilele care incep cu underline (_).▫ Apare in doua cazuri:▫ Apare in doua cazuri: Cand valoarea unei variabile nu este folosita in

cadrul unui clauze. In lucrul cu predicatul fail.

Variabila anonima - continuare• Variabila nu este folosita in cadrul unui predicat:

semn(X, 1) :- X >= 0.semn(X, 1) :- X >= 0.semn(_X, -1).

• In lucrul cu predicatul fail:parinte(andrei, dan).parinte(andrei, laura).copii(Tata) :- parinte(Tata, Copil), tab(2), copii(Tata) :- parinte(Tata, Copil), tab(2),

write_ln(Copil), fail.copii(_OriceVariabila).

Operaţii matematice in Prolog• Pentru evaluarea unei expresii aritmetice, se

foloseste predicatul predefinit is:foloseste predicatul predefinit is:▫ X is <expresie aritmetică>.▫ suma(N1, N2, S) :- S is N1 + N2.

• Operatori matematici utilizati:▫ <, >, =<, >=, == (sau =), =\= (sau \=).▫ Ultimii doi operatori sunt pentru a verifica▫ Ultimii doi operatori sunt pentru a verifica

egalitatea dintre două numere, respectiv pentru averifica dacă două numere sunt diferite.

Operaţii matematice in Prolog• Scrierea 2+3 nu reprezintă o instrucţiune care

păstrează rezultatul acestei adunări.păstrează rezultatul acestei adunări.• Reprezintă mai degrabă atomul ‘adunarea lui 2 cu 3’.• Termenul 2+3 este diferit de termenul 4+1.

numar(3).numar(4).numar(5).? – numar(2 + 1).? – numar(2 + 1).No? – X is 2 + 1, numar(X).X = 3Yes

Unificarea in Prolog• Reprezinta modul în care Prologul realizează

potrivirile între termeni:potrivirile între termeni:▫ X = marian. Yes

▫ marian = andrei. No

▫ place(maria, X) = place(Y, andrei). Y = maria, X = andrei Y = maria, X = andrei

▫ f(X, a) = f(a, X). X = a

▫ place(maria, X) = place(X, andrei). No

Recursivitatea in Prolog

• Programarea în Prolog depinde foarte mult deacest principiu.acest principiu.

• Prolog-ul este important si fiindca ne invata sagandim recursiv.

• Recursivitatea implică definirea unui predicat înfuncţie de el însuşi.

• Mecanismul recursivitatii consta in faptul caîntotdeauna definim predicatul la o scală maiîntotdeauna definim predicatul la o scală maimică.

• Este echivalentă cu demonstrarea prin inducţiedin matematică.

Recursivitatea in Prolog• O definitie recursiva are doua parti:

▫ Conditia elementara.▫ Conditia elementara.▫ Partea recursiva.

• Condiţia elementară defineşte un caz simplu, care ştim că este întotdeauna adevărat.

• Partea recursivă simplifică problema eliminând iniţial un anumit grad de complexitate şi apoi apelându-se ea însăşi.apelându-se ea însăşi.

• La fiecare nivel, condiţia elementară este verificată: ▫ dacă s-a ajuns la ea, recursivitatea se încheie;▫ altfel, recursivitatea continuă.

Recursivitatea in Prolog• Factorialul:

factorial(0,1). factorial(N,F) :- N>0, N1 is N-1, factorial(N1,F1), F factorial(N,F) :- N>0, N1 is N-1, factorial(N1,F1), F

is N * F1.

?- factorial(3, X).X = 6

Recursivitatea in Prolog• Implementarea functiilor recursive, de exemplu,

Fibonacci:Fibonacci:

f(1) = 1, f(2) = 1, f(n) = f(n - 1) + f(n - 2)

fibonacci(1, 1).fibonacci(2, 1).fibonacci(N, F):- N1 is N – 1, N2 is N – 2, fibonacci(N, F):- N1 is N – 1, N2 is N – 2,

fibonacci(N1, F1), fibonacci(N2, F2), F is F1 + F2.

?- fibonacci(3, X).X = 2

Liste in Prolog

• Data viitoare… • Data viitoare…

Liste in Prolog

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Liste• Sunt structuri care se folosesc pentru reprezentarea unei

secvenţe ordonate de termeni Prolog.

2/56

secvenţe ordonate de termeni Prolog.▫ [doar, un, exemplu]▫ []▫ [a, b, c, d, e]▫ [1, 21, 4, -17]▫ [4, [6], c, [-1, 12]]

• Virgula folosită în construcţia listelor este doar un• Virgula folosită în construcţia listelor este doar unseparator de argumente.

• Listele sunt secvenţe ordonate de termeni Prolog, decilista [a,b,c] nu este aceeaşi cu lista [a, c, b].

Accesarea elementelor unei liste

• Pentru a accesa elementele unei liste, putem

3/56

• Pentru a accesa elementele unei liste, putem împarţi lista în două părţi: primul element (dacă există unul!) şi restul listei.

• Ce se obtine din apelul urmator:▫ ? - [X|Y] = [a, b, c, d].

X = aY = [b, c, d]

Accesarea elementelor unei liste• Ce se obtine din apelul urmator:

▫ ? - [X|Y] = [a, b, c, d].

4/56

▫ ? - [X|Y] = [a, b, c, d].

• Primul element din listă, a, se numeşte şi capul listei (HEAD).

X = aY = [b, c, d]

• Lista formată prin ştergerea capului reprezintă coadalistei iniţiale (TAIL): [b, c, d].

▫ Ea poate fi mai departe prelucrată si este referita prin variabila Y.

• Presupunem că avem lista X = [b, c, d] şi vrem să

Adaugarea unui element la o lista

5/56

• Presupunem că avem lista X = [b, c, d] şi vrem să adăugăm elementul a la începutul listei X:

• Lista_rezultat = [a | X].• Cum scriem programul care face acest lucru?

adauga(A, X, Y):- Y=[A|X].adauga(A, X, Y):- Y=[A|X].

X – lista initialaA – elementul de adaugatY – lista care se va obtine

• Se pot adăuga (sau elimina) şi mai multe

Adaugarea de elemente la o lista

6/56

• Se pot adăuga (sau elimina) şi mai multeelemente odată.

• Dacă dorim, de exemplu, să adăugam laînceputul listei X elementele a, b şi c, pentruobţinerea unei liste Y:obţinerea unei liste Y:

• Y = [a, b, c | X]

Stergere de elemente dintr-o lista

• Dacă vrem să luam trei elemente din capul listei

7/56

• Dacă vrem să luam trei elemente din capul listeiX astfel încât lista rămasă Y să poată fi folositămai departe în cadrul programului:

• X = [A, B, C | Y]

• Lista vidă se notează cu [ ] si, evident, aceasta nupoate fi descompusă.

Unificarea la liste

• [a, b, c] = [c, a, b] ▫ întoarce No pentru că ordinea termenilor

8/56

▫ întoarce No pentru că ordinea termenilor contează

• [X] = [a, b, c] ▫ întoarce No pentru că cele două liste au lungimi

diferite

• [X|Y] = [doar, un ,exemplu] ▫ întoarce răspuns pozitiv şi X = doar, iar Y = [un,

exemplu]

Unificarea la liste

• [X,Y|Z] = [a, b, c, d] ▫ întoarce Yes şi X = a, Y = b, Z = [c, d]

9/56

▫ întoarce Yes şi X = a, Y = b, Z = [c, d]

• [X|Y] = [] ▫ întoarce No pentru că lista vidă nu poate fi

descompusă

• [X|Y] = [[a, [b, c]],d] • [X|Y] = [[a, [b, c]],d] ▫ întoarce Yes şi X = [a,[b, c]] şi Y = [d]

• [X|Y] = [a] ▫ întoarce Yes şi X = a, Y = []

Unificarea la liste

• [a, b | X] = [A, B, c]

10/56

• [a, b] = [b, a]

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

• [a, [b, c]] = [a, b, c]• [a, [b, c]] = [a, b, c]

• [a, X] = [X, b]

Unificarea la liste

• [a | []] = [X]

11/56

• [a, b, X, c] = [A, B, Y]

• [H | T] = [[a, b], [c, d]]

• [[X],Y] = [a, b]• [[X],Y] = [a, b]

Afisarea elementelor unei liste• Pentru afişarea elementelor unei liste vom face

apel la recursivitate.

12/56

apel la recursivitate.

• Luăm elementele din listă, unul câte unul, şi leafişăm.

• Momentul de oprire a algoritmului este atuncicând lista va fi goală.când lista va fi goală.

• Aşadar, condiţia elementară (de oprire arecursivităţii) va fi când lista e vidă.

Afisarea elementelor unei liste

Adauga trei spatii

13/56

afis([]).afis([Prim | Rest]) :- write(Prim), tab(3),

afis(Rest).

spatii

Apartenenta unui element la o lista

Are 2 argumente

14/56

• Vom defini predicatul apartine/2, unde primulargument reprezintă elementul pentru careverificăm apartenenţa, iar al doilea este lista.

• X aparţine listei dacă este capul listei sau dacă

argumente

• X aparţine listei dacă este capul listei sau dacăaparţine coadei acesteia.

Apartenenta unui element la o lista

• Altfel spus, clauza care testeaza unificarea dintre elementuldat si capul listei reprezintă condiţia de oprire a recursivităţii,

15/56

dat si capul listei reprezintă condiţia de oprire a recursivităţii,clauza care se verifică la fiecare reapelare a predicatuluiapartine.

apartine(X, [X | _]).apartine(X, [Y | Rest]) :- apartine(X, Rest).

?- apartine (3, [1, 3, 2]).?- apartine (3, [1, 3, 2]).Yes.

?- apartine (4, [1, 3, 2]).No.

Apartenenta unui element la o lista

16/56

Numarul elementelor dintr-o lista

• Dacă lista este vidă, numarul elementelor sale

17/56

• Dacă lista este vidă, numarul elementelor saleeste zero: aceasta este condiţia de oprire arecursivităţii.

• În clauza recursiva, primul element din listă nune interesează, vrem doar să îl eliminăm ca sănumărăm câte elemente are lista rămasă.numărăm câte elemente are lista rămasă.

• N-ul curent va fi, de fiecare data, egal cu 1 plusnumărul elementelor din lista rămasă.

Numarul elementelor dintr-o lista

18/56

nr_elem([], 0). nr_elem([_ | Rest], N) :- nr_elem(Rest, N1), N is

N1 + 1.

?- nr_elem([1, 2, 3], X).

X = 3

Suma elementelor dintr-o lista• Dacă lista este vidă, suma elementelor sale este

zero: aceasta este condiţia de oprire a

19/56

zero: aceasta este condiţia de oprire arecursivităţii.

• În clauza recursiva, primul element din listă neinteresează de data aceasta, dupa care calculamsuma elementelor din lista rămasă.

• S-ul curent va fi, de fiecare data, egal cuelementul curent plus suma elementelor dinlista rămasă.

Suma elementelor dintr-o lista

20/56

suma([], 0).suma([P|Rest], S) :- suma(Rest, S1), S is S1 + P.

?- suma([1, 2, 3], X).?- suma([1, 2, 3], X).

X = 6

Media elementelor unei liste

• Media unei liste se calculeaza drept suma elementelor din lista / numarul acestora.

21/56

elementelor din lista / numarul acestora.

media(L) :- nr_elem(L, N), suma(L, S), Media is S/N, write('Media este '), write(Media).

?- media([1, 2, 3]).?- media([1, 2, 3]).

Media este 2.

Ultimul element dintr-o lista

22/56

• Condiţia de oprire este ca X să fie ultimul element din listă – coada listei este o listă vidă.

• Daca restul listei curente nu este vid, ignoram capul listei.capul listei.

Ultimul element dintr-o lista

ultim([X | []], X).

23/56

Echivalent putem scrie ultim([X],X).

ultim([X | []], X).ultim([_ | Rest], X) :- ultim(Rest, X).

?- ultim([1, 2, 3], X).

X = 3X = 3

Maximul unei liste

• Consideram primul element al listei ca fiind maximul.

24/56

• Apelam un alt predicat ce are drept argumente lista ramasa si elementul considerat.

• Parcurgem restul listei; daca gasim un element (capul listei curente) mai mare decat maximul, acesta va deveni noul maxim.

• Altfel, mergem mai departe in restul listei.

• Recursivitatea se incheie cand ajung la lista vida si afisez argumentul corespunzator maximului.

Maximul unei liste

max([P|Rest]) :- Max = P, max1(Rest, Max).

25/56

max1([], Max) :- write('Maximul este '), write(Max).

max1([P|R], Max) :- P > Max, max1(R, P); max1(R, Max).

?- max([4, 2, 5, 1]).Maximul este 5.

Pozitia pe care se afla maximul unei liste

26/56

• Consideram primul element al listei ca fiindmaximul si stabilim pozitia maximului drept 1.

• Apelam un alt predicat ce are drept argumente:▫ lista ramasa▫ elementul considerat drept maxim▫ elementul considerat drept maxim▫ pozitia pe care se afla acesta▫ si un contor care va numara elementele.

Pozitia pe care se afla maximul unei liste

• Parcurgem lista; daca gasim un element (capul noii

27/56

• Parcurgem lista; daca gasim un element (capul noiiliste) mai mare decat maximul:▫ acesta va deveni noul maxim▫ pozitia pe care se afla maximul devine contorul curent▫ si se incrementeaza contorul.

• Altfel, mergem mai departe in restul listei,incrementand contorul.incrementand contorul.

• Recursivitatea se incheie cand ajung la lista vida siafisez argumentul corespunzator pozitiei pe care seafla maximul.

Pozitia maximului unei liste

poz_max([P|Rest]) :- poz_max(Rest, P, 1, 1).

28/56

poz_max([P|Rest]) :- poz_max(Rest, P, 1, 1).

poz_max([], _, _, Poz) :- write('Maximul se gaseste pe pozitia '), write(Poz).

poz_max([P|R], Max, Contor, Poz) :- Contor1 is Contor + 1, Max < P, poz_max(R, P, Contor1, Contor1).

poz_max([_|R], Max, Contor, Poz) :- Contor1 is Contor + 1, poz_max(R, Max, poz_max(R, Max,

Contor1, Poz).

?- poz_max([4, 2, 5, 1]).Maximul se gaseste pe pozitia 3

Compararea lungimilor a doua liste• Predicatul va avea ca

argumente doua liste si va

29/56

argumente doua liste si vaintoarce unul din urmatoareleraspunsuri posibile:▫ Liste egale

▫ Prima este mai lunga▫ A doua este mai lunga▫ A doua este mai lunga

• Se parcurg cele doua liste, ignorand capetele, pana una din ele se termina.

Compararea lungimilor a doua liste

compar([],[]):-write('Cele doua liste au acelasi

30/56

compar([],[]):-write('Cele doua liste au acelasinumar de elemente.').

compar(_L, []):-write('Prima lista are mai multeelemente decat cea de a

doua!').compar([], _L):-write('A doua lista are mai multecompar([], _L):-write('A doua lista are mai multe

elemente decat prima!').compar([_X|R1], [_Y|R2]) :- compar(R1, R2).

Compararea lungimilor a doua liste?- compar([1,2,3], [4, 5]).

31/56

Prima lista e mai lunga

?- compar([1,2], [4, 5]).

Cele doua liste sunt egale

?- compar([1,2], [3, 4, 5]).

A doua lista e mai lunga

Inversarea unei liste

• Se apeleaza un predicat care, pe langa listainitiala si lista in care depunem rezultatul,

32/56

initiala si lista in care depunem rezultatul,contine si o lista temporara care este initial vida.

• Capul listei curente se adauga la inceputul listeitemporare – acesta era initial goala, decielementele se vor adauga in ordine inversa.elementele se vor adauga in ordine inversa.

• Cand lista care trebuie inversata devine vida,unificam lista finala cu cea temporara.

Inversarea unei liste

inv(L, Linv) :- inv1(L, [], Linv).

33/56

inv(L, Linv) :- inv1(L, [], Linv).

inv1([], L, L).inv1([X|Rest], Temp, L) :- inv1(Rest, [X|Temp],

L).

?- inv([1, 2, 3], L).L = [3, 2, 1]

Interclasarea a doua liste

• Ce presupune interclasarea?

34/56

• Ce presupune interclasarea?

• Avem doua liste care trebuie unite intr-una singura.

• Cele doua liste trebuie sa fie ordonate crescator.• Cele doua liste trebuie sa fie ordonate crescator.

• Elementele listei rezultate trebuie sa fie de asemenea in ordine crescatoare.

Interclasarea a doua liste

• Capetele celor doua liste ce trebuie unite se compara.

35/56

compara.

• Cel mai mic dintre ele se va adauga la lista rezultat.

• Daca sunt egale, se adauga doar o data.• Daca sunt egale, se adauga doar o data.

• Daca una dintre ele este vida, lista rezultat este cealalta.

Interclasarea a doua listeinterclasez([], L, L).interclasez(L, [], L).

36/56

interclasez(L, [], L).interclasez([P1|R1], [P2|R2], [P1|R3]) :- P1 < P2,

interclasez(R1, [P2|R2], R3).interclasez([P1|R1], [P1|R2], [P1|R3]) :-

interclasez(R1, R2, R3).

interclasez(R1, [P2|R2], [P2|R3]) :- interclasez(R1, interclasez(R1, [P2|R2], [P2|R3]) :- interclasez(R1,

R2, R3).

?- interclasez([1, 3, 7], [2, 3, 4, 8], L).L = [1, 2, 3, 4, 7, 8]

Prefixul si sufixul unei liste• Prefixul unei liste reprezinta primele N elemente

ale unei liste.

37/56

ale unei liste.

• Sufixul unei liste reprezinta ultimele M elementeale unei liste.

• Prefixul ar putea fi asociat cu primele slide-uridin lista care formeaza acest curs.din lista care formeaza acest curs.

• Iar sufixul… ultimele slide-uri (sufixul e maifrumos)

Prefixul unei liste• Pentru a testa daca o lista e prefixul altei liste,

compar element cu element cele doua liste.

38/56

compar element cu element cele doua liste.

• Adica, verific daca elementul cap al unei listeprefix este egal cu cel al listei complete.

• Daca raspunsul este afirmativ, merg maideparte.departe.

• Prima lista e prefix a celei de-a doua daca, la unmoment dat, lista prefix se incheie.

Prefixul unei liste

prefix([], _L).

39/56

prefix([], _L).prefix([X|R1], [X|R2]) :- prefix(R1, R2).

?- prefix([1,2], [1, 2, 3]).Yes

?- prefix([1,3], [1, 2,3]).No

Sufixul unei liste

• Pentru a testa daca o lista e sufixul altei liste,parcurg lista completa pana intalnesc exact lista

40/56

parcurg lista completa pana intalnesc exact listasufix.

• Adica, scot elementul cap al listei mari, panacand cele doua liste sunt egale.

• Recursivitatea se opreste deci cand cele douaargumente sunt egale.

Sufixul unei liste

sufix(L, L).

41/56

sufix(L, L).sufix(L, [_Y|Rest]) :- sufix(L, Rest).

?- sufix([1,2,3],[1,2]).No

?- sufix([1, 2, 3], [3]).Yes

Sublista unei liste

• O lista va fi sublista unei alte liste daca este

42/56

• O lista va fi sublista unei alte liste daca este sufixul prefixului listei mari.

Prefixul listei mari

Sufixul prefixului listei mari

Sublista unei listesublista(L1, L2):-prefix(L, L2), sufix(L1, L).

?- sublista([1,2], [3,1,2,5,6]).

43/56

?- sublista([1,2], [3,1,2,5,6]).Yes?- sublista([1,2], [3, 1, 4, 5, 2])No

L2

L

L1

Pozitii pare, pozitii impare9 0 3 2 4 5 6 7

44/56

• Enunt problema:

▫ Se dă o listă: să se obţină două liste din aceasta astfel încât prima din ele să conţină elementele de pe poziţiile pare iar a doua pe cele de pe poziţiile impare.

• Vom avea asadar o singura lista ca argument de intrare si doua liste ca argumente de iesire.

• Tratam intai situatia in care lista data contine un singur element, apoi cand lista are doua elemente.

parimpar([X], [], [X]).

Pozitii pare, pozitii impare9 0 3 2 4 5 6 7

45/56

parimpar([X], [], [X]).parimpar([X, Y],[Y], [X]).parimpar([X, Y|R], [Y|R1], [X|R2]) :- parimpar(R, R1, R2).

• In cea de a treia clauza, mutam primul element X din lista data in lista a treia, iar pe cel de-al doilea, Y, in a doua lista.

• Interogare:• Interogare:▫ ? – pare([ion, marius, mananca, invata, mere, prolog], P, I). P = [marius, invata, prolog] I = [ion, mananca, mere]

parimpar([X], [], [X]).

Pozitii pare, pozitii impare9 0 3 2 4 5 6 7

46/56

parimpar([X], [], [X]).parimpar([X, Y],[Y], [X]).parimpar([X, Y|R], [Y|R1], [X|R2]) :- parimpar(R, R1, R2).

• Cea de a treia clauza a programului poate fi scrisa sub forma urmatoare:

parimpar([X, Y|R], Pare, Impare):-parimpar(R, Pare1, Impare1), Pare = [Y|Pare1], Impare=[X|Impare1].

Pozitia i dintr-o lista

• Enuntul problemei:

47/56

• Enuntul problemei:

▫ Dându-se o listă şi un număr întreg pozitiv i, să se găsească elementul aflat pe poziţia i în listă.

• Avem doua argumente de intrare, o lista si un numar care da pozitia care ne intereseaza.

• Cum rezolvam problema: scadem i-ul cu cate o unitate si, in acelasi timp, scoatem cate un element din lista. Cand i-ul este 1, primul element din lista este cel cautat.

Pozitia i dintr-o lista

pozi([X|_], 1, X).

48/56

pozi([X|_], 1, X).pozi([_A|R], I, X) :- I1 is I - 1, pozi(R, I1, X).

• Asadar, al treilea argument al predicatului pozi ia valoarea primului element din lista atunci cand al doilea argument este egal cu 1.

• Altfel, i este scazut, iar din lista data ca primul argument • Altfel, i este scazut, iar din lista data ca primul argument extragem un element la apelarea recursiva.

• Interogarea:▫ ? - pozi([mere, portocale, pere, gutui], 2, Ce). Ce = portocale

Pozitia unui element intr-o lista1 4 6 7 8 9 0 3 2 4 5 6 7

49/56

• Enunt problema:▫ Având date o listă şi un element care aparţine

acestei liste, să se specifice pe ce poziţie este situat elementul în lista dată.

• Avem doua argumente de intrare:▫ Lista in care se gaseste elementul▫ Lista in care se gaseste elementul▫ Elementul pentru care trebuie sa gasim pozitia

• Vom mai construi un predicat care sa contina si o variabila contor care este initial 1.

Pozitia unui element intr-o lista

pozx(L, X, P):- pozx(L, X, 1, P).

1 4 6 7 8 9 0 3 2 4 5 6 7

50/56

pozx(L, X, P):- pozx(L, X, 1, P).pozx([X|_], X, P, P).pozx([_|R], X, C, P) :- C1 is C + 1, pozx(R, X, C1, P).

• Predicatul pozx cu 4 argumente este vazut ca unul diferit de cel cu acelasi nume dar cu 3 argumente.

• Conditia de oprire: primul element din lista corespunde cu elementul dat. elementul dat.

▫ In acest moment, contorul se unifica cu variabila aflata pe pozitia patru.

• In concluzie, ideea este ca se scot elemente din lista pana cand pe prima pozitie se afla chiar ceea ce cautam.

Pozitia unui element intr-o lista1 4 6 7 8 9 0 3 2 4 5 6 7

51/56

pozx(L, X, P):- pozx(L, X, 1, P).pozx([X|_], X, P, P).pozx([_|R], X, C, P) :- C1 is C + 1, pozx(R, X, C1, P).

• Interogarea:▫ ? – pozx([ion, petre, marin, olivia], marin, P).▫ ? – pozx([ion, petre, marin, olivia], marin, P). P = 3

Stergerea aparitiilor unui element dintr-o lista

52/56

• Enunt problema:▫ Să se şteargă toate apariţiile unui element dintr-o

listă.• Avem doua argumente de intrare:

▫ Lista din care se vor sterge aparitiile unui element▫ Elementul care trebuie sters▫ Elementul care trebuie sters

• Argumentul de iesire va fi noua lista carenu va mai contine elementul dat.

Stergerea aparitiilor unui element dintr-o lista

sterg([], _, []).

53/56

sterg([], _, []).sterg([N|Rest], N, Rez) :- sterg(Rest, N, Rez).sterg([M|Rest], N, [M|Rez]) :- sterg(Rest, N, Rez).

• Se parcurge lista data element cu element si, daca elementul aflat in capul listei este chiar cel cautat, nu se copiaza in lista rezultat. Altfel, se copiaza.

• Atunci cand lista data este vida, si lista rezultat este initializata cu • Atunci cand lista data este vida, si lista rezultat este initializata cu lista vida.

• Interogare:▫ ? – sterg([1, 4, 6, 8, 6, 12, 6], 6, L). L = [1, 4, 8, 12]

Eliminarea duplicatelor

• Enunt problema:

54/56

• Enunt problema:▫ Scrieţi un predicat Prolog care să realizeze

eliminarea duplicatelor dintr-o listă dată.• Argument de intrare:

▫ O lista data• Argument de iesire:

▫ Lista rezultata prin eliminarea duplicatelor din ▫ Lista rezultata prin eliminarea duplicatelor din lista data.

• Vom face uz de predicatul apartine/2 prezentat mai devreme.

Eliminarea duplicatelor

duplicate([], []).

55/56

duplicate([], []).duplicate([X|R1], L) :- apartine(X, R1), duplicate(R1, L).duplicate([X|R1], [X|R2]) :- duplicate(R1, R2).

• Luam fiecare element din prima lista si verificam daca apartine restului listei (adica daca mai apare in lista).▫ Daca nu mai apare, atunci il adaugam in lista rezultat▫ Altfel, nu il adaugam.▫ Altfel, nu il adaugam.

• Interogare▫ ? – duplicate([7, 9, 7, 11, 11], L). L = [9, 7, 11]

Pe saptamana viitoare!

56/56

Pe saptamana viitoare!

Structuri compuse in Prolog

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Ce sunt structurile?

2/41

• Cea mai mare parte din informaţia pe care vrem să o reprezentăm într-un program este compusă, adică ea constă din entităţi care au mai multe atribute diferite

▫ De exemplu, entitatea persoană poate avea mai multe atribute precum vârstă, înăltime, greutate etc.

• În programele realizate până acum, am utilizat numai fapte şi • În programele realizate până acum, am utilizat numai fapte şi reguli care foloseau structuri de date simple.

▫ Argumentele predicatelor folosite au fost atomi sau numere.

• Aceste date simple pot însă fi combinate pentru a construi tipuri de date complexe numite structuri.

• O structură constă dintr-un functor şi un număr fix

3/41

Ce sunt structurile?

• O structură constă dintr-un functor şi un număr fix de argumente:

structura(arg1, arg2, …, argn)

• Fiind un termen, structura poate să apară în cadrul unei clauze oriunde poate apărea o variabilă sau o constantă.

• Precum toţi ceilalţi termeni folosiţi anterior, nici structurile nu trebuie declarate▫ Le folosim direct în cadrul programului, acolo unde

avem nevoie de ele.

4/41

Ce sunt structurile?

• Deşi arată precum predicatele, nu funcţionează ca acestea.

▫ Predicatele sunt relatii, iar structurile sunt obiecte.

▫ Diferenţa dintre cele două este realizată de către Prolog după locul în care apar într-o clauză. Prolog după locul în care apar într-o clauză.

• Structurile nu apar niciodată singure, ci doar ca argumente pentru predicate.

Exemplu de utilizare a unor structuri

5/41

student(adrian, prezente(8), proiect(bun)).student(marius, prezente(2), proiect(copiat)).student(andreea, prezente(9), proiect(bun)).student(ovidiu, prezente(4), proiect(slab)).

• Daca vrem sa aflam care sunt studentii care pot sa ia o nota • Daca vrem sa aflam care sunt studentii care pot sa ia o nota peste 7, putem folosi interogarea:▫ ? – student(X, prezente(Y), proiect(bun)), Y > 6, writeln(X), fail.

adrianandreea

Exemplu de utilizare a unor structuri

6/41

student(adrian, prezente(8), proiect(bun)).student(marius, prezente(2), proiect(copiat)).student(andreea, prezente(9), proiect(bun)).student(ovidiu, prezente(4), proiect(slab)).

• Daca vrem sa aflam care sunt studentii care nu vor intra in • Daca vrem sa aflam care sunt studentii care nu vor intra in examen, putem folosi interogarea:▫ ? – student(X, prezente(_Y), proiect(copiat)), writeln(X), fail.

mariusUnderline-ul indică faptul cănu suntem interesaţi devaloarea cu care este unificatacest câmp.

7/41

Alt exemplu de utilizare a unor structuri

• O structura poate avea si mai mult de un singur argument (cum a fost cazul in exemplul precedent).

• Exemplu:

are(ionut, calculator(asus, ‘3 Ghz’, ‘RAM 1 GB’)).are(ionut, calculator(asus, ‘3 Ghz’, ‘RAM 1 GB’)).

are(ovidiu, calculator(hp, ‘2.7 Ghz’, ‘RAM 512 MB’)).

are(ovidiu, calculator(dell, ‘2.4 Ghz’, ‘RAM 512 MB’)).

Arbori binari• Un arbore binar are proprietatea că pentru un nod

părinte: ▫ fiecare nod aflat în partea stângă a sa are o valoare numerică

8/41

▫ fiecare nod aflat în partea stângă a sa are o valoare numerică mai mică decât a sa şi

▫ fiecare nod aflat în partea dreaptă a nodului părinte are o valoare mai mare decât a sa.

8

4 11

3 6 9 16

Arbori binari

• Pentru reprezentarea în Prolog, presupunem că fiecare nod are câte două legături către alţi arbori:

9/41

câte două legături către alţi arbori: ▫ una către subarborele stâng ▫ una către subarborele drept

arb(8, arb(4, arb(3, n, n), arb(6, n, n)), arb(11, arb(9, n, n), arb(16, n, n)))

Se reprezinta asadar sub forma

8

asadar sub forma unei structuri

compuse. 4 11

3 6 9 16

Arbori binari• Scrieţi un predicat Prolog care să calculeze suma

elementelor arborelui.

10/41

elementelor arborelui.

suma(n, 0).suma(arb(R, n, n), R).suma(arb(Radacina, S, D), Suma) :- suma(S, S1),

suma(D, S2), Suma is Radacina + S1 + S2.

Arbori binari• Scrieţi un predicat Prolog care să verifice

existenta unui număr dat într-un arbore binar.

11/41

existenta unui număr dat într-un arbore binar.

cauta(n, _):- write(‘Nu exista.’).cauta(arb(X, _, _), X):- write(‘Numarul exista.’).cauta(arb(Rad, S, _D), X) :- X < Rad, cauta(S, X).cauta(arb(_Rad, _S, D), X) :- cauta(D, X).cauta(arb(_Rad, _S, D), X) :- cauta(D, X).

Arbori binari• Scrieţi predicate Prolog care să realizeze parcurgerea

unui arbore binar în preordine, în inordine şi în

12/41

unui arbore binar în preordine, în inordine şi în postordine.

• Preordine: 1. Vizitam radacina.2. Vizitam subarborele stang in preordine.3. Vizitam subarborele drept in preordine.3. Vizitam subarborele drept in preordine.

• Parcurgerea:▫ 8, 4, 3, 6, 11, 9, 16.

Parcurgerea in preordine

13/41

• preord(n).• preord(arb(Rad, S, D)) :-writeln(Rad),

preord(S), preord(D).

Parcurgerea in inordine• Inordine:

1. Vizitam subarborele stang in inordine.

14/41

1. Vizitam subarborele stang in inordine.2. Vizitam radacina.3. Vizitam subarborele drept in inordine.

• Parcurgerea:▫ 3, 4, 6, 8, 9, 11, 16

Parcurgerea in postordine• Postordine:

1. Vizitam subarborele stang in postordine.

15/41

1. Vizitam subarborele stang in postordine.2. Vizitam subarborele drept in postordine.3. Vizitam radacina.

• Parcurgerea:▫ 3, 6, 4, 9, 16, 11, 8

Inordine si postordine?

16/41

• Cum se implementeaza in Prolog?

▫ Solutia este triviala si ramane ca tema.

Inserarea unui element intr-un arbore binar

17/41

• Realizaţi un predicat Prolog care să realizeze inserarea unui element in cadrul arborelui.

Unde este locul

meu?...8

4 11

3 6 9 16

10

Inserarea unui element intr-un arbore binar

• Realizaţi un predicat Prolog care să realizeze inserarea unui element in cadrul arborelui.

18/41

inserarea unui element in cadrul arborelui.

8

4 11

3 6 9 16

10

Inserarea unui element intr-un arbore binar

19/41

ins(Val, n, arb(Val, n, n)).ins(Val, arb(Rad, L_T, R_T), Rez) :- Val < Rad,

ins(Val, L_T, Rez1), Rez = arb(Rad, Rez1, R_T).ins(Val, arb(Rad, L_T, R_T), Rez) :- Val > Rad,

ins(Val, R_T, Rez1), Rez = arb(Rad, L_T, Rez1).

Intrari si iesiri in PrologIntrari si iesiri in Prolog

Altfel spus, cum citim dintr-un fisier si cum scriem Altfel spus, cum citim dintr-un fisier si cum scriem intr-unul folosind Prologul.

Intrari si iesiri in Prolog

• Orice sursă sau destinaţie de date este numită în

21/41

• Orice sursă sau destinaţie de date este numită în Prolog stream (canal de intrare, sau de ieşire).

• Cele mai utilizate predicate pentru citirea şi scrierea datelor sunt, evident, read şi write.

Vom folosi si altele candvom invata sa lucram cuvom invata sa lucram cucaractere si string-uri inProlog.

Intrari si iesiri in Prolog

22/41

• Atat write/1 cat si read/1 au un singur argument şi folosesc canalul curent de ieşire, respectiv de intrare.

▫ Cele predefinite sunt ecranul şi tastatura. ? – read(X), write(‘Am citit termenul’), tab(1),

write(X).write(X).

▫ La citire, dupa scrierea termenului care trebuie citit, se pune punct (.).

• Pentru a citi dintr-un fişier nu trebuie decât să

Intrari in Prolog

23/41

• Pentru a citi dintr-un fişier nu trebuie decât să facem din fişierul respectiv canalul curent.

• Predicatele care fac acest lucru sunt:

▫ see(F) – fişierul dat ca argument devine fişier de intrare curent. intrare curent. Fişierul F este deschis pentru citire, iar pointerul de

fişier este poziţionat la începutul lui.

▫ seen – închide fişierul de intrare curent şi stabileşte canalul de intrare tastatura.

• Pentru a scrie într-un fişier trebuie să facem din fişierul

Iesiri in Prolog

24/41

• Pentru a scrie într-un fişier trebuie să facem din fişierul respectiv canalul curent.

• Predicatele care fac acest lucru sunt:▫ tell(F) – deschide fişierul F pentru scriere şi îl face fişier

de ieşire curent. Dacă fişierul există deja, conţinutul său este şters, altfel, fişierul

este creat.

▫ append(F) – face acelasi luru ca si tell/1, cu deosebirea ca, daca fisierul exista, continutul sau nu este sters, ci se scrie in continuare.

▫ told – închide fişierul de ieşire curent stabilind ca stream de ieşire ecranul.

Intrari/iesiri in Prolog

• Predicatul read(-Term) citeşte în variabila Term următorul

25/41

• Predicatul read(-Term) citeşte în variabila Term următorul termen din fişierul de intrare. ▫ Termenul citit trebuie să se termine cu caracterul punct în

cadrul fişierului.

• Există o constantă specială în Prolog, end_of_file, care este returnată atunci când s-au citit toate datele dintr-un fişier.

• Un fişier Prolog poate fi încărcat din interiorul unui alt • Un fişier Prolog poate fi încărcat din interiorul unui alt fişier, cu ajutorul predicatului consult(NumeFisier).

• nl provoacă trecerea la o linie următoare (new line), iar tab(N) adaugă N spaţii faţă de poziţia la care este situat pointerul în canalul de ieşire curent.

Exemplu

26/41

• Avem fişierul de intrare in.txt care conţine câte un numărurmat de caracterul punct pe fiecare linie. Scrieţi în fişierulpare.txt numerele conţinute în in.txt care sunt pare, iar înimpare.txt numerele care sunt impare.

Separarea elementelor pare de cele impare

27/41

separ([], [], []).separ([P|R1], L2, [P|R2]) :- Rest is P mod 2, Rest = 1,

separ(R1, L2, R2).separ([P|R1], [P|R2], L2) :- separ(R1, R2, L2).

Afisarea elementelor unei liste

28/41

afis([]).afis([P|R]) :- write(P), nl, afis(R).

Citirea din in.txt si scrierea in pare.txtsi impare.txt

29/41

exemplu :- see('in.txt'), citesc([]), seen, write('Totul este gata').

citesc(L) :- read(N), N \= end_of_file, append(L, [N], Rez), citesc(Rez).

citesc(L) :- separ(L, Pare, Impare), pare(Pare), impare(Impare).citesc(L) :- separ(L, Pare, Impare), pare(Pare), impare(Impare).

pare(L) :- tell('pare.txt'), afis(L), told.impare(L) :- tell('impare.txt'), afis(L), told.

Rularea programului

30/41

• Scriem mai intai intr-un fisier text cateva numere urmate de caracterul punct, fiecare aflate pe cate un rand.▫ Il salvam la aceeasi locatie unde se afla si programul.

Rularea programului

31/41

Alt exemplu

32/41

• Dacă fisierul in.txt conţine câte un număr urmat depunct pe fiecare linie, scrieţi un predicat Prolog care săintroducă în fişierul suma.txt mesajul Suma este urmatde valoarea sumei numerelor din in.txt.

• Cum se rezolva?• Cum se rezolva?▫ Avem acelasi cod de la programul precedent pentru citirea

elementelor intr-o lista.▫ Se calculeaza apoi suma elementelor din lista si se scrie

aceasta in fisierul suma.txt.

Sau… gasim o alta rezolvare

33/41

suma:- see('in.txt'), tell('suma.txt'), calc(0), told, seen.

calc(S) :- read(N), N \= end_of_file, S1 is S + N, calc(S1).calc(S) :- write('Suma este '), write(S).

Selectati numerele prime

34/41

• Având fişierul in.txt dat ca in exemplele precedente, realizaţi un predicat Prolog care să scrie în fişierul prime.txt numai acele numere care sunt prime. ▫ Citim toate numerele intr-o lista, ca la exemplul

initial (predicatul citesc/1), apoi selectam initial (predicatul citesc/1), apoi selectam numerele prime intr-o lista pe care o afisam apoi in fisierul tinta. Simplu, nu?

Verificarea daca un numar este prim

35/41

prim(N) :- prim(N, 2).

prim(N, K) :- I is N/2, K > I.prim(N, K) :- R is N mod K, R \= 0, K1 is K + 1,

prim(N, K1).

Fara sa folosim o lista temporara…

prime :- see('in.txt'), tell('prime.txt'), told, calcul, seen.

calcul :- read(X), X \= end_of_file, verific(X), calcul.calcul.

verific(X) :- prim(X), append('prime.txt'), write_ln(X), verific(X) :- prim(X), append('prime.txt'), write_ln(X), told.

verific(_).

37/41

Selectati numerele prime

Ordonarea unui sir de numere

38/41

• Având în fişierul de intrare in.txt câte un număr urmat de caracterul punct pe fiecare linie, construiţi un predicat Prolog care să scrie în fişierul ordonat.txt şirul de numere ordonat crescător.▫ Citim numerele din in.txt intr-o lista, le ordonam in alta lista

si le scriem apoi in fisierul ordonat.txt.si le scriem apoi in fisierul ordonat.txt.▫ O sa facem in continuare numai ordonarea elementelor unei

liste.▫ Pentru aceasta, vom folosi metoda quicksort care utilizeaza

mecanismul divide et impera.

Ordonarea elementelor unei liste

39/41

sortez([], []).sortez([P|Rest], Lrez):- selectez(P, Rest, Mici, Mari),

sortez(Mici, MiciSort), sortez(Mari, MariSort), append(MiciSort, [P|MariSort], Lrez).

selectez(_, [], [], []).selectez(P, [P1|Rest], [P1|Mici], Mari):- P1 < P,

selectez(P, Rest, Mici, Mari).selectez(P, [P1|Rest], Mici, [P1|Mari]):- selectez(P, Rest,

Mici, Mari).

Rularea algoritmului de sortare

40/41

• Citirea elementelor dintr-un fisier si scrierea elementelor • Citirea elementelor dintr-un fisier si scrierea elementelor sortate in fisierul sortat urmeaza sa fie facute de…

Pe saptamana viitoare!

41/41

Pe saptamana viitoare!

Backtracking

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Backtracking - exemplu

• Sa consideram programul:

2/32

• Sa consideram programul:

natural(0).natural(N) :- natural(N1), N is N1 + 1.

• Ce va genera Prolog-ul in urma unui apel de• Ce va genera Prolog-ul in urma unui apel deforma:

? – natural(N), write(N), nl, sleep(1), fail.

Backtracking - exemplu

• Se va genera un ciclu infinit:▫ numerele succesive sunt generate prin

3/32

▫ numerele succesive sunt generate prinbacktracking.

• Primul număr natural este generat şi afişat.

• Apoi fail forţeaza backtracking-ul să acţioneze.• Apoi fail forţeaza backtracking-ul să acţioneze.

• A doua clauză este apelată, generând numerenaturale succesive.

Generare numere naturale

4/32

Un alt exemplu de backtracking

prefixN(N, [N]).

5/32

prefixN(N, [N]).prefixN(N, [N|L]) :- N1 is N + 1, prefixN(N1, L).

• Pentru a folosi backtrackingul, avem nevoie de un apel de forma:

? - prefixN(1, L), write(L), nl, sleep(1), fail.? - prefixN(1, L), write(L), nl, sleep(1), fail.

• Acesta generează o infinitate de liste care încep cu valoarea N.

Generare de liste incrementale care incep cu un element dat

6/32

Backtracking

• Backtracking-ul în Prolog este asadar foarte uşor

7/32

• Backtracking-ul în Prolog este asadar foarte uşor de utilizat.

• Predicatul fail/0 este cel care duce la forţarea backtrackingului: ▫ el întoarce întotdeauna un rezultat negativ şi duce ▫ el întoarce întotdeauna un rezultat negativ şi duce

la reapelarea predicatelor care se află înaintea sa, ori de câte ori este posibil.

Permutari

• Sa generam permutările muţimii {a, b, c, d}folosind backtracking-ul in acest scop.

8/32

folosind backtracking-ul in acest scop.• Mai intai definim cele 4 litere:

litera(a).litera(b).litera(c).litera(c).litera(d).

Permutari

permutare(X, Y, Z, T) :- litera(X), litera(Y), litera(Z), litera(T), X \= Y, X \=Z, Y \= Z, X \=T, Y\=T,Z \=T.

9/32

litera(T), X \= Y, X \=Z, Y \= Z, X \=T, Y\=T,Z \=T.

permutari :- permutare(X, Y, Z, T), write(X), tab(1), write(Y), tab(1), write(Z), tab(1), write(T), nl, fail.

permutari.

• Apelăm simplu, predicatul permutari:• Apelăm simplu, predicatul permutari:

? – permutari.

Permutari

• Iata permutarile rezultate:

10/32

Taietura (Cut)• Uneori însă, din cauza backtracking-ului, sunt

întoarse rezultate nedorite.

11/32

întoarse rezultate nedorite.

• Pentru evitarea acestora, Prologul ne vine înajutor, prin ceea ce se numeşte taietura.

• Cu ajutorul acestui procedeu, Prologul oferăposibilitatea opririi backtrackingului.posibilitatea opririi backtrackingului.

• Procedeul se mai numeşte cut şi se notează cusemnul exclamării (!).

Taietura (Cut)

12/32

• Tăietura face ca Prologul să blocheze toatedeciziile făcute până la momentul apariţiei sale(adică a semnului exclamării).

• Asta înseamnă că, dacă backtracking-ul era îndesfăşurare, el va fi oprit şi nu se vor mai căutadesfăşurare, el va fi oprit şi nu se vor mai căutaalte alternative pentru ce se găseşte înainte detăietură (!).

Exemplulocul(1, simion).locul(2, maria).

13/32

locul(2, maria).locul(3, sorin).locul(3, cristian).

? – locul(3, Nume), write(Nume), nl, fail.sorincristiancristian

? – locul(3, Nume), !, write(Nume), nl, fail.sorin

Mai adaugam:despre(1, ' este castigatorul

concursului.').

14/32

? – spune(3).concursului.').despre(1, ' primeste 100$.').despre(2, ' a câştigat locul II.').despre(2, ' primeste 50$.').despre(3, ' a castigat locul III').despre(3, ' primeste 25$.').spune(Loc) :- locul(Loc, Cine),

? – spune(3).sorin

a castigat locul IIIprimeste 25$.

cristiana castigat locul IIIprimeste 25$.spune(Loc) :- locul(Loc, Cine),

write(Cine), nl, despre(Loc, Despre), tab(3), write(Despre), nl, fail.

primeste 25$.

Cut• Dacă vrem însă să ştim care este primul dintre

cei care au luat locul trei şi câteva lucruri despre

15/32

cei care au luat locul trei şi câteva lucruri despreel, avem nevoie de tăietură - adăugăm lapredicatul spune/1 semnul exclamării astfel:

spune(Loc) :- locul(Loc, Cine), write(Cine), nl, !,despre(Loc, Despre), tab(3), write(Despre), nl,fail.fail.

• Acum, după adăugarea făcută, backtrackingul seva aplica numai asupra predicatelor aflate dupasemnul exclamării.

Taietura• Tăietura se foloseşte de obicei în interiorul unui

predicat unde Prolog-ul a găsit primul răspuns

16/32

predicat unde Prolog-ul a găsit primul răspuns posibil despre care se doresc mai multe detalii.

• Sau pentru a forţa un predicat să întoarcă un răspuns negativ (adică fail) într-o anumită situaţie şi nu vrem să caute alte soluţii.

• Tăietura însă, în general, măreşte complexitatea codului decât să o simplifice, iar utilizarea ei este codului decât să o simplifice, iar utilizarea ei este bine să fie evitată pe cât posibil: ▫ se spune chiar că ea reprezintă goto-ul

programării logice.

1

32

4 5 6 7

Arbori si grafuri

1

2 4

3

5

7

6 8

9

Arbori

18/32

1

32

• Cum reprezentam acest arbore?

4 5 6 7

Arbori

% tata(X,Y) - X este tatal lui Y

19/32

1

32

tata(0,1).tata(1,2).tata(1,3).tata(2,4).tata(2,5).

4 5 6 7

Aceasta confirma faptul ca 1 este radacina arborelui.

tata(2,5).tata(2,6).tata(3,7).

1 este radacina arborelui.

Parcurgere in adancime

20/32

1

32

• Care ar fi parcurgerea in adancime a acestui arbore?

4 5 6 7

• 1, 2, 4, 5, 6, 3, 7

Parcurgere in adancime

21/32

Gaseste toti X care au o anumita relatie cu un Y dat; acestia

se pun in lista L

1

32

findall(X, relatie(X,Y), L)

parcurgere:-tata(0, Rad), p([Rad]).

se pun in lista L

4 5 6 7

p([]).p([Nod|Rest]) :- write(Nod), tab(2), findall(D,

tata(Nod, D), LC), append(LC, Rest, Stiva), p(Stiva).

Parcurgere in adancime

22/32

?-parcurgere.

1

32?-parcurgere.

4 5 6 7

Parcurgere in latime

23/32

1

32

• Care ar fi parcurgerea in latime a acestui arbore?

• 1, 2, 3, 4, 5, 6, 7

4 5 6 7

• 1, 2, 3, 4, 5, 6, 7

Parcurgere in latime

24/32

1

32

parcurgere_latime:-tata(0, Rad), c([Rad]).

c([]).

4 5 6 7

c([]).c([P|Rest]) :- write(P), tab(2), findall(D, tata(P,

D), LC), append(Rest, LC, Coada), c(Coada).

Parcurgere in latime

?-parcurgere_latime.

25/32

1

32

4 5 6 7

Grafuri orientate

26/32

1 5 9

• Cum reprezentam acest graf?

2 4

3 7

6 8

• Cum reprezentam acest graf?

Grafuri orientate

marc(1, 2).marc(1, 4).

27/32

1 5 9

marc(1, 4).marc(1, 5).marc(3, 2).marc(3, 7).marc(4, 3).marc(5, 6).

2 4

3 7

6 8

marc(5, 6).marc(6, 8).marc(7, 6).marc(9, 8).

Grafuri orientate

28/32

1 5 9

• Sa determinam toate drumurile intre doua

2 4

3 7

6 8

• Sa determinam toate drumurile intre doua noduri ale acestui graf.

Grafuri orientate

29/32

1 5 9

drum :- write('Introduceti nodul de start: '), read(X), write('Introduceti nodul destinatie: '),

2 4

3 7

6 8

read(X), write('Introduceti nodul destinatie: '), read(Y), drum(X, Y, L), write_ln(L), fail.

drum.

Grafuri orientate

30/32

1 5 9

drum(X, Y, L) :- drum(X, Y, [X], L).

drum(X, X, L, L).

2 4

3 7

6 8

drum(X, X, L, L).drum(X, Y, L, Lista) :- marc(X, Z),

not(member(Z, L)), append(L, [Z], L1), drum(Z, Y, L1, Lista).

Grafuri orientate

?- drum.

31/32

1 5 9

2 4

3 7

6 8

Pe saptamana viitoare!

32/32

Pe saptamana viitoare!

Caractere şi stringuri

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Scrierea şi citirea caracterelor

• Prologul are predicate predefinite folosite

2/36

• Prologul are predicate predefinite folositepentru scrierea şi citirea câte unui caracter.

• Predicatul put/1 va scrie un singur caracter.

• Din păcate insă, argumentul trebuie să fie unîntreg care reprezintă caracterul ASCII.întreg care reprezintă caracterul ASCII.

Scrierea caracterelor

• Deci, pentru scrierea mesajului merge, ar trebui

3/36

• Deci, pentru scrierea mesajului merge, ar trebui să apelăm:

? - put(109), put(101), put(114), put(103), put(101).

• Evident însă, oricine ar prefera să folosească:• Evident însă, oricine ar prefera să folosească:

? – write(’merge’).

Scrierea caracterelor

4/36

• Dacă folosim însă ca argument la predicatulwrite/1 un mesaj scris între ghilimele:▫ ne vor fi afişate chiar valorile întregi care

reprezintă fiecare caracter în cod ASCII.

? – write(”merge”).? – write(”merge”).[109, 101, 114, 103, 101]

Scrierea caracterelor

5/36

• Având astfel lista de valori numerice, este simplu de realizat un predicat recursiv care sa scrie toate caracterele, rând pe rând.

scriestringul([]).scriestringul([P|R]) :- put(P), tab(1), scriestringul([P|R]) :- put(P), tab(1),

scriestringul(R).

Scrierea caracterelor

? - scriestringul(”merge”).

6/36

? - scriestringul(”merge”).

Citirea caracterelor7/36

• Pentru citirea unui caracter folosim predicatul get/1:▫ acesta citeşte doar primul caracter din cele

introduse.

• Poate fi introdus orice caracter.• Poate fi introdus orice caracter.

Citirea caracterelor

• La citire, nu mai trebuie să incheiem, după ce am

8/36

• La citire, nu mai trebuie să incheiem, după ce am introdus caracterul de citit, cu punct (.).

• Punctul poate fi chiar caracterul de citit.

• Dacă citirea se face dintr-un fişier, verificarea de sfârşit de fişier se face comparând valoarea dată sfârşit de fişier se face comparând valoarea dată ca argument la predicatul get/1 cu –1.

Citirea caracterelor

• Predicatul citesc/0 definit în continuare, citeşte

9/36

• Predicatul citesc/0 definit în continuare, citeşteun caracter şi afişează valoarea sacorespunzătoare în codul ASCII:

citesc :- write('Introduceti un caracter:'), get(C),nl, write(Valoarea caracterului '), put(C), write(’in cod ASCII este:’), write(C).in cod ASCII este:’), write(C).

Citirea caracterelor

?-citesc.

10/36

Predicate predefinite la stringuri

• Deja am folosit mai devreme stringuri:▫ ele sunt atomi simpli, scrişi însă între ghilimele

11/36

• Trecerea de la atomi la lista care conţinenumere întregi în cod ASCII se face prinintermediul predicatului

• string_to_list(+String, -Ascii) sau • string_to_list(+String, -Ascii) sau • string_to_list(-String, +Ascii)

String -> List

? – string_to_list(string, Valoare).

12/36

? - string_to_list(String, [99,101,118,97]).

Concatenare stringuri

• string_concat(+String1, +String2, -String3)

13/36

• string_concat este un predicat similar lui atom_concat/3, cu singura deosebire că argumentul de ieşire aici este de tipul string. ▫ De subliniat faptul că argumentele de intrare sunt ▫ De subliniat faptul că argumentele de intrare sunt

date ca atomi.

Concatenare stringuri

? - string_concat(campu, lung, Tot).

14/36

?- string_concat(campu, Ce, campulung).

Lungime sir de caractere

• string_length(+String, -Lungime)

15/36

? - string_length(‘un string mare de tot’, M).

? - string_length(altceva, M).

String <-> Atom

• string_to_atom(+String, -Atom) sau • string_to_atom(-String, +Atom)

16/36

?- string_to_atom("string de transformat", Atom).

?- string_to_atom(String, exemplu).?- string_to_atom(String, exemplu).

Determinarea subsirului unui sir• sub_string(+String, +Start, +Lungime, -Rest, -

Substring)

• Primul argument este sirul de caractere din care

17/36

• Primul argument este sirul de caractere din care extragem un subşir.

• Start este un întreg pozitiv care dă poziţia de la care selectăm subşirul.

• Al treilea argument dă lungimea subşirului.

• Rest este tot un întreg pozitiv care spune câte poziţii mai sunt până la sfârşitul şirului iniţial.

• Iar ultimul argument reprezintă chiar subşirul căutat.

Determinarea subsirului unui sir

?- sub_string('Alin e tare nebun', 0, 11, Rest, Valoare).

18/36

?- sub_string('Alin e tare nebun', _, 5, 0, Valoare).

Determinarea subsirului unui sir

?- sub_string('Alin e tare nebun', 5, Cat, 6, Valoare).

19/36

?- sub_string('Alin e tare nebun', Inceput,Cat,Rest,nebun).

Determinarea subsirului unui sir

• De subliniat că şi în cazul acestui predicat, ca şi la string_concat/3:▫ argumentele de intrare sunt atomi

20/36

▫ argumentele de intrare sunt atomi▫ doar cel de ieşire reprezentând un string.

• După cum se vede din exemple:▫ numai primul argument trebuie să fie întotdeauna

instanţiatinstanţiat▫ celelalte pot fi calculate.

Transformare data si ora in string21/36

• convert_time(+Timp, -Valoare)

• Primul argument poate fi obţinut prin apelarea predicatului get_time/1: obţinem astfel data şi ora curente.

Transformare data si ora in string

?- get_time(Timp), convert_time(Timp, Data), nl, write('Data este '), write(Data).

22/36

Transformare data si ora in string23/36

• Pentru manevrarea mai uşoară a datei şi orei, mai există şi predicatul

• convert_time(+Timp, -Anul, -Luna, -Ziua, -Ora, -Minute, -Secunde, -Milisecunde).

Transformare data si ora in string

?- get_time(Timp), convert_time(Timp, An, Luna, Zi, Ora, Minute, Secunde, Milisec).

24/36

Palindrom

• Verificaţi cu ajutorul unui predicat dacă un

25/36

• Verificaţi cu ajutorul unui predicat dacă un cuvânt dat este palindrom.

• Un palindrom este un cuvânt care are aceeaşi formă, fie că este citit de la stânga, fie de la dreapta.

• Exemple: capac, lupul, cojoc, rar.

Palindrom

palindrom :- write('Introduceti cuvantul de

26/36

palindrom :- write('Introduceti cuvantul de verificat:'), read(Cuvant), string_to_list(Cuvant, Lista), palindrom(Lista).

palindrom(Lista) :- invers(Lista, Lista), write('Da, cuvantul este un palindrom!').

palindrom(_) :- write('Cuvantul nu este un palindrom(_) :- write('Cuvantul nu este un palindrom!').

Inversarea unei liste - recapitulare

invers(L1, L2) :- inv(L1, [], L2).

27/36

invers(L1, L2) :- inv(L1, [], L2).

inv([], L, L).inv([X|R], Lt, L) :- inv(R, [X|Lt], L).

Palindrom

28/36

Vocale

• Având un text introdus în fişierul intrare.txt, să se numere câte vocale se găsesc în acesta.

29/36

se numere câte vocale se găsesc în acesta.

• Pregatesc fisierul intrare.txt.

• Il deschid in citire.

• Colectez caracter de caracter.• Colectez caracter de caracter.▫ Daca acela care este curent este membru in

multimea data de vocale, il numar;▫ Altfel, parcurgem fisierul mai departe.

Vocale

vocale :- see('intrare.txt'), numar(0), seen.

30/36

numar(N) :- get(Caracter), Caracter \= -1, verific(Caracter, N, N1), numar(N1).

numar(N) :- write('Am gasit '), write(N), write(' vocale.').

verific(Caracter, N, N1) :-verific(Caracter, N, N1) :-string_to_list(aeiouAEIOU,Lista), member(Caracter, Lista), N1 is N + 1.

verific(_, N, N).

Vocale

31/36

Permutari de caractere

32/36

• Având un sir de caractere introdus de la consolă, să se genereze şi să se afişeze toate permutările de caractere posibile.

Permutari de caractere

permut :- write('Introduceti cuvantul pentru care generam permutarea:'), read(Cuvant),

33/36

generam permutarea:'), read(Cuvant), string_to_list(Cuvant, CLista), permut(CLista).

permut(L) :- permut(L, Rez), scr(Rez), nl, fail.permut(_).

scr([]).scr([P|R]) :- put(P), scr(R).

Permutari de caractere

permut([], []).permut(L, P) :- selecteaza(X, L, Rest),

34/36

permut(L, P) :- selecteaza(X, L, Rest), permut(Rest, RestPerm), P = [X|RestPerm].

selecteaza(X, [X|R], R).selecteaza(X, [Y|R], [Y|R1]) :- selecteaza(X, R,

R1).R1).

Permutari de caractere

35/36

Pe saptamana viitoare!

36/36

Pe saptamana viitoare!

Baze de date dinamice

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Baze de date dinamice

• Este evident faptul ca un program Prolog este obază de date ce conţine predicate.

2/61

bază de date ce conţine predicate.

• Până acum, am introdus clauzele pentrupredicatele realizate de noi din interiorulprogramului.

• Prologul ne oferă însă posibilitatea să manevrămbaza de date în mod direct şi ne oferă predicatepredefinite care fac acest lucru.

Adaugare clauze

• assert(X) – adaugă clauza X ca ultima clauză a

3/61

• assert(X) – adaugă clauza X ca ultima clauză apredicatului din care face parte.

• asserta(X) – clauza X este adăugată ca primaclauză a predicatului.

• assertz(X) – acelaşi efect cu assert(X).

Retragere clauze

4/61

• retract(X) – scoate clauza X din baza de date.

• retractall(X) – sunt eliminate toate faptele sauregulile din baza de date pentru care capulclauzei este unificat cu X.

Exemplu

• Presupunem că avem următoarele fapte într-unfişier Prolog:

5/61

fişier Prolog:

copil(ionut).copil(marian).copil(simona).

• Presupunem că ei sunt introduşi în ordineavârstei lor.

Apelare

• Cu un apel de forma:

6/61

• Cu un apel de forma:

? - copil(C).

• obţinem, evident, C = ionut. • Pentru:

? - copil(C), write(C), nl, fail.

• vor fi afişaţi toţi copiii găsiţi.

Adaugare clauze

• Dacă se iveşte un nou copil, şi anume, Iulia, trebuiesă avem în vedere faptul că ea este cea mai mică.

7/61

să avem în vedere faptul că ea este cea mai mică.Avem nevoie de un apel de forma:

? – asserta(copil(iulia)).

• Putem verifica acum faptul că Iulia a fost inserată înbaza de date dinamică folosind una din cele douăbaza de date dinamică folosind una din cele douăapelări de mai sus, pentru afişarea primului copil,respectiv pentru afişarea tuturor copiilor:

? - copil(C), write(C), nl, fail.

Retragere clauze

• Să luăm în continuare cazul în care Simona atrecut de vârsta la care mai poate fi numită copil.

8/61

trecut de vârsta la care mai poate fi numită copil.Pentru a o elimina din baza de date, apelăm:

? – retract(copil(simona)).

• Din nou putem verifica dacă a fost eliminată,• Din nou putem verifica dacă a fost eliminată,afişând toţi copiii:

? - copil(C), write(C), nl, fail.

Adaugare clauze

• Să presupunem, în continuare, că toatepersonajele prezentate mai sus, cât timp au fost

9/61

personajele prezentate mai sus, cât timp au fostcopii, au fost buni prieteni.

• Pentru a introduce predicatul prieteni/2 descrismai sus, chiar de la consola Prologului, folosimurmătoarea comandă:următoarea comandă:

? – assert((prieteni(X,Y):-copil(X), copil(Y))).

Retragere clauze

10/61

• Când toţi copiii au trecut de vârsta majoratului, pentru a informa şi programul nostru că ei nu mai sunt copii, vom apela:

? – retractall(copil(_)).

Exerciţiu

• Presupunem că avem următoarea schemă a unei

11/61

• Presupunem că avem următoarea schemă a uneicase:

Exerciţiu

• Definiţi:▫ legăturile dintre camere (eventual printr-un

12/61

▫ legăturile dintre camere (eventual printr-unpredicat numit uşă)

▫ locul unde se află subiectul (presupunem că iniţial▫ locul unde se află subiectul (presupunem că iniţialse află în birou).

Exerciţiu• La un moment dat, dacă se apeleaza un predicat

numit mergi/1, sistemul să spună:

13/61

numit mergi/1, sistemul să spună:▫ care este camera în care se află subiectul

▫ dacă nu poate merge în camera specificată caargument,

▫ iar dacă poate, unde poate merge mai departe deacolo.

Exemplu

?- mergi(bucatarie).Esti in birou

14/61

Esti in birouAi mers in bucatariePoti merge in:

birousufrageriesubsolsubsol

? – mergi(hol).Nu poti ajunge direct din bucatarie in hol

Rezolvare

:-dynamic aici/1.

15/61

usa(birou, hol).usa(bucatarie, birou).usa(hol, sufragerie).usa(bucatarie, subsol).usa(sufragerie, bucatarie).

Trebuie specificat faptul ca vom defini un predicat in mod

dinamic.Se declara numele predicatului

conectat(X,Y) :- usa(X,Y).conectat(X,Y) :- usa(Y,X).

aici(birou).

Se declara numele predicatului si numarul sau de argumente.

Rezolvare

mergi(Camera) :- aici(Aici), not(conectat(Aici, Camera)), write('Esti in '), write(Aici), nl, write('Nu

16/61

Camera)), write('Esti in '), write(Aici), nl, write('Nu poti ajunge din '), write(Aici), write(' direct in '), write(Camera).

mergi(Camera) :- aici(Aici), write('Esti in '), write(Aici), nl, write('Ai mers in '), write(Camera), retract(aici(Aici)), assert(aici(Camera)), nl, write('Poti merge in '), nl, unde.write('Poti merge in '), nl, unde.

unde :- aici(Aici), conectat(Aici, Camera), tab(2), write(Camera), nl, fail.

unde.

Rezultate

17/61

Alt exemplu

• Construiţi predicatul declar/1 care are ca

18/61

• Construiţi predicatul declar/1 care are ca argument o listă de numere întregi şi care realizează două alte predicate, par/1 şi impar/1care vor avea ca argumente numai numerele care sunt pare, respectiv numai elementele care sunt impare din cadrul listei.

Rezolvare

19/61

:-dynamic par/1.:-dynamic impar/1.declar([]).declar([P|R]) :- P1 is P mod 2, P1 = 0,

assert(par(P)), declar(R).assert(par(P)), declar(R).declar([P|R]) :- assert(impar(P)), declar(R).

Rezultate

20/61

Functii de memorare

• O functie de memorare salveaza rezultatele unor subcalcule pentru a putea fi folosite direct, in

21/61

subcalcule pentru a putea fi folosite direct, in apelari viitoare, fara a mai fi recalculate.

• Prototipul unei astfel de functii este retin(deductie(arg1, …, argn))

• Implementarea se realizeaza astfel:retin(deductie(arg1, …, argn)):-deductie(arg1, …,

argn), asserta(deductie(arg1, …, argn)).

Functii de memorare

• Se verifica veridicitatea faptului respectiv si, in caz

22/61

• Se verifica veridicitatea faptului respectiv si, in caz afirmativ, se introduce in memoria Prolog-ului.

retin(deductie(arg1, …, argn)):-deductie(arg1, …, argn), asserta(deductie(arg1, …, argn)).

• Data urmatoare cand se doreste atingerea scopului • Data urmatoare cand se doreste atingerea scopului (afirmatia din capul clauzei), solutia va fi gasita direct in memorie, fara a repeta calculele.

Turnurile din Hanoi

• Enunt: Se dau N discuri si 3 cuie: A, B, C.

23/61

cuie: A, B, C.

• Se cere sa se mute toate discurile de pe cuiul A pe cuiul B, folosindu-se drept cui intermediar C. A C Bcui intermediar C.

• Discurile sunt intotdeauna asezate unul peste altul in ordinea crescatoare a marimii.

A C B

Turnurile din Hanoi - solutie

• Problema se rezolva recursiv.

24/61

• Problema se rezolva recursiv.

• Se muta N – 1 discuri de pe cuiul A pe cuiul C.

• Se muta apoi cele N – 1 discuri de pe C pe B.

• In final, se aseaza cuiul 1 de pe A pe B.

• Cand avem 1 disc, se muta de pe A pe B.

Turnurile din Hanoi - Prologhanoi1(N):-hanoi1(N, a, b, c, L), afis(L).

25/61

hanoi1(N,A,B,_C,[(A,B)]):-N = 1.hanoi1(N,A,B,C,M):-N > 1, N1 is N - 1,

hanoi1(N1,A,C,B,M1), hanoi1(N1,C,B,A,M2),append(M1, [(A, B)|M2], M).

afis([]).afis([X|Rest]):-writeln(X), afis(Rest).

Turnurile din Hanoi – functii de memorare:-dynamic(hanoi/5).

26/61

:-dynamic(hanoi/5).

hanoi(N):-hanoi(N, a, b, c, L), afis(L).

hanoi(N,A,B,_C,[(A,B)]):-N = 1.hanoi(N,A,B,C,M):-N > 1, N1 is N - 1,

retin(hanoi(N1,A,C,B,M1)), retin(hanoi(N1,A,C,B,M1)), hanoi(N1,C,B,A,M2),append(M1, [(A, B)|M2], M).

retin(P):-P, asserta(P).

Turnurile din Hanoi

• Se utilizeaza functiile memo pentru a imbunatati performanta programului initial.

27/61

performanta programului initial.

• Solutia acestei probleme, cand sunt N discuri, necesita 2^N – 1 mutari.

• Modul de solutionare al problemei rezolva in mod • Modul de solutionare al problemei rezolva in mod repetat subprobleme prin mutarea unui numar identic de discuri.

Turnurile din Hanoi

• O functie de memorare poate fi folosita pentru a retine mutarile facute pentru fiecare subproblema cu un numar mai mic de discuri.

28/61

cu un numar mai mic de discuri.

• Cand se ajunge la reapelarea unui fapt care a fost deja calculat, se poate folosi secventa de mutari deja retinuta in memorie, in loc de a le recalcula.

• Se memoreaza prima clauza recursiva, de muta N • Se memoreaza prima clauza recursiva, de muta N – 1 discuri, a predicatului hanoi si poate fi folosita de a doua apelare recursiva a sa pentru N – 1 discuri.

Rulare

29/61

A C B

Predicatul REPEAT

Repeat

• Dacă o anumită clauză trebuie satisfăcută demai multe ori, facem apel la predicatul fail care

31/61

mai multe ori, facem apel la predicatul fail careface ca Prologul să încerce să găsească toatesoluţiile (presupunând că nu folosim tăietura).

• Totuşi, în unele situaţii, este necesar să repetămnumai o anumită parte de program, înainte de acontinua procesul cu restul clauzei.continua procesul cu restul clauzei.

• Acest gen de situaţii apar atunci când vrem sărealizăm operaţii iterative, precum citirea dintr-un fişier ori la realizarea unui meniu.

Repeat• În general, predicatul repeat este folosit în construcţii de

forma:

32/61

nume_predicat :- repeat, % încep buclaafisez_meniu, % afişez meniul pentru

utilizatorcitesc_optiunea(N),% citesc data introdusă de

către utilizatorvalidez_optiunea(N), % verific dacă există în

cadrul meniuluiexecut_cerinta(N), % realizez ce era execut_cerinta(N), % realizez ce era

specificat în meniu

pentru Nverific_oprirea(N), % verific dacă este

condiţia de terminare!. % oprirea iteraţiei.

Exemplu

• Următorul predicat citesc/0 face citirea de latastatură a unor valori pe care le afişează apoi

33/61

tastatură a unor valori pe care le afişează apoiutilizatorului.

• Procesul se termină atunci când utilizatorulintroduce gata.

citesc :- repeat, meniu, read(X), write('Am citit '),write(X), write('!'), nl, X == gata, !.

meniu :- nl, write('---------'), nl, write('Exemplubanal!'), nl, write('Orice introduceti, afisam!'), nl,write('Pentru oprirea repeat-ului, tastati gata.'), nl,write('---------'), nl, nl.

Exemplu

34/61

Alt exemplu

• Realizaţi un predicat care să simuleze predicatul

35/61

• Realizaţi un predicat care să simuleze predicatul predefinit consult/1.

Rezolvare

compilez(Fisier) :- see(Fisier), repeat,

36/61

compilez(Fisier) :- see(Fisier), repeat,read(X), assert(X),X == end_of_file,!, seen.

Rezultate

37/61

Alt exemplu

• Avem relaţiile existente într-o familie date

38/61

• Avem relaţiile existente într-o familie dateprintr-un singur predicat, persoana(Nume, Sex,Copii), unde primul argument reprezintă numelepersoanei în cauză, al doilea ne spune dacă estebărbat sau femeie, iar al treilea este o listă(poate fi vidă) care conţine numele copiilorpersoanei la care ne referim.persoanei la care ne referim.

Alt exemplu

• Utilizatorului îi va fi prezentat un meniu care îipermite să facă următoarele operaţii:

39/61

permite să facă următoarele operaţii:

▫ Să adauge o persoană (întreabă dacă estebărbat sau femeie); se validează iniţial dacăexistă sau nu în baza de cunoştinţe introdusă.Presupunem că nu există două persoane cuacelaşi nume.acelaşi nume.

▫ Să şteargă o persoană din baza de cunoştinţe.

▫ Să adauge informaţia că X este copilul lui Y.

Alt exemplu

• Continuare meniu:

40/61

• Continuare meniu:

▫ Eliminarea lui X din lista de copii a lui Y.

▫ Eliminarea tuturor persoanelor din bază.

▫ O opţiune care să-i permită utilizatorului săsalveze baza de cunoştinţe într-un fişier,respectiv să o citească dintr-un fişier.

Faptele

persoana(andrei, barbat, [cristi, elena]).persoana(cristi, barbat, [adriana, marius, ovidiu]).

41/61

persoana(cristi, barbat, [adriana, marius, ovidiu]).persoana(elena, femeie, [ana]).persoana(marius, barbat, []).persoana(ovidiu, barbat, []).persoana(george, barbat, []).persoana(adriana, femeie, []).persoana(adriana, femeie, []).persoana(ana, femeie, [george]).

Adaugarea unei noi persoane

42/61

adaugare :- write('Numele celui ce va fi adaugat: '), read(Nume), not(persoana(Nume, _, _)), write('Sexul '), read(Sex), assert(persoana(Nume, Sex, [])), nl, write('Persoana a fost adaugata!').

adaugare :- write('Exista deja in baza noastra!').

Adaugarea unei noi persoane

43/61

Stergerea unei persoane

44/61

stergere :- write('Numele celui ce va fi sters: '), read(Nume), persoana(Nume, _, _), retract(persoana(Nume, _, _)), write(Nume), write(' a fost sters!').

stergere :- nl, write('Nu exista in baza de stergere :- nl, write('Nu exista in baza de cunostinte!').

Stergerea unei persoane

45/61

Adaugarea copilului X la Y

copil(_X, Y) :- not(persoana(Y, _, _)), write(Y),

46/61

copil(_X, Y) :- not(persoana(Y, _, _)), write(Y), write(' nu exista in baza noastra de cunostinte!').

copil(X, Y) :- retract(persoana(Y, SexY, CopiiY)), assert(persoana(Y, SexY, [X|CopiiY])), nl, write('Acum '), write(X), write(' este copilul lui write('Acum '), write(X), write(' este copilul lui '), write(Y).

Adaugarea copilului X la Y

47/61

Eliminarea copilului X de la Y

elimcopil(_X, Y) :- not(persoana(Y, _, _)),

48/61

elimcopil(_X, Y) :- not(persoana(Y, _, _)), write(Y), write(' nu exista in baza noastra de cunostinte!').

elimcopil(X, Y) :- persoana(Y, _, Copii), not(member(X, Copii)), write(X), write(' nici nu e copilul lui '), write(Y), write('.'). e copilul lui '), write(Y), write('.').

Eliminarea copilului X de la Y

elimcopil(X, Y) :- retract(persoana(Y, SexY,

49/61

elimcopil(X, Y) :- retract(persoana(Y, SexY, CopiiY)), elim(X, CopiiY, CopiiiY), assert(persoana(Y, SexY, CopiiiY)), write('Acum '), write(X), write(' nu mai este copilul lui '), write(Y).

elim(_, [], []).elim(_, [], []).elim(X, [X|R], L) :- elim(X, R, L).elim(X, [P|R], [P|L]) :- elim(X, R, L).

Eliminarea copilului X de la Y50/61

Eliminarea tuturor persoanelor din bazaelimintot :- retractall(persoana(_, _, _)), nl,

51/61

elimintot :- retractall(persoana(_, _, _)), nl, write('Baza de cunostinte este acum goala!').

Salvarea bazei de cunostinte intr-un fisier

52/61

salvez(Fisier) :- tell(Fisier), salvez, told, write('Fisierul a fost salvat').

salvez :- persoana(Nume, Sex, Copii), write('persoana('), write(Nume), write(','), write(Sex), write(','), write(Copii), write(').'), nl, write(Sex), write(','), write(Copii), write(').'), nl, fail.

salvez.

Salvarea bazei de cunostinte intr-un fisier

53/61

Incarcarea unui fisier in memorie

deschid(Fisier) :- consult(Fisier), nl,

54/61

deschid(Fisier) :- consult(Fisier), nl, write('Fisierul '), write(Fisier), write(' a fost incarcat.'), nl.

Meniulmeniu :- nl, nl, tab(15), write('Selectati un numar

din cadrul meniului'), nl, nl, write('1. Listare

55/61

din cadrul meniului'), nl, nl, write('1. Listare baza de cunostinte.'), nl, write('2. Adaugare persoana.'), nl, write('3. Eliminare persoana.'), nl, write('4. Adaugarea lui X ca si copil al lui Y.'), nl, write('5. Stergerea lui X ca si copil al lui Y.'), nl, write('6. Eliminarea tuturor persoanelor.'), nl, write('7. Salvarea bazei de cunostinte intr-un write('7. Salvarea bazei de cunostinte intr-un fisier Prolog.'), nl, write('8. Incarcarea bazei de cunostinte dintr-un fisier Prolog.'), nl, write('9. Incheiere.'), nl, nl.

Partea principalaprincipal :- repeat, meniu, read(Optiune),

proceseaza(Optiune), nl, Optiune == 9, !.

56/61

proceseaza(Optiune), nl, Optiune == 9, !.

proceseaza(1) :- persoana(Nume, Sex, []), write(Nume), write(' este '), write(Sex), write(' si nu are copii.'), nl, fail.

proceseaza(1) :- persoana(Nume, barbat, Copii), Copii \= [], write(Nume), write(' este barbat iar copiii lui sunt: '), afiscopii(Copii), nl, fail.

proceseaza(1) :- persoana(Nume, femeie, Copii), Copii sunt: '), afiscopii(Copii), nl, fail.

proceseaza(1) :- persoana(Nume, femeie, Copii), Copii \= [], write(Nume), write(' este femeie iar copiii ei sunt: '), afiscopii(Copii), nl, fail.

proceseaza(1).

Partea principala

proceseaza(2) :- adaugare, !.

57/61

proceseaza(2) :- adaugare, !.proceseaza(3) :- stergere, !.proceseaza(4) :- write('Copilul X: '), read(X),

write('Parintele Y: '), read(Y), copil(X, Y), !.proceseaza(5) :- write('Copilul X: '), read(X),

write('Parintele Y: '), read(Y), elimcopil(X, Y), !.proceseaza(6) :- elimintot,!.proceseaza(6) :- elimintot,!.

Partea principala

proceseaza(7) :- write('Numele fisierului Prolog

58/61

proceseaza(7) :- write('Numele fisierului Prolog (introduceti-l intre apostrofuri si cu extensia pl): '), read(NumeFis), salvez(NumeFis), !.

proceseaza(8) :- write('Numele fisierului din care se face incarcarea (intre apostrofuri si cu extensia pl): '), read(Fis), deschid(Fis),!.

proceseaza(9).proceseaza(9).

Partea principala

59/61

afiscopii([]).afiscopii([P|R]) :- write(P), write(', '), afiscopii(R).

Rulare60/61

Pe saptamana viitoare!

61/61

Pe saptamana viitoare!

Programare functionala. Fundamentele limbajului LISP

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Bibliografie

• Stuart C. Shapiro, Common Lisp: An InteractiveApproach, Computer Science Press, 1992.

• Internet.

Introducere

• Vom opera cu mediul standard al limbajuluiLISP.LISP.

• Acest lucru presupune ca vom lucra inCOMMON LISP.

• Common Lisp aduce o interfata simpla, de tip• Common Lisp aduce o interfata simpla, de tipDOS.

• In particular, vom lucra cu implementareaCLISP 2.30.

Introducere

• In momentul in care pornim Common Lisp, ne• In momentul in care pornim Common Lisp, nevom afla deja in fata prompterului Lisp.

• Prompterul va astepta sa introducem ceea ce, incadrul programarii functionale, poarta numelede S-expresie (expresie simbolica).

• Dupa ce S-expresia este scrisa, apasam tastaENTER.

Ciclul citire-evaluare-scriere al Lisp

• Atunci cand dam Lisp-ului o S-expresie, acesta va• Atunci cand dam Lisp-ului o S-expresie, acesta vaproduce urmatorii pasi:

▫ Va citi S-expresia.

▫ Va interpreta S-expresia drept reprezentarea scrisaa unui forme (obiect Lisp ce trebuie evaluat).a unui forme (obiect Lisp ce trebuie evaluat).

▫ Va evalua forma drept alt (sau poate chiar acelasi)obiect valoare.

Ciclul citire-evaluare-scriere al Lisp

▫ Va alege o reprezentare scrisa pentru obiectul▫ Va alege o reprezentare scrisa pentru obiectulvaloare.

▫ Va scrie reprezentarea scrisa pe care a ales-o.

• Dupa ce intoarce valoarea, prompterul Lisp va• Dupa ce intoarce valoarea, prompterul Lisp vareaparea si va astepta o noua expresie.

Ciclul citire-evaluare-scriere al Lisp

• Acesta este modul de folosire al Lisp:• Acesta este modul de folosire al Lisp:

▫ Utilizatorul introduce reprezentarea scrisa a uneiforme.

▫ Lisp o evalueaza.▫ Lisp o evalueaza.

▫ Apoi, trimite inapoi o reprezentare scrisa a valoriiformei.

Un prim exemplu• O S-expresie simpla pe care o vom introduce este

numeralul in scriere araba, 3.numeralul in scriere araba, 3.

• Aceasta este una din reprezentarile scrise pe carele folosim pentru numarul 3.

• Oamenii folosesc si numeralul in scriere romanaIII.III.

• Aceasta este deci distinctia pe care o face si Lispintre un obiect si diferitele sale posibilitati dereprezentare scrisa.

Exemplu

• Lisp interpreteaza numeralul 3 ca reprezentand• Lisp interpreteaza numeralul 3 ca reprezentandnumarul 3.

• Evalueaza aceasta forma – adica obiectulnumeric 3.

• In Lisp, numerele sunt evaluate in ele insele.• In Lisp, numerele sunt evaluate in ele insele.

• Lisp va alege o reprezentare scrisa pentru 3 si vautiliza, de asemenea, numeralul arab 3.

Interactiunea cu Lisp

Debugger-ul din Lisp

• Daca in introducerea unei S-expresii se face vreo greseala, va intra debugger-ul Lisp-ului.greseala, va intra debugger-ul Lisp-ului.

• Acesta mai poarta numele si de ciclu (sau pachet) break.

• Acesta arata ca un prompter Lisp obisnuit, doar ca exista aici niste comenzi speciale pentru a obtine exista aici niste comenzi speciale pentru a obtine informatii despre ce presupune eroarea.

• Deocamdata, vom parasi aceste bucle break, tastand :q.

Debugger-ul din Lisp

Terminarea sesiunii de Lisp

Numere in Lisp

• Numerele sunt unul dintre tipurile de baza aleLisp-ului.Lisp-ului.

• Se pot folosi numere intregi sau reale.

• In cadrul intregilor, nu putem folosi insa virgulesau spatii:sau spatii:▫ 12 345 sau 12,345 sunt reprezentari incorecte de

intregi.▫ Vom scrie direct 12345.

Numere in Lisp

• Pentru a scrie un intreg negativ, vom inserasemnul “-” in fata sa, iar pentru unul pozitivsemnul “-” in fata sa, iar pentru unul pozitivputem de asemenea pune semnul “+”:▫ -34, +25 sunt expresii corecte de intregi.

• Un intreg se poate termina cu “.” – acesta va ficitit drept intreg:citit drept intreg:▫ 12. va fi egal cu a scrie 12.▫ 12.0 va fi insa interpretat drept real.

Numere in Lisp

• Numerele reale sunt construite cu ajutorul• Numerele reale sunt construite cu ajutorulsemnului “.” si cu cel putin o cifra dupa punct:▫ 12.9▫ 13.0

• Pot fi scrise si sub forma stiintifica, cu semnul de• Pot fi scrise si sub forma stiintifica, cu semnul deexponent:▫ 0.34e-2 – care inseamna 0.34 × 10-2.

Numere in Lisp

Numere in Lisp

Caracterul “;” se foloseste pentru a

comenta o anumita parte . Rezulta ca ce se

afla dupa el este ignorat.

Liste in Lisp

• LisP = List Processing

• Care este reprezentarea scrisa a unei liste?

• Conform lui S. C. Shapiro, definitia unei S-expresii lista este:▫ O paranteza stanga urmata de zero sau mai multe

S-expresii urmate de o paranteza dreapta este o S-S-expresii urmate de o paranteza dreapta este o S-expresie lista.

• S-expresiile se delimiteaza una de alta prinspatii.

Exemple

• (1 3.2 2 4)

• (1 (2 3.3) 4)

• ()

• ((1 3.2 2 4))• ((1 3.2 2 4))

• (())

Liste

• In acest moment, Lisp-ul citeste expresia care este data de utilizator si incearca sa o evalueze.este data de utilizator si incearca sa o evalueze.

• Pana la a evalua o lista, ii vom cere Lisp-ului doar sa ne afiseze lista introdusa.

• Putem impiedica evaluarea unei liste si, in loc, sa • Putem impiedica evaluarea unei liste si, in loc, sa obtinem printarea ei folosind semnul de apostrof inaintea S-expresiei lista.

Exemple de interactiune cu Lisp

Modul de reprezentare scrisa ales de Lisp pentru lista vida.

Liste

• Lisp-ul va ignora de asemenea spatiile in plus sau ENTER-urile.sau ENTER-urile.

• Daca toate parantezele deschise nu sunt inchise de utilizator, Lisp-ul va astepta in continuare paranteze dreapta.

• Se pot pune mai multe paranteze dreapta decat stanga; Lisp-ul le va ignora pe cele in plus.

Exemple

Expresii aritmetice in Lisp

• Evaluarea obiectelor lista este operatia de baza in Lisp.in Lisp.

• Conform lui S. C. Shapiro:▫ Valoarea unei liste este cea obtinuta prin aplicarea

functiei denumita de primul argument (membru) al listei asupra valorilor celorlalti membri ai listei.al listei asupra valorilor celorlalti membri ai listei.

• Vom incepe evaluarea listelor cu ajutorul operatorilor matematici de baza: +, -, *, /.

Exemplu

Notatia prefixa Cambridge

• Formatul sub care expresiile aritmetice sunt • Formatul sub care expresiile aritmetice sunt scrise sub forma de lista poarta numele de notatie prefixa Cambridge.

• Numele provine de la cel care a dezvoltat aceasta notatie – John McCarthy de la MIT, Cambridge, MA – si de la faptul ca operatorul “prefixeaza” MA – si de la faptul ca operatorul “prefixeaza” (este inaintea) operanzilor sai.

Notatia prefixa Cambridge

• Termenul a fost preluat de la notatia polonezaprefixa, unde functia e scrisa inainteaprefixa, unde functia e scrisa inainteaargumentelor sale.

• Acest format difera de cel matematic clasic de tipinfix:▫ In care operatorul este scris intre operanzii sai (de

ex. 12 + 4)ex. 12 + 4)▫ sau in care functia este scrisa inainte de argumente

dar nu in paranteza cu ele (de ex. f(x, y)).▫ Aceasta din urma se va scrie in Lisp sub forma: (f x

y)

Notatia prefixa Cambridge

• Avantajul major al acestei notatii este ca scrierea ramane foarte simplu de utilizat indiferent de numarul de argumente: ▫ 1, ▫ 2 ▫ sau chiar mai multe ducand la operatii succesive

Exemple de interactiune

Am uitat sa inseram un spatiu!

Evaluarea listelor

• Daca argumentele functiilor aritmetice sunt intregi, rezultatul va fi intreg.

• Daca unul dintre argumente este real, atunci rezultatul va fi real.rezultatul va fi real.

Evaluarea listelor

• Exceptie se face daca incercam sa impartim un • Exceptie se face daca incercam sa impartim un intreg la un alt intreg si valoarea rezultata nu este exacta:▫ Rezultatul va fi ceea ce poarta numele de fractie: 2

numere separate de semnul “/”, pozitive sau negative.

▫ Fractia va fi reprezentata de catre Lisp sub forma ▫ Fractia va fi reprezentata de catre Lisp sub forma simplificata.

• Si utilizatorul poate introduce fractii, chiar si sub forma nesimplificata.

Exemple

Evaluarea listelor• Putem avea expresii aritmetice incluse in alte

expresii aritmetice – cum este natural in matematica, de exemplu, 5 × (3 + 4).matematica, de exemplu, 5 × (3 + 4).

• In Lisp, aceasta expresie se va scrie sub forma:(× 5 (+ 3 4))

• In schimb, 5 × 3 + 4 se scrie:• In schimb, 5 × 3 + 4 se scrie:(+ (× 5 3) 4)

• In general f(x, g(y)) se va scrie sub forma:(f x (g y))

Interactiune

Exercitiu

• Sa calculam radacinile ecuatiei:

2x2 + 7x + 5 = 0• Acestea sunt:

52477 2 22

52477

Exercitiu

• Le vom scrie Lisp-ului sub forma:

• (/ (+ -7.0 (sqrt (- (expt 7 2) (* 4 2 5)))) (* 2 2))

• siFunctia ridicare

la putere – 2 argumente

Functia radical –un singur argument

• (/ (- -7.0 (sqrt (- (expt 7 2) (* 4 2 5)))) (* 2 2))

Interactiune

Testarea egalitatii

• Verificarea egalitatii se face cu operatorul “=”.

• I se pot da 1, 2 sau mai multe argumente.

• Argumentele pot fi de tipuri numerice diferite –“=” testeaza numai egalitatea numerica.

• Intoarce TRUE (T) daca numerele sunt egale si FALSE (NIL) altfel.

Interactiune

Exercitiu

• Utilizand Lisp-ul, gasiti valorile pentru:▫ (25 + 30) × 15/2

▫ 6 × 3.1416

▫ Media numerelor 5, 6.7, -23.2, 75 si 100.3

Pe saptamana viitoare…

Fundamentele limbajului LISP (2)Fundamentele limbajului LISP (2)

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

2

Stringuri si caractere

• Un string este un vector de caractere.

• Este scris de catre Lisp ca secventa caracterelor sale inconjurata de ghilimele.

• Ca si numerele, stringurile sunt evaluate in ele insele.

• Sa scriem un string la prompterul de Lisp.

> “Acesta este un string.”

3

Exemplu

4

Stringuri

• Un string poate fi oricat de lung si poate contine• Un string poate fi oricat de lung si poate continecaractere precum ENTER.

• In afara numerelor, Common Lisp are deasemenea functii care opereaza si asupraobiectelor de alt tip.

• De exemplu, pentru a afla numarul de caracteredintr-un string, se foloseste functia length.

5

Exemplu

6

Stringuri

• O alta functie predefinita este string=.

• Aceasta functie intoarce TRUE daca cele douastringuri date ca argumente sunt alcatuite dinaceleasi caractere si FALSE, in caz contrar.aceleasi caractere si FALSE, in caz contrar.

7

Exemple

8

Stringuri

• Pentru a accesa un anumit caracter in string, se utilizeaza formularea (char string index).utilizeaza formularea (char string index).

• char este predefinit, string reprezinta sirul dorit iar index pozitia caracterului care va fi intors.

• Index-ul primului caracter din string e 0.• Index-ul primului caracter din string e 0.

• Index-ul nu trebuie sa depaseasca lungimea sirului.

9

Exemple

10

Caractere

• Se poate observa ca un caracter este scris de catre Lisp cu prefixul #\.

• Tot in acelasi mod va da si utilizatorul caracterele.caracterele.

• Un caracter este evaluat in el insusi.

11

Exemple

12

Caractere

• Pentru testarea faptului ca doua caractere sunt • Pentru testarea faptului ca doua caractere sunt identice, se foloseste functia char=.

• La fel ca la testarea egalitatii pentru numere, dar diferit de aceeasi testare pentru stringuri, aceasta functie poate lua orice numar de argumente.argumente.

13

Exemple

14

Stringuri si caractere

• Pentru a utiliza simbolul “ ca parte a unui string, • Pentru a utiliza simbolul “ ca parte a unui string, va trebui sa folosim caracterul \.

• Pentru a utiliza caracterul \ apoi ca parte a unui string, trebuie sa mai adaugam inca unul in fata sa.sa.

15

Exemple

• Observati ca lungimea stringurilor nu este• Observati ca lungimea stringurilor nu esteinfluentata de caracterul \.

16

Exemple

• Pentru testarea egalitatii a doua stringuri, adoua poate fi si caracter.doua poate fi si caracter.

• Pentru acelasi lucru in cazul caracterelor,amandoua trebuie sa fie de acest fel.

17

Caracterele spatiu si ENTER

18

Simboluri

• Simbolurile sunt un alt tip de data in Lisp.

• Pentru a reprezenta un simbol, se folosesc secvente de litere si caracterele * si -:▫ De exemplu: paul, pi, *read-base*

• Un simbol poate reprezenta ceva pentru care • Un simbol poate reprezenta ceva pentru care dorim sa stocam informatii:▫ De exemplu, paul poate reprezenta o persoana.

19

Simboluri

• Simbolurile sunt de asemenea folosite drept variabile.variabile.

• Astfel, un simbol poate avea o valoare: se spune ca este legat, sau este, dimpotriva, nelegat (fara valoare).

• Unele simboluri legate sunt: pi, *read-base*, *print-base* si *package*.

20

Exemple

• pi reprezinta valoarea simbolului matematic.• *read-base* si *print-base* specifica in ce baza • *read-base* si *print-base* specifica in ce baza

vor fi scrise numerele de catre utilizator, respectiv de Lisp.

• *package* specifica pachetul in care ne aflam curent.

21

Simboluri• Cele mai importante simboluri in Lisp sunt T si

NIL.NIL.

• T reprezinta true, iar NIL desemneaza false si lista vida.

• Aceste simboluri sunt legate chiar la ele insele.

22

Simboluri

• Atunci cand vrem sa utilizam un simbol si nu • Atunci cand vrem sa utilizam un simbol si nu valoarea sa, punem ‘ in fata acestuia.

23

Simboluri

• Putem scrie simbolurile cu litere mici, Lisp le converteste la litere mari.

24

Simboluri

• Pentru a testa egalitatea dintre doua simboluri, se foloseste functia eql. se foloseste functia eql.

25

Functia eql

• Aceasta functie e mai generala chiar, testanddaca sunt identice oricare doua obiecte Lisp:▫ Simboluri▫ Caractere▫ Numere de acelasi tip▫ Numere de acelasi tip

26

Exemple

27

Simboluri

• Orice simbol are un nume reprezentat de unstring.

• Putem afla acest nume utilizand functia symbol-name.

28

Exemplu

29

Simboluri

• Daca se doreste ca un caracter sa ramana scris cu litera mica in cadrul unui simbol, se va folosi \.

• Daca vrem ca Lisp sa pastreze literele exact cum le dam, le vom scrie intre ||.

30

Exemplu

• Pentru a scrie un simbol, Lisp foloseste si ||.

• Acestea nu fac parte din simbol sau din numele sau.

31

Exemplu

• Simbolurile cu litere diferite ca marime sunt la • Simbolurile cu litere diferite ca marime sunt la randul lor diferite.

32

Mai multe exemple

33

Tipul unui obiect

• Pentru a afla care este tipul unui anume obiect,se foloseste functia type-of.se foloseste functia type-of.

34

Mai multe exemple

35

Pachete

• Fiecare multime de simboluri este pastrata intr-• Fiecare multime de simboluri este pastrata intr-un pachet Common Lisp.

• Utilizatorul isi poate crea propriul pachet si il poate exporta pentru ca altii sa il poata utiliza.

• Un pachet poate fi evident importat in alt pachet.

36

Pachete

• Am vazut ca un simbol poate avea diverse• Am vazut ca un simbol poate avea diversereprezentari si totusi sa ramana acelasi simbol,cu acelasi nume.

• In continuare vom vedea ca simboluri diferitepot avea acelasi nume daca sunt in pachetepot avea acelasi nume daca sunt in pachetediferite.

37

Pachete

• Atunci cand interactionam cu Lisp, ne aflam deja intr-un pachet.intr-un pachet.

• Putem vedea pachetul curent verificand valoarea simbolului *package*.

38

Functia describe

• Prin apelul acestei functii Lisp putem afla diverse proprietati despre obiecte.diverse proprietati despre obiecte.

• Printre altele, putem vedea pachetul din care fac parte diferite simboluri.

39

Exemple

40

Exemple

41

Pachete

• Ne putem muta in alt pachet apeland functia in-• Ne putem muta in alt pachet apeland functia in-package.

• Acolo putem referi simboluri existente sau unele noi create de utilizator.

42

Exemplu

43

Pachete• Pentru a referi un acelasi simbol din alt pachet,

folosim exprimarea:folosim exprimarea:

nume_pachet::nume_simbol

• Cele doua simboluri sunt diferite.

44

Pachete• Sa ne intoarcem acum la pachetul common-lisp-

user si sa aflam informatii despre simbolul ‘paul.

45

Pachete

• Cele doua simboluri din pachete diferite nu sunt identice decat ca nume.identice decat ca nume.

46

Pachete

• Un simbol poate fi exportat din pachetul sau prin apelarea functiei export.apelarea functiei export.

• Numele unui simbol extern este de forma:

nume_pachet:nume_simbol

47

Exemplu

48

Pachete

• Pentru a afla daca un simbol a fost exportat sau este inca intern intr-un anumit pachet, se poate proceda precum in cele ce urmeaza.proceda precum in cele ce urmeaza.

49

Pachete• Putem de asemenea defini pachete noi.

50

Pachete• Daca dorim sa importam un simbol extern

dintr-un pachet in altul folosim functia import.

51

Alte observatii

• Atunci cand referim prima data un simbol, Lisp • Atunci cand referim prima data un simbol, Lisp il si construieste; deci, un simbol nou nu trebuie declarat inainte.

• Daca vom incerca sa suprascriem un simbol care deja exista intr-un pachet, vom primi mesaj de eroare.eroare.

52

Exemplu

53

Alte observatii

• Simbolurile standard din pachetul lisp suntexterne si importate automat in alte pachete.externe si importate automat in alte pachete.

54

Pachetele ca tip de data

• Ca tip de data in Lisp, putem afla mai multeinformatii despre pachetul curent.informatii despre pachetul curent.

55

Pachetele ca tip de data

• Pe langa functiile deja cunoscute, find-package nespune pachetul al carui nume prescurtat il stim.spune pachetul al carui nume prescurtat il stim.

56

Procesarea de baza a listelor• Pana acum am discutat despre evaluarea S-

expresiilor care erau date sub forma de liste.

• In continuare, vom discuta despre liste ca tip debaza in Lisp.

• Pentru a crea o lista, se foloseste functia de baza:(cons obiect lista), unde:▫ primul argument poate fi orice obiect Lisp▫ primul argument poate fi orice obiect Lisp▫ al doilea este o lista▫ intoarce o lista cu primul argument inserat ca prim

membru si restul listei fiind al doilea argument

57

Exemplu

58

Primul element si restul listei

• Pentru a accesa primul element al unei liste silista ramasa, se folosesc predicatelelista ramasa, se folosesc predicatele

(first list) si (rest list)

59

Mai multe exemple

60

Procesarea listelor

• Functia equal spune daca elementele a doua liste sunt egale doua cate doua sau nu.liste sunt egale doua cate doua sau nu.

61

Functia de determinare a lungimii unei liste

62

Alte observatii

• Dupa ce am tastat o forma in Lisp, o putemimediat reapela cu *; cu ** putem reapelaimediat reapela cu *; cu ** putem reapelapenultima forma.

63

Pe saptamana viitoare…

Programare in Lisp pur

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

2

Definirea propriilor functii

• “Un program Lisp este o colectie de functii scrise de un programator Lisp” - S. C. Shapiro.de un programator Lisp” - S. C. Shapiro.

• Pentru a defini o functie, se foloseste forma defun:

(defun functie lista_variabile string_documentie forma)(defun functie lista_variabile string_documentie forma)

▫ functie este un simbol;▫ lista_variabile este o lista de simboluri;▫ string_documentatie este un string;▫ forma este o forma Lisp.

3

Definirea propriilor functii

(defun functie lista_variabile string_documentie forma)

▫ defun intoarce functie;▫ defineste functie ca fiind numele unei functii;▫ argumentele (atributele) sale formale sunt simbolurile din

lista_variabile;▫ definitia sa se afla in forma;▫ documentatia (explicatiile) pentru aceasta functie sunt in

string_documentatie.

• Observatie: Argumentele formale ale functiei poarta numele de • Observatie: Argumentele formale ale functiei poarta numele de variabile lambda.

• Numele provine de la calculul lambda al lui A. Church care sta la baza Lisp-ului.

4

Exemplu

Simbol

• Se defineste functia lista.• Aceasta ia trei obiecte Lisp ca atribute actuale.• Intoarce o lista care are ca membri cele trei obiecte

Lisp date ca argumente.

Simbol

Lisp date ca argumente.• Dupa ce functia este evaluata, aceasta se poate

folosi ca si cum ar fi una predefinita in Lisp.• Putem afla de asemenea informatii despre functie

cu expresia documentation.

5

Apelarea unei functii

• Cand o functie este apelata, se parcurg urmatorii pasi:▫ Lisp verifica daca primul membru al listei este un simbol care reprezinta

o functie;▫ Obiectele date ca argumente sunt evaluate;▫ Valorile obiectelor devin valorile atributelor formale.▫ Variabilele formale sunt legate la valorile date;▫ Forma care reprezinta definitia functiei este evaluata;▫ Atributele formale sunt dezlegate;▫ Atributele formale sunt dezlegate;▫ Valoarea formei de definitie este intoarsa.

6

Apelarea unei functii - exemplu• Cand o functie este apelata, se

parcurg urmatorii pasi:▫ Lisp verifica daca primul

• Cand functia definita esteapelata, avem urmatorii pasi:

▫ Lisp verifica daca primulmembru al listei este un simbolcare reprezinta o functie;

▫ Obiectele date ca argumente suntevaluate;

▫ Valorile obiectelor devin valorileatributelor formale, adicavariabilele formale sunt legate lavalorile date;

▫ Forma care reprezinta definitiafunctiei este evaluata;

▫ lista este un simbol carereprezinta o functie;

▫ ‘a este evaluat drept A, (cons ‘b‘()) ca (B) si ‘c drept C;

▫ o1 este legat la A, o2 la (B) si o3la C;

▫ (cons o1 ( cons o2 (cons o3 ‘())))este evaluata, fiecare obiectavand valorile de mai sus;

functiei este evaluata;

▫ Atributele formale suntdezlegate;

▫ Valoarea formei de definitie esteintoarsa.

avand valorile de mai sus;▫ o1, o2 si o3 revin la valorile

initiale;▫ Se intoarce (A (B) C).

7

Reversul unei liste de doua numere

• Folosind functiile predefinite first si second caredau primul si cel de-al doilea element al uneidau primul si cel de-al doilea element al uneiliste, sa se defineasca o functie care inverseaza ceidoi membri ai unei liste.

8

Patratul unui numar

• Definiti o functie care sa calculeze patratul unui• Definiti o functie care sa calculeze patratul unuinumar dat n.

9

Reversul unei liste de trei numere

• Folosind functia predefinita third care da• Folosind functia predefinita third care daelementul de pe pozitia a treia dintr-o lista, sa sedefineasca o functie care inverseaza cei treimembri ai unei liste.

10

Definirea de functii in pachete

• Sa definim un nou pachet pentru a defini• Sa definim un nou pachet pentru a definifunctiile personale.

• Ne mutam din pachetul curent in cel nou definit.

• Sa definim, de exemplu, pachetul invatare.• Sa definim, de exemplu, pachetul invatare.

• In interiorul sau sa definim functia fn.

11

Definirea de functii in pachete

• Apelam functia describe pentru a vedea daca fn• Apelam functia describe pentru a vedea daca fneste un simbol mostenit din alt pachet.

• Daca acesta este cazul, apelam functia shadowavand ca argument functia existenta.

• Reapelam describe pentru a fi siguri ca fn esteacum simbol al pachetului invatare.

12

Definirea de functii in pachete

• Definim functia fn.

• Testam functia nou definita.

• Verificam daca functia fn originala se poate incafolosi, utilizand formularea ce include pachetulcare o exporta.care o exporta.

• Exportam simbolul functional nou definit, dacadorim folosirea sa si in alte pachete.

13

Redefinirea functiei predefinite last

• last intoarce o lista formata din ultimul element dintr-o lista data.dintr-o lista data.

• In versiunea noastra, va intoarce al treilea element al unei liste date.

14

Continuare

15

Continuare

16

Alt exemplu• Sa definim o functie care sa recunoasca semnul

intrebarii, ?.

17

Problema!

• Cand am definit functia, ne aflam in pachetulinvatare, in care argumentul sau era invatare::?.invatare, in care argumentul sau era invatare::?.

• Cand il testam in pachetul common-lisp-user,argumentul va fi common-lisp-user::?.

• Cele doua simboluri sunt evident diferite.

18

Reformulare

• Intentionam de fapt nu sa recunoastem simbolul?, ci sa recunoastem orice simbol al carui nume?, ci sa recunoastem orice simbol al carui numede afisare este ?.

19

Salvarea definitiilor intr-un fisier

• Pentru a putea salva functiile definite pentru oreapelare urmatoare, le vom stoca intr-un fisierreapelare urmatoare, le vom stoca intr-un fisiercu extensia *.lisp.

• Acest fisier il putem crea in Notepad, avand grijaca, in momentul salvarii sa alegem optiunea AllFiles.

• Fisierul se salveaza in directorul in care aveminstalat Lisp-ul.

20

Compilarea si incarcarea definitiilor• Pentru a vedea eventualele erori/atentionari,• Pentru a vedea eventualele erori/atentionari,

vom compila fisierul rezultat, prin apelareafunctiei (compile-file “nume.lisp”).

• Pentru incarcarea in memoria Lisp, se folosesteapelarea (load “nume”).

• Se apeleaza apoi functia definita in modul clasicde lucru cu Lisp.

21

Exemplu – Suma a trei numere

22

Inversarea unei liste de 4 membri

23

Calculul discriminantului• Presupunem ca avem o ecuatie de gradul 2 fara

radacini complexe.• Testul complet al tuturor posibilitatilor va• Testul complet al tuturor posibilitatilor va

constitui o parte a cursului viitor.

24

Calculul radacinilor ecuatiei de gradul 2• Presupunem ca ecuatia nu are radacini complexe

si ca a este diferit de 0.

25

Functii predicat

• Acestea sunt functiile care intorc fie True (T), fieFalse (NIL).False (NIL).

• Pana acum, am intalnit exemple de astfel defunctii, =, char=, string=, eql, equal.

• O multime standard de functii predicat sunt celecare verifica tipul obiectelor Lisp.care verifica tipul obiectelor Lisp.

• O astfel de functie intoarce T daca tipulobiectului este cel specificat si NIL, altfel.

26

Exemple

27

Combinarea functiilor predicat

• Pentru a alatura rezultatele functiilor predicat,Lisp utilizeaza operatorii logici and si or.Lisp utilizeaza operatorii logici and si or.

• Acesti operatori lucreaza cu un numar arbitrar deelemente.

• Fiecare se opreste atunci cand intalneste primul • Fiecare se opreste atunci cand intalneste primul rezultat al unui predicat care deja conduce la raspunsul final:▫ Un T in cazul unei disjunctii;▫ Un NIL in cazul unei conjuctii.

28

Testarea raportului a doua numere

29

Compararea a doua numere

30

Lungimea unui string / a unei liste

31

Pe saptamana viitoare…

Expresii conditionale

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

2

Conditionalul

• Lisp are doua tipuri de expresii conditionale:▫ IF▫ IF▫ COND

• Functia care exprima clasicul IF are formularea (if test then else):▫ if e cuvint cheie.▫ if e cuvint cheie.▫ Daca test e adevarat, atunci se intoarce valoarea

lui then; altfel, vom obtine valoarea lui else.

3

Exemple simple

4

Exemple – Testare raport

5

Exemple – Modulul unui numar

6

Exemplu – Functia semn

7

Conditionalul COND• Expresia IF este potrivita pentru a alege intre

doua calcule pe baza unui singur test.

• Insa, in momentul in care avem de ales intre teste multiple, folosirea lui IF este greoaie si greselile pot aparea foarte usor.

• In aceste cazuri, vom utiliza alternativa lui IF si • In aceste cazuri, vom utiliza alternativa lui IF si anume conditionalul COND.

• Evident, in cazul invers, cand avem un singur test, este mai eficient sa folosim IF.

8

Conditionalul COND

• Functia COND are sintaxa (cond (p1 e1) … (pnen)):en)):▫ Evalueaza pi-urile in ordine pana cand unul dintre

ele, pj, este true.▫ Atunci intoarce ej.▫ Daca niciun pi nu este evaluat ca True, atunci

intoarce False.

• Fiecare lista (pi ei) poarta numele de pereche COND:▫ pi este testul (conditia).▫ ei este expresia.

9

Exemplu – Functia semn - Reluare

10

Asemanare cu IF-ul procedural

if p then e (cond (p e )if p1 then e1

else if p2 then e2

else if p3 then e3

else e4

(cond (p1 e1)(p2 e2)(p3 e3)(t e4))

11

Recursivitate

12

Folosirea functiilor recursive

• Sa calculam recursiv suma a doua numerenenegative.nenegative.

13

Observarea recursivitatii

14

Definirea unei functii recursive

• Fiecare functie recursiva poate avea formularea:▫ (defun functie lista_variabile (cond ▫ (defun functie lista_variabile (cond

perechi_cond))▫ sau (defun functie lista_variabile (if test then

else)).

• In cazul unei functii recursive corect definite, un apel cu parametri nepotriviti poate genera o apel cu parametri nepotriviti poate genera o recursivitate infinita.

15

Functia ASSERT

• Pentru a evita argumente gresite, atunci cand definim o functie putem folosi constructia assert.definim o functie putem folosi constructia assert.

• Sintaxa acesteia este:(assert asertie (variabile_de_schimbat) string

variabile_mentionate)▫ Asertia este evaluata.▫ Daca este True, functia se executa normal.▫ Daca este True, functia se executa normal.

16

Functia ASSERT

(assert asertie (variabile_de_schimbat) stringvariabile_mentionate)▫ Daca este False, Lisp printeaza o eroare:▫ Daca este False, Lisp printeaza o eroare: Ii da utilizatorului optiunea de a termina sau de a

schimba valorile acelor variabile_de_schimbat. Mesajul din string este afisat. In acest string putem mentiona anumite variabile,

scriind ~S pentru fiecare si trecandu-le in cadrulcampului variabile_mentionate.campului variabile_mentionate.

17

Redefinim suma a doua numere

18

19

O alta versiune a sumei

20

Produsul a doi intregi nenegativi

21

Produsul a doi intregi nenegativi

22

Produsul a doi intregi nenegativi

23

Ridicarea unui numar la putere

24

Recursivitatea la liste

• Ca prim exemplu, sa incercam definirea versiuniiproprii a functiei length, care determinaproprii a functiei length, care determinalungimea unei liste.

• Partea recursiva: Lungimea unei liste nevideeste cu o unitate mai mare decat lungimearestului listei.restului listei.

• Conditia de terminare: Lungimea listei vide() este 0.

25

Lungimea unei liste

(

26

Apartenenta unui element la o lista

27

Testarea daca o lista e formata sau nu numai din numere

28

Testarea daca o lista e formata sau nu numai din numere – alta versiune

29

Verificarea egalitatii lungimii a doua liste

30

Verificarea egalitatii lungimii a doua liste - varianta

31

Pe saptamana viitoare…

Recursivitate – continuare

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Testarea daca e1 se afla inaintea lui e2 in lista l

Numarul aparitiilor unui element intr-o lista

Testarea egalitatii elementelor a doua liste

Elementul de pe pozitia n din lista l

Obtinerea listei elementelor de dupa pozitia n

Construirea copiei unei liste

Concatenarea a doua liste

Inversarea unei liste

Transforma un element simplu in

lista ce contine acel element.

Inversa – alta solutie

Substituirea primei aparitii a unui element dintr-o lista cu un element nou

Transformarea din lista in multime

Reuniunea a doua multimi

Lista cu primele n elemente din lista data ca argument

Inserarea unui element intr-o multime

Intersectia a doua multimi

Diferenta a doua multimi

Verificarea daca o multime e submultime a unei alte multimi

Egalitatea a doua multimi – fara a lua in considerare ordinea elementelor

Produsul cartezian a doua multimiVom defini o functie care cupleaza un element dat

cu fiecare membru al unei liste, rezultand o lista a liste, rezultand o lista a

cuplurilor formate.

Cuplarea unui element cu fiecare membru al unei liste

Prefixul unei liste

Sufixul unei liste

Schimbul intre 2 elemente dintr-o lista

Pozitia unui element intr-o listaPozitia unui element intr-o lista

La apelare, contorul se initializeaza cu valoarea 1.

Adunarea succesiva a cate doua elemente dintr-o lista

∑n− 1

�x [ i ]� x [i�1]�∑i= 1

�x [ i ]� x [i�1]�

Impartirea unei liste in doua liste: prima cu elemente pare, cea de-a doua cu impare

Impartirea unei liste in doua liste: prima cu elementele de pe pozitiile pare, cea de-a doua cu cele de pe pozitiile impare

Determinarea tuturor numerelor pana la un numar n dat, care sunt divizibile cu un numar k

Maximul unei liste – Metoda 1

Maximul unei liste – Metoda 2

Maximul unei liste – Metoda 3

Quicksort – sortarea unei liste

Pe saptamana viitoare…

Prolog vs. Lisp prin Exemple

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Numarul elementelor dintr-o lista

• Dacă lista este vidă, numarul elementelor sale• Dacă lista este vidă, numarul elementelor saleeste zero: aceasta este condiţia de oprire arecursivităţii.

• În clauza recursiva, primul element din listă nune interesează, vrem doar să îl eliminăm ca sănumărăm câte elemente are lista rămasă.numărăm câte elemente are lista rămasă.

• Numărul curent va fi, de fiecare data, egal cu 1plus numărul elementelor din lista rămasă.

Numarul elementelor dintr-o listaPROLOG

nr_elem([], 0). nr_elem([_ | Rest], N) :- nr_elem(Rest, N1), N is N1 + 1.

?- nr_elem([1, 2, 3], X).

X = 3

LISP

(defun lungime(l)(if (null l) 0 (+ 1 (lungime (rest l)))) )

>(lungime ‘(1 5 6 4))4

Suma elementelor dintr-o lista• Dacă lista este vidă, suma elementelor sale este

zero: aceasta este condiţia de oprire azero: aceasta este condiţia de oprire arecursivităţii.

• În clauza recursiva, primul element din listă neinteresează de data aceasta, dupa carecalculam suma elementelor din lista rămasă.

• Suma curentă va fi, de fiecare data, egală cuelementul curent plus suma elementelor din listarămasă.

Suma elementelor dintr-o listaPROLOG

suma([], 0).suma([P|Rest], S) :- suma(Rest, S1), S is S1 + P.

?- suma([1, 2, 3], X).

X = 6

LISP

(defun suma (l)(if (null l) 0 (+ (first l) (suma (rest l))))) >(suma ‘(1 5 6 4))16

Media elementelor unei liste

• Media unei liste se calculeaza drept suma elementelor din lista / numarul acestora.elementelor din lista / numarul acestora.

PROLOG

media(L) :- nr_elem(L, N), suma(L, S), Media is S/N, write('Media este '), write(Media).

LISP

(load “suma”)(load “lungime”)(defun media (l)(/ (suma l) (lungime l))

?- media([1, 2, 3]).

Media este 2.

(/ (suma l) (lungime l)))

>(media ‘(1 5 6 4))4

Apartenenta unui element la o lista

7/56

• Vom defini predicatul apartine/2, unde primulargument reprezintă elementul pentru careverificăm apartenenţa, iar al doilea este lista.

• X aparţine listei dacă este capul listei sau dacă• X aparţine listei dacă este capul listei sau dacăaparţine coadei acesteia.

12:15 PM

Apartenenta unui element la o lista

PROLOG

8/56

LISP

(defun membru (n l)apartine(X, [X | _]).apartine(X, [Y | Rest]) :- apartine(X, Rest).

?- apartine (3, [1, 3, 2]).Yes

?- apartine (4, [1, 3, 2]).

(defun membru (n l)(cond ((null l) nil)((eql n (first l)) t)(t (membru n (rest l)))))

>(membru 3 ‘(1 4 3 5 6))TNo

12:15 PM

T

>(membru 3 ‘(1 5 6 8))NIL

Inversarea unei liste

• Pe langa lista initiala si lista in care depunemrezultatul, se considera si o lista temporara care

9/56

rezultatul, se considera si o lista temporara careeste initial vida.

• Capul listei curente se adauga la inceputul listeitemporare – acesta era initial goala, decielementele se vor adauga in ordine inversa.elementele se vor adauga in ordine inversa.

• Cand lista care trebuie inversata devine vida,unificam lista finala cu cea temporara.

12:15 PM

Inversarea unei listePROLOG

inv(L, Linv) :- inv1(L, [], Linv).inv1([], L, L).inv1([], L, L).inv1([X|Rest], Temp, L) :- inv1(Rest, [X|Temp], L).

?- inv([1, 2, 3], L).L = [3, 2, 1]

LISP

(defun inversa (l)(inv l '()))

(defun inv(l1 l2)(if (null l1) l2 (inv (rest l1) (cons (first l1) l2))))

>(inversa ‘(1 2 3 4))(4 3 2 1)

Pozitia i dintr-o lista

• Enuntul problemei:• Enuntul problemei:

▫ Dându-se o listă şi un număr întreg pozitiv i, să se găsească elementul aflat pe poziţia i în listă.

• Avem doua argumente de intrare, o lista si un numar care da pozitia care ne intereseaza.

• Cum rezolvam problema: scadem i-ul cu cate o unitate si, in acelasi timp, scoatem cate un element din lista. Cand i-ul este 1, primul element din lista este cel cautat.

Pozitia i dintr-o listaPROLOG

pozi([X|_], 1, X).pozi([X|_], 1, X).pozi([_A|R], I, X) :- I1 is I - 1, pozi(R, I1, X).

? - pozi([mere, portocale, pere, gutui], 2, Ce).Ce = portocale

LISP

(defun elemi(i l)(if (= n 1) (first l) (elemi (- n 1) (rest l))))

>(elemi 3 ‘(1 4 5 6))5

Pozitia unui element intr-o lista1 4 6 7 8 9 0 3 2 4 5 6 7

13/56

• Enunt problema:▫ Având date o listă şi un element care aparţine

acestei liste, să se specifice pe ce poziţie este situat elementul în lista dată.

• Avem doua argumente de intrare:▫ Lista in care se gaseste elementul▫ Lista in care se gaseste elementul▫ Elementul pentru care trebuie sa gasim pozitia

• Vom mai construi un predicat care sa contina si o variabila contor care este initial 1.

Pozitia unui element intr-o listaPROLOGpozx(L, X, P):- pozx(L, X, 1, P).

1 4 6 7 8 9 0 3 2 4 5 6 7

14/56

pozx(L, X, P):- pozx(L, X, 1, P).pozx([X|_], X, P, P).pozx([_|R], X, C, P) :- C1 is C + 1, pozx(R, X, C1, P).

? – pozx([ion, petre, marin, olivia], marin, P).P = 3

LISP(defun pozitia (l el p)(defun pozitia (l el p)(if (eql el (first l)) p (pozitia (rest l) el (+ p 1))))

(defun poz (l el)(poz l el 1))

>(poz ‘(a b c d e) ‘d 1)4

Stergerea aparitiilor unui element dintr-o lista

15/56

• Enunt problema:▫ Să se şteargă toate apariţiile unui element dintr-o

listă.• Avem doua argumente de intrare:

▫ Lista din care se vor sterge aparitiile unui element▫ Elementul care trebuie sters▫ Elementul care trebuie sters

• Argumentul de iesire va fi noua lista carenu va mai contine elementul dat.

Stergerea aparitiilor unui element dintr-o lista

PROLOG

16/56

sterg([], _, []).sterg([N|Rest], N, Rez) :- sterg(Rest, N, Rez).sterg([M|Rest], N, [M|Rez]) :- sterg(Rest, N, Rez).? – sterg([1, 4, 6, 8, 6, 12, 6], 6, L).L = [1, 4, 8, 12]

LISP(defun sterg (l el)(defun sterg (l el)(cond ((null l) ‘())((eql (first l) el) (sterg (rest l) el))(t (cons (first l) (sterg (rest l) el))) )>(sterg ‘(1 4 6 8 6 12 6) 6)(1 4 8 12)

Eliminarea duplicatelor dintr-o lista• Enunt problema:

▫ Să se realizeze eliminarea duplicatelor dintr-o listă dată.dată.

• Argument de intrare:▫ O lista data

• Argument de iesire:▫ Lista rezultata prin eliminarea duplicatelor din lista

data.data.• Luam fiecare element din prima lista si verificam

daca apartine restului listei (adica daca mai apare in lista).▫ Daca nu mai apare, atunci il adaugam in lista rezultat▫ Altfel, nu il adaugam.

12:15 PM

Eliminarea duplicatelor dintr-o lista

PROLOGduplicate([], []).duplicate([X|R1], L) :- apartine(X, R1),

duplicate(R1, L).duplicate(R1, L).duplicate([X|R1], [X|R2]) :- duplicate(R1, R2).

? – duplicate([7, 9, 7, 11, 11], L).L = [9, 7, 11]

LISP

(defun duplicate(l)(defun duplicate(l)(cond ((null l) '())((member (first l) (rest l)) (duplicate (rest l)))(t (cons (first l) (duplicate (rest l)))))))>(duplicate ‘(7 9 7 11 11))(9 7 11)

Maximul unei liste

• Consideram primul element al listei ca fiind maximul.

19/56

• Apelam un alt program ce are drept argumente lista ramasa si elementul considerat.

• Parcurgem restul listei; daca gasim un element (capul listei curente) mai mare decat maximul, acesta va deveni noul maxim.

• Altfel, mergem mai departe in restul listei.

• Recursivitatea se incheie cand ajung la lista vida si se intoarce argumentul corespunzator maximului.

Maximul unei listePROLOG

max([P|Rest]) :- Max = P, max1(Rest, Max, M).

20/56

max([P|Rest]) :- Max = P, max1(Rest, Max, M).max1([], Max, Max).max1([P|R], Max, M) :- P > Max, max1(R, P, M); max1(R, Max, M).

?- max([4, 2, 5, 1]).Maximul este 5. LISP

(defun maxim2 (l max)(cond ((null l) (cond ((null l)

max)

((> (first l) max) (maxim2 (rest l) (first l)))

(t (maxim2 (rest l) max))))

(defun maxim1 (l)

(maxim2 (rest

Pozitia pe care se afla maximul unei liste

21/56

• Consideram primul element al listei ca fiind maximul sistabilim pozitia maximului drept 1.

• Apelam un alt predicat ce are drept argumente:▫ lista ramasa▫ elementul considerat drept maxim▫ elementul considerat drept maxim▫ pozitia pe care se afla acesta▫ si un contor care va numara elementele.

Pozitia pe care se afla maximul unei liste

• Parcurgem lista; daca gasim un element (capul noii liste)

22/56

• Parcurgem lista; daca gasim un element (capul noii liste)mai mare decat maximul:▫ acesta va deveni noul maxim▫ pozitia pe care se afla maximul devine contorul curent▫ si se incrementeaza contorul.

• Altfel, mergem mai departe in restul listei, incrementandcontorul.contorul.

• Recursivitatea se incheie cand ajung la lista vida siafisez argumentul corespunzator pozitiei pe care se aflamaximul.

Pozitia maximului unei listePROLOG

poz_max([P|Rest]) :- poz_max(Rest, P, 1, 1).

23/56

poz_max([P|Rest]) :- poz_max(Rest, P, 1, 1).

poz_max([], _, _, Poz) :- write('Maximul se gaseste pe pozitia '), write(Poz).

poz_max([P|R], Max, Contor, Poz) :- Contor1 is Contor + 1, Max < P, poz_max(R, P, Contor1,

Contor1).poz_max([_|R], Max, Contor, Poz) :- Contor1 is Contor + 1, poz_max([_|R], Max, Contor, Poz) :- Contor1 is Contor + 1,

poz_max(R, Max, Contor1, Poz).

?- poz_max([4, 2, 5, 1]).Maximul se gaseste pe pozitia 3

Pozitia maximului unei listeLISP

(defun pozmax(l)

24/56

(defun pozmax(l)(pozm (rest l) (first l) 1 2))

(defun pozm (l m p c)(cond ((null l) p)((> (first l) m) (pozm (rest l) (first l) c (+ c 1)))(t (pozm (rest l) m p (+ c 1)))(t (pozm (rest l) m p (+ c 1)))))> (poz_max ‘(4 2 5 1)3

Interclasarea a doua liste

• Ce presupune interclasarea?

25/56

• Ce presupune interclasarea?

• Avem doua liste care trebuie unite intr-una singura.

• Cele doua liste trebuie sa fie ordonate crescator.

• Elementele listei rezultate trebuie sa fie de asemenea in • Elementele listei rezultate trebuie sa fie de asemenea in ordine crescatoare.

Interclasarea a doua liste

• Capetele celor doua liste ce trebuie unite se compara.

26/56

• Capetele celor doua liste ce trebuie unite se compara.

• Cel mai mic dintre ele se va adauga la lista rezultat.

• Daca sunt egale, se adauga doar o data.

• Daca una dintre ele este vida, lista rezultat este cealalta.• Daca una dintre ele este vida, lista rezultat este cealalta.

Interclasarea a doua listePROLOG

27/56

interclasez([], L, L).interclasez(L, [], L).interclasez([P1|R1], [P2|R2], [P1|R3]) :- P1 < P2,

interclasez(R1, [P2|R2], R3).interclasez([P1|R1], [P1|R2], [P1|R3]) :- interclasez(R1, R2, R3).interclasez(R1, [P2|R2], [P2|R3]) :- interclasez(R1, R2, R3).

?- interclasez([1, 3, 7], [2, 3, 4, 8], L).?- interclasez([1, 3, 7], [2, 3, 4, 8], L).L = [1, 2, 3, 4, 7, 8]

Interclasarea a doua listeLISP

28/56

(defun interclasez (l1 l2)(cond ((null l1) l2)((null l2) l1)((< (first l1) (first l2)) (cons (first l1) (interclasez (rest l1) l2)))((= (first l1) (first l2)) (cons (first l1) (interclasez (rest l1) (rest l2))))(t (cons (first l2) (interclasez (rest l1) l2))))

> (interclasez ‘(1 3 7) ‘(2 3 4 8))(1 2 3 4 7 8)

Prefixul unei liste

• Pentru a testa daca o lista e prefixul altei liste,

29/56

• Pentru a testa daca o lista e prefixul altei liste,compar element cu element cele doua liste.

• Adica, verific daca elementul cap al unei liste prefixeste egal cu cel al listei complete.

• Daca raspunsul este afirmativ, merg mai departe.• Daca raspunsul este afirmativ, merg mai departe.

• Prima lista e prefix a celei de-a doua daca, la unmoment dat, lista prefix se incheie.

Prefixul unei listePROLOG

prefix([], _L).

30/56

prefix([X|R1], [X|R2]) :- prefix(R1, R2).

?- prefix([1,2], [1, 2, 3]).Yes

?- prefix([1,3], [1, 2,3]).No LISP

(defun prefix (l1 l2)(cond ((null l) t)

((eql (first l1) (first l2)) (prefix (rest l1) (rest l2)))(t nil)))

>(prefix ‘(1 2) ‘(1 2 3)) t>(prefix ‘(1 3) ‘(1 2 3)) nil

Sufixul unei liste

• Pentru a testa daca o lista e sufixul altei liste,

31/56

• Pentru a testa daca o lista e sufixul altei liste,parcurg lista completa pana intalnesc exact listasufix.

• Adica, scot elementul cap al listei mari, pana candcele doua liste sunt egale.

• Recursivitatea se opreste deci cand cele douaargumente sunt egale.

Sufixul unei listePROLOG

sufix(L, L).sufix(L, [_Y|Rest]) :- sufix(L, Rest).

32/56

sufix(L, [_Y|Rest]) :- sufix(L, Rest).

?- sufix([1,2,3],[1,2]).No?- sufix([1, 2, 3], [3]).Yes LISP

(defun sufix (l1 l2)(cond ((null l2) nil)(cond ((null l2) nil)((equal l1 l2) t)(t (sufix l1 (rest l2)))))

>(sufix ‘(2 3) ‘(1 2 3)) t

>(sufix ‘(1 3) ‘(1 2 3))nil

Numere pare, numere impare

• Enunt problema: • Enunt problema:

▫ Se dă o listă: să se obţină două liste din aceasta astfel încât prima din ele să conţină elementele pare iar a doua pe cele impare.

• Vom avea asadar o singura lista ca argument de intrare si doua liste ca argumente de iesire.

Numere pare, numere impare

PROLOG

pareimpare([], [], []).pareimpare([X|Rest], [X|R1], L2):-X1 is X mod 2, X1=0,

pareimpare(Rest, R1, L2).pareimpare([X|Rest], L1, [X|R2]):-pareimpare(Rest, L1, R2).

?- pareimpare([1, 2, 3, 4, 5, 6], L1, L2).L1=[2, 4 , 6]L1=[2, 4 , 6]L2=[1, 3, 5]

Numere pare, numere impareLISP(defun pare (l)(defun pare (l)(cond ((null l) '())((= (mod (first l) 2) 0) (cons (first l) (pare (rest l))))(t (pare (rest l)))))

(defun impare (l)(cond ((null l) '())((/= (mod (first l) 2) 0) (cons (first l) (impare (rest l))))(t (impare (rest l)))))(t (impare (rest l)))))

(defun pareimpare (l)(cons (pare l) (cons (impare l) '())))

>(pareimpare ‘(1 2 3 4 5 6))((2 4 6) (1 3 5))

Pozitii pare, pozitii impare

• Enunt problema:

▫ Se dă o listă: să se obţină două liste din aceasta astfel încât prima din ele să conţină elementele de pe poziţiile pare iar a doua pe cele de pe poziţiile impare.

• Vom avea asadar o singura lista ca argument de • Vom avea asadar o singura lista ca argument de intrare si doua liste ca argumente de iesire.

PROLOGparimpar([X], [], [X]).parimpar([X, Y],[Y], [X]).

Pozitii pare, pozitii impare

parimpar([X, Y],[Y], [X]).parimpar([X, Y|R], [Y|R1], [X|R2]) :- parimpar(R, R1, R2).? – pare([ion, marius, mananca, invata, mere, prolog], P, I).P = [marius, invata, prolog]I = [ion, mananca, mere]

LISP(defun impar(l)(if (null l) '() (cons (first l) (impar (rest (rest l))))))))(defun par(l)(if (null l) '() (cons (second l) (par (rest (rest l))))))(defun parimpar(l)(cons (impar l) (cons (par l) '())))>(parimpar ‘(1 5 6))((1 6) (5))

Ordonarea unui sir de numere

38/41

• Având în fişierul de intrare in.txt câte un număr urmat de caracterul punct pe fiecare linie, construiţi un predicat Prolog care să scrie în fişierul ordonat.txt şirul de numere ordonat crescător.▫ Citim numerele din in.txt intr-o lista, le ordonam in alta lista

si le scriem apoi in fisierul ordonat.txt.si le scriem apoi in fisierul ordonat.txt.▫ O sa facem in continuare numai ordonarea elementelor unei

liste.▫ Pentru aceasta, vom folosi metoda quicksort care utilizeaza

mecanismul divide et impera.

Ordonarea elementelor unei liste

39/41

PROLOG

sortez([], []).sortez([P|Rest], Lrez):- selectez(P, Rest, Mici, Mari), sortez(Mici,

MiciSort), sortez(Mari, MariSort), append(MiciSort, [P|MariSort], Lrez).

selectez(_, [], [], []).selectez(P, [P1|Rest], [P1|Mici], Mari):- P1 < P, selectez(P, Rest, Mici,

Mari).selectez(P, [P1|Rest], Mici, [P1|Mari]):- selectez(P, Rest, Mici, Mari).

?-sortez([2, 4, 5, 3, 1], L).L=[1, 2, 3, 4, 5]

Ordonarea elementelor unei listeLISP

(defun sortez (l)(if (null l) '()(append (sortez (selectMici (first l) (rest l))) (list (first l)) (sortez

40/41

(append (sortez (selectMici (first l) (rest l))) (list (first l)) (sortez (selectMari (first l) (rest l))))))

(defun selectMari (el l)(cond ((null l) '())((< el (first l)) (cons (first l) (selectMari el (rest l))))(t (selectMari el (rest l)))))

(defun selectMici (el l)(cond ((null l) '())((> el (first l)) (cons (first l) (selectMici el (rest l))))(t (selectMici el (rest l)))))

>(sortez ‘(1 4 5 3 2))(1 2 3 4 5)

Pana saptamana viitoare…

Prolog & Lisp vs. Java.Hill-climbing

Ruxandra Stoeanhttp://inf.ucv.ro/~rstoeanruxandra.stoean@inf.ucv.ro

Avantajele programelor PNP

Sunt orientate catre implementari logice.Sunt orientate catre implementari logice.Sunt extrem de potrivite pentru sistemele expert

apartinand domeniului inteligentei artificiale.Sunt usor de analizat, transformat, verificat,

intretinut.In general, sunt eficiente si competitive inIn general, sunt eficiente si competitive in

comparatie cu programele nedeclarative.Programul este mai degraba “intrebat” decat

executat.

Dezavantajeleprogramelor PNP

Nu sunt usor implementabile si utilizabile pentrualgoritmii matematici de cautare si optimizare dincadrul inteligentei artificiale.

Nu sunt cele mai potrivite pentru exprimareaalgoritmilor folositi in industrie.algoritmilor folositi in industrie.

Au mecanisme mai putin directe decat ale celornedeclarative si sunt considerate de multe ori mai“neprietenoase”.

O privire de ansamblu

• Cursul prezent incearca formularea uneiconcluzii finale asupra:concluzii finale asupra:▫ Diferentelor intre programarea procedurala si cea

nonprocedurala▫ Avantajelor si dezavantajelor fiecareia

• Se doreste asadar:▫ Rezolvarea unei probleme reale▫ Utilizarea unui algoritm clasic de inteligenta▫ Utilizarea unui algoritm clasic de inteligenta

artificiala▫ Comparatia intre implementarile in Prolog, Lisp

(programare nonprocedurala) si Java (programareprocedurala)

Hill-climbing

• Este ca si cand ai urca un munte, este ceata foarte deasa si ai avea foarte deasa si ai avea amnezie.

• Este vorba de o miscare continua inspre valori mai bune, mai mari (de aici, urcusul pe munte).

• Algoritmul nu mentine un arbore de cautare, un arbore de cautare, prin urmare, pentru fiecare nod se retine numai starea pe care o reprezinta si evaluarea sa.

Hill-climbing

A

B

functia hill_climbing(problema) intoarce o solutieSe pastreaza la fiecare reapelare: nodul curent si nodul urmator.

curent = genereaza_nod(stare_initiala[problema])

Cat timp este posibil executaurmator = un succesor al nodului curent

6/17

urmator = un succesor al nodului curentDaca eval(urmator) < eval(curent) atunci

intoarce curentcurent = urmator

Sfarsit cat timp

Dezavantaje hill-climbing

• Maxime locale: este vorba de un varf care este mai mic decat cel mai inalt varf din spatiul mai mic decat cel mai inalt varf din spatiul starilor. Cand se ajunge la maxime locale, algoritmul se opreste pentru ca nu mai poate cobori dealul.▫ Solutia gasita poate fi foarte slaba!

7/17

Problema comis-voiajorului• Problema:

▫ Se dau n oraşe

▫ Să se găsească un tur ▫ Să se găsească un tur complet de lungime minimală

• Reprezentare:

▫ Etichetăm oraşele 1, 2, … , n

▫ Un tur complet este o permutare (pt. n =4:permutare (pt. n =4:[1,2,3,4], [3,4,2,1])

• Spaţiul de căutare este imens:pentru 30 de oraşe sunt 30! 1032 tururi posibile!

Implementarea in Prolog

• Inregistrarea oraselor si a distantelor dintre

dist(1,2,10).dist(1,3,25).

dist(3,4,5).dist(3,5,20).

si a distantelor dintre acestea se va realiza prin adaugarea de fapte si formularea comutativitatii.

dist(1,3,25).dist(1,4,30).dist(1,5,15).dist(1,6,30).dist(1,7,40).dist(2,3,20).dist(2,4,10).

dist(3,5,20).dist(3,6,25).dist(3,7,7).dist(4,5,30).dist(4,6,15).dist(4,7,20).dist(5,6,17).comutativitatii. dist(2,4,10).

dist(2,5,25).dist(2,6,35).dist(2,7,45).

dist(5,6,17).dist(5,7,10).dist(6,7,10).orase(7).d(A,B,X):-dist(A,B,X).

d(A,B,X):-dist(B,A,X).

Predicatul principal

• Apeleaza algoritmul de hill-climbing si afiseaza drumul optim si costul sau.optim si costul sau.

• Se specifica de asemenea configuratia principala si numarul de iteratii (considerat drept conditie de oprire).

go:-start(Drum), hill_climber(Drum, 0, DrumOpt), writeln('Drumul optim este'), writeln(DrumOpt), write('Costul sau este '), eval(DrumOpt, FOpt), write('Costul sau este '), eval(DrumOpt, FOpt), writeln(FOpt).

start([1, 2, 3, 4, 5, 6, 7]).iteratii(200).

Algoritmul de hill-climbing

• Se implementeaza functia definita in algoritm.• Daca nu s-a ajuns la numarul de iteratii maxim, se • Daca nu s-a ajuns la numarul de iteratii maxim, se

incearca “urcarea” intr-o configuratie succesoare.• Daca se va reusi, nodul curent devine cel nou gasit si se

reapeleaza predicatul recursiv.

hill_climber(Drum, GenAnt, Drum):-iteratii(No), GenAnt == No.== No.

hill_climber(Drum, GenAnt, DrumOpt):-iteratii(No), GenAnt \= No, eval(Drum, F), climb(Drum, F, DrumNou),GenNou is GenAnt + 1, hill_climber(DrumNou, GenNou, DrumOpt).

Predicatul de urcare

• Se cauta un succesor al nodului curent.• Daca evaluarea acestuia este mai buna decat a • Daca evaluarea acestuia este mai buna decat a

celui curent, nodul curent devine cel nou generat.• Altfel, nodul curent ramane neschimbat.

climb(Drum, F, DrumNou):-mutatie(Drum, Drum1), eval(Drum1, FNou), FNou < F, Drum1), eval(Drum1, FNou), FNou < F, DrumNou = Drum1.

climb(Drum, _, Drum).

Evaluarea unei configuratii

• Evaluarea presupune costul drumului asociat unei configuratii, adica suma distantelor dintre unei configuratii, adica suma distantelor dintre orasele din circuit.

eval([X|Rest], F):-eval1([X|Rest], F1), ultim([X|Rest], U), d(U, X, D), F is F1 + D.

eval1([_], 0).eval1([_], 0).eval1([X,Y|Rest], F):-d(X,Y,C), eval1([Y|Rest], F1),

F is F1 + C.ultim([X], X).ultim([_|Rest], X):-ultim(Rest, X).

Mutarea intr-un nod succesor

• Presupune, de fapt, aplicarea unui operator de mutatie asupra configuratiei curente.asupra configuratiei curente.

• Se genereaza aleator doua pozitii si valorile corespunzatoare sunt interschimbate.

mutatie(Drum, Drum1):-orase(N), genereaza(N, Poz1, Poz2), cauta(Drum, Poz1, X),

cauta(Drum, Poz2, Y), schimba(Drum, X, Y, Drum1).cauta(Drum, Poz2, Y), schimba(Drum, X, Y, Drum1).

genereaza(N, Poz1, Poz2):- repeat, P1 is random(N) + 1, P2 is random(N) + 1, P1 \= P2, !, Poz1 = P1, Poz2 = P2.

Interschimbare

• Se cauta in configuratia curenta care sunt valorile de pe cele doua pozitii generate aleator.cele doua pozitii generate aleator.

• Cele doua valori se schimba intre ele.

cauta([X|_], 1, X).cauta([_|Rest], Poz, El):-Poz1 is Poz - 1, cauta(Rest, Poz1,

El).

schimba([], _, _, []).schimba([X|Rest], A, B, [B|Rest1]):- X == A, schimba(Rest, schimba([X|Rest], A, B, [B|Rest1]):- X == A, schimba(Rest,

A, B, Rest1).schimba([X|Rest], A, B, [A|Rest1]):- X == B, schimba(Rest,

A, B, Rest1).schimba([X|Rest], A, B, [X|Rest1]):- X \= A, X \= B,

schimba(Rest, A, B, Rest1).

Implementarea in Lisp• Inregistrarea conexiunilor dintre orase si costurilor asociate.

(setf (get 'n1 'conectat) '(n2 n3 n4 n5 n6 n7))(setf (get 'n1 'conectat) '(n2 n3 n4 n5 n6 n7))(setf (get 'n1 'costuri) '(10 25 30 15 30 40))(setf (get 'n2 'conectat) '(n1 n3 n4 n5 n6 n7))(setf (get 'n2 'costuri) '(10 20 10 25 35 45))(setf (get 'n3 'conectat) '(n1 n2 n4 n5 n6 n7))(setf (get 'n3 'costuri) '(25 20 5 20 25 7))(setf (get 'n4 'conectat) '(n1 n2 n3 n5 n6 n7))(setf (get 'n4 'costuri) '(30 10 5 30 15 20))(setf (get 'n5 'conectat) '(n1 n2 n3 n4 n6 n7))(setf (get 'n5 'conectat) '(n1 n2 n3 n4 n6 n7))(setf (get 'n5 'costuri) '(15 25 20 30 17 10))(setf (get 'n6 'conectat) '(n1 n2 n3 n4 n5 n7))(setf (get 'n6 'costuri) '(30 35 25 15 17 10))(setf (get 'n7 'conectat) '(n1 n2 n3 n4 n5 n6))(setf (get 'n7 'costuri) '(40 45 7 20 10 10))

Functia principala si ciclul de hill-climbing

• Se incepe cu o configuratie initiala si iteratia 0.• Pana cand se ajunge la numarul de iteratii maxim,

se incearca “urcarea” intr-o configuratie mai buna.se incearca “urcarea” intr-o configuratie mai buna.• Atunci cand algoritumul se opreste, se intoarce

drumul optim si costul sau.

(defun porneste ()(hill_climber '(n1 n2 n3 n4 n5 n6 n7) 0))

(defun hill_climber (l it)(if (= it 200) (cons (performanta l) l) (hill_climber (climb l) (+ it 1))))

“Urcarea” intr-o configuratie mai buna• Se genereaza o noua configuratie a drumului prin

schimbul valorilor intre doua pozitii aleator selectate.selectate.

• Daca evaluarea acesteia este mai buna decat cea a nodului curent, se retine noul drum drept cel curent.

(defun climb (l)(if (< (performanta (mutatie l)) (performanta l)) (get (if (< (performanta (mutatie l)) (performanta l)) (get

'lista 'mut) l))

(defun mutatie (l)(setf (get 'lista ' mut) (schimba l (cauta l (+ (random

7) 1)) (cauta l (+ (random 7) 1)))))

Evaluarea unei configuratii• Drumul va fi permanent dat de lista nodurilor.• Pentru a calcula costul sau, va trebui sa gasim lista

costurilor asociata acestei succesiuni de noduri, ce va fi costurilor asociata acestei succesiuni de noduri, ce va fi apoi insumata.

(defun performanta (l)(evaluare (append (transforma l) (fl l))))

(defun evaluare (l)(if (null l) 0(if (null l) 0(+ (first l) (evaluare (rest l)))))

(defun fl (l)(cons (gaseste (first (last l)) (first l)) '()))

Lista costurilor asociate drumului

• Pentru doua orase, se cauta pozitia celui de-al doilea in lista de conexiuni a primului.doilea in lista de conexiuni a primului.

• Apoi se cauta costul corespunzator acestei pozitii in lista costurilor asociat primului oras.

(defun transforma (l)(if (null (rest l)) '() (cons (gaseste (first l) (second l))

(transforma (rest l)))))

(defun gaseste (x y)(cauta (get x 'costuri) (cauta2 (get x 'conectat) y 1)) )

Doua functii de cautare

; intoarce elementul de pe pozitia poz din lista l(defun cauta (l poz)(defun cauta (l poz)(if (= poz 1) (first l) (cauta (rest l) (- poz 1))))

; intoarce pozitia elementului el din lista l(defun cauta2 (l el p)(defun cauta2 (l el p)(if (eql el (first l)) p (cauta2 (rest l) el (+ p 1))))

Mutatia asupra unei configuratii

• Se genereaza aleator doua pozitii in vector si se interschimba elementele de la aceste locatii.interschimba elementele de la aceste locatii.

• Se retine noua configuratie, pentru cazul in care este mai performanta.

(defun mutatie (l)(setf (get 'lista ' mut) (schimba l (cauta l (+ (random 7) 1))

(cauta l (+ (random 7) 1)))))

(defun schimba (l p1 p2)(cond ((null l) '())((eql (first l) p1) (cons p2 (schimba (rest l) p1 p2)))((eql (first l) p2) (cons p1 (schimba (rest l) p1 p2))) (t (cons (first l) (schimba (rest l) p1 p2)))))

Implementarea in Java

• Inregistrarea conexiunilor intre

d[0][1] = 10;d[0][2] = 25;d[0][3] = 30;

d[2][3] = 5;d[2][4] = 20;d[2][5] = 25;conexiunilor intre

orase si a costurilor asociate, plus comutativitatea.

d[0][3] = 30;d[0][4] = 15;d[0][5] = 30;d[0][6] = 40;d[1][2] = 20;d[1][3] = 10;d[1][4] = 25;

d[2][5] = 25;d[2][6] = 7;d[3][4] = 30;d[3][5] = 15;d[3][6] = 20;d[4][5] = 17;d[4][6] = 10;for (int i = 0; i < n; i++) d[1][4] = 25;

d[1][5] = 35;d[1][6] = 45;

d[4][6] = 10;d[5][6] = 10;

for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)

d[j][i] = d[i][j];

Functia principalapublic void go()

{int t = 1;int t = 1;

// initializarea drumului - primei solutii potentialefor (int i = 0; i < n; i++)

drum[i] = i;evalD = eval(drum);

while (t < it){

for (int i = 0; i < n; i++) // se lucreaza cu un drumTemp pentru modificaridrumTemp[i] = drum[i];

climb();climb();t++;

}

// afisam solutia finala si costul acesteiafor (int i = 0; i < n; i++)

System.out.print(drum[i] + " ");System.out.println(" cu evaluarea " + evalD);

}

Urcarea intr-o noua solutie

public void climb(){{mutatie();double evalNou = eval(drumTemp);if (evalNou < evalD){

for (int i = 0; i < n; i++)drum[i] = drumTemp[i];drum[i] = drumTemp[i];

evalD = evalNou;}

}

Evaluarea unei solutii

public double eval(int[] sol)public double eval(int[] sol){double evaluare = 0;

for (int i = 0; i < n - 1; i++)evaluare += d[sol[i]][sol[i + 1]];

evaluare += d[sol[n - 1]][sol[0]];evaluare += d[sol[n - 1]][sol[0]];

return evaluare;}

Mutatia asupra unei solutiipublic void mutatie()

{int p1 = -1, p2 = -1;

{int p1 = -1, p2 = -1;

// se genereaza doua pozitii aleatoare in vectorul drumTemp,// adica se aleg doua orase care se vor schimba in traseul posibilwhile (p1 == p2){

p1 = (int)Math.round((n - 1)* Math.random());p2 = (int)Math.round((n - 1)* Math.random());

}}

// schimba pozitiile p1 si p2 in drumTempint temp;temp = drumTemp[p1];drumTemp[p1] = drumTemp[p2];drumTemp[p2] = temp;

}

Ce sa alegem?

Programare procedurala Programare nonproceduralaProgramare procedurala Programare nonprocedurala Implementare directa/clasica a algoritmilor Eficienta Portabilitate Implementare dificila a cunostintelor despre

Prolog Inregistrare directa a cunostintelor despre problema Recursivitate Gandire speciala

cunostintelor despre problema

Lisp Apropiere de gandirea matematica Recursivitate Sintaxa dificila

Pana la examen…