Inteligenta Artificiala

Nu esti logat.

Laboratorul 2

(Deadline: 26.03.2017 23:59:59)

Daca nu sunteti logati exercitiile nu se mai afiseaza.

Liste

O lista se reprezinta sub forma: [el1,el2,...,eln].
O lista poate cuprinde elemente de tipuri diferite. Lista [a, 10, 4.5, X, str(a,b), [1,2,3], 1/2] e o lista valida.

Lista vida o notam astfel: [].

O notatie speciala pentru liste este [PrimulElement|RestulListei]. Prin aceasta se separa primul element de celelalte. PrimulElement e chiar primul element din lista pe cand RestulListei reprezinta lista ce contine toate elementele de la al 2-lea incolo (poate fi si vida daca lista initiala continea un singur element, de exemplu [a]=[a|[]]). De obicei notatia este [H|T], unde H vine de la head si T de la tail. Se pot scoate si mai multe elemente inainte de bara verticala, caz in care notatia arata in felul urmator: [H1,H2,...,Hn|T].

Exemple: | ?- L=[1|[2,3]].
L = [1,2,3] ?
yes
| ?- L=[1,2|[3]].
L = [1,2,3] ?
yes
| ?- L=[1,2,3|[]].
L = [1,2,3] ?
yes
| ?-

O lista nevida e un termen compus: | ?- compound([a,b,c]).
yes
| ?- compound([a]).
yes

Pe de alta parte o lista vida este un termen simplu: | ?- compound([]).
no

Aceasta se intampla deoarece modul de scriere al listei, cu paranteze drepte este doar o alta forma de scriere a structurii .(PrimElement,RestLista), parantezele drepte si virgula fiind de fapt operatori. | ?- L = .(a,[b,c]).
L = [a,b,c] ?
yes
| ?- L = .(a,[]).
L = [a] ?
yes

Unificarea in cazul listelor

Cum listele sunt elemente compuse, modul de realizare a unificarii este acelasi cu cel al termenilor compusi generali.

Atunci cand scriem listele sub forma desfasurata (adica nu sub forma [H|T], ci cu toate elementele enumerate), unificarea se realizeaza element de element, asa cum se poate vedea in exemplele de mai jos: | ?- [1,A,3]=[1,2,B].
A = 2,
B = 3 ?
yes
| ?- [1,2,C]=[2,D,3].
no
| ?-

Cand scriem listele sub forma [H1,...,Hn|T], nu este obligatoriu ca si termenul din stanga si cel din dreapta sa fie scrise astfel. Prologul, evident, face calculele necesare pentru a realiza unificarea. Iata cateva exemple: | ?- [H|T]=[1,2,3].
H = 1,
T = [2,3] ?
yes
| ?- [X|[]]=[1].
X = 1 ?
yes
| ?- [X]=[1,2].
no
| ?- [1,2|[3]]=[H|T].
H = 1,
T = [2,3] ?
yes
| ?-

Afisarea unei variabile de tip lista in consola

In momentul in care in inteogare apare o variabila de tip lista, valoarea ei calculata va fi afisata in consola ca si pentru celelalte variabile, insa apare o problema la listele mai lungi (care au mai mult de 10 elemente), cum se poate observa in exemplele simple de interogari de mai jos unde s-a folosit doar operatorul de unificare =. | ?- L=[1,2,3].
L = [1,2,3] ?
yes
| ?-
| ?- L=[1,2,3,4,5,6,7,8,9,10].
L = [1,2,3,4,5,6,7,8,9,10] ?
yes
| ?- L=[1,2,3,4,5,6,7,8,9,10,11].
L = [1,2,3,4,5,6,7,8,9,10|...] ?
yes
| ?-

In ultimul exemplu, cel marcat cu galben, lista are mai mult de 10 elemente. In cadrul setarilor default ale sicstus-ului, pentru o lista mai mare de 10 elemente se afiseaza doar primele 10, urmate de |... (observati operatorul |) care simbolizeaza restul listei. Daca dorim sa afisam mai multe elemente din listele mai lungi, trebuie sa intram in meniul de afisare al variabilelor. Putem face asta in urmatorul mod:

| ?- L=[1,2,3,4,5,6,7,8,9,10,11].
L = [1,2,3,4,5,6,7,8,9,10|...] ? h
Top-level options:
   RET y     no more choices
     ; n     more choices
       b     break
       <     reset printdepth
       < <n> set printdepth
       ^     reset subterm
       ^ <n> set subterm
     ? h     print this information
? < 50
L = [1,2,3,4,5,6,7,8,9,10,11] ?
yes
| ?-

In momentul in care ne afiseaza valoarea unei variabile si apare dupa ea simbolul ?, de fapt un prompt care asteapta o comanda, in loc sa dam comenzile obisnuite de enter sau ; pentru a continua sau a opri afisarea solutiilor, vom da optiunea h care afiseaza toate comenzile posibile. Dintre optiunile posibile observam si < <n> set printdepth care, dupa cum ii spune si numele, seteaza adancimea de printare. Si il setam de exemplu la 50, cum se vede si in afisajul de mai sus. Daca vreti sa reveniti la afisarea default puteti scrie doar un < fara numar in dreapta, si atunci va reveni la maximul de 10 elemente.

Mai multe despre liste gasiti in documentatia pentru sicstus prolog.

Rezolvarea unei probleme cu liste

In majoritatea cazurilor, un exercitiu cu liste se refera ori la generarea unei liste ori la parcurgerea ei si prelucrarea elementelor sale. Unele exercitii pot implica atat generare cat si parcurgere de liste.

Modurile de tratare ale celor doua cazuri sunt foarte asemanatoare. Pseudocodul pentru problemele de parcurgere si evaluare a elementelor unei liste arata astfel: pred([H|T], ...alti parametri...):- ... diverse conditii... , evalueaza(H), pred(T, ...), ... .
pred([], ...) :- ... .
Practic lista se scrie sub forma [H|T] pentru a izola primul element de restul listei si pentru a-l prelucra, iar apoi restul listei, T, este trimis intr-un apel recursiv al predicatului, pentru a-i fi evaluate mai departe elementele. Practic T-ul din acest prim pas recursiv incepe cu al doilea element al listei si cand ajunge in apelul recursiv sa fie unificat cu o scriere de forma [H1|T1] va fi izolat si prelucrat primul sau element (adica al doilea din lista initiala). Acest procedeu se repeta pana se ajunge la prelucrarea ultimului element. In acel apel recursiv lista data ca parametru a ajunss doar cu un element si anume ultimul din lista initiala si cum stim ca lista [elem]=[elem|[]] urmatorul apel recursiv va fi pentru o lista vida, adica pred([], ...). In acel moment stim ca evaluarea listei s-a terminat deci trebuie sa oprim si recursivitatea, ceea ce se realizeaza printr-o regula in care parametrul corespunzator listei este dat sub forma de lista vida, adica: pred([], ...) :- ... .

Pseudocodul general pentru rezolvarea problemelor de generare a unei liste: pred([H|T], ...alti parametri...):- ... diverse conditii... , calculeaza(H), pred(T, ...), ... .
pred([], ...) :- ... .
Ce s-a schimbat aici? In loc sa cunoastem lista si sa prelucram valoarea lui H, o calculam. Iar apoi trimitem restul necunoscut al listei, T, intr-un apel recursiv. In urma primului pas de recursivitate, cand se calculeaza primul element al T-ului transmis, de fapt se calculeaza elementul de pe a doua pozitie din lista initiala. Procedeul continua pana la calcularea ultimului element. In acel moment, lista trebuie "inchisa". Pentru a va gandi la acest ultim pas, cel mai simplu e sa va ganditi la cazul particular in care lista calculata va rezulta intr-o lista cu un singur element. Lista e calculata prin apelul pred([H|T], ...). L-am calculat pe H; dar cat trebuie sa fie T ca sa avem o lista de un singur element? [H|[]]=[H] deci T ar trebui sa fie egal cu [], motiv pentru care punem acea ultima instructiune: pred([], ...) :- ... . care are intr-adevar rolul de a termina lista.

Concatenarea a doua liste

Sa luam un exemplu de exercitiu cu liste. Presupunem ca dorim sa construim un predicat care primeste doua liste si realizeaza concatenarea lor intr-o a treia lista (lista rezultat). Deci predicatul nostru va avea doi parametri de intrare si unul de iesire:
concat(+L1,+L2,-L3)
Pentru a realiza lista rezultat ar trebui sa copiem in ea elementele din prima lista si in urma lor elementele din a doua lista. Evident, nu putem realiza acest lucru in mod direct, deoarece dintr-o lista, in Prolog, putem accesa in mod direct doar primul element, prin scrierea [H|T], sau primele n elemente, cu n fixat printr-o scriere de forma [H1,H2,..,Hn|T].

Sa consideram exemplul cu L1=[a,b,c] si L2=[d,e,f] lista rezultat va fi [a,b,c,d,e,f]:

Observam ca lista rezultat are finalul egal cu L2, deci ea ar putea fi unificata cu o scriere de forma [H1,H2,...,Hn|L2], punand astfel lista L2, la final, ca element intreg fara a fi necesar sa-i descoperim elementele. Problema noastra e cu primele elemente, cele din L1, pentru ca nu cunoastem n-ul, nu stim pe caz general cate elemente sunt in prima lista ca sa putem sa le concatenam direct, deci nu putem folosi in mod direct scrierea [H1,H2,...,Hn|L2].

Practic trebuie sa descoperim elementele din L1 mai intai, si asta vom reliza printr-o parcurgere (element de element), deci lista L1 va fi scrisa sub forma: [H|T]. De asemenea mai putem observa din exemplul de mai sus ca primul element din L1 ([a,b,c]) e acelasi cu primul element din lista rezultat ([a,b,c,d,e,f]). Prin urmare concat va avea pasul recursiv de forma:
concat([H|T],L2,[H|Trez]):- concat(T,L2,Trez). Astfel, pentru exemplu, avem initial datele in formatul urmator:



Deci, cand facem interogarea | ?- concat([a,b,c],[d,e,f],L). cand va intra in primul pas de recursivitate datele vor fi prelucrate astfel:



Dupa cum se observa, lista [a,b,c] trimisa ca parametru se unifica in regula recursiva cu primul parametru al lui concat scris sub forma [H|T] astfel in H ajunge a iar in T lista [b,c]. Cum H a fost folosit si pentru a desemna primul element al listei rezultat, acesta devine tot a. Apoi prin apelul recursiv concat(T,L2,Trez) practic se face interogarea interna concat([b,c],[d,e,f],Trez). Cu alte cuvinte din lista L1 de 3 elemente ajungem la lista T de 2 elemente (care incepe de la al doilea element al lui L1), iar din lista rezultat am calculat primul element, urmand sa calculam si restul elementelor de la 2 incolo (restul de lista reprezentat prin Trez). Astfel, in urmatorul pas de recursivitate, avem:



Ca si inainte s-a copiat primul element din prima lista (care e de fapt al doilea element din lista L1) in prima pozitie din sublista rezultat (spun sublista fiindca ea e de fapt doar o parte din lista rezultat; observati faptul ca a fost practic calculat elementul de pe pozitia a doua din lista rezultat reala). Iar acum intern se realizeaza apelul concat([c],[d,e,f],Trez)

In pasul al treilea de recursivitate, lista [c] unificata cu [H|T] ajunge [c|[]], deci practic T-ul acum e lista vida.



S-a ajuns si la calcularea celui de-al treilea element din lista reala, insa odata cu acest pas s-au terminat si elementele din prima lista. Ce ramane de facut? Stim ca in rezultat ar trebui acum sa urmeze a doua lista. Dar prin faptul ca lista rezultat e scrisa drept [H|Trez], unde H-ul e deja calculat (adica are valoarea c, deci avem rezultatul intermediar [c|Trez]) si cum stim ca Trez trebuie sa contina chiar elementele din lista a doua putem pur si simplu sa spunem ca Trez e egal cu L2 (adica sa le unificam). Deci rezulta si regula (pasul de oprire): concat([],L2,L2). Aceasta regula rezolva si faptul ca nu tratam nicaieri cazul cand prima lista era vida. In urma pasului 3 apelul intern concat([],[d,e,f],Trez) nu mai poate intra pe regula recursiva deoarece scrierea [H|T] implica o lista cu cel putin un element, iar primul parametru acum e lista vida. Deci va intra pe regula pas de oprire. Aici prin faptul ca am folosit L2 si pe primul parametru si pe al doilea practic i-am spus Prologului ca avem in parametrii 2 si 3 acelasi obiect. Cum L2 e instantiat, dar al treilea nu, cele doua se unifica si la revenirea din apel in pasul 3 recursiv Trez va avea valoarea [d,e,f].

Acum se realizeaza intoarcerea din recursivitate. Apelurile au fost:
concat([a,b,c],[d,e,f],Trez) concat([b,c],[d,e,f],Trez) concat([c],[d,e,f],Trez) concat([],[d,e,f],Trez)
cu Trez necunoscut, iar revenirile sunt:
concat([],[d,e,f],[d,e,f]) concat([c],[d,e,f],[c,d,e,f]) concat([b,c],[d,e,f],[b,c,d,e,f]) concat([a,b,c],[d,e,f],[a,b,c,d,e,f])

Predicate predefinite pentru liste, in Sicstus 4

In Sicstus 4 exista cateva predicate predefinite pentru lucrul cu liste:

Predicatul member(?X,?L) verifica daca termenul X se afla in lista L, asa cum se vede in exemplul de mai jos:

| ?- member(3,[1,2,3,4,5]).
yes
| ?- member(7,[1,2,3,4,5]).
no
| ?-

Verificarea se face prin unificare, cu alte cuvinte, daca se poate realiza unificarea dintre X si unul dintre elementele listei, atunci predicatul se termina cu succes. De aceea predicatul member poate fi apelat si cu un X neinstantiat, si atunci X va avea pe rand ca valori, elementele din lista L:

| ?- member(X,[a,b,c,d]).
X = a ? ;
X = b ? ;
X = c ? ;
X = d ? ;
no
| ?-

Mai jos avem si un exemplu cu termeni compusi:

| ?- member(a(X,Y),[a(1,2),b(2,3), a(3,2,1), a(10,11)]).
X = 1,
Y = 2 ? ;
X = 10,
Y = 11 ? ;
no
| ?-
Predicatul member se poate apela si astfel: | ?- member(1,L).
L = [1|_A] ?
yes
| ?

In L se obtine o lista in care primul element este X-ul (in cazul acesta X=1) si coada listei este neinstantiata

Predicatul length(?L,?N) calculeaza in N lungimea unei liste date, L.

| ?- length([a,b,c,d],N).
N = 4 ?
yes
| ?-

In unele probleme avem nevoie sa pornim de la o lista de lungime data, dar fara niste valori efective in elemente. Pentru aceasta putem folosi length cu N-ul dat si L-ul neinstantiat:

| ?- length(L,4). L = [_A,_B,_C,_D] ? yes | ?-

Predicatul append(?L1,?L2,?Lrez)

este folosit pentru a concatena doua liste. Daca L1 si L2 sunt instantiate, atunci in Lrez se va calcula concatenarea lor: | ?- append([a,b,c,d],[e,f,g],L).
L = [a,b,c,d,e,f,g] ?
yes
Daca Lrez e instantiat iar in L1 se afla un prefix de-al lui Lrez, atunci in L2 se va calcula sufixul lui Lrez, obtinut prin eliminarea inceputului egal cu L1. | ?- append([a,b,c,d],L2,[a,b,c,d,x,y]).
L2 = [x,y] ?
yes

Daca Lrez e instantiat iar in L2 se afla un sufix de-al lui Lrez, atunci in L1 se va calcula prefixul lui Lrez, obtinut prin eliminarea sfarsitului egal cu L2.

| ?- append(L1,[x,y],[a,b,c,d,x,y]).
L1 = [a,b,c,d] ?
yes
| ?-

Predicatul sort(+L,-Ls) primeste o lista ca parametru de intrare si calculeaza lista continand elementele din prima lista in ordine crescatoare. De asemenea, elimina si duplicatele

| ?- sort([a,r,z,a,b,c,d],L).
L = [a,b,c,d,r,z] ?
yes
| ?-

Predicate predefinite - liste

Predicatul member(?X,?L) verifica daca o lista L contine elementul X.

| ?- member(b,[a,b,c]).
yes
| ?- member(x,[a,b,c]).
no
| ?-

Cand lista e instantiata, dar X-ul nu, predicatul member va avea ca solutii succesive in X elementele listei

| ?- member(X,[a,b,c]).
X = a ? ;
X = b ? ;
X = c ? ;
no
| ?-

Verificarea se face insa prin unificare (adica daca, de fapt, exista un element in lista data care sa se unifice cu X), de aceea merge si sa selectam din lista elementele care urmeaza un anume format, asa cum se vede in exemplul de mai jos: | ?- member(X+Y,[3+2,1-1,5+6+7,4+2]).
X = 3,
Y = 2 ? ;
X = 5+6,
Y = 7 ? ;
X = 4,
Y = 2 ? ;
no
| ?-

Predicatul append(?L1,?L2,?Lrez) se foloseste in special pentru concatenarea listelor.

| ?- append([a,b],[b,c,d],L).
L = [a,b,b,c,d] ?
yes
| ?-

Dar poate fi folosit si pentru a gasi toate perechile de liste care concatentate dau lista de pe al treilea parametru.

| ?- append(L1,L2,[b,c,d]).
L1 = [],
L2 = [b,c,d] ? ;
L1 = [b],
L2 = [c,d] ? ;
L1 = [b,c],
L2 = [d] ? ;
L1 = [b,c,d],
L2 = [] ? ;
no
| ?-

De asemenea putem avea doar unul dintre primii doi parametri instantiati, impreuna cu al treilea instantiat. De exemplu L1 si Lrez sunt instantiate, atunci calculeaza valoarea lui L2 astfel incat concatenarea dintre L1 si L2 sa fie Lrez. Putem gandi si ca obtinem lista prin taierea prefixului(daca L1 instantiat si L2 neinstantiat) sau sufixului (in cazul cand L1 neinstantiat si L2 instantiat) din lista de pe al treilea parametru.

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

Pentru calcularea lungimii unei liste putem folosi predicatul length(?Lista,?Nr). | ?- length([a,b,c],N).
N = 3 ?
yes
| ?-

Putem folosi predicatul si ca sa creem o lista de lungime N cu elemente neinstantiate.

| ?- length(L,3).
L = [_A,_B,_C] ?
yes
| ?-

Un exemplu de folosire a acestei forme. Vrem sa scriem predicatul care ne da prefixul de lungime N dintr-o lista data. De exemplu pentru lista de forma [a,b,c,d,e] si N=3, rezultatul ar fi [a,b,c].

%prefix(+L,+N,-Prefix)
prefix(L,N,Prefix):- length(Prefix,N),append(Prefix,_,L).

Mai sus s-a creat o lista numita Prefix, de N elemente neinstantiate care a fost transmisa lui append. Astfel append a gasit doua liste: una unificata cu Prefix, de lungime N, continand primele N elemente ale lui L si lista cu restul elementelor din L (care nu ne intereseaza, motiv pentru care am folosit _ ). | ?- prefix([a,b,c,d,e],3,Pref).
Pref = [a,b,c] ?
yes
| ?-

Siruri de caractere

Sirurile de caractere sunt practic liste de coduri ASCII. Sirurile de caractere se scriu intre ghilimele.

Atentie, nu le confundati cu sirurile intre apostroafe, acelea fiind de fapt atomi. Observati mai clar diferenta aici: | ?- X='abc'.
X = abc ?
yes
| ?- X="abc".
X = [97,98,99] ?
yes

Se poate realiza transformarea de la un sir de caractere la un atom (si invers) cu ajutorul unui predicat built-in: atom_codes(Atom, Sir) | ?- atom_codes(abc,Sir).
Sir = [97,98,99] ?
yes
| ?- atom_codes(T,"abc").
T = abc ?
yes

De asemenea exista si predicatul atom_chars(Atom,Lista_caractere) care pentru un atom dat obtine lista caracterelor ce-l compun, sau invers, fiind data lista, poate obtine atomul respectiv | ?- atom_chars(abc,L).
L = [a,b,c] ?
yes
| ?- atom_chars(T,[a,b,c]).
T = abc ?
yes

Atentie, nu uitati ca e o diferenta intre sirul "abc" care e o lista de coduri ASCII, deci de numere, si lista de caractere (atomi formati dintr-o singura litera) [a,b,c].

Pentru a obtine caracterul corespunzator unui cod ASCII, si invers pentru a obtine codul ASCII pentru un caracter dat, se foloseste char_code(Caracter,Cod) | ?- char_code(a,C).
C = 97 ?
yes

Asa cum e atom_chars pentru atomi, exista un predicat similar si pentru numere: number_chars | ?- number_chars(1234,L).
L = ['1','2','3','4'] ?
yes
| ?- number_chars(12.5,L).
L = ['1','2','.','5'] ?
yes
| ?- number_chars(-12.5,L).
L = [-,'1','2','.','5'] ?
yes
| ?-

Atentie, primul parametru trebuie sa fie neaparat numar. Nu va accepta un atom format doar din caractere numerice:

| ?- number_chars('1234',L).
! Type error in argument 1 of number_chars/2
! expected a number, but found '1234'
! goal:  number_chars('1234',_114)
| ?-

Similar, echivalentul lui atom_codes, dar pentru numere, este number_codes

| ?- number_codes(-12.5,L).
L = [45,49,50,46,53] ?
yes
| ?-

Ca sa concatenam doi atomi folosim atom_concat(+Atom1,+Atom2,-AtomiConcatenati).

| ?- atom_concat(abc,def,Rez).
Rez = abcdef ?
yes

Predicatul atom_concat poate fi folosit si sub forma atom_concat(-Atom1,-Atom2,+Atom) si calculeaza in Atom1 si Atom2 toate variantele de atomi care prin concatenare ar fi rezultat in al treilea parametru.

| ?- atom_concat(A1,A2,xy).
A1 = '',
A2 = xy ? ;
A1 = x,
A2 = y ? ;
A1 = xy,
A2 = '' ? ;
no
| ?-

Avem si un predicat care calculeaza lungimea unui atom: atom_length(+Atom,-Lungime) | ?- atom_length(abc, N).
N = 3 ?
yes
| ?-

Observatie: Adesea veti intalni o notatie de forma nume_predicat/numar. Numarul respectiv reprezinta numarul de parametri corespunzatori predicatului. Doua predicate cu acelasi nume dar cu numere diferite de parametri sunt predicate diferite.

Operatorul =..

In partea stanga trebuie sa aiba o structura iar in partea dreapta o lista. Poate fi folosit pentru a desparti o structura intr-o lista ce are ca prim element numele structurii, iar ca elemente urmatoare argumentele structurii, sau invers poate primi o lista pe care sa o transforme intr-o structura. De exemplu: | ?- ab(1,2,3)=..L.
L = [ab,1,2,3] ?
yes
| ?- X=..[ab,1,2,3].
X = ab(1,2,3) ?
yes
| ?- ab(1,2,3)=..[ab,1,2,3].
yes
| ?-

Putem avea in partea stanga si structuri compuse, formate cu ajutorul operatorilor: | ?- 2*4-8 =.. L.
L = [-,2*4,8] ?
yes
| ?-
| ?- X=.. [+, 2, 3].
X = 2+3 ?
yes
| ?-

In partea din stanga sunt acceptati si termeni simpli, caz in care lista din dreapta va contine un singur element (egal in valoare cu termenul simplu din partea stanga a operatorului): | ?- a =.. L.
L = [a] ?
yes
| ?- 2 =.. L.
L = [2] ?
yes
| ?- X =.. [a].
X = a ?
yes
| ?-

Predicate de I/O pentru consola

Predicatul read(-Termen) citeste un termen de la tastura si realizeaza unificarea dintre acesta si variabila data ca parametru al lui read. Citirea se face pana la intalnirea unui punct. | ?- read(X).
|: 3.
X = 3 ? yes
| ?- read(X).
|: 'ceva intre apostroafe'.
X = 'ceva intre apostroafe' ? yes
(yes apare imediat dupa semnul intrebarii deoarece enter-ul introdus dupa punct ramane in bufferul consolei si e transmis atunci cand afiseaza solutia, ca si cum am fi dat enter dupa afisarea solutiei).

Se pot citi si termeni compusi: | ?- read(X),X=t(Y).
|: t(a).
X = t(a),
Y = a ? yes
| ?-

Sau: | ?- read(a(X)).
|: a(5).
X = 5 ? yes
| ?-

Putem citi in acest mod inclusiv expresii formate cu operatori:

| ?- read(X+Y).
|: 2+3.
X = 2,
Y = 3 ? yes

Evident, merge sa facem asta numai daca termenul primit de read se poate unifica cu ceea ce citeste de la tastatura:

| ?- read(X+Y).
|: 7.
no
| ?-

Tot un predicat de citire este si get_char(-Chr). Acesta citeste insa, nu pana la punct, ci doar un caracter. | ?- get_char(X).
|: a

X = a ? yes
| ?-

Desi citeste doar un caracter, get_char asteapta apasarea tastei enter. Astfel chiar daca intorducem mai multe caractere, asa cum se vede in exemplul de mai jos, predicatul se va termina dupa ce apasam enter si va unifica termenul dat ca parametru doar cu primul caracter din sirul intordus de la tastatura.

| ?- get_char(X).
|: abc

X = a ? % Break level 1
% 1
| ?-

Un predicat foarte asemanator cu get_char este si get_code(-Chr). Acesta citeste codul ASCII al caracterului introdus de la tastatura, si ca si get_char citeste dupa ce s-a apasat tasta enter.

| ?- get_code(X).
|: a

X = 97 ? yes
| ?- get_code(X).
|: abc

X = 97 ? % Break level 1
% 1
| ?-

Predicatul write(+Termen) scrie valoarea acelui termen in consola. Write nu primeste ca argument decat un singur termen, deci daca dorim sa afisam mai multe valori va trebui sa folosim cate un write pentru fiecare. Termenii pot contine caractere escape precum \n, \t etc. | ?- write(3).
3
yes
| ?- write('miau miau').
miau miau
yes
| ?- write('miau\nmiau').
miau
miau
yes

Predicatul nl afiseaza o linie noua.

Predicatul format(+Termen_de_afisat,+Lista_argumente) afiseaza termenul din primul parametru inlocuind secventele de tipul ~cod cu argumentele corespunzatoare din lista data ca al doilea parametru. Codurile sunt asemanatoare celor acceptate de functia printf din C, de exemplu in loc de %d, avem ~d, in loc de %s, avem ~s etc. La acestea se mai adauga cateva coduri specifice prologului. | ?- format('Un exemplu de lista: ~p cu ~d elemente',[[a,b,c],3]).
Un exemplu de lista: [a,b,c] cu 3 elemente
yes

Curatarea ecranului consolei

Nu exista un predicat predefinit care sa curete consola, si pana de curand chiar credeam ca nu se poate face acest lucru, dar am gasit definitia unui predicat, care poate face asta, pe o lista de discutii. Predicatul foloseste format si are urmatoarea definitie:

clear:-format('~c~s~c~s', [0x1b, "[H", 0x1b, "[2J"]).

Pentru a intelege mai bine de unde vin codurile respective, puteti citi despre secventele escape de control pentru terminale ANSI. Consola prolog respecta standardul ANSI deci suporta si ea aceste coduri.
Secventa 0x1b reprezinta codul caracterului ESC scris in hexa (observati prefixul 0x). Codul <ESC>[H conform linkului mai devreme precizat muta cursorul pe pozitia de home a consolei (in coltul din stanga sus). Codul <ESC>[2J sterge ecranul si muta cursorul inapoi la home-ul consolei.

De fapt din ce am testat am vazut ca merge scris direct:

format('~c~s', [0x1b, "[2J"]).
insa am considerat ca e mai bine sa vedeti si forma care se regasea pe lista de discutii mentionata la inceput.

Prin urmare va puteti defini un predicat:

sterge_ecran:-format('~c~s', [0x1b, "[2J"]).


Exercitii bonus:

Cut

Cut se noteaza cu ! si reprezinta operatia de oprire a backtracking-ului. Despre el puteti citi mai multe in documentatie dar si in capitolul scris de Blackburn, Bos si Striegnitz

Exista doua tipuri de cut:

Exercitii de laborator si teme: