momente şi schiţe de informatică şi matematică
To attain knowledge, write. To attain wisdom, rewrite.

Informatica evoluează, învăţământul nu!

point-and-click
2008 apr

Pe un caiet de matematică am văzut o lecţie de informatică - ceea ce nu este surprinzător sau interzis, fiindcă aceste discipline de învăţământ ar trebui să fie (altfel chiar sunt) foarte strâns legate între ele. În cadrul acelui text am văzut imediat secvenţa de funcţie C++ if( ... ) return 0; else return ... şi m-am găsit eu să încerc să lămuresc elevilor că else este neavenit în această formulare cu return (în toate limbajele, nu numai în C++). Elevii s-or fi simţit ofensaţi (de! ce mă bag eu...), fiindcă mi-au replicat scurt, chiar brutal: Aşa ne-a zis Domnu', ba şi Aşa scrie în manual! - Punct

Păi aşa a zis...

este o sintagmă esenţială azi. "Aşa a zis Guvernul", "Aşa a zis Ministerul", "Aşa a zis Inspectoratul", "Aşa a zis Domnul Inspector", "Aşa a zis Doamna Director", "Aşa a zis Domnu' Diriginte", "Aşa a zis domnu' profesor", "Aşa a zis Doamna", "Aşa scrie în Manual", "Aşa scrie în Regulament", etc.
Aşa a zis ... Te scuteşte să judeci tu, te scuteşte să-ţi asumi sau să justifici decizii şi încă alte asemenea avantaje (îndrăgite de oportunişti). Indiferent de chestiune şi de argumente - nu avem ce să discutăm, "aşa a zis" cutare entitate superioară care se subînţelege că este indiscutabil mai pregătită şi mai în cunoştinţă de cauză decât alde mine sau alde tine.

…aţi auzit de baronul Epstein? Nici eu, dar ştiu o istorie; avea omu' o pasiune, să joace şah şi frecventa o cafenea din Viena în acest scop. Ei… pe vremea ceea (prin anii 1850) şahul se juca în mod obişnuit, prin cafenelele Europei şi erau fel de fel de jucători, mai tineri sau mai în vârstă, mai bogaţi sau mai modeşti. Baronului Epstein nu-i plăcea de loc să piardă şi se supăra dacă încercai să-i explici că mutarea lui e proastă, iată de ce, mai bine s-ar gândi la altceva... Şi se întâmpla frecvent şi să piardă şi să se supere, în faţa unui tinerel modest, Steinitz (ei... poate-aţi auzit: a câştigat primul titlu de "world's chess champion" (1866) şi mai mult, a elaborat elementele de bază ale strategiei jocului de şah). La un moment mai tensionat, Epstein îi zice deja ofensat: Tinere - dar ştii tu cu cine joci?! iar Steinitz îi răspunde: Da, sunteţi baronul Epstein; ...în birjă, aici eu sunt Epstein!.

Cam la fel stau lucrurile şi cu "Aşa a zis...". De multe ori nu baronul are dreptate, ci eu.

Câte n-au fost, situaţii în care ceea ce zicea Ministerul, sau Inspectorul, sau Directorul - n-au stat ulterior în picioare, sau s-au dovedit de-a dreptul proaste ziceri şi impuneri…

Pe la începutul anului şcolar 1990-91, Ministerul a trimis în toate şcolile un ordin privind reluarea organizării activităţii de "pregătire premilitară", directorul a expus-o la loc de cinste pe afişier şi s-a supărat când hârtia cu ştampila şi semnătura însuşi a Domnului Ministru... a fost coborâtă la coş de un profesor (gest foarte neelegant, desigur o reacţie instinctivă faţă de oportunism… dar ordinul a fost ignorat - nu ştim dacă retras - ulterior).

Prin 1993-94 alde mine făceam ca limbaj de programare la clasă şi Basic şi ceva ASM (cod Z80) şi mai ales Pascal (pe vremea aceea, un compilator de Pascal pentru microcalculatoare pe 8 biţi); inspectoarea mi-a reproşat că "programa nu prevede Pascal - cum de-mi permit eu să încalc programa?!" (ulterior, de prin 1995 toţi făceau la clasă Pascal - că aşa prevedeau noile programe).

Povestea a trecut aproape neobservată (s-a răzbunat totuşi, mult mai târziu); dar ea s-a repetat peste vreo doi-trei ani: făceam şi Pascal la clasă, dar trecusem în principal pe C şi C++ (ei... şi ceva ASM pentru I286) iar inspectoarea (aceeaşi, desigur) mi-a reproşat a doua oară "programa prevede Pascal, nu C" (peste vreo trei-patru ani s-a trecut la C, exceptând pe cei care se obişnuiseră cu Pascal şi n-aveau timp pentru a învăţa C). Nu mai ştiu prin ce an, eu făceam Visual FoxPro iar ceilalţi făceau în continuare şi încă vreo câţiva ani ulteriori, FoxPro-for-DOS (da, de vreo doi-trei ani manualele vizează Visual FoxPro).

Privind trecerea de la Pascal la C, în învăţământul nostru, ne amintim că la Olimpiada de informatică de la Helsinki echipa noastră a fost surprinsă să vadă că nu toată lumea face Pascal şi nu toată lumea face Borland C++ 3.1 şi nu toată lumea foloseşte Windows (s-a folosit un sistem Linux şi un compilator GNU GCC pentru C). Şi imediat după aceea şi Gazeta de Informatică a anunţa explicit cerinţa ca programele transmise la redacţie să folosească limbajul C şi nu Pascal.

Între 2004-07 am făcut cu elevii pe la laboratoarele de informatică, elemente de javascript, de HTML, de CSS (şi am constatat că tocmai acestea i-ar interesa şi i-ar atrage mai mult decât Borland C++ 3.1); ţin minte că ne obişnuisem la un moment, să furnizăm rezultatele diverselor programe C++ sub formă de documente HTML, unele chiar mai complexe; cam în acelaşi sens, în ultimul an văd (din afară, de data aceasta) că lumea începe să facă C# şi SQL (că aşa zice acum programa).

Iată redate mai sus o serie de fapte certe (subliniez: demonstrabile "negru pe hârtie", poate să greşesc la ani doar), din propria experienţă de "profesor de informatică" - dar cu siguranţă există şi alţi profesori cu experienţe similare. Aceste fapte evidenţiază că mai ales în ceea ce priveşte dezvoltarea învăţământului la Informatică, nu "cei de sus" au vreo vorbă de zis, ci numai aceia care înţeleg şi se străduiesc permanent să ţină pasul cu evoluţia reală a informaticii în lume.

Însă lucrurile stau pe dos. Cei de sus, invocaţi la tot pasul cu "Aşa a zis..." reduc profesorul la Diplomă şi la ştampilă asemeni unor funcţionari (se mândresc parcă: "înalţi funcţionari"); dar evident că în domeniul informaticii, nu Diplomele şi nu ştampilele asigură pregătirea cât mai aproape de linia curentă a evoluţiei domeniului.

Un profesor care nu are în degete experienţa muncii la o aplicaţie mai complexă, care n-a trecut dincolo de exerciţiile din manual, care nu cunoaşte altceva decât buchia cărţii (ce să mai vorbim de vreo experienţă proprie de "reinventare a roţii") - nu prea are ce să-i înveţe pe elevi, cu toate Diplomele şi Atestatele lui consemnate oficial. Da, sigur, va reuşi să-i pregătească pe elevi pentru "atestat" şi pentru "bacalaureat", poate şi pentru "olimpiadă" - dar nici într-un caz pentru o carieră nemediocră, în domeniul informaticii.

Datorită lui "Aşa a zis..." şi datorită orientării birocratice din ce în ce mai evidente, o să tot avem generaţii mediocre. Noroc să zicem, că din ce în ce mai mulţi elevi pasionaţi încearcă şi singuri să-şi deschidă orizonturile, folosind Internetul şi diversele comunităţi specializate; se conturează din ce în ce mai clar un învăţământ informatic paralel celui oficial şi care - nimic nou în asta - într-un viitor mai mult sau mai puţin îndepărtat îi va lua locul.

Defectele de principiu ale educaţiei informatice actuale

S-a instituit acest fals grosolan: există chipurile numai Windows, numai Microsoft-Office, numai aplicaţii Windows şi numai licenţă Windows - probabil, explicaţia decurge din agreement-ul (convenabil desigur pentru ambele părţi) prin care Microsoft îi determină pe vânzători să preinstaleze Windows pe calculatoarele vândute. Se ignoră complet mişcarea "free software", care prin proiectele pe care le-a generat ("open-source software", GNU, GCC, Linux, Firefox, etc.) a avut şi are cele mai importante contribuţii la dezvoltarea informaticii.

Dar şi mai probabil - adoptarea unei direcţii greşite se datorează ca de obicei, unei proaste cunoaşteri şi interpretării forţate a istoriei şi legăturilor domeniului respectiv - manifestate prin trunchiere şi printr-o falsă suprapunere a unei porţiuni umflate artificial, peste întreg domeniul (în speţă, pe scurt - pentru şcoala VI-XII avem imaginea de identitate, Informatică = Windows + produse Microsoft + pseudocod, ceea ce este un fals grosolan).

Anul trecut am aflat şi eu acest fapt: la clasa a VI-a se face C++ (desigur, Borland C++ 3.1); probabil că la mai multe şcoli, probabil că şi într-a VII-a şi a VIII-a. Nu ştiu dacă "Aşa zice programa", sau dacă "Aşa zice Inspectorul" şi chiar nu mă interesez - probabil că "aşa zice"; mie mi se pare aşa de proastă alegere (subliniez, la clasa a VI-a) că nici nu mă străduiesc să mai formulez argumente.

Bazele domeniului sunt expediate cam la toate nivelele prin "Calculatorul este alcătuit din Unitate Centrală, tastatură, etc; Unitatea Centrală este această carcasă; etc."; apoi se trece la click pe iconiţe, la poze, la muzică şi la jocuri, la "pseudocod" şi desigur la IDE Borland C++ 3.1. Apoi, desigur se va trece la C#... (altă direcţie greşită). Programarea ca atare este repartizată de programele oficiale (cine le-o fi şi respectând în buchia lor, din comoditate sau mai degrabă din neştiinţă - e altă poveste) foarte prost: dacă e să respecţi programa, atunci elevii vor auzi despre "subprograme" abia după ce termină un an de C/C++; ori "funcţie" este conceptul indispensabil programării şi se cuvine să fie introdus de la bun început (şi în fond, "funcţie" este de bază şi la matematică, iar aici este respectat cum se cuvine - fiind studiat chiar de la începutul clasei a IX-a).

Elevul care se va conforma unei asemenea şcoli VI-XII şi va dori să devină programator (dar nu oarecare), va trebui s-o ia aproape de la capăt (nu degeaba, la facultate - sau, după ce termini Liceul - se cam ia totul de la capăt!).

Datorită restrângerii la produse Microsoft şi la desktop, orientarea principală este utilizarea aplicaţiilor, prin învăţarea opţiunilor de meniu şi însuşirea "tehnologiei" point-and-click.

Inclusiv învăţarea programării în limbajul C++ este subsumată utilizării interfeţei IDE ("Integrated Development Environment") oferite de Borland C++ 3.1: se foloseşte editorul integrat pentru a scrie codul sursă şi se folosesc meniurile IDE sau scurtăturile corespunzătoare pentru a executa programul; se ignoră complet instrumentele de programare tipice, puse la dispoziţie şi de pachetul Borland (Turbo Debugger, de exemplu).

În fond, realitatea este că limbajul C este prezentat şi învăţat la nivel de pseudocod, vizând doar reguli de sintaxă; "program" este redus la ".CPP", nu are nimic de-a face nici cu sistemul de operare şi nici cu calculatorul pe care se execută; limbajul C nu are de-a face cu nici un alt limbaj (exceptând… "limbajul pseudocod" care se dovedeşte esenţial la proba de bacalaureat). Toată strădania învăţământului rezidă în a-i dresa pe elevi să facă click, să utilizeze opţiunile oferite de meniuri şi în plus să-i înveţe "limbajul pseudocod", pentru "formarea gândirii algoritmice".

Inclusiv la profilul matematică-informatică, se face o disciplină numită "Tehnologia informaţiilor şi a comunicării" care este însă complet separată faţă de "programare în C++"; este de-a dreptul hilară insistenţa manualelor de C++ de liceu faţă de "interfaţă prietenoasă de prezentare pe ecran a rezultatelor programului", când "prezentarea" ar implica în mod firesc tocmai cunoştinţe de "tehnologia informaţiilor şi comunicării" (de exemplu, HTML şi CSS). Sunt complet ignorate tehnologiile reale de lucru: HTML, CSS, limbajul Javascript - care sunt implicate azi de majoritatea browserelor, reducând iarăşi totul la point-and-click cu produse precum Front-Page (evident - fără nici o legătură cu "C++").

Reducerea la point-and-click este o cale greşită

Manualele de C++ sunt pline de "se citeşte de la tastatură... să se afişeze...". Dar chiar este adevărat: comunicarea între utilizator (dar mai ales, programator) şi calculator implică în primul rând un terminal text (sau consolă), care este o interfaţă serială pentru introducere / afişare de text (fiecare sistem de operare alocă pentru aceasta trei descriptori de fişier: STDIN asociat tastaturii, STDOUT şi SDTERR asociate ecranului).

Două categorii fundamentale de aplicaţii, sunt destinate să fie rulate prin intermediul unui terminal: interpretoarele de comenzi şi editoarele de text. Un interpretor de linie de comandă este un program care citeşte linii de text introduse de utilizator, le interpretează în contextul sistemului de operare respectiv (caz în care este denumit de obicei shell), sau în contextul unui anumit limbaj de programare şi execută comenzile sau instrucţiunile respective. Sistemele de operare bazate pe DOS oferă ca shell programul COMMAND.COM (sau CMD.EXE, pe ultimele versiuni de Windows); Linux oferă mai multe shell-uri, care de fapt sunt ele însele adevărate limbaje de programare (de exemplu, programul bash). Un editor de text - se ştie ce este (sub Windows avem Notepad); diferenţa esenţială faţă de un "word processor" este aceea că fişierul-text rezultat conţine exact codurile ASCII (eventual, Unicode) ale caracterelor tastate, în timp ce fişierul rezultat de sub Word are un format special (conţinând şi alte secvenţe de cod specifice, pe lângă ceea ce s-a tastat).

Pentru orice programator, terminalele şi editoarele de text sunt instrumentele esenţiale; ori tocmai acestea, sunt complet neglijate de către învăţământul informatic actual (în favoarea "tehnologiilor" point-and-click) - în pofida sintagmei aşa de frecvente în manuale "se citeşte de la tastatură... să se afişeze...".

Pentru a realiza o aplicaţie, indiferent de limbajele implicate, programatorul procedează de regulă cam aşa: deschide un terminal; creează un director de lucru (cu o comandă ca mkdir); lansează un editor de text şi editează fişierele necesare; lansează de pe linia de comandă, compilatorul corespunzător (obţinând fişierele obiect corespunzătoare codului sursă); apelează de pe linia de comandă link-editorul corespunzător, obţinând fişierul executabil final (care eventual, poate fi rulat şi el din linia de comandă).

Această manieră de lucru a fost introdusă de Unix (încă din 1969) şi a fost popularizată de exemplu, de prima carte (fundamentală) de limbaj C (apărută acum 30 de ani); a fost "popularizată" fară nici un soi de sublinieri sau "argumente" - pur şi simplu a fost folosită din start, fiind considerată implicit drept maniera de lucru firească.

Lucrând astfel, programatorul poate folosi pentru aplicaţia sa mai multe limbaje, poate angaja dacă este necesar diverse compilatoare sau interpretoare, poate beneficia eventual şi de faptul că un fişier-text poate să aibă şi o dimensiune foarte mare (inaccesibil în acest caz, pentru un mediu IDE).

Dresat 4 ani de zile cu "point-and-click", cu iconiţe şi cu IDE-uri, absolventul nostru cu "atestat de programator" va fi de regulă un bun client Microsoft, poate şi un bun funcţionar public - dar îi va fi foarte greu să depăşească statutul de programator pasiv sau mediocru.

Iar if( ... ) return 0; else return ...; este o formulare greşită

return serveşte într-un subprogram pentru a ceda controlul execuţiei către programul apelant; instrucţiunile din subprogram care urmează după return nu mai sunt executate (la urma urmei, "return = întoarce-te").

Dacă testul din if este satisfăcut, atunci se va executa return 0; - deci se va reveni în programul apelant, ignorând instrucţiunile următoare din subprogram; dacă testul din if nu este satisfăcut, atunci "linia" if( ... ) return 0; va fi ignorată şi va intra în execuţie "linia" următoare. Prin urmare, else este inutil aici.

Formularea corectă ar fi if( ... ) return 0; return ...; (fără else); sau - folosind "operatorul condiţional", existent în majoritatea limbajelor - return ( condiţie ? 0 : ... );

Întrebuinţarea lui else în acest context, nu este doar un abuz grotesc de limbaj, dar arată şi superficialitate privind înţelegerea mecanismului de comunicare între program şi subprogram. Dar este o superficialitate explicabilă, câtă vreme: calculatorul este văzut pe deasupra; limbajele şi programele sunt izolate faţă de calculator, fiind reduse la instrumente pentru descrierea algoritmilor (simplificând până la pseudocod, formula uşor de memorat "programe = algoritmi + structuri de date"); informatica este redusă complet la "algoritmică" (plus point-and-click); iar… "return = întoarce-te".

Faţetele unui program

Pentru a intui mecanismele pe care se bazează comunicarea între programe, întâi trebuie să fie clar că acest cuvânt program desemnează mai multe aspecte, diferite între ele; putem vorbi de program—sursă, de program—obiect, de program—executabil.

Un cod sursă care constituie un program este înregistrat de regulă în unul sau mai multe fişiere-text (Aa... înregistrat în fişier-text; să fie şi aceasta clar: nu litera a se "înregistrează" în fişier, ci codul ASCII aferent, adică valoarea numerică 97; nu secvenţa de litere 'return' - ci secvenţa codurilor ASCII (sau Unicode) corespunzătoare). Codul sursă conţine secvenţe de instrucţiuni şi declaraţii conforme cu sintaxa şi regulile de formulare ale limbajului respectiv; acestea pot fi citite şi pot fi eventual modificate folosind un editor de text obişnuit, dar... nu pot fi executate ca atare de către calculator.

De altfel, putem constata separarea convenţională care se practică: fişierele-sursă au de obicei extensii precum .CPP, .H, .C, .PAS, etc. diferite de extensiile folosite pentru "executabile" .EXE, .COM, .a, etc. Aceasta este legată în fond de separarea fundamentală: însuşi microprocesorul este astfel conceput încât să separe datele (ar corespunde cu "fişier-text") de codul executabil: zona de date este separată de cea care păstrează codul, iar pentru zona de cod există mecanisme speciale de acces şi parcurgere automată.

Codul sursă este transformat în cod executabil, de obicei în două etape: compilare şi editarea legăturilor - folosind un compilator (sau după caz, un asamblor) corespunzător limbajului respectiv şi un link-editor.

Compilatorul verifică în prealabil, corectitudinea sintactică a codului sursă; dacă nu se întâlnesc erori de sintaxă, atunci compilatorul constituie codul obiect corespunzător. Codul obiect este înregistrat într-un fişier-obiect; în principal, codul obiect conţine instrucţiuni în formatul binar specific microprocesorului (numit generic CPU), care sunt direct executabile de către CPU. Desigur, instrucţiunile în format binar din codul obiect reprezintă (în anumite grupări ale lor) "traducerea" în limbajul CPU a instrucţiunilor întâlnite în codul sursă.

Fiecare tip de CPU are propriul său cod maşină - propriul set de instrucţiuni (constituind un limbaj "low-level", în care până la urmă ajung să fie reprezentate toate programele, indiferent de limbajul "high-level" în care sunt formulate iniţial). În general, fiecare instrucţiune din setul de instrucţiuni nativ al CPU cuprinde un câmp de biţi numit opcode, care specifică operaţia realizată de acea instrucţiune; alte câmpuri de biţi din cadrul instrucţiunii specifică natura operanzilor implicaţi, sau locaţiile acestora în memorie, sau direct valoarea unuia dintre operanzi.

Limbajul maşină cuprinde numai coduri numerice (nu simbolice, ca în limbajele de nivel superior); bineînţeles că este foarte incomod să scrii programe direct în cod-maşină (dar a fost o vreme, prin anii '40-50-60, când programatorii scriau direct în cod-maşină). Limbajele de asamblare asociază codurilor numerice din setul de instrucţiuni ale CPU, coduri mnemonice - uşurând enorm programarea la nivelul CPU; de exemplu, instrucţiunea I80486 reprezentată în cod maşină prin secvenţa de octeţi 80 06 34 12 03 (scrisă aici în hexazecimal) are opcode = 80, reprezentând o operaţie de adunare a unui număr (aici - 3) la conţinutul locaţiei de adresă indicată (aici - la adresa 0x1234); în limbajul de asamblare TASM pentru microprocesorul I80484, instrucţiunea respectivă se reprezintă mult mai convenabil, prin add [1234], byte ptr 3.

De vreme ce codul-obiect cuprinde instrucţiuni care chiar pot fi executate de către CPU - de ce să mai fie necesară şi o etapă de "link-editare"? De fapt n-ar fi necesară, dacă programul în cauză ar fi singurul program care trebuie rulat de către CPU. Dar de fapt, CPU trebuie să execute mereu şi programe-system (lansate de către kernelul sistemului de operare rezident) şi trebuie să poată executa în mod concurent, diverse alte programe lansate de către utilizator (de exemplu când acesta deschide un editor de text, apoi intră într-un browser, citeşte Mailul, etc.). Pot să existe deci mai multe programe aflate în stare de execuţie; un program aflat în execuţie este numit de obicei proces, distingând astfel de "program" care în fond e o colecţie pasivă de instrucţiuni.

Sistemul de operare creează şi menţine câte un "context de execuţie" pentru fiecare proces existent, înglobând: codul maşină executabil asociat procesului, o zonă de memorie pentru păstrarea datelor specificate în programul respectiv, o zonă de memorie numită "stiva de apeluri" a programului care serveşte pentru a păstra adresele de legătură între programe şi subprograme cât şi pentru comunicarea parametrilor între programe şi subprograme, o zonă de memorie numită "heap" pentru necesităţile de memorare temporară ale procesului, un număr de descriptori de fişier sau de alte resurse alocate procesului, un număr de atribute de securitate şi de permisiuni asociate procesului.

Un link-editor - make, llink, etc. - investighează conţinutul fişierului sau fişierelor obiect asociate programului şi stabileşte ce resurse de memorie (pentru date, pentru cod, pentru stiva de apeluri, pentru heap) necesită execuţia programului, ce alte fişiere obiect (biblioteci) trebuie incluse (în situaţia când programul foloseşte funcţii conţinute în acestea), etc. (tabele de simboluri, informaţie de depanare, pe ce tip de sistem de operare şi de CPU este destinat să ruleze codul, etc.) - şi în final, constituie fişierul executabil; când acesta este încărcat pentru a executa programul respectiv, sistemul de operare "ştie" să citească informaţiile încorporate de către link-editor, putând astfel să realizeze alocările necesare noului proces, înainte de a-l pune efectiv în starea de execuţie.

Toată descrierea de mai sus este o simplificare intuitivă a conceptelor şi ideilor care stau la baza activităţii obişnuite a calculatorului - aceea de a rula programe. Pentru înţelegerea raporturilor şi dependenţelor între programe, sistem de operare, CPU - ar fi binevenită o analogie potrivită; de exemplu, ne putem gândi la activitatea desfăşurată într-o şcoală: există un "sistem de operare" care asigură procedurile de bază ale activităţii, pentru toate şcolile (prin legislaţia, regulamentele şi instituţiile încorporate); pentru a începe activitatea (la 15 septembrie) trebuie ca sistemul sau subsistemul să aloce resursele necesare (profesori, elevi, cataloage, spaţiu de efectuare a activităţilor etc.); cataloagele şi alte documente şcolare, de exemplu orarul şcolii - sunt un fel de "fişiere-obiect" pe baza cărora se vor executa activităţile în şcoală; mai multe "procese" se desfăşoară simultan (în paralel), sau se intercalează şi se reiau pe diferite paliere temporale fixate (în mod concurent).

Conceptele şi dependenţele relevate intuitiv mai sus, sunt posibile şi se explică datorită structurii de principiu a microprocesorului şi a conexiunilor acestuia cu memoria şi cu alte resurse fizice ale calculatorului. Scheletul de bază al oricărui limbaj de programare corespunde structurii tipice de CPU, precum şi posibilităţilor de corelare cu sistemul de operare. "{" şi "}", sau "begin" şi "end" nu sunt "paranteze delimitatoare pentru program", ci activează subrutine complexe prin care compilatoarele asigură şi legătura necesară cu sistemul de operare.

vezi Cărţile mele (de programare)

docerpro | Prev | Next