Sisteme Expert

Laboratorul 6


(Deadline: 05.05.2019 23:59:59)

Daca nu sunteti logati exercitiile nu se mai afiseaza.

Module si bilioteci

Biblioteci din Sicstus Prolog

Se vor prezenta doar bibliotecile de care aveti nevoie in teme, dar daca sunteti interesati puteti vizualiza si lista completa de biblioteci oferite de Sicstus Prolog.

Biblotecile pe care le vom folosi:

Atentie, in cadrul laboratorului nu sunt explicate toate predicatele din bibliotecile mentionate mai sus, pe acestea este necesar sa le citit voi direct din documentatie. In cadrul laboratorului sunt mentionate doar predicatele cele mai importante si mai frecvent folosite prin teme (dar asta nu inseamna ca nu pot sa apara in teme si teste si din celelalte care nu apar in exemple)

Predicatele unei biblioteci se incarca prin predicatul use_module(library(nume_biblioteca)), de exemplu, in consola putem scrie:

| ?- use_module(library(random)).
% loading c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/random.po...
% module random imported into user
%  loading c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/types.po...
%  module types imported into random
%  loaded c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/types.po in module types, 0 msec 536 bytes
%  loading foreign resource c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/x86-win32-nt-4/random.dll in module random
% loaded c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/random.po in module random, 0 msec 12112 bytes
yes
| ?-
In acest moment putem folosi oricare din predicatele definite in cadrul acestei biblioteci. Bineinteles dupa inchiderea si redeschiderea consolei, predicatele nu vor mai fi incarcate si va trebui sa efectuam din nou un use_module.

Atunci cand avem nevoie de predicatele unei biblioteci in cadrul unui program vom apela use_module cu bibliotecile necesare, printr-o directiva, pentru ca incarcarea modulelor sa se faca in timpul consultarii programului, iar la inteogarea diverselor predicate din program sa avem deja biblioteca incarcata.
Sa luam ca exemplu programul urmator in care se calculeaza suma a N numere random (iar numerele random se si afiseaza pe masura ce se genereaza).

:-use_module(library(random)).

%suma(+Nr,-Suma)
suma(0,0).
suma(N,S):-N>0, N1 is N-1, suma(N1,S1),
            random(X),write(X),nl, S is S1+X.
Observam mai jos mesajele care insotesc incarcarea bibliotecii (marcate cu galben) afisate in timpul consultarii:
| ?- :-
consult('E:/schimbate_pe site/inteligenta artificiala 2013/exemplu_random_suma.pl').
% consulting e:/schimbate_pe site/inteligenta artificiala 2013/exemplu_random_suma.pl...
%  loading c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/random.po...
%  module random imported into user
%   loading c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/types.po...
%   module types imported into random
%   loaded c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/types.po in module types, 0 msec 536 bytes
%   loading foreign resource c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/x86-win32-nt-4/random.dll in module random
%%  loaded c:/documents and settings/userpc/desktop/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/random.po in module random, 0 msec 12128 bytes

% consulted e:/schimbate_pe site/inteligenta artificiala 2013/exemplu_random_suma.pl in module user, 0 msec 12792 bytes
| ?- suma(3,S).
0.2163110752346893
0.6344657121210742
0.7621277925774055
S = 1.612904579933169 ?
yes
| ?-

Timp

Cateva predicate utile referitoare la data curenta ori la timestamp gasiti in biblioteca library(system).

Sa luam ca exemplu un program care calculeaza in cat timp se afiseaza numerele de la 0 la 5000.

:-use_module(library(system)).

scrie_nr(N,N).
scrie_nr(N,C):-C<N, C1 is C+1, write(C), write(' '), scrie_nr(N,C1).


durata_scriere(D):-now(D1), scrie_nr(5000,0), now(D2), D is D2-D1.
Dupa ce termina de afisat, in D va fi numarul de secunde in care s-a executat programul.


Generare numere random.

Se foloseste biblioteca library(random). Predicatul random(X), calculeaza in X un numar random in intervalul [0,1). Atentie, la intoarcerea in backtracking la reinterogarea lui random nu se intoarce un nou numar aleator
| ?- random(X).
X = 0.2393746982829037 ? ;
no
| ?-
O sa observati ca la prima interogare a predicatului random(X) dupa ce a fost deschisa consola, primim mereu aceeasi valoare. Aceasta se intampla deoarece are acelasi seed setat default. Seed-ul este, conform documentatiei, o structura de 4 elemente random(X,Y,Z,B). Pentru a va asigura ca obtineti un numar random diferit de fiecare data cand deschideti consola, ar trebui sa setati seed-ul in functie de ceva ce se schimba in mod constant de la ultima deschidere a consolei. Deci, ne vom folosi de timp, si anume de predicatul now, care ne ofera de fiecare data un alt numar (timestamp). Pentru a seta seed-ul oricare si oricati din cei patru parametri pot sa depinda de timestamp restul ramanand eventual fixati pe o anume valoare (mai putin cazul cand X, Y, Z raman fixati si doar B variaza - am vazut ca da in unele cazuri acelasi numar - nu cunosc motivul fiindca nu am reusit inca sa aflu modul de calcul al numerelor pseudoaleatoare din cadrul Sicstus-ului). Deci un exemplu de astfel de predicat ar fi:
:-use_module(library(random)).
:-use_module(library(system)).
seteaza_seed:-now(X1), X is X1 mod 30000,
              now(B),setrand(random(X,100,100,B)).

Fisiere si directoare

In biblioteca library(file_systems) veti intalni predicate cu care puteti manipula fisierele si directoarele(sa testati daca exista, sa le stergeti, sa le redenumiti).

De exemplu, puteti vedea care este working directory-ul:
| ?- current_directory(D).
D = 'e:/schimbate_pe site/inteligenta artificiala 2013/' ?
yes
| ?-
Puteti folosi predicatul close_all_streams care va inchide, cum ii spune si numele toate fisierele deschise, evitand astfel eventualele erori care apar cand doriti sa deschideti un fisier cae a ramas deschis din alta interogare.
| ?- close_all_streams.
yes
Se pot crea si sterge directoare, se poate testa existenta unui director:
| ?- directory_exists('piticei').
no
| ?- make_directory('piticei').
yes
| ?- directory_exists('piticei').
yes
| ?-

Se poate obtine lista cu fisierele dintr-un director. De exemplu pentru:
[continut director]
| ?- use_module(library(file_systems)).
% loading d:/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/file_systems.po...
% module file_systems imported into user
%  module types imported into file_systems
%  module system imported into file_systems
%  loading d:/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/lists.po...
%  module lists imported into file_systems
%   module types imported into lists
%  loaded d:/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/lists.po in module lists, 0 msec 45976 bytes
% loaded d:/sicstus prolog 4.0.2/sicstus prolog 4.0.2/library/file_systems.po in module file_systems, 47 msec 67928 bytes
yes
| ?- file_members_of_directory('D:/folder_test',L).
L = ['a.txt'-'d:/folder_test/a.txt','abc.txt'-'d:/folder_test/abc.txt','b.txt'-'d:/folder_test/b.txt','bau.txt'-'d:/folder_test/bau.txt','bzz.txt'-'d:/folder_test/bzz.txt','x.txt'-'d:/folder_test/x.txt','xyz.txt'-'d:/folder_test/xyz.txt'] ?
yes
| ?-

Si exista inca multe alte predicate utile despre care va recomand sa cititi in pagina de documentatie a bibliotecii library(file_systems) pentru a va putea realiza temele.

Executie in timp limitat

In unele cazuri avem nevoie sa oprim executia daca aceasta dureaza prea mult. Pentru aceasta vom folosi predicatul time_out(:Scop,+Timp_ms,-Rezultat) din biblioteca library(timeout). Asa cum e precizat in documentatie, predicatul time_out porneste inteorgarea Scop, dar daca executia acesteia depaseste Timp_ms (masurat in milisecunde) atunci se opreste executia si Rezultat va avea valoarea time_out, altfel, daca executia nu dureaza mai mult de Timp_ms milisecunde aceasta se realizeaza cu succes iar in Rezultat e returnata chiar valoarea success.

Pentru a vedea un exemplu, consideram definit urmatorul predicat, care nu face altceva decat sa numere de la N pana la 0, afisand la final gata.

numara(0):-write(gata).
numara(N):-N>0,N1 is N-1,numara(N1).
Facem interogarea folosind timeout pentru N=10000 si apoi pentru N=100000
| ?- time_out(numara(10000),100,R).
gata
R = success ?
yes
| ?- time_out(numara(100000),100,R).
R = time_out ?
yes
| ?-
Se observa ca pentru prima interogare executia a durat mai putin de 1000 milisecunde (= 1 secunda), al treilea parametru al lui time_out fiind instantiat cu succes. Pentru N=100000 insa dureaza mai mult de o secunda si time_out-ul opreste executia inainte ca predicatul sa ajunga la sfarsit, instantiand R cu valoarea time_out (adica a fost depasit timpul alocat).

Atentie, time_out pentru apeluri blocante precum read sau sleep nu va lua in considerare timpul de executie al acestora. In exemplul de mai jos, 3-ul s-a introdus dupa 2 secunde de la apel, si cu toate acestea time_out-ul a rezultat cu succes. In cel de-al doilea exemplu, sleep opreste executia pentru 2 secude insa time_out setat pe 1000 ms = 1 secunda in continuare rezulta in succes

| ?- time_out(read(X),100,R).
|: 3.
R = success,
X = 3 ? yes
| ?- time_out((write(a),sleep(2),write(b)),1000,R).
ab
R = success ?
yes
| ?-

Definirea propriilor module se face foarte usor, puteti citi in documentatia Prolog despre acest lucru.

Probleme rezolvate folosind biblioteci

Numere random

Fapt aleator

Enuntul exercitiului. Se considera o baza de cunostinte cu niste castiguri posibile. Informatiile vor fi memorate in predicatul castig_posibil. castig_posibil('neacastigator').
castig_posibil(1).
castig_posibil(5).
castig_posibil(50).
castig_posibil(100).
castig_posibil(500).
Se cere sa se scrie un predicat numit extrage_loz, care afiseaza un castig aleator (toate castigurile au aceeasi probabilitate) din baza de cunostinte. Dupa afisarea unui castig se va afisa si o intrebare "Mai doresti sa extragi un loz?". Daca utilizatorul raspunde "da", se afiseaza un nou castig si se repeta intrebarea. Daca utilizatorul raspunde "nu" (sau orice altceva), se termina executia predicatului.

Pentru ca avem nevoie de numere random, trebuie sa setam seed-ul pentru ele. :-use_module(library(random)).
:-use_module(library(system)).


:-seteaza_seed.

seteaza_seed:-now(X1), X is X1 mod 30000,
              now(B),setrand(random(X,100,100,B)).
Observati ca setarea seed-ului s-a facut printr-o directiva, pentru a fi setat inaintea oricarei interogari: :-seteaza_seed.
Predicatul extrage_loz ar trebui sa aiba acces la lista posibilitatilor de extragere (obtinute din baza de cunostinte. Facem asta printr-un findall: findall(Castig, castig_posibil(Castig),L) Acum in lista L avem toate valorile din castig_posibil. Trebuie sa alegem un element aleator din lista. Facem asta folosind predicatul random_member(-Element, +Lista) care calculeaza in Element un element random din Lista. random_member(Loz,L) Urmeaza doar sa afisam eventualul castig, impreuna cu intrebarea pentru extragerea urmatoare, citind apoi raspunsul: write(Loz),nl,
write('Mai doresti sa extragi un loz?\n'),
read(X)
Daca raspunsul este "da", trebuie sa executam o noua extragere (apelam inca o data predicatul extrage_loz. Daca este alt raspuns, trebuie doar sa terminam predicatul cu succes. (X==da -> extrage_loz; true)

Avand in vedere cele explicate mai sus, iata si programul complet: :-use_module(library(random)).
:-use_module(library(system)).


:-seteaza_seed.

seteaza_seed:-now(X1), X is X1 mod 30000,
              now(B),setrand(random(X,100,100,B)).

castig_posibil('neacastigator').
castig_posibil(1).
castig_posibil(5).
castig_posibil(50).
castig_posibil(100).
castig_posibil(500).

extrage_loz:-     findall(Castig, castig_posibil(Castig),L),
                random_member(Loz,L),
                write(Loz),nl,
                write('Mai doresti sa extragi un loz?\n'),
                read(X),
                (X==da -> extrage_loz; true).

Un exemplu de executie ar fi: | ?- extrage_loz.
neacastigator
Mai doresti sa extragi un loz?
|: da.
neacastigator
Mai doresti sa extragi un loz?
|: da.
100
Mai doresti sa extragi un loz?
|: da.
500
Mai doresti sa extragi un loz?
|: nu.  
yes
| ?-

Matrice cu numere aleatoare

Enuntul exercitiului. Sa se scrie un predicat genereaza_matrice_random(+N,+A,+B,-M) care genereaza o matrice M de dimensiune N*N umpluta cu elemente aleatoare din intervalul [A,B).

Cum avem de lucrat cu numere aleatoare, primul lucru pe care il facem este setarea seed-ului: :-use_module(library(random)).
:-use_module(library(system)).


:-seteaza_seed.

seteaza_seed:-now(X1), X is X1 mod 30000,
              now(B),setrand(random(X,100,100,B)).

Pentru a genera o matrice N*N trebuie sa salvam variabila N in 2 parametri. Daca ar fi salvata doar intr-unul, atunci cand decrementam N pentru generarea unei noi linii, pierdem valoarea initiala care ne-ar da numarul de elemente de pe o linie de generat(adica numarul de coloane). Deci vom crea un alt predicat: matrice_random(+NrLinii,+NrColoane,+A,+B,-M). Astfel avem: genereaza_matrice_random(N,A,B,M):- matrice_random(N,N,A,B,M).

Pentru a genera o matrice, stim ca trebuie sa avem un predicat care genereaza cate o linie. Acest predicat trebuie apelat la randul lui de un predicat care genereaza toate liniile (si care va fi de fapt matrice_random). Predicatul dupa fiecare generare de linii va scadea 1 din numarul de linii de generat. Deci evident trebuie testat inainte de a genera o linie daca numarul de linii de generat este mai mare decat 0. matrice_random(NL,NC, A,B,[Linie|M]):- NL>0, linie_random(NC,A,B, Linie), NL1 is NL-1, matrice_random(NL1,NC,A,B,M). Predicatul linie_random(NC,A,B, Linie) este cel care va genera o cate o linie din matrice, cu numere aleatoare.

Predicatul se va opri cand numarul de linii ajunge sa fie 0 (moment in care seteaza coda matricii la lista vida (ca sa se adune in fata ei toate liniile generate in pasii din apelurile recursive anterioare): matrice_random(0,_,_,_,[]).

Sa definim acum si linie_random(+NC,+A,+B, -Linie). Variabila NC reprezinta numarul de coloane, deci numarul de elemente de pe o linie. Folosim strategia de mai sus, si decrementam NC cu 1, dupa generarea fiecarui element din linie. Ne vom opri cand NC ajunge 0, moment in care finalizam linia cu o lista vida (acesta fiind ultimul tail al listei, in fata caruia se vor aduna toate elementele generate in apelurile recursive anterioare). linie_random(N, A,B,[H|T]):- N>0, N1 is N-1, random(A,B,H), linie_random(N1,A,B,T).
linie_random(0, _,_,[]).

Avand in vedere cele explicate mai sus, iata si programul complet: :-use_module(library(random)).
:-use_module(library(system)).

:-seteaza_seed.

seteaza_seed:-now(X1), X is X1 mod 30000,
              now(B),setrand(random(X,100,100,B)).
              

genereaza_matrice_random(N,A,B,M):- matrice_random(N,N,A,B,M).

%matrice_random(+NrLinii,+NrColoane,+A,+B,-M)
matrice_random(NL,NC, A,B,[Linie|M]):- NL>0, linie_random(NC,A,B, Linie), NL1 is NL-1, matrice_random(NL1,NC,A,B,M).
matrice_random(0,_,_,_,[]).

%linie_random(+NrColoane,+A,+B, -Linie)
linie_random(N, A,B,[H|T]):- N>0, N1 is N-1, random(A,B,H), linie_random(N1,A,B,T).
linie_random(0, _,_,[]).

Un exemplu de executie ar fi: | ?- genereaza_matrice_random(7,1,11,M).
M = [[1,10,8,6,2,8,8],[6,1,1,5,2,4,2],[5,6,9,6,9,5,7],[9,2,6,8,5,2,5],[7,5,9,8,4,7|...],[2,10,2,3,6|...],[5,5,9,5|...]] ? <100
M = [[1,10,8,6,2,8,8],[6,1,1,5,2,4,2],[5,6,9,6,9,5,7],[9,2,6,8,5,2,5],[7,5,9,8,4,7,7],[2,10,2,3,6,8,2],[5,5,9,5,2,6,10]] ?
yes
| ?-
Observati ca prima oara cand a oferit solutia, unele liste erau date incomplet (cu "..."), motiv pentru care am marit bufferul cu comanda "<100".

Matricea obtinuta este: 1 10 8 6 2 8 8
6 1 1 5 2 4 2
5 6 9 6 9 5 7
9 2 6 8 5 2 5
7 5 9 8 4 7 7
2 10 2 3 6 8 2
5 5 9 5 2 6 10

Pozitii aleatoare in lista

Enuntul exercitiului. Sa se scrie un predicat care sterge intr-o ordine random numerele impare dintr-o lista de numere naturale. Dupa fiecare stergere va afisa lista rezultata. Forma predicatului va fi sterge_rand_impare(+L).

Pentru a putea sterge usor numerele trebuie sa stim cate sunt (notam cu Ni numarul de elemente impare). Astfel vom avea un predicat care numara cate elemente impare sunt in lista: %numara_impare(+L,-Ni)
numara_impare([H|T],Ni):- numara_impare(T,NiT), (H mod 2 =:=0 -> Ni=NiT; Ni is NiT +1).
numara_impare([],0).

Avand numarul de numere impare Ni, la fiecare iteratie alegem un numar aleator K intre 0 si Ni (exclusiv): random(0,Ni,K) Apoi stergem al K-lea numar impar, dupa care afisam lista. Deci trebuie sa facem un predicat care stie sa stearga un element de pe o pozitie data: %sterge_lista_poz(+Lista, +Pozitie, -ListaRezultata).
sterge_lista_poz([_|T], 0, T).
sterge_lista_poz([H|T], Poz, [H|Trez]):- Poz>0, Poz1 is Poz-1, sterge_lista_poz(T,Poz1,Trez).
Pe care il vom apela: sterge_lista_poz(L,K,Lnou) ca sa stergem elementul de pe pozitia random K.

De fiecare data cand stergem un numar, decrementam Ni deoarece scade numarul de elemente impare (de sters). Deci in predicatul care sterge elementele, va trebui sa avem si instructiunea: NiNou is Ni-1

Astfel predicatul care sterge numerele impare va avea forma finala: sterge_impare(L,Ni):- Ni>0, random(0,Ni,K), sterge_lista_poz(L,K,Lnou),write(Lnou),nl, NiNou is Ni-1, sterge_impare(Lnou, NiNou).
sterge_impare(_,0).

Avand in vedere cele explicate mai sus, iata si programul complet: :-use_module(library(random)).
:-use_module(library(system)).

:-seteaza_seed.

seteaza_seed:-now(X1), X is X1 mod 30000,
              now(B),setrand(random(X,100,100,B)).
%sterge_rand_impare(+L)                        
sterge_rand_impare(L):- numara_impare(L,Ni), sterge_impare(L,Ni).

%numara_impare(+L,-Ni)
numara_impare([H|T],Ni):- numara_impare(T,NiT), (H mod 2 =:=0 -> Ni=NiT; Ni is NiT +1).
numara_impare([],0).

sterge_impare(L,Ni):- Ni>0, random(0,Ni,K), sterge_lista_poz(L,K,Lnou),write(Lnou),nl, NiNou is Ni-1, sterge_impare(Lnou, NiNou).
sterge_impare(_,0).

%sterge_lista_poz(+Lista, +Pozitie, -ListaRezultata).
sterge_lista_poz([_|T], 0, T).
sterge_lista_poz([H|T], Poz, [H|Trez]):- Poz>0, Poz1 is Poz-1, sterge_lista_poz(T,Poz1,Trez).

Vom da doua exemple de executie pentru a se observa ordinea aleatoare a disparitiei elementelor: | ?- sterge_rand_impare([1,4,5,8,7,9,11,10]).                                        
[1,4,8,7,9,11,10]
[1,4,8,9,11,10]
[1,4,9,11,10]
[4,9,11,10]
[9,11,10]
yes
| ?- sterge_rand_impare([1,4,5,8,7,9,11,10]).
[1,5,8,7,9,11,10]
[5,8,7,9,11,10]
[5,8,9,11,10]
[8,9,11,10]
[9,11,10]
yes
| ?-

Functii de timp (din biblioteca system)

In exemple pot sa apara si predicate din alte biblioteci.

Masurarea timpului de executie

Enuntul exercitiului. Sa se scrie un predicat care citeste numere de la tastatura pana este intalnit numarul 0. Predicatul calculeaza suma numerelor in variabila S si, la final, afiseaza timpul executiei (in secunde). In cazul in care se citeste altceva decat un numar, predicatul esueaza.

Pentru a calcula timpul de executie in secunde, ar trebui ca atat la inceputul predicatului cat si la sfarsitul predicatului sa preluam timestamp-urile, si apoi sa le scadem: ... now(TimpInitial), /* taskurile predicatului */ now(TimpFinal), Secunde is TimpFinal-TimpInitial ...

Vom citi numerele folosind repeat, ceea ce inseamna ca ne trebuie un predicat dinamic in care sa mamoram suma elementelor citite, la fiecare pas. Predicatul dinamic se va numi suma si va avea ca unic parametru chiar suma numerelor citite pana in momentul curent.

La inceputul programului vom sterge toate eventualele fapte ale predicatului suma (ne asiguram ca nu au ramas din vreo rulare anterioara) si adaugam un unic fapt cu suma egala cu 0: retractall(suma(_)), assert(suma(0))

Repetitiv citim cate un X si daca acesta este numar si e diferit de zero, il vom aduna la suma memorata in predicatul dinamic. Astfel, vom prelua si sterge suma veche, vom aduna numarul la ea, obtinand suma noua si vom adauga suma noua in baza de cunostinte: retract(suma(Sv)), Sn is Sv+X, assert(suma(Sn)),

Conditia de iesire din zona repetitiva va fi ca X==0.

Avand in vedere cele explicate mai sus, iata si programul complet: :-use_module(library(system)).

:-dynamic suma/1.

citeste_numere(Suma):-  now(TimpInitial),once((retractall(suma(_)), assert(suma(0)))),
                        repeat,
                            read(X),
                            (
                                X==0
                            ;
                                number(X)->
                                    retract(suma(Sv)), Sn is Sv+X, assert(suma(Sn)),fail
                                    ;
                                    !, fail
                            ),
                        !,
                        retract(suma(Suma)),
                        now(TimpFinal),
                        Secunde is TimpFinal-TimpInitial,
                        format('Secunde trecute: ~p\n', [Secunde]).

Doua exemple de executie ar fi: | ?- citeste_numere(Suma).                                                           
|: 2.
|: 5.
|: 3.
|: 7.
|: 0.
Secunde trecute: 8
Suma = 17 ? yes
| ?-
| ?- citeste_numere(Suma).
|: 3.
|: a.
no
| ?-

Crearea unei "animatii" prin introducerea unui delay intre afisari

Enuntul exercitiului. Sa se scrie un predicat genereaza_animatie(+N) care primeste un numar N si genereaza o matrice de dimensiune N*N formata doar din zerouri. La fiecare jumatate de secunda un zero aleator din matrice se va transforma in simbolul *. Dupa fiecare astfel de schimbare, matricea va fi reafisata in locul matricei vechi (deci trebuie curatat ecranul inainte de reafisare). Timpul intre afisari este de jumatate de secunda. Predicatul se va termina cand in matrice nu se mai gasesc zerouri.

De exemplu, pentru N=5 vrem sa ajungem de la: 0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
la * * * * *
* * * * *
* * * * *
* * * * *
* * * * *

Pentru generarea matricii initiale ne trebuie un predicat care primeste un numar N si un element Elem si genereaza o lista de lungime N cu toate elementele egale cu Elem: list_identic(Elem, N, [Elem|T]):- N>0, N1 is N-1, list_identic(Elem, N1, T).
list_identic(_,0,[]).

Acest predicat il vom apela de doua ori. Prima oara ca sa cream o lista (linie in matrice) de lungime N formata doar din zerouri: list_identic(0,N,Linie) si a doua oara ca sa repetam de N ori linia creata, in matrice: list_identic(Linie, N, M)

Strategia abordata va fi crearea unui contor cu numarul total de zerouri (care la inceput este chiar N*N): Nr0 is N*N si la fiecare inlocuire a unui zero cu asterisc, numarul va fi decrementat.

Inlocuirile se vor realiza pana Nr0 ajunge egal cu 0. La fiecare pas se genereaza un numar natural aleator (notat cu Poz) in intervalul [0, Nr0) si se parcurg elementele din matrice pana sa ajunge la al Poz-lea element 0 din matrice, moment in care acesta se inlocuieste.

Este necesar pentru a realiza animatia sa avem si un predicat de stergere a ecranului: clear:-format('~c~s~c~s', [0x1b, "[H", 0x1b, "[2J"]).

De asemenea avem nevoie de un predicat de afisare a unei matrici, linie cu linie: afis_mat([Linie|RestMatrice]):-afis_lin(Linie),afis_mat(RestMatrice).
afis_mat([]).

afis_lin([H]):-write(H),nl,!.
afis_lin([H|T]):-write(H),write(' '),afis_lin(T).

Avand in vedere cele explicate mai sus, iata si programul complet. Predicatul principal este genereaza_animatie. :-use_module(library(random)).
:-use_module(library(system)).

:-seteaza_seed.

seteaza_seed:-now(X1), X is X1 mod 30000,
              now(B),setrand(random(X,100,100,B)).

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

list_identic(Elem, N, [Elem|T]):- N>0, N1 is N-1, list_identic(Elem, N1, T).
list_identic(_,0,[]).

genereaza_animatie(N):- list_identic(0,N,Linie), list_identic(Linie, N, M), Nr0 is N*N, animatie(M, Nr0).

animatie(M,Nr0):-Nr0>0, clear, random(0,Nr0,Poz),nl,schimba_poz_mat(Poz,M,Mnou), afis_mat(Mnou),nl,nl, sleep(0.5), Nr0_nou is Nr0-1, animatie(Mnou, Nr0_nou).
animatie(_,0).

afis_mat([Linie|RestMatrice]):-afis_lin(Linie),afis_mat(RestMatrice).
afis_mat([]).

afis_lin([H]):-write(H),nl,!.
afis_lin([H|T]):-write(H),write(' '),afis_lin(T).

schimba_poz_mat(Poz,[Linie|M],[LinieRez|MRez]):- Poz>=0, schimba_poz_in_linie(Poz, Linie, LinieRez, Poz1), schimba_poz_mat(Poz1, M, MRez).
schimba_poz_mat(-1,M,MRez):- MRez=M.

schimba_poz_in_linie(-1,  Linie, LinieRez, -1) :- !, LinieRez=Linie.
schimba_poz_in_linie(Poz,[H|Linie], [HRez |LinieRez], Pozfinal):-  
                    (H==0 ->
                        (Poz>0 ->
                            Poz1 is Poz-1,
                            HRez=H
                        ;
                            Poz==0,
                            HRez='*',
                            Poz1 = -1
                        )
                    ;
                        HRez=H, Poz1 is Poz
                    ),
                    schimba_poz_in_linie(Poz1, Linie,LinieRez, Pozfinal).
schimba_poz_in_linie(Poz,[],[],Pozfinal):- Pozfinal=Poz.

Interogati voi predicatul pentru a vedea animatia.

Sistemul de fisiere

In exemple pot sa apara si predicate din alte biblioteci.

Iterarea prin fisierele unui director

Enuntul exercitiului. Sa se scrie un predicat care primeste calea unei director si calculeaza intr-un parametru Nr cate fisiere contin in nume o litera data ( o vom nota cu Lit). Forma predicatului va fi cate_fisiere(+Director, +Litera, -Nr).

Mai intai obtinem lista de fisiere din director cu predicatul files_members_of_directory. Elementele din lista sunt de forma CaleRelativa-CaleAbsoluta. Vom parcurge lista element de element folosind doar calea relativa. La fiecare pas, transformam calea relativa in lista de caractere, eliminam extensia si apoi vedem daca litera data e membru in lista de caractere provenita din numele fisierului.

Avand in vedere cele explicate mai sus, iata si programul complet: :-use_module(library(file_systems)).

cate_fisiere(Director, Litera, Nr):- file_members_of_directory(Director, LF), numara_fisiere(LF, Litera, Nr).
                            
numara_fisiere([CR - _|T], Litera, Nr):- numara_fisiere(T, Litera, NrT), atom_chars(CR,LCR), append(Lnume,['.'|_], LCR), (member(Litera,Lnume)-> Nr is NrT+1; Nr is NrT).
numara_fisiere([],0).

Un exemplu de executie pentru folderul:
[continut director]
ar fi: | ?- cate_fisiere('D:/folder_test',a,Nr).                                            
Nr = 3 ?
yes
| ?-