Florin-Marian BÎRLEANU
Abecedar de
programare De la zero barat la bun de programat Pitești, 2018
0
ISBN: 978-973-0-26328-2
Cuprins Introducere
5
Partea I: Bazele gândirii de programator Modulul I - Liniuţe
1. Instrucțiuni
12
1.1 Lecţie 1.2 Exerciţii 1.3 Soluţii
12 19 24
2. Variabile
36
2.1 Lecţie 2.2 Exerciţii 2.3 Soluţii
36 41 45
3. Decizii
55
3.1 Lecţie 3.2 Exerciţii 3.3 Soluţii
55 59 61
4. Repetări
69
4.1 Lecţie 4.2 Exerciţii 4.3 Soluţii
69 75 78 1
Modulul II - Bastonaşe
5. Vectori
89
5.1 Lecţie 5.2 Exerciţii 5.3 Soluţii
89 98 101
6. Funcții
119
6.1 Lecţie 6.2 Exerciţii 6.3 Soluţii
119 129 131
7. Operatori
147
7.1 Lecţie 7.2 Exerciţii 7.3 Soluţii
147 156 158
8. Texte
167
8.1 Lecţie 8.2 Exerciţii 8.3 Soluţii
167 172 175
Modulul III - Litere
9. Taste
184
9.1 Lecţie 9.2 Exerciţii 9.3 Soluţii
184 190 192
10. Aleatoare
203
10.1 Lecţie 10.2 Exerciţii 10.3 Soluţii
203 208 210 2
11. Animaţii
222
11.1 Lecţie 11.2 Exerciţii 11.3 Soluţii
222 225 227
12. Clickuri
244
12.1 Lecţie 12.2 Exerciţii 12.3 Soluţii
244 246 248
Partea a II-a: Noţiuni de programare adevărată Modulul IV - Cuvinte
13. Pointeri
268
14. Tipuri
277
15. Şiruri
288
16. Structuri
293
Modulul V - Propoziţii
17. Liste
299
18. Stive
305
19. Cozi
313
20. Arbori
322
Modulul VI - Fraze
21. Compilatoare
329
22. Paradigme
338 3
23. Limbaje
345
24. Aplica ţii
356
Concluzie
360
4
Introducere Dacă omul ar fi fost un animal, s-ar s-ar fi mulţumit să vâneze, să mănânce, să doarmă şi să se reproducă. Dar nu. Odată ce şişia satisfăcut nevoile de bază, el a început imediat să caute căi de a-şi a-şi îmbunătăţi viaţa viaţa – de a-şi a-şi spori confortul şi siguranţa, de a-şi a-şi procura mai uşor cele necesare vieţii, de aa -şi dezvolta diverse abilităţi, şi aşa mai departe. Astfel au apărut primii "programatori", care au fost pescarii. Da, pot spune că că cel care a inventat pescuitul a gândit (întrun mod rudimentar) ca un programator. Adică Adică şi-a fă făcut mental un scenariu care tradus în cuvinte ar putea suna cam aşa: "Stiu că prin aceast ă zonă tr ăiesc peşti.
Şi mai ştiu că peştii caut ă hrană. Dacă le ofer momeal ă, se vor duce la ea.
Şi dacă muşcă momeala, se vor prinde în ac. " Arată Arată cam ca un program de calculator. Declaraţ Declara ţii şi instrucţiuni. instrucţiuni. Apoi a apă apărut sclavia. Stă Stăpânii de sclavi au fost şi ei "programatori". Atunci cand dă dădeau porunci: "F ă treaba X, dup ă care f ă treaba Y şi apoi f ă treaba Z." 5
Sau: "Du-te la A şi caut ă-l pe X. Dacă îl găseşti, d ă-i Y. Dacă nu, du-te la B şi d ă-i Z." Acestea sunt, practic, "programe". Iar sclavul a fost prima formă formă de "calculator". Adică Adică "ceva" capabil să să memoreze instrucţ instrucţiuni şi să să le execute precis. Recent (la scara istoriei) a apă apărut "sclavul" perfect. Cu o memorie şi cu o abilitate de a efectua calcule şi de a răspunde rapid la comenzi mult superioare abilităţ abilit ăţilor ilor orică oricărui om. Calculatorul. Face exact ce îi spui. O singură singur ă problemă problemă are: nu lasă lasă loc de ambiguităţ ambiguităţi.i. Nu ghiceş ghiceşte gânduri. Şi nici nu se sperie de bici sau de temniţ temniţe umede şi întunecate. Prin urmare, dacă dacă vrei să-l pui la muncă muncă, singura soluţ soluţie este să să îi vorbeş vorbeşti pe limba lui. O limbă limbă rigidă rigidă şi pe alocuri redundantă redundantă, dar clară clară şi fără loc de interpretă interpretări.
O limbă pe care când ajungi să o "vorbeşti" devii de neoprit. Nu ca şi cum ţi-ar creş creşte solzi de oţ oţel şi ai sufla flă flăcări pe gură gură. Ci ca şi cum calculatorul ţi-ar deveni un dragon cu aripi uriaş uriaşe ce te-ar duce pe spatele lui că c ătre tărâmuri la care acum doar visezi. Un dragon extrem de puternic, fioros (poate), dar foarte docil. Abilitatea de a programa devine pe zi ce trece o abilitate tot mai utilă utilă omului de azi. Aproape că că nu există există activitate umană umană pe care calculatorul să s ă nu o poată poată îmbună îmbunătăţi. ăţi. Bineînţ Bineînţeles, însă însă, că el poate şi înră înrăutăţ utăţii lucrurile atunci când e folosit în mod necorespunză necorespunzător. Mă Mă gândesc, de exemplu, la acei oameni care ies împreună la restaurant şi stau mai mult cu ochii în telefon decât să vorbească unii cu alţii. Dar nu despre despre aspectele negative vreau să îţi îţ i vorbesc. 6
Oricum, ele nu caracterizează calculatorul, ci pe cel care îl foloseşte. La fel cum nu cuţitul ce răneşte e rău, ci posesorul mâinii care îl mânuieşte. Calculatorul e un "cuţit" foarte bine ascuţit şi pe care îl poţi folosi ca să pregăteşti nişte "feluri de mâncare" foarte gustoase. Ba chiar e un "cuţit" foarte special, care ştie să îţi prepare singur "mâncarea" dacă îi descrii bine "reţeta". Şi trebuie să o descrii clar, concis, neambiguu – pe "limba" lui, adică. Cred că ai înţeles ideea. Şi cred că ştii deja foarte bine şi ce poate să facă un calculator. Doar foloseşti zilnic tot felul de programe (fie pe calculator, fie pe telefon sau pe tabletă). Comunicare, muncă, relaxare, creativitate, educaţie – sunt doar câteva dintre aspectele vietii tale în care calculatorul îţi poate fi (sau îţi este deja) un ajutor de nădejde. Bineînţeles că poţi "mânca" toată viaţa "reţetele" realizate de alţii. Dar nu ţi-ai dorit niciodată să-ţi construieşti propria "reţetă"? Sau doar s-o modifici puţin. Poate ai vrea, de exemplu, ca o anumită aplicaţie pe care o foloseşti să fie mai puţin "picantă" sau să aibă "legumele" mai puţin "fierte". Ce poţi să faci? Poţi să înveţi "limba" calculatoarelor. Da, poţi – nu e o treabă doar pentru genii.
Va veni o vreme (şi nu mai e chiar mult până atunci) când (aproape) toţi cei care ştiu să scrie şi să citească vor şti şi să programeze. Încă suntem departe. Dar asta e bine. Căci ai oportunitatea de a fi încă printre primii care intră î n acest viitor de care vorbesc. Abilitatea de a programa este aşa de căutată şi de bine plătită în prezent de către angajatori tocmai fiindcă e (încă) rară. Dar şi abilitatea de a scrie şi a citi era pe vremuri întâlnită doar la un grup restrâns de oameni învăţaţi. Iar î n ziua de azi ar fi penibil ca cineva să scrie în CV că ştie să citească şi scrie, nu? Similar, va veni o vreme când nimeni 7
nu-şi va mai pune problema că nu ştii deloc să programezi (măcar la un nivel de bază). "Păi dacă tot va veni vremea aia, ce rost are s ă mă apuc deacum de programare? " te poţi întreba. Cu cât te apuci mai devreme, cu atât poţi avansa mai mult (şi cu mai puţin efort).
E o investiţie în tine pe care (odată ce vei prinde gustul programă rii) nu numai că nu o vei regreta, ci îţi vei dori să te fi apucat mai din timp. Ce presupune abilitatea de a programa calculatoare? Şi de unde ştii dacă o ai în tine sau nu? La cea de-a doua întrebare răspunsul e că cel mai probabil o ai. Reciteşte rândurile de mai sus, dacă nu mă crezi. Ştii să îţi planifici în detaliu o vacanţă, să organizezi o petrecere sau să faci lista de cumpărături pentru masa de Crăciun? Da? Înseamnă că ești bun(ă) pentru programare. Poate că nu mă crezi (încă). Mi s-a mai întâmplat. E normal să eziţi în faţa necunoscutului. Dar eu o să te ajut să îţi faci intrarea în acest domeniu fascinant al progrămării. Nu pe uşa din dos şi nici prin vreo fereastră, ci direct pe uşa principală, cu capul sus şi cu paşi încrezători. Au trecut vremurile când iniţierea în programare însemna nişte texte gri afişate pe un fundal negru. Acum trăim în viitor. Acum programarea e colorată, e veselă, e jucăuşă. E la fel de puternică, dar mult mai atractivă. Dacă ai citit până aici înseamnă că ţi-am captat interesul. Mă bucur pentru asta şi sunt conştient că am responsabilitatea de a veni cu ceva valoros în întâmpinarea interesului tău. Cartea de față își propune să te ia de mână și să te introducă pas cu pas, într-o manieră facilă și directă, în universul programării. Vei vedea că pe tot parcusul ei mă adresez ție în mod direct, fără înflorituri redundante sau ocolișuri politicoase, în ideea de a te aduce cât mai rapid și mai simplu 8
cu putință din punctul zero în punctul în care ești capabil să dansezi liber cu conceptele fundamentale din programarea calculatoarelor. Cartea este organizată în două părţi. Prima parte îşi propune să te ajute să deprinzi bazele gândirii de programator. Având în vedere importanţa deosebită a construirii unui fundament solid, toate lecţiile din prima parte sunt însoţite de exerciţi i rezolvate. În partea a doua vei deprinde noţiuni mai avansate, dar esenţiale dacă îţi doreşti să faci programare adevărată.
Îţi urez lectură fascinantă şi mai ales acţiune captivantă. Căci programarea se învaţă nu atât citind, cât experimentând. Florin Bîrleanu
9
Partea I: Bazele gândirii de programator Când te apuci să construieşti o casă, care e primul lucru pe care îl faci? Acoperişul? Pereţii? Fundaţia, desigur! În această primă parte îţi vei construi fundaţia unei gândiri solide de programator. Adică vei învăţa din ce este compus un program de calculator şi cum poţi îmbina cele câteva instrucţiuni de bază pentru a construi programe complexe. Nu vreau să îţi vorbesc încă despre compilatoare şi limbaje de programare. Cred că în această etapă a drumului tău mai mult te-ar încurca decât să te ajute. De aceea am ales ca pentru acest “abecedar” să folosesc un JavaScript simplificat, care rulează direct într-un browser web, fără să fie nevoie să foloseşti un anumit sistem de operare sau să îţi instalezi programe speciale. Aşa că tot ceea ce vei învăţa în această primă parte poate fi experimentat la acest link: http://igotopia.ro/programez-online/ În lipsa unei conexiuni la Internet, poţi folosi varianta offline a simulatorului, pe care ai primit-o la achiziţionarea acestei cărţi. Trebuie doar să dezarhivezi fişierul ZIP într-un dosar nou şi să deschizi fişierul HTML de acolo. 10
Modulul I
Liniuţe Mai ţii minte cum ai învăţat să scrii? Mai întâi ai învăţat să faci liniuţe şi bastonaşe, apoi ai învăţat să construieşti cu ajutorul lor litere ce ţi-au permis să formezi cuvinte cu care ai învăţat apoi să faci propoziţii si fraze. În acest prim modul vei învăţa “liniuţele”. Adică vei învăţa care sunt cele mai importante elemente ce stau la baza construirii unui program de calculator. Vei învăţa că un program este compus dintr-o înşiruire de instrucţiuni care se vor executa una după alta şi că nişte lucruri numite variabile se folosesc pe post de casete (ca cele de bijuterii) în care ţii valori. Vei învăţa şi nişte instrucţiuni speciale cu ajutorul cărora poţi spune calculatorului să facă ceva doar dacă e adevărată o anumită condiţie sau să repete ceva atâta timp cât e adevărată o anumită condiţie. Lecţiile din acest modul sunt “motorul” programării, aşa că te rog să le tratezi cu seriozitatea pe care o merită.
11
1. Instrucțiuni 1.1 Lecţie Hai să ne apucăm de treabă! Mai intâi, ce îți trebuie pentru a incepe să înveți să programezi un calculator? Probabil deja te gândești că un prim lucru absolut necesar este calculatorul. Ei bine, nu! Consider că prima lecție pe care ar trebui să o învețe un viitor programator nu are nicio trebuință de calculator. Adevărata creație nu se întâmplă acolo. Ci adevărata creație rezultă dintr -o reorganizare a minții, dintr-o antrenare a creierului în direcția dobândirii unei noi abilități, a unui nou mod de a vedea lumea. (Poate că ultima afirmație ți se pare un pic exagerată. Poate că tu îți doresti doar să înveți să faci programe de calculator, și nu să îți schimbi modul de a vedea lumea. În cazul acesta te avertizez că vei putea ajunge cel mult un zugrav, și nicidecum un pictor.) O foaie de hârtie și un pix. Atât îți trebuie pentru a învăța cea mai importantă lecție din programarea calculatoarelor. Si în ce constă această lecție?
Constă în a înțelege faptul că un calculator poate să execute doar un set foarte limitat de instrucțiuni. Și că orice lucru pe care ne dorim ca un calculator să -l facă trebuie să -l putem “sparge” într-un șir format din aceste 12
intrucțiuni pe care calculatorul le cunoaște și le poate executa. (La fel ca în jocul Tetris — avem doar câteva tipuri de piese și trebuie să ne descurcăm cu ce avem pentru a construi linii.) Să luăm un exemplu simplu. Să zicem că avem un calculator virtual care știe doar să aprindă și să stingă câte un punct situat la coordonate oarecare pe un ecran de 10×10 astfel de puncte. Te rog să desenezi pe foaia de hârtie o schiță a acestui ecran. Ar trebui să-ți iasă ceva ca în imaginea alăturată (mai puțin cele două puncte marcate). Pentru a identifica fiecare dintre cele 100 de puncte de pe ecran putem, de exemplu, denumi coloanele si liniile folosind numere. Astfel, punctul marcat cu roșu se găsește pe coloana 7 (numărând de la stânga la dreapta și începând cu 1) și pe linia 3 (numărând de jos în sus și începând cu 1), deci la coordonatele (7, 3). (Punctul negru din stânga jos este situat la coordonatele (1, 1) și constituie originea ecranului nostru virtual.) Pentru ca lucrurile să fie şi mai clare, am marcat pe figura din stânga modul cum sunt numerotate coloanele (pe axa x) şi liniile (pe axa y) ecranului virtual. Punctul negru marcat este la coordonatele (3, 4). Am spus că acest calculator imaginar de care 13
vorbim știe să aprindă și să stingă oricare dintre cele 100 de puncte. Altfel spus, el poate executa doar două instrucțiuni: • Aprinde punctul de la coordonatele (x, y). • Stinge punctul de la coordonatele (x, y). Putem simplifica lucrurile simplificând denumirile acestor comenzi în Aprinde(x, y) și, respectiv, Stinge(x, y). Iată, în continuare, cum am putea folosi acest calculator imaginar pentru a desena o floare. Marchează-ți, te rog, pe foaia de hârtie punctele care trebuie aprinse pentru a desena floarea din imaginea de alături. Apoi notează-ți pentru fiecare dintre aceste puncte coordonatele. Tot ce mai trebuie acum pentru a instrui calculatorul nostru virtual să deseneze această floare este să îi dăm comenzile pentru aprinderea fiecărui punct. Te rog să îți notezi pe foaie succesiunea de comenzi rezultată. La mine programul rezultat astfel este următorul: Aprinde(1, 5) Aprinde(1, 6) Aprinde(2, 2) Aprinde(2, 3) Aprinde(2, 4) Aprinde(2, 7) Aprinde(2, 8) Aprinde(2, 9) Aprinde(3, 2) 14
Aprinde(3, 4) Aprinde(3, 7) Aprinde(3, 9) Aprinde(4, 2) Aprinde(4, 3) Aprinde(4, 5) Aprinde(4, 6) Aprinde(4, 8) Aprinde(4, 9) Aprinde(5, 1) Aprinde(5, 4) Aprinde(5, 5) Aprinde(5, 6) Aprinde(5, 7) Aprinde(5, 10) Aprinde(6, 1) Aprinde(6, 4) Aprinde(6, 5) Aprinde(6, 6) Aprinde(6, 7) Aprinde(6, 10) Aprinde(7, 2) Aprinde(7, 3) Aprinde(7, 5) Aprinde(7, 6) Aprinde(7, 8) Aprinde(7, 9) Aprinde(8, 2) Aprinde(8, 4) Aprinde(8, 7) Aprinde(8, 9) Aprinde(9, 2) Aprinde(9, 3) Aprinde(9, 4) Aprinde(9, 7) Aprinde(9, 8) Aprinde(9, 9) 15
Aprinde(10, 5) Aprinde(10, 6) Poate că programul tău aprinde punctele într-o altă ordine, însă ce e important acum e faptul că efectul final este același. Acum haide să vedem cum am putea face ca discul din centrul florii să dispară . Evident, va trebui să folosim instrucțiunea Stinge(x, y), unde (x, y) vor fi pe rând coordonatele fiecaruia dintre punctele ce construiesc acel cerc plin. Notează-ți și acest program, după care te rog să-l compari cu programul listat în continuare: Stinge(4, 5) Stinge(4, 6) Stinge(5, 4) Stinge(5, 5) Stinge(5, 6) Stinge(5, 7) Stinge(6, 4) Stinge(6, 5) Stinge(6, 6) Stinge(6, 7) Stinge(7, 5) Stinge(7, 6) E simplu, nu? (Este ca și cum am avea un robot pe care îl putem programa să deseneze sau să șteargă un punct pe o tablă la o poziție indicată. După cum ai văzut, prin succesiuni de astfel de instrucțiuni simple putem realiza pe acea tablă o mulțime uriașă de desene. (Dacă facem un mic calcul, observăm că putem construi un număr de 2 100 desene distincte. Cel mai probabil, însă, cea mai mare parte dintre aceste desene vor arăta doar ca niște împrăștieri aleatoare de puncte, fără nicio semnificație.) Practic, singurul lucru care ne limitează este faptul că tabla permite doar 10×10 puncte și doar două culori (stins (ALB), și aprins (NEGRU)). Însă într16
un calculator real aceste limitări nu sunt prezente — ci dispunem de peste 1 000 000 de puncte și peste 10 000 000 de culori.) Poate că ceea ce ai făcut până aici ți s-a părut o joacă mult prea simplă și care nu are mare legătură cu ideea de programare a calculatoarelor. Însă te asigur că dacă ai înțeles acestă lecție și ai făcut singur cele două mici programe ai făcut un pas uriaș în a înțelege cum se programează un calculator, caci ai înțeles unul dintre conceptele de bază — acela de instrucțiune. Ce ne-am face, însă, dacă ne-am propune ca după ce am desenat această floare să desenăm altceva? Cel mai indicat ar fi ca mai întâi să ștergem toate punctele, după care să le aprindem pe cele care compun noul desen. În cazul în care am cunoaște desenul ce a fost afișat anterior am putea, evident, să înlocuim în programul prin care l-am generat fiecare instrucțiune de tip Aprinde(x, y) cu o intrucțiune de tip Stinge(x, y). Însă hai să ne gândim la o situație mai avansată, în care dintr-un motiv sau altul nu ne rămâne altă soluție decât să ștergem absolut toate punctele de pe ecran înainte de a face noul desen. Ar trebui, așadar, să scriem 100 de instrucțiuni de tip Stinge(x, y). Cam mult de muncă pentru un simplu ecran de 10×10 puncte, nu? (Nici nu vreau să mă gândesc în condițiile astea la cazul unui ecran de 1280×800 pixeli.) Banuiești deja, sunt convins, că există o soluție mult mai facilă decât cele 100 de instrucțiuni de tip Stinge(x, y). Pentru a o înțelege, însă, este nevoie de conceptul de variabilă (care e doar o denumire dată unei mici bucăți dintr-o memorie în care putem scrie și din care putem citi valori). Voi diseca acest concept în capitolul următor. Pentru moment te-aș ruga să te gândești cum l-ai putea schița pe Mickey Mouse folosind calculatorul virtual discutat în acest capitol. Căci da, principala utilitate a calcultorului 17
este aceea ce a fi un instrument de creație. Și da, scopul acestei cărți este de a te învăța să programezi de la zero, dar în același timp și de a-ți delecta creierul și de a te încânta ca un joc. Gata programul? Iată versiunea mea: Aprinde(2, 10); Aprinde(3, 10); Aprinde(1, 9); Aprinde(4, 9); Aprinde(1, 8); Aprinde(4, 8); Aprinde(2, 7); Aprinde(3, 7); Aprinde(8, 10); Aprinde(9, 10); Aprinde(7, 9); Aprinde(10, 9); Aprinde(7, 8); Aprinde(10, 8); Aprinde(8, 7); Aprinde(9, 7); Aprinde(4, 7); Aprinde(5, 7); Aprinde(6, 7); Aprinde(7, 7); Aprinde(3, 6); Aprinde(3, 5); Aprinde(3, 4); Aprinde(3, 3); Aprinde(4, 2); Aprinde(5, 2); Aprinde(6, 2); Aprinde(7, 2); Aprinde(8, 3); Aprinde(8, 4); Aprinde(8, 5); Aprinde(8, 6); Simplu, nu? (A se observa că spre deosebire de programele scrise până acum, aici fiecare instrucțiune este terminată cu semnul ";". Utilizarea lui nu este, însă, obligatorie atunci când scriem doar câte o instrucțiune pe linie.) Acum hai să mai muncim încă un pic. Înlocuiește-l pe Aprinde 18
din primele 8 instrucțiuni cu Stinge. Gata? Ce crezi că se va întâmpla cu Mickey dacă rulezi acest program? Evident, îi va dispărea urechea stângă.
Iată-ne acum ajunși la finalul primei lecții de programare. Î ncă suntem departe de a construi un program adevărat, însă dacă m-ai urmărit pas cu pas până aici și vei face la fel pe parcursul capitolelor următoare, mintea ta se va transforma subtil într-o minte de programator. Vei deprinde abilitățile de bază pe care orice bun programator trebuie să le stăpânească. Vei putea apoi, cu ajutorul lor, să faci cu ușurință tranziția către învățarea oricărui limbaj de programare dorești. Toate programele pe care le-ai făcut până aici (ca și cele ce vor urma în capitolele următoare) le poți verifica (și chiar îți recomand insistent s-o faci) fie folosind simulatorul online de la adresa http://igotopia.ro/programez-online/, fie (în lipsa unei conexiuni la Internet) folosind simulatorul offline pe care l-ai primit împreună cu această carte. În caseta de text din partea stângă îți scrii programul dorit, iar în micul ecran din partea dreaptă vei putea vedea rezultatele după apăsarea butonului de jos. (Butonul de sus îl poți folosi pentru a șterge întreg ecranul și a desena grilajul gri care delimitează cele 10x10 puncte.) Tot ce îți trebuie pentru a putea învăţa să programezi este, așadar, un browser web. Plus curiozitate, dorință de a construi, entuziasm şi un pic de profunzime.
1.2 Exerciţii Lecţia 1 – Exerciţiul 1: 19
Scrie un program care desenează pe ecranul virtual de 10x10 puncte un pătrat negru plin cu latura de 3 puncte. 1.1) De câte instrucţiuni Aprinde este nevoie? 1.2) Câte astfel de pătrate distincte s-ar putea desena pe ecranul virtual? (sau, altfel spus: în câte locuri diferite de pe ecran poţi desena pătratul acela?) 1.3) Care este numărul maxim de astfel de pătrate distincte ce s-ar putea desena în acelaşi timp pe ecranul virtual fără suprapuneri între ele?
Lecţia 1 – Exerciţiul 2: Scrie un program care să aibă ca efect desenarea pe ecranul virtual a unui pătrat alb cu latura de 3 puncte. 2.1) De câte instrucţiuni Aprinde este nevoie? 2.2) Câte astfel de pătrate diferite s-ar putea desena pe ecranul virtual fără suprapuneri între ele?
Lecţia 1 – Exerciţiul 3: A se vedea figura alăturată. Scrie un program care să aibă ca efect generarea acestei imagini. 3.1)
De câte instrucţiuni Aprinde a fost nevoie? 3.2) De câte instrucţiuni Aprinde ar fi fost nevoie dacă ecranul virtual ar fi avut doar 8x8 puncte? Dar dacă ar fi avut doar 6x6 puncte? Dar pentru 4x4 puncte? Dar pentru 2x2 puncte? 20
3.3) De câte instrucţiuni Aprinde ar fi fost nevoie dacă ecranul virtual ar fi avut 100x100 puncte?
Lecţia 1 – Exerciţiul 4: A se vedea figura alăturată. Scrie un program care să aibă ca efect generarea acestei imagini. 4.1)
De câte instrucţiuni Aprinde a fost nevoie? 4.2)
De câte instrucţiuni Aprinde ar fi fost nevoie dacă ecranul virtual ar fi avut 100x100 puncte? 4.3) Dar dacă ecranul ar fi avut 101x101 puncte?
Lecţia 1 – Exerciţiul 5: Scrie un program care să aibă ca efect desenarea unei linii negre între punctele: 5.1) (3, 4) şi (9, 4) ◄ linie orizontală 5.2) (3, 4) şi (3, 6) ◄ linie verticală 5.3) (3, 6) şi (9, 4) ◄ linie oblică 5.4) Care ţi-a fost strategia pentru a rezolva punctul anterior? 5.5) Care este aria triunghiului desenat pe ecran (dacă presupunem că pătraţelele negre ar fi nişte puncte fără dimensiune şi distanţele dintre ele ar fi egale cu 1) ? 21
Lecţia 1 – Exerciţiul 6: 6.1) Ce figură geometrică desenează următorul program?: Aprinde(7, 8); Aprinde(7, 7); Aprinde(7, 6); Aprinde(6, 7); Aprinde(8, 7); 6.2) Dar programul următor?: Aprinde(1, 3); Aprinde(2, 2); Aprinde(2, 3); Aprinde(2, 4); Aprinde(3, 1); Aprinde(3, 2); Aprinde(3, 3); Aprinde(3, 4); Aprinde(3, 5); Aprinde(4, 2); Aprinde(4, 3); Aprinde(4, 4); Aprinde(5, 3); 6.3) Dacă centrele pătrăţelelor din colţurile figurii de la 6.1 ar fi legate prin linii, care ar fi aria figurii rezultate (în ipoteza că latura unui pătrăţel este egală cu 1)? 6.4) Dar aria figurii de la 6.2? 6.5) Scrie un program care are ca efect desenarea unei alte figuri geometrice care să aibă aceeaşi arie (calculată în condiţiile spuse la 6.3) ca şi figura de la 6.1.
Lecţia 1 – Exerciţiul 7: Adevărat sau fals? (Ia să vedem cum stai cu logica...) 7.1) Pătratul este un caz particular de romb. 7.2) Pătratul este un caz particular de dreptunghi. 7.3) Pătratul este un caz particular de paralelogram. 7.4) Pătratul este un caz particular de trapez. 22
7.5) Propoziţia 7.1 e adevarată şi propoziţia 7.3 e falsă. 7.6) Propoziţia 7.1 e adevarată sau propoziţia 7.3 e falsă. 7.7) Una dintre propoziţiile anterioare e adevarată. 7.8) Niciuna dintre propoziţiile anterioare nu e adevarată.
Lecţia 1 – Exerciţiul 8: 8.1) Ce întrebare mi-ai pune dacă te-aş întreba care este al 16-lea punct de pe ecranul de 10x10 puncte? 8.2) Care ar fi răspunsul tău la întrebarea de la 8.1 dacă răspunsul meu la întrebarea ta ar fi “E la alegerea ta. ”? 8.3) Câte astfel de alegeri posibile ai fi avut dacă ecranul era de 2x2 puncte? 8.4) Dar dacă ecranul era de 3x3 puncte?
Lecţia 1 – Exerciţiul 9: 9.1) Câte modalităţi diferite de a plasa la întâmplare un punct negru pe ecranul de 10x10 puncte există? 9.2) Dar dacă în loc de un singur punct ar trebui să plasezi două puncte? 9.3) Câte desene (folosind doar două culori) diferite se pot realiza pe un ecran de 2x2 puncte? 9.4) Dar pe un ecran de 3x3 puncte? 9.5) Dar pe un ecran de 100x100 puncte?
23
Lecţia 1 – Exerciţiul 10: 10.1) Care ar fi coordonatele punctului (7, 2) dacă originea (adică punctul (0, 0)) sistemului de axe de coordonate (x, y) ar fi situată în punctul (10, 10)? 10.2) Dar dacă am adăuga la această deplasare şi faptul că axa x creşte la stânga şi axa y în jos?
1.3 Soluţii Lecţia 1 – Exerciţiul 1: Aprinde(1, 1); Aprinde(2, 1); Aprinde(3, 1); Aprinde(1, 2); Aprinde(2, 2); Aprinde(3, 2); Aprinde(1, 3); Aprinde(2, 3); Aprinde(3, 3); 1.1) Au fost necesare 9 instrucţiuni. 1.2) Dacă mut pătratul la dreapta cu câte o poziţie, de câte ori îl pot muta? De atâtea ori cât ar fi necesar pentru ca latura din dreapta să ajungă pe coloana cu x = 10 a ecranului. Iniţial latura din dreapta este pe coloana cu x = 3. Câţi paşi sunt de la x = 3 până la x = 10 (inclusiv)? Răspunsul e 10 – 3 (+ 1). Înseamnă că pe orizontală pătratul poate sta în 8 poziţii diferite. Dar pentru fiecare dintre aceste poziţii îl pot muta şi pe verticală, nu? În câte poziâii diferite se poate găsi pătratul pe verticală? Urmând un raţionament similar cu cel de mai sus obţin că şi pe verticală se poate găsi în 10 – 3 + 1 = 8 poziţii diferite. Deci pe orizonală pătratul poate sta în 8 poziţii diferite, iar pentru fiecare dintre aceste poziţii poate sta pe verticală în 8 24
poziţii diferite. Deci 8 poziţii verticale pentru prima poziţie orizontală, încă 8 poziţii verticale pentru a doua poziţie orizontală, ... şi încă 8 poziţii verticale pentru a opta poziţie orizontală. În total 8+8+... +8 (de opt ori) = 8*8 = 64. 1.3) Câte pătrate de 3x3 puncte aş putea pune pe orizontală fără să se suprapună unele peste altele (putând, în schimb, să se atingă)? Nu e greu de observat că răspunsul e dat de câtul împărţirii lăţimii ecranului (adică 10 puncte) la latura pătratului (adica 3 puncte). Adica e vorba de câtul împărţirii lui 10 la 3, adică 3. (Dacă aş impune condiţia suplimentară ca aceste pătrate să nu se atingă între ele, atunci ar trebui să calculez câtul împărţirii lui 10 nu la 3, ci la 3+1 (pentru a avea grijă să las un spaţiu de un punct alb între pătrate). Dar observ că în cazul în care ecranul ar fi fost de 7x7 formula asta nu ar fi mers (căci după cel mai din dreapta pătrat nu e obligatoriu sa mai încapă un punct alb). Formula corectă ar fi, deci, “câtul împărţirii lui (10+1) la (3+1)”.) Deci aş putea pune 3 pătrate pe orizontală. Iar pe verticală raţionamentul e similar. Înseamnă că fiecare linie orizontală cu trei pătrate aş putea-o repeta pe verticală de 3 ori. Rezultă că în total voi avea pe ecran 3*3 = 9 pătrate. (Iar pentru situaţia în care aş fi dorit ca pătratele să nu se suprapună între ele, răspunsul ar fi fost 2*2 = 4 pătrate.)
Lecţia 1 – Exerciţiul 2: Aprinde( x , y );, unde x şi y iau pe rând toate valorile de la 1 la 10, cu excepţia combinaţiilor din programul prezentat la soluţia de la Exerciţiul 1. 2.1) În total pe ecran sunt 10*10 puncte. Adică 100 de puncte. Dar programul nu conţine 100 de instrucţiuni, ci 100 minus numărul de instrucţiuni necesare pentru desenarea unui pătrat de 3x3 puncte, plin (adică 9 instrucţiuni). 100 – 9 25
= 91. 2.2) Răspunsul e similar cu cel din soluţia de la Exerciţiul 1.3.
Lecţia 1 – Exerciţiul 3: Aprinde(1, 1); Aprinde(1, 2); ...; Aprinde(1, 10); Aprinde(2, 10); Aprinde(3, 10); ...; Aprinde(10, 10); Aprinde(10, 9); Aprinde(10, 8); ...; Aprinde(10, 1); Aprinde(9, 1); Aprinde(8, 1); ...; Aprinde(3, 1); Aprinde(3, 2); Aprinde(3, 3); ...; Aprinde(3, 8); Aprinde(4, 8); Aprinde(5, 8); ...; Aprinde(8, 8); Aprinde(8, 7); Aprinde(8, 6); ...; Aprinde(8, 3); Aprinde(7, 3); Aprinde(6, 3); Aprinde(5, 3); Aprinde(5, 4); Aprinde(5, 5); Aprinde(5, 6); Aprinde(6, 6); 3.1) Câte puncte negre conţine desenul? Încep din colţul stânga-jos, urmăresc laturile în sens invers trigonometric (adică în sensul acelor de ceasornic) şi notez numărul de puncte negre din fiecare latură (având grijă să nu am suprapuneri de un punct cu latura imediat anterioară). Obţin următoarele dimensiuni de laturi: 10, 9, 9, 7, 7, 5, 5, 3, 3, 1 Suma lor cât este? 59. 3.2) Pentru un ecran de 8x8 puncte aş fi avut următoarele dimensiuni de laturi: 26
8, 7, 7, 5, 5, 3, 3, 1 Deci în total 39 de instrucţiuni. Pentru un ecran de 6x6 puncte: 6, 5, 5, 3, 3, 1 Deci în total 23 de instrucţiuni. Pentru ecranul de 4x4 puncte: 4, 3, 3, 1 Deci în total 11 instrucţiuni. Iar pentru ecranul de 2x2 puncte: 2, 1 Adică 3 instrucţiuni. 3.3) Deja se vede un model, nu-i aşa? Înseamnă că pentru un ecran de 100x100 puncte aş avea următoarele dimensiuni de laturi: 100, 99, 99, 97, 97, 95, 95, ..., 5, 5, 3, 3, 1 Se pune problema acum cum pot să calculez aceasta sumă? Poate că pare greu, dar nu e chiar aşa. Dacă pun deoparte primul şi ultimul termen (adică 100 şi 1) şi iau termenii din sumă în ordine inversă, am aşa: 3+3+5+5+7+7+...+97+97+99+99 = 2* (3+5+7+...97+99) Deci problema s-a redus la a calcula suma tuturor numerelor impare de la 3 la 99. Cum pot face asta? O variantă este să scriu fiecare număr din sumă sub forma 2*k+1. Rezulta aşa: 3 = 2*1 + 1 5 = 2*2 + 1 27
7 = 2*3 + 1 ... 97 = 2*48 + 1 99 = 2*49 + 1 Înseamnă că suma 3+5+7+...+97+99 este egală cu suma termenilor din dreapta semnelor de egalitate. Adică este egală (dacă îl dau pe 2 factor comun şi îi adun pe cei 49 de 1) cu 2 * (1+2+3+...+48+49) + 49. Acum problema s-a redus la a calcula suma tuturor numerelor întregi de la 1 la 49. Ca să nu mai intrăm în detalii, hai să folosim direct formula n*(n+1)/2. (Chiar nu e greu de retinut, nu? En pe lângă en plus unu, totul supra doi . Oricum, nu e foarte dificil de demonstrat formula asta dacă observi că suma elementelor egal depărtate de capete este egală: 1+49 = 2+48 = 3+47 = ... = 25+25.) Rezultă că suma 1+2+3+...+48+49 este egala cu (49*50)/2 = 1225. Iar acum ne întoarcem la calculele lăsate în aşteptare (vezi un pic mai sus). Obtinem că 2*1225 + 49 = 2499. Şi (făcând calculele lăsate în aşteptare ceva mai sus obţinem că) răspunsul la intrebare e: 2*2499 + 100 + 1 = 5099.
Lecţia 1 – Exerciţiul 4: Aprinde(1, 10); Aprinde(1, 7); Aprinde(2, 8); Aprinde(3, 9); Aprinde(4, 10); ... Aprinde(1, 1); Aprinde(2, 2); Aprinde(3, 3);... Aprinde(10, 10); ... 28
Aprinde(7, 1); Aprinde(8, 2); Aprinde(9, 3); Aprinde(10, 4); Aprinde(10, 1); 4.1) Câte puncte sunt pe fiecare linie neagră diagonală, începând din colţul stânga-sus? 1, 4, 7, 10, 7, 4, 1 Adică în total 34 de puncte, adică tot atâtea instrucţiuni Aprinde. 4.2) Dacă ecranul ar fi avut 100x100 de puncte, nu e greu de “văzut” că numarul de puncte negre de pe liniile diagonale ar fi fost aşa: 1, 4, 7, 10, 13, 16, 19, ..., 97, 100, 97, ..., 19, 16, 13, 10, 7, 4, 1 Rămâne doar să însumez toate aceste numere. Suma lor e: 2 * (1+4+7+10+...+97) + 100 Suma din paranteze arată dubios, nu? Dar e clar că numerele consecutive din suma dintre paranteze sunt din 3 în 3. Adică: 1 = 1 = 1+0*3 4 = 1+3 = 1+1*3 7 = 1+3+3 = 1+2*3 10 = 1+3+3+3 = 1+3*3 ... 97 = ... = 1+32*3 Deci 1+4+7+...+97 = 33*1 + 3*(0+1+2+3+...+32). Iar suma de la 1 la 32 cât e? (Aminteste-ţi formula din soluţia de la Exerciţiul 3.3.) (32*(32+1))/2 = 528. Înseamnă că suma 1+4+7+...+97 este egală cu: 33+3*528 = 1617. 29
Revenind la suma iniţială, obţin că numărul total de puncte ar fi: 2 * 1617 + 100 = 3334. 4.3) Dacă ecranul ar fi avut 101x101 puncte, numărul de puncte negre de pe liniile diagonale ar fi aşa: 1, 4, 7, 10, ..., 97, 100, 99, 96, 93,..., 3 Suma 1+4+7+...+97 deja am calculat (la punctul anterior) că este egală cu 1617. Mai rămâne de calculat suma 3+6+9+...+99. Pentru asta observ că îl pot da pe 3 factor comun şi obtin că suma acesta este egală cu 3*(1+2+3+..+33). Iar suma 1+2+3+...33 este egală (conform formulei “ n pe langa n+1, totul supra 2 ”) cu 33*34/2 = 561. Inmulţind cu 3 rezultă 1683. Rezultă că suma totală este 1617+100+1683. Adică 3400.
Lecţia 1 – Exerciţiul 5: 5.1) Aprinde(3, 4);Aprinde(4, 4);Aprinde(5, 4); Aprinde(6, 4);Aprinde(7, 4);Aprinde(8, 4);Aprinde(9, 4); 5.2) Aprinde(3, 4); Aprinde(3, 5); Aprinde(3, 6); 5.3) Aprinde(3, 6); Aprinde(4, 6); Aprinde(5, 5); Aprinde(6, 5); Aprinde(7, 5); Aprinde(8, 4); Aprinde(9, 4); 5.4) Am trasat o linie imaginară între centrele pătrăţelelor (3, 30
6) şi (9, 4) şi am văzut din care pătrăţele situate între cele doua puncte linia trasată “muşcă” cel mai mult. 5.5) Triunghiul este dreptunghic, deci aria sa este egală cu jumătate din produsul catetelor. Una dintre catete are lungimea de la centrul punctului (3, 4) până la centrul punctului (3, 6) – deci are lungimea de 6 – 4 = 2 – iar cealaltă catetă are lungimea de la centrul punctului (3, 4) până la centrul punctului (9, 4) – deci are lungimea 9 – 3 = 6. Înseamnă că aria tringhiului (dreptunghic) este (jumătate din produsul catetelor sale, adică) 2*6/2 = 6.
Lecţia 1 – Exerciţiul 6: 6.1) Un romb. 6.2) La fel. 6.3) Aria unui romb este jumătate din produsul diagonalelor sale. Diagonala verticală este de la centrul punctului (7, 8) până la centrul punctului (7, 6) – deci are o lungime de 8 – 6 = 2 – iar diagonala orizontală este de la centrul punctului (6, 7) până la centrul punctului (8, 7) – deci are o lungime de 8 – 6 = 2. Înseamnă că aria rombului este 2*2/2 = 2. 6.4) Urmând un raţionament similar obţin că aria este (51)*(5-1)/2 = 8. 6.5) Trebuie să desenez o figură geometrică ce are aria 2. Pot face un dreptunghi cu lăţimea de 1 şi lungimea de 2. Aprinde(1, 1); Aprinde(2, 1); Aprinde(3, 1); Aprinde(1, 2); Aprinde(2, 2); Aprinde(3, 2); 31
Lecţia 1 – Exerciţiul 7: 7.1) Adevărat. 7.2) Adevărat. 7.3) Adevărat. 7.4) Fals. 7.5) Fals. 7.6) Adevărat. 7.7) Adevărat dacă “una” înseamnă “cel puţin una” şi fals daca “una” înseamnă “doar una”. 7.8) Fals.
Lecţia 1 – Exerciţiul 8: 8.1) În ce ordine ar trebui să număr punctele şi de la care punct să încep? 8.2) Aş alege să le număr începând la la punctul (1, 1) şi aş merge spre dreapta, apoi aş trece la linia cu y = 2 şi aş merge, la fel, spre dreapta şi aşa mai departe până aş ajunge la punctul (10, 10). Înseamnă că al 16-lea punct ar fi situat pe linia a 2-a şi coloana a 6-a, deci la coordonatele (6, 2). 8.3) Dacă ecranul era de 2x2 puncte înseamnă că ar fi avut în total 4 puncte. Câte modalităţi diferite de a ordona cele 4 puncte aş avea? Pentru primul punct aş avea 4 alegeri diferite (adica aş putea alege drept prim punct pe oricare dintre cele 4). Pentru al doilea punct mi-ar mai rămâne doar 3 alegeri (căci n-aş mai 32
putea să-l aleg pe primul). Pentru al treilea mi-ar mai rămâne doar 2 alegeri (căci pe primele două puncte nu le-aş mai putea alege). Iar pentru ultimul punct aş avea o singură posibilitate. Deci în total aş avea 4*3*2*1 posibilităţi. 8.4) Dacă ecranul era de 3x3 puncte înseamnă că ar fi avut în total 9 puncte. Pentru a calcula câte modalităţi diferite de a le ordona există, pot urma un raţionament similar cu cel de mai sus. Drept prim punct îl pot alege pe oricare dintre cele 9, deci aş avea 9 posiblităţi. Odată ales un prim punct, îmi mai rămân 8 posiblităţi pentru a alege cel de-al doilea punct, apoi 7 posiblităţi pentru al treilea punct, şi aşa mai departe până la ultimul punct. Înseamnă că în total aş avea 9*8*7*6*5*4*3*2*1 = 362880. Un număr uimitor de mare, nu? (Poate de asta în matematică operaţia 9*8*7*...*2*1 se notează cu “9!” (care se citeşte “nouă factorial ”).)
Lecţia 1 – Exerciţiul 9: 9.1) Bănuiesc că e evident că răspunsul e 100, căci 100 de puncte sunt în total pe ecranul de 10x10, iar punctul negru îl pot plasa pe oricare dintre acele puncte. 9.2) Dacă ar trebui să plasez nu un punct, ci două, atunci pentru primul punct aş avea 100 de posiblităţi, iar pentru cel de-al doilea mi-ar mai rămâne 99 de posibilităţi, deci aş avea în total 9900 de posibilităţi. Numai că ordinea punctelor nu contează – fie că am ales întâi punctul A şi apoi punctul B, este acelaşi lucru. În cele 9900 de posibilităţi se găsesc atât situaţiile când punctul A este primul şi punctul B este al doilea, cât şi situaţiile când punctul B este primul şi punctul A este al doilea. Deci răspunsul nu este 9900, ci 9900/2, adică 4950. 33
(În matematică treaba asta se cheamă “combinări de 100 luate câte două” – ceea ce se traduce prin “ Dacă am 100 de puncte diferite, câte variante diferite de a alege la întâmplare doar două din cele 100 de puncte exist ă?”. Formula generală pentru “combin ări de n luate câte k ” este “n!/(k!*(n-k)!)”.)
9.3) Altfel spus, această î ntrebare se traduce prin “ câte moduri diferite de a aprinde oricâte puncte pe ecranul de 2x2 puncte exist ă”, sau “câte moduri diferite de a alege oricâte
elemente dintr-o mulţime de 4 puncte există”. Adică răspunsul ar fi “combinări de 4 luate câte 0“+”combinări de 4 luate câte 1”+”combinări de 4 luate câte 2”+”combinări de 4 luate câte 3”+”combinări de 4 luate câte 4”. Iar suma asta este (conform matematicii) egală cu “2 la puterea 4”, adică 2*2*2*2 (de 4 ori), adică 2^4, adică 16. Dar răspunsul se poate demonstra şi altfel: Fiecare dintre cele 4 puncte poate să aibă 2 stări posibile – aprins şi stins. Pentru fiecare dintre cele două stări ale unui punct oarecare oricare alt punct oarecare poate avea tot două stări posibile, şi pentru orice combinaţie de stări ale acestor două puncte oricare al treilea punct poate avea tot două stări posibile, şi aşa mai departe. Deci în total numărul de combinaţii posibile de stări ar fi 2*2*...*2 de atâtea ori câte puncte sunt. Adică 2 la puterea 4, adică 16. 9.4) Printr-un raţionament similar obţin că pe un ecran de 3x3 puncte (deci un ecran care conţine 9 puncte) pot face 2 la puterea 9 desene binare distincte. Adica 512 desene distincte (inclusiv cazul când ecranul este complet alb şi cazul când ecranul este complet negru). 9.5) Similar, pe un ecran de 100x100 puncte se pot face 2^(100*100) desene binare distincte.
34
Lecţia 1 – Exerciţiul 10: 10.1) Pentru a ajunge la punctul (7, 2) din punctul (10, 10) ar trebui să mă deplasez la stânga (deci în sens invers axei x) cu 3 puncte şi în jos (deci în sens invers axei z) cu 8 puncte. Dacă la (10, 10) ar fi originea (adică punctul (0, 0)), înseamnă că noile coordonate vor fi (-3, -8). 10.2) Dacă noua axă x (cea cu punctul (0, 0) situat în colţul dreapta-sus al ecranului de 10x10) ar creşte la stânga, înseamnă că dacă mă deplasez 3 puncte la stânga nu trebuie să le trec cu minus, ci cu plus; iar dacă noua axă y ar creşte în jos înseamnă că dacă mă deplasez 8 puncte în jos nu avansez pe partea negativă a axei, ci pe cea pozitivă – deci pe 8 nu îl trec cu minus, ci cu plus. Aşadar, răspunsul e (3, 8).
35
2. Variabile 2.1 Lecţie În capitolul anterior ai văzut că cel mai important lucru pe care trebuie să-l cunoști legat de programarea calculatoarelor este acela că dispui de un set de instrucțiuni elementare cu ajutorul cărora poți construi programe oricât de complexe. La fel cum un arhitect poate realiza construcții spectaculoase folosind doar câteva elemente de bază. Acum e timpul să înțelegi și cel de-al doilea dintre pilonii pe care se bazează programarea calculatoarelor. Utilizându-l, nu numai că vei putea să îți reduci dimensiunea programelor scriind porțiuni generice, ci îți vei extinde enorm gama de aplicații pe care le poti realiza. Este vorba despre variabile. Practic, nu există program care să nu le folosească. Bine, cu excepția programelor scrise în capitolul anterior — ai putea zice. Și ai avea dreptate. Parțial. Căci deși variabilele nu intervin explicit în acele programe, ele se regăsesc implicit în instrucțiunile Aprinde și Stinge. Îți amintești că le-am prezentat generic sub forma Aprinde(x, y) și Stinge(x, y)? Ei bine, x și y de aici sunt variabile. Sunt niște obiecte ce pot reține valori. Apoi, cu ajutorul acestor valori instrucțiunea Aprinde, de exemplu, va ști ce beculețe să aprindă. (A se remarca faptul că atât Aprinde(x, y), cât și Stinge(x, y), ne duc cu gândul la f(x, y). Îţi aminteşti cumva de la matematică ce notai de obicei prin f(x, y)? Da, o funcţie f de 36
două variabile, x și y . (Ca să fim și mai riguroși, de fapt f(x, y) reprezintă valoarea acestei funcții în punctul (x, y).) Acesta este motivul pentru care instrucțiuni ca Aprinde(x, y) și Stinge(x, y) sunt numite funcții . (Scrierea lor într-un program poartă numele de apel al funcției respective). Vom vedea întrun capitol ulterior că ne putem construi singuri astfel de funcții, în plus față de funcțiile de bază pe care le avem la dispoziție.) Ce este, deci, o variabilă ?
Am spus anterior că este un soi de obiect (virtual) ce poate reține valori. E ca o mică bucată de hârtie pe care calculatorul poate scrie valori cu creionul și le poate șterge cu guma de șters. Hai să vedem cum stau lucrurile pe exemple concrete. Să construim, pentru început, o variabilă numită a. Putem face acest lucru scriind: var a Dacă dorim să-i dăm acum lui a o valoare, să zicem 3, o putem face scriind pur și simplu: a=3 (Menționez că declararea și inițializarea unei variabile se pot face și într-o singura instrucțiune, de genul var a = 3.) Bun, avem acum o variabilă numită a, în care am stocat valoarea 3. Să vedem cum o putem folosi. Ce crezi că vor face acum, de exemplu, instrucțiunile următoare: Aprinde(a, 1) Aprinde(a, a) Dacă ai zis că prima dintre ele aprinde beculețul de la coordonatele (3, 1), ai avut dreptate. Căci valoarea stocată în 37
variabila a la momentul execuției instrucțiunii Aprinde(a, 1) este 3, deci instrucțiunea care se execută practic este Aprinde(3, 1). Similar, cea de-a doua instrucțiune este echivalentă cu Aprinde(3, 3) (deci efectul său va fi acela de a aprinde punctul de la coordonatele (3, 3)). Dacă m-ai urmărit până aici ar trebui să îți fie ușor să înțelegi efectul următorului program: var a = 1 Aprinde(a, 1) Aprinde(a, 2) a=2 Aprinde(a, 1) Aprinde(a, 2) În prima linie se declară o variabilă numită a și i se atribuie valoarea 1. În următoarele două linii se aprind punctele (1, 1) și (1, 2). Apoi se atribuie variabilei a valoarea 2. (Adică valoarea anterioară (și anume valoarea 1) se șterge și se scrie în a valoarea 2.) Deci ultimele două instrucțiuni (deși identice cu instrucțiunile de pe liniile 2 și 3 din program) vor aprinde punctele (2, 1) și (2, 2). O modalitate mai practică de a explica funcționarea unui program o constituie comentariile. Putem introduce un comentariu într-un program scriind //. Atât aceste două simboluri, cât și tot ce urmează după ele până la sfârșitul liniei (adică acolo unde se apasă tasta Enter ) nu va fi considerat instrucțiune, ci va fi ignorat de către calculator.
Varianta comentată a programului anterior ar putea arăta astfel: // Program de desenare patrat var a = 1 // definesc variabila a si // o initializez cu valoarea 1 Aprinde(a, 1) // aprind punctul (a, 1), adica (1, 1) -38
Aprinde(a, 2) a=2 Aprinde(a, 1) Aprinde(a, 2)
// caci valoarea din a este 1 // aprind punctul (a, 2), adica (1, 2) // sterg valoarea anterioara scrisa in a, // si scriu in variabila a valoarea 2 // aprind punctul (a, 1), adica (2, 1) -// caci valoarea din a este 2 // aprind punctul (a, 2), adica (2, 2)
În varianta aceasta, atât instrucțiunile programului, cât și explicațiile, se pot pune laolaltă într-un același program. (Ba chiar este recomandat ca orice program să conțină comentarii pentru a facilita înțelegerea ulterioară a lui.) Î ți recomand ca odată ajuns în acest punct să iei o pauză de la citit (dacă nu cumva ai făcut-o deja) și să experimentezi (folosind simulatorul despre care am vorbit) programul discutat aici. Gata? Programul a avut efectul așteptat? Ești liber să faci modificări la el și să vezi noile rezultate. (Prin astfel de experimente personale vei ajunge să înveți mult mai rapid și mai profund decât prin citirea a zeci de cărți.) Ce ar fi dacă în linia 4 din program (primul program, cel fără comentarii) în loc de a = 2 ai scrie a = a+1? Ce crezi că s-ar modifica? Dacă ai avut curiozitatea de a testa programul, atunci ai văzut că rezultatul final este același. Ceea ce era, oricum, normal, nu? Hai să vedem mai îndeaproape ce face instrucțiunea a = a+1. Mai întâi adună 1 la valoarea stocată în a (și dacă te uiți la a doua linie din program vei vedea că în a era stocată valoarea 1, deci rezultatul operației acesteia de adunare va fi 2), după care memorează în a valoarea calculată (bineînțeles, după ce șterge în prealabil vechea valoare din a). 39
Scrisă în cuvinte, instrucțiunea a = a+1 s-ar traduce în stochează în variabila a rezultatul adunării dintre valoarea cur entă din a și valoarea 1. Dar dacă aș vrea să folosesc în program o a doua variabilă, numită b și având stocată în ea valoarea 7, cum aș face? Bineînteles, cu intrucțiunea var b = 7. Similar, cu var c aș defini o a treia variabilă, numita c. Î n condițiile astea, ce ar face intrucțiunea c = b+a? Dar instrucțiunea c = b-a? Poți vedea pe viu rezultatele acestor instrucțiuni rulând programul de mai jos în simulator. var a = 3 var b = 7 var c c = b+a Aprinde(c, c) c = b-a Aprinde(c, c) Ce s-a întâmplat? Prima instrucțiune Aprinde(c, c) a aprins punctul de la coordonatele (10, 10), iar cea de-a doua instrucțiune Aprinde(c, c) a aprins punctul de la coordonatele (4, 4). Exact așa cum te așteptai, nu? Propun să încheiem acestă lecție introductivă despre variabile cu analiza programului următor: // Defineste doua variabile var a = 1 var b = 10 // Aprinde pe linia 1 punctul corespunzator // valorii din variabila a Aprinde(a, 1) // Aprinde pe linia 2 punctul corespunzator // valorii variabilei b 40
Aprinde(b, 2) // Si acum sa definim o a treia variabila, c, // si sa facem niste operatii var c = a a=b b=c // Oare ce s-a intamplat? Sa vedem in continuare. // Aprinde pe linia 3 punctul corespunzator // valorii variabilei a Aprinde(a, 3) // Aprinde pe linia 4 punctul corespunzator // valorii variabilei b Aprinde(b, 4) Ai rulat programul acesta în simulator? E clar ce face? Dacă nu, atunci îți recomand să iei o foaie de hârtie și un pix și să rulezi de mână, pas cu pas, programul. Pentru fiecare dintre variabilele a, b și c rezervă-ți pe foaie câte o coloană albă și notează în acele coloane valorile pe care le conțin variabilele respective la pasul de timp curent. În momentul în care în program unei variabile i se atribuie o nouă valoare, taie valoarea veche cu o linie și scrie noua valoare.
Felicitări! Ai parcurs deja jumătate din drumul însușirii celor patru piloni fundamentali ai programării calculatoarelor. Urmează două instrucțiuni care ascund în ele secretul forței acestui domeniu fascinant.
2.2 Exerciţii Lecţia 2 – Exerciţiul 1: 1.1) Scrie un program care să deseneze un pătrat de 2x2 puncte ce are colţul din stânga sus la coordonatele (a, b). (a 41
şi b sunt două variabile în care se vor pune valori la alegere.) Indicaţie: Se pot folosi operatorii + şi -. 1.2) Dar dacă la poziţia (a, b) ar fi colţul din stânga jos al pătratului?
Lecţia 2 – Exerciţiul 2: 2.1) Scrie un program care să deseneze o linie orizontală de lungime 5 puncte, în care punctul central e la coordonatele (a, b). (a şi b sunt două variabile în care poţi pune orice valori doreşti.) 2.2) Dar dacă linia era nu orizontală, ci verticală?
Lecţia 2 – Exerciţiul 3: Se dă desenul din poza alaturată. 3.1) Scrie programul ce are ca efect generarea acestui desen, folosind doar instrucţiuni Aprinde. 3.2) Modifică programul de la 3.1 astfel încât desenul să poată fi mutat pe orizontală oriunde pe ecran prin modificarea unei singure instrucţiuni din program. 3.3) Aceeaşi cerinţă ca la 3.2, doar că mutarea să se poată face nu pe orizontală, ci pe verticală. 42
Lecţia 2 – Exerciţiul 4: Se dă programul: ... var a = x; a = a+1; a = 2*a; Aprinde(a+1, a-1); Rezultatul e cel din poza alăturată. Care este instrucţiunea ce lipseşte în locul celor 3 puncte de la începutul programului?
Lecţia 2 – Exerciţiul 5: Să se scrie un program în care se definesc 3 variabile numite a, b şi c şi o variabilă numită x în care să se pună: 5.1) suma valorilor primelor 3 variabile. 5.2) media valorilor lor. 5.3) cât la sută din a reprezintă b.
Lecţia 2 – Exerciţiul 6: Într-un program sunt definite trei variabile numite a, b, şi c. Să se scrie secvenţa de program care: 6.1) mută valoarea lui a în b, valoarea lui b în c şi valoarea lui c în a. 43
6.2) pune în b media dintre a şi c, în a media dintre c şi b şi în c media dintre b şi a.
Lecţia 2 – Exerciţiul 7: Să se scrie un program care generează desenul din poza alăturată. Cum ai face ca prin modificarea unei singure instrucţiuni din el săgeata din desen să nu mai fie orientată spre dreapta, ci spre stânga?
Lecţia 2 – Exerciţiul 8: Să se scrie un program care generează următorul desen, situat în centrul ecranului de 10x10 puncte. (A se vedea poza alăturată.) 8.1) Ce modificări ar trebui făcute în coordonatele date ca parametri instrucţiunilor Aprinde, pentru ca “8”-ul din desen să se transforme în “infinit” (8 culcat )? 8.2) Cum ai face programul de desenare a “8”-ului astfel încât prin modificarea unei singure instrucţiuni “8”-ul să se transforme î n “3”?
Lecţia 2 – Exerciţiul 9: 44
Cum ai face un program care atunci când începe cu instrucţiunea “var nr = 0;” să aibă ca efect desenarea pe ecran a cifrei “0”, iar când începe cu instrucţiunea “var nr = 1;” să aibă ca efect desenarea pe ecran a cifrei “1”?
Lecţia 2 – Exerciţiul 10: Se dă programul: var a = ...; Aprinde(2*a+1, a*a); 10.1) Câte desene diferite poate genera acest program pe ecranul de 10x10 puncte (în ipoteza că prima instrucţiune ar fi completată cu o valoare pentru variabila a)? 10.2) Fă o modificare (validă) de un singur caracter (adică literă sau simbol) în cadrul programului de mai sus astfel încât programul rezultat să nu mai deseneze niciun punct negru pe grilajul de 10x10 puncte (indiferent de valoarea ce i s-ar atribui variabilei a).
2.3 Soluţii Lecţia 2 – Exerciţiul 1: 1.1)
// Colţul stânga sus la (a, b) var a = 9; var b = 10; Aprinde(a+0, b-0); Aprinde(a+0, b-1); 45
Aprinde(a+1, b-0); Aprinde(a+1, b-1);
1.2)
// Colţul stânga jos la (a, b) var a = 1; var b = 1; Aprinde(a+0, b+0); Aprinde(a+0, b+1); Aprinde(a+1, b+0); Aprinde(a+1, b+1);
Observaţie: Ştiu că o expresie de genul “a+0” ar fi mai eficientă sub forma echivalentă “a”, însă am optat pentru prima variantă pentru a face clar faptul că am pornit de la punctul (a, b) şi apoi m-am deplasat de la el cu un număr de paşi. (Zero paşi, în acest caz.)
Lecţia 2 – Exerciţiul 2: 2.1) // Linie orizontală de 5 puncte, cu punctul central la (a, b) var a = 3; var b = 1; Aprinde(a-2, b); Aprinde(a-1, b); Aprinde(a-0, b); 46
Aprinde(a+1, b); Aprinde(a+2, b); 2.2) // Linie verticală de 5 puncte, cu punctul central la (a, b) var a = 1; var b = 3; Aprinde(a, b-2); Aprinde(a, b-1); Aprinde(a, b-0); Aprinde(a, b+1); Aprinde(a, b+2);
Lecţia 2 – Exerciţiul 3: 3.1) // Desenare Aprinde(4, 7); Aprinde(5, 5); Aprinde(5, 6); Aprinde(6, 5); Aprinde(6, 6); Aprinde(7, 7); 3.2) // Deplasare pe orizontală prin modificarea variabilei a var a = 4; Aprinde(a+0, 7); 47
Aprinde(a+1, 5); Aprinde(a+1, 6); Aprinde(a+2, 5); Aprinde(a+2, 6); Aprinde(a+3, 7); 3) Deplasare pe verticală prin modificarea variabilei b var b = 7; Aprinde(4, b-0); Aprinde(5, b-2); Aprinde(5, b-1); Aprinde(6, b-2); Aprinde(6, b-1); Aprinde(7, b-0);
Lecţia 2 – Exerciţiul 4: Pe ecran s-a aprins punctul de la coordonatele (7, 5). Înseamnă că instrucţiunea care s-a executat pentru aprinderea lui a fost Aprinde(7, 5). Dar în program s-a apelat de fapt Aprinde(a+1, a-1). Înseamnă că: a+1 = 7 şi a-1 = 5. 48
Rezultă că: a = 6. Dar dacă luăm în ordine inversă operaţiile din program prin care valoarea variabilei “a” a ajuns 6 obţinem că ea a avut anterior instrucţiunii “a = 2*a;” valoarea 6/2, adica 3. (Am obţinut asta din ecuaţia “6 = 2*a”.) Şi anterior instrucţiunii “a = a+1;” a avut valoarea 3-1, adică 2. (Am obţinut asta din ecuaţia “3 = a+1”.) Iar prin instrucţiunea “var a = x;” variabila “a” a fost creată şi i s-a dat valoarea variabilei “x”. Dar am obţinut prin raţionamentul de mai sus că valoarea din “a” la acest punct din program a fost 2. Deci ecuaţia este “2 = x”. Înseamnă că variabila “x” trebuie să aibă valoarea 2, ceea ce înseamnă că în locul celor trei puncte aş putea pune o instrucţiune prin care definesc o variabilă numită “x” şi pun în ea valoarea 2, adică: var x = 2;
Lecţia 2 – Exerciţiul 5: var a = 4; var b = 5; var c = 6; 5.1) // Suma var x = a+b+c; 5.2) // Media var x = (a+b+c)/3; 49
5.3) // Procentul var x = (b*100)/a;
Lecţia 2 – Exerciţiul 6: var a = 4; var b = 5; var c = 6; 6.1) // a-->b-->c-->a var temp1 = b; // o variabila in care stochez temporar // o valoare ce urmeaza a fi stearsa b = a; var temp2 = c; // o variabila in care stochez temporar // o valoare ce urmeaza a fi stearsa c = temp1; // nu mai e nevoie sa stochez temporar valoarea lui “a”, // caci a fost deja salvata in “b” a = temp2; Sau alfel, cu o singură variabilă “temporara”: var temp = a; a = c; c = b; 50
b = temp; 6.2) // Medii var temp_b = b; b = (a+c)/2; var temp_a = a; a = (c+temp_b)/2; c = (temp_b+temp_a)/2;
Lecţia 2 – Exerciţiul 7: Aprinde(6, 3); Aprinde(7, 4); Aprinde(8, 5); Aprinde(7, 6); Aprinde(6, 7); Iar pentru a putea schimba orientarea săgeţii: var or = 1; // 1 <-- pentru orientare stanga; // 0 <-- pentru orientare dreapta Aprinde(6, 3); Aprinde(7 - or*2, 4); Aprinde(8 - or*4, 5); Aprinde(7 - or*2, 6); Aprinde(6, 7); 51
Lecţia 2 – Exerciţiul 8: Aprinde(4, 3); Aprinde(4, 4); Aprinde(4, 6); Aprinde(4, 7); Aprinde(5, 2); Aprinde(6, 2); Aprinde(5, 5); Aprinde(6, 5); Aprinde(5, 8); Aprinde(6, 8); Aprinde(7, 3); Aprinde(7, 4); Aprinde(7, 6); Aprinde(7, 7); 8.1) Dacă în fiecare apel al instrucţiunii Aprinde din programul de mai sus s-ar inversa primul cu al doilea parametru (adică dacă s-ar inversa între ele coordonatele x şi y), î n loc de “8”, pe ecran ar apărea “infinit”. 8.2) // “8” vs “3” var trei = 1; // 1<-- pentru “3”; 0 <-- pentru “8” Aprinde(4 + trei*3, 3); 52
Aprinde(4 + trei*3, 4); Aprinde(4 + trei*3, 6); Aprinde(4 + trei*3, 7); Aprinde(5, 2); Aprinde(6, 2); Aprinde(5, 5); Aprinde(6, 5); Aprinde(5, 8); Aprinde(6, 8); Aprinde(7, 3); Aprinde(7, 4); Aprinde(7, 6); Aprinde(7, 7);
Lecţia 2 – Exerciţiul 9: var nr = 0; Aprinde(1+nr*2, 2); Aprinde(1+nr*1, 3); Aprinde(2+nr*1, 1); Aprinde(2+nr*1, 4); Aprinde(3, 2); Aprinde(3, 3); 53
Lecţia 2 – Exerciţiul 10: 10.1) Pentru ca desenul să fie pe ecranul de 10x10 puncte, punctul (2*a+1, a*a) trebuie să fie situat în interiorul ecranului, adică trebuie să fie respectate în acelaşi timp inegalităţile: 0 < 2*a+1 < 11 0 < a*a < 11 Prima dintre inegalităţi e adevărată pentru următoarele valori ale variabilei a: 0, 1, 2, 3, 4, 5. A doua inegalitate este adevarată pentru următoarele valori ale lui a: 1, 2, 3. Intersecţia acestor două mulţimi este egală cu cea de-a doua mulţime, şi anume {1, 2, 3}. Cum această mulţime are trei elemente, înseamnă că pe ecranul de 10x10 puncte pot fi desenate doar trei puncte distincte (şi anume punctele (3, 1), (5, 4) şi (7, 9)). Deci răspunsul la intrebarea câte desene diferite poate genera programul este 4 (dacă ţinem cont şi de desenul “ecran gol”). 10.2) De exemplu, pot modifica instrucţiunea Aprinde prin inserarea unui caracter astfel: Aprinde(2*a+11, a*a); Sau, altfel, pot să modific caracterul “*” in “-“: Aprinde(2*a+1, a-a);
54
3. Decizii 3.1 Lecţie Dacă ai ajuns până în acest punct, știi deja că un calculator nu poate să facă altceva decât să execute o serie de instrucțiuni precise, una după alta (așa cum ai putut vedea în capitolul 1). Ai văzut și experimentat până aici instrucțiuni ce au un efect vizual imediat (instrucțiunile Aprinde și Stinge, ce aprind, respectiv sting, un punct negru situat la coordonatele specificate pe un ecran virtual), precum și instrucțiuni cu ajutorul cărora poți defini variabile și stoca valori în ele (așa cum ai văzut în capitolul 2). Niciuna dintre aceste instrucțiuni nu pare să fie, însă, deosebit de inteligentă. Cum este posibil, atunci, ca un calculator să poată executa operații cu un grad semnificativ de inteligență? Secretul constă în instrucțiunea dacă (if — în limba engleză). Cu ajutorul ei, calculatorul e capabil să raspundă la comenzi exprimate într-un limbaj foarte apropiat de cel uman, de genul dacă o anumită condiție e îndeplinită, atunci execută o serie de instrucțiuni; altfel execută o altă serie de instrucțiuni .
Într-un program aceasta s-ar scrie în felul următor: 55
if (conditie) { // instructiuni … } else { // alte instructiuni … } În loc de conditie se pot pune verificări de egalități sau inegalități între expresii ce pot conține valori, variabile și operatori. De exemplu, pentru a face un program de tip ghicește numărul , am putea testa o condiție de genul if (nr_ghicit == nr_secret) …, unde nr_secret este o variabilă în care am stocat numărul secret ce trebuie ghicit de către jucător, iar nr_ghicit este numărul introdus de către jucător în încercarea de a ghici numărul secret. (A se remarca faptul că testarea egalității dintre două valori numerice, valori de variabile sau expresii se face cu operatorul ==, și nu cu operatorul = (care este operatorul cu ajutorul căruia putem stoca o valoare într-o variabilă, așa cum ai văzut în capitolul anterior).) Î n cazul în care egalitatea de mai sus este adevarată , putem să-i afişăm jucătorului un mesaj în care să-i spunem că a ghicit. In caz contrar, ar trebui să verificăm dacă numărul introdus de către jucător este mai mare sau mai mic decât numărul ce trebuie ghicit și să afișăm un mesaj adecvat. Programul ar putea arăta în felul următor: // ------------------------------// Joc "Ghiceste numarul" // ------------------------------// Definesc variabilele: var nr_secret = 16; // numarul ce trebuie ghicit var nr_ghicit = 10; // <-- aici se introduc incercarile de 56
// ghicire a numarului // Si apoi testez daca s-a ghicit numarul sau nu: if (nr_ghicit == nr_secret) { // Daca s-a ghicit numarul secret, // atunci afiseaza “DA” pe ecran Aprinde(2, 3); Aprinde(2, 4); Aprinde(2, 5); Aprinde(2, 6); Aprinde(3, 3); Aprinde(3, 6); Aprinde(4, 4); Aprinde(4, 5); Aprinde(6, 3); Aprinde(6, 4); Aprinde(6, 5); Aprinde(8, 3); Aprinde(8, 4); Aprinde(8, 5); Aprinde(7, 6); Aprinde(7, 4); } else { // Daca nu s-a ghicit numarul secret, atunci afiseaza // “>” (mai mare) -- daca numarul secret // este mai mare, // sau “<” (mai mic) -- daca numarul secret // este mai mic. if (nr_secret > nr_ghicit) { Aprinde(6, 3); Aprinde(7, 4); Aprinde(8, 5); Aprinde(7, 6); Aprinde(6, 7); } else { Aprinde(5, 3); Aprinde(4, 4); Aprinde(3, 5); Aprinde(4, 6); Aprinde(5, 7); } } Te rog să testezi acest program folosind simulatorul. (Poate că ești tentat să-l copiezi și să-l rulezi. Dacă vrei doar să simți rapid rezultatul, este în regulă. Dar dacă vrei cu adevărat să înveți programare, atunci îți recomand să scrii tu însuți toate programele pe care le testezi. În felul ăsta vei deveni 57
programator cu ușurință, fără să îți dai seama.) Ce a apărut de ecran? Acum
apasă
butonul Reseteaza afisajul (butonul de sus din simulator) și modifică în program valoarea variabilei nr_ghicit, punând în loc de 10 valoarea 20. Ce se întâmplă acum dacă apeși butonul Executa programul (butonul de jos din simulator)? Dar dacă repeți operațiunile, folosind de astă dată pentru nr_ghicit valoarea 16? Așa cum ai putut observa în acest program, în cadrul unui bloc de instrucțiuni dintr-o instrucțiune if se pot pune alte instrucțiuni de tip if. (Blocul de instrucțiuni din ramura else conține o instrucțiune if … else … .) De asemenea, mai trebuie să menţionez faptul că acoladele care apar în formatul instructiunii if sunt opţionale atunci când blocul respectiv de instrucţiuni e format dintr-o singură instrucţiune. În plus, întreaga ramură else poate să lipsească (atunci când e goală). De exemplu, dacă doresc ca variabila b să primească valoarea 1 dacă valoarea din variabila a este negativă şi să primească valoarea 0 în caz contrar, pot scrie aşa: ... if (a<0) b = 1; else b = 0; 58
Puteam să scriu, însă, aceeaşi secvenţă de program şi în felul următor: ... b = 0; if (a<0) b = 1; Deși instrucțiunea if este extrem de importantă, nu voi insista prea mult asupra ei aici. Ea va tot reveni în discuție în capitolele următoare, și va deveni din ce în ce mai clară pe măsură ce o vei experimenta. Ca și în cazul unui joc de Go, regulile în programare sunt destul de puține și simple, însă prin îmbinarea lor în moduri ingenioase se pot obține rezultate spectaculoase.
3.2 Exerciţii Lecţia 3 – Exerciţiul 1: Cum ai face un program care atunci când începe cu instrucţiunea “var nr = 0;” să aibă ca efect desenarea pe ecran a cifrei “0”, iar atunci când începe cu instrucţiunea “var nr = 1;” să aibă ca efect desenarea pe ecran a cifrei “1”? (Exercitiul este identic cu exerciţiul 9 de la lecţia 2, dar rezolvarea lui e mai simpla cu “if”, nu-i aşa?)
Lecţia 3 – Exerciţiul 2: Cum ai “traduce” într-un program condiţiile: 2.1) “daca suma valorilor variabilelor a şi b este număr par” 2.2) “daca a şi b au aceeaşi paritate” (adică sunt fie ambele 59
pare, fie ambele impare) 2.3) “daca a şi b au parităţi diferite”.
Lecţia 3 – Exerciţiul 3: Scrie o secvenţă de instrucţiuni care să aibă ca efect aprinderea punctului (9, 9) doar dacă ultima cifră a numărului întreg stocat în variabila nr este 9.
Lecţia 3 – Exerciţiul 4: Scrie o secvenţă de program care să aprindă punctul (10, 10) doar dacă valoarea variabilei a este 10 şi valoarea variabilei b este 10.
Lecţia 3 – Exerciţiul 5: Scrie o secvenţă de program care să aprindă punctul (10, 10) doar dacă valoarea variabilei a este 10 sau valoarea variabilei b este 10.
Lecţia 3 – Exerciţiul 6: Scrie o secvenţă de program care să aprindă punctul (10, 10) doar dacă doar una dintre variabilele a şi b are valoarea 10.
Lecţia 3 – Exerciţiul 7: În variabila x se găseşte stocat un număr întreg mai mic decât 1000. Să se scrie o secvenţă de program care să aibă ca efect depozitarea în variabila n a numărului de cifre ale 60
numărului x.
Lecţia 3 – Exerciţiul 8: Î n variabila “nr” se găseste stocat un număr de maxim 3 cifre. Să se scrie o secvenţă de program care are ca efect aprinderea punctului (8, 8) dacă suma cifrelor lui nr este: 8.1) egală cu 8. 8.2) multiplu de 8.
Lecţia 3 – Exerciţiul 9: Să se scrie o secvenţă de program care aprinde punctul (10, 10) dacă în variabila a se găseşte valoarea 0, în b se găseşte valoarea 1 şi în c se găseşte fie valoarea 0, fie 1. În caz contrar, secvenţa de program va aprinde punctul (1, 1).
Lecţia 3 – Exerciţiul 10: Să se scrie un program care are ca efect aprinderea punctului (3, 4) doar dacă nu este adevărat că valoarea variabilei x nu ar fi mai mare decât 4 dacă s-ar scădea 3 din ea.
3.3 Soluţii Lecţia 3 – Exerciţiul 1: var nr = 0; if (nr == 0) 61
{ Aprinde(1, 2); Aprinde(1, 3); Aprinde(2, 1); Aprinde(2, 4); Aprinde(3, 2); Aprinde(3, 3); } else if (nr == 1) { Aprinde(3, 2); Aprinde(2, 3); Aprinde(3, 1); Aprinde(3, 4); Aprinde(3, 3); }
Lecţia 3 – Exerciţiul 2: 2.1) // Suma e pară if ( ((a+b) % 2) == 0 ) 2.2) // Au aceeaşi paritate if ( (a % 2) == (b % 2) ) 62
2.3) // Au parităţi diferite if ( (a % 2) != (b % 2) )
Lecţia 3 – Exerciţiul 3: if ( (nr%10) == 9 ) { Aprinde(9, 9); }
Lecţia 3 – Exerciţiul 4: if (a == 10) { if (b == 10) { Aprinde(10, 10); } }
Lecţia 3 – Exerciţiul 5: if (a == 10) { Aprinde(10, 10); 63
} if (b == 10) { Aprinde(10, 10); }
Lecţia 3 – Exerciţiul 6: if (a == 10) { if (b != 10) { Aprinde(10, 10); } } if (b == 10) { if (a != 10) { Aprinde(10, 10); } }
Lecţia 3 – Exerciţiul 7: var x = 987; // o valoare mai mică decât 1000 var n = 1; if (x > 9) 64
{ n = n+1; x = (x - (x%10)) / 10; // îi tai ultima cifră if (x > 9) { n = n+1; } }
Lecţia 3 – Exerciţiul 8: 8.1) Suma cifrelor e egală cu 8 var nr = 17; // un număr întreg de maxim 3 cifre var suma = (nr%10); nr = (nr - (nr%10)) / 10; if (nr > 0) { suma = suma+(nr%10); nr = (nr - (nr%10)) / 10; if (nr > 0) { suma = suma+(nr%10); } 65
} if (suma == 8) { Aprinde(8, 8); } 8.2) Suma cifrelor e multiplu de 8 var nr = 897; var suma = (nr%10); nr = (nr - (nr%10)) / 10; if (nr > 0) { suma = suma+(nr%10); nr = (nr - (nr%10)) / 10; if (nr > 0) { suma = suma+(nr%10); } } if (suma % 8 == 0) { Aprinde(8, 8); } 66
Lecţia 3 – Exerciţiul 9: var ok = 0; if (a==0) { if (b==1) { if (c==0) ok = 1; if (c==1) ok = 1; } } if (ok == 1) Aprinde(10, 10); else Aprinde(1, 1);
Lecţia 3 – Exerciţiul 10: if (x-3 <= 4) { } 67
else { Aprinde(3, 4); } Sau aşa: if (x-3 > 4) Aprinde(3, 4);
68
4. Repetări 4.1 Lecţie Deja cu ceea ce ai învățat în capitolele anterioare poți construi programe destul de complexe. Pentru a face programe serioase, însă, îți mai lipsește o singură lecție. Este vorba despre o instrucțiune cu ajutorul căreia vei putea, printre altele, scrie în câteva rânduri programe care altfel ți-ar fi necesitat zeci sau sute de linii. O instrucțiune ce îți va deschide poarta către programarea adevărată, permitându-ți să faci și alte lucruri nebănuite. Instrucțiunea aceasta este while (cât timp — în limba română). Formatul în care ea se scrie în programe este următorul: while (conditie) { // instructiuni … } Ca și în cazul instrucțiunii if (dacă) învățate în lecția anterioară, în loc de conditie se pot pune verificări de egalități sau inegalități între expresii ce pot conține valori, variabile și operatori. De fapt, instrucțiunea while seamănă mult cu instrucțiunea if. Singura diferență dintre instrucțiunea scrisă mai sus (adică 69
while (conditie) {//instructiuni …}) și instrucțiunea if (conditie) {//instructiuni …} este aceea că dacă condiția conditie este adevărată, în cazul instrucțiunii if blocul de instrucțiuni se execută o singură dată, în vreme ce în cazul instrucțiunii while acesta se poate executa de mai multe ori, atâta timp cât condiția conditie rămâne adevărat[ (sau, altfel spus, până când condiția conditie nu mai este adevărată). Scrisă în limbaj natural, instrucțiunea while de mai sus s-ar traduce în cât timp condiția este adevărată, execută instrucțiunile scrise între acolade. Evident, dacă în blocul de instrucțiuni scrise între acolade nu există nimic care să afecteze condiția, atunci (în cazul în care aceasta a fost inițial adevărată) ea va ramâne mereu adevărată, ceea ce înseamnă că blocul de instrucțiuni se va repeta la nesfârșit. În mod normal, însă, condiția testată conține o variabilă a cărei valoare se va modifica pe parcursul executării blocului de intrucțiuni. Haide să vedem un exemplu. Să zicem că dorim să aprindem linia de jos (adică linia cu coordonata de pe axa y egală cu 1) din ecranul nostru de 10×10 puncte. Bineînțeles, putem face asta cu următorul program (așa cum am văzut încă din prima lecție): Aprinde(1, 1) Aprinde(2, 1) Aprinde(3, 1) Aprinde(4, 1) Aprinde(5, 1) Aprinde(6, 1) Aprinde(7, 1) Aprinde(8, 1) Aprinde(9, 1) Aprinde(10, 1) 70
În regulă, dar mai simplu nu se poate? Ba da. Să observăm, mai întâi, că x-ul transmis instrucțiunii Aprinde(x, y) variază de la 1 la 10, în vreme ce y-ul rămâne constant (și egal cu 1). Ce-ar fi, atunci, să luăm o variabilă (pe care o putem numi chiar x) cu ajutorul căreia să parcurgem întreaga gamă de valori 1, 2, 3, …, 10. Putem face asta cu următorul pogram: var x = 1 while (x <= 10) { Aprinde(x, 1) x = x+1 } Hai să vedem pas cu pas ce face programul acesta: 1) Mai întâi definește o variabilă numită x și o inițializează cu valoarea 1. 2) Urmează apoi instrucțiunea cât timp x este mai mic sau egal cu 10… 3) … aprinde punctul de la coordonatele (x, 1), și mărește cu 1 valoarea memorată în variabila x . Dacă ar fi să derulăm pașii 2) și 3), am vedea că mai întâi x este egal cu 1 (de la pasul 1 ), după care: 21) se testează dacă (x <= 10). Rezultatul este adevărat (căci x este egal cu 1), deci se execută pasul 3): 31) Aprinde(1, 1); x = 1+1; 22) apoi se testează din nou dacă (x <= 10). Rezultatul este tot adevărat (căci x este acum egal cu 2), deci se executa din nou pasul 3: 71
32) Aprinde(2, 1); x = 2+1; 23) și iar se verifică dacă (x <= 10). Cum rezultatul este adevărat (x fiind egal acum cu 3), se execută pasul 3): 33) Aprinde(3, 1); x = 3+1; … (și așa mai departe… Hai să trecem peste câțiva pași și să ajungem direct la pasul în care x are valoarea 10.) 210) se verifică dacă (x <= 10). Rezultatul este adevărat (căci x este egal cu 10), deci se execută pasul 3): 310) Aprinde(10, 1); x = 10+1; 211) și iarăși se verifică dacă x este mai mic sau egal cu 10. De astă dată, însă, rezultatul este fals (căci x are acum valoarea 11), deci nu se mai execută pasul 3), ci se iese din instrucțiunea while. Acum testează, te rog, programul în simulator. Ce s-a întâmplat după ce ai apăsat butonul Executa programul din simulator? Așa cum te așteptai, s-a aprins întreaga linie de jos din afișaj. Bun, dar dacă dorim acum să aprindem pe linia 2 nu întreaga linie, ci doar punctele de pe coloanele 1, 3, 5, 7 și 9? În primul rând, observăm că ne dorim de fapt să aprindem toate punctele aflate la coordonatele (x, y), cu proprietatea ca y este egal cu 2, iar x ia pe rând valorile 1, 3, 5, 7, 9 (adică toate valorile impare de la 1 la 10). Folosind această observație, programul rezultă cu destulă ușurință. Practic putem folosi programul anterior, în care în loc de Aprinde(x, 1) punem Aprinde(x, 2) (căci nu dorim să aprindem puncte de pe linia 1, ci de pe linia 2). De asemenea, funcția Aprinde nu trebuie să se mai apeleze la 72
fiecare pas, ci doar la pașii pentru care x are valoare impară. Prin urmare, trebuie să ne folosim de instrucțiunea if pentru a transmite calculatorului o comandă de genul execută instrucțiunea Aprinde(x, 2) doar dacă x este număr impar . Cum putem testa daca x este număr impar? O variantă posibilă este să verificăm restul împărțirii lui x la 2. (Această operație poate fi realizată folosind operatorul %.) Dacă acest rest este egal cu 1, înseamnă că x este impar. Acum avem tot ce ne trebuie, așa că hai să construim programul: var x = 1; while (x <= 10) { if ( (x % 2) == 1) { Aprinde(x, 2); } x = x+1; } Testează și acest program, te rog. Merge, nu-i așa? (Ce-ar fi să-l modifici astfel încât pe linia 3 să se aprindă beculețele situate pe coloane pare?) Iată că deja am ajuns să scriem programe serioase. Probabil că un neinițiat s-ar uita la programul anterior ca la un text în chineză scris cu litere arabe. Însă dacă ai urmărit cu atenție cele trei capitole anterioare, ai văzut că lucrurile nu sunt nici pe departe atât de complicate pe cât par, ci totul se înlănțuiește într-o manieră simplă și logică. Propun să încheiem lecția aceasta cu un exemplu ceva mai complicat. Să zicem că am dori să aprindem toate punctele de pe ecranul de 10×10 puncte. 73
Ce ar trebui să facem, deci? Bineînțeles, am putea să punem 100 de instrucțiuni de tip Aprinde( x , y ). Sau am putea să observăm că de fapt tot ce trebuie să facem este să avem două variabile (numite, de exemplu, chiar x și y) cu ajutorul cărora să apelăm Aprinde(x, y). Și ar mai trebui să observăm și că variabila x trebuie să parcurgă toate valorile de la 1 la 10 pentru fiecare valoare a variabilei y (care, la rândul ei, ar trebui să parcurgă toate valorile de la 1 la 10). Aceasta înseamnă că ne vom putea folosi de primul program de mai sus (cel care aprindea toate punctele de pe linia cu y egal cu 1), numai că în blocul de instrucțiuni din acel while nu vom scrie Aprinde(x, 1), ci Aprinde(x, y), unde y va fi o variabilă pe care o vom fi declarat la începutul programului și o vom fi inițializat cu 1. Va trebui apoi ca întreg acel program să fie executat din nou pentru y având valoarea 2. Și apoi pentru y având valoarea 3. Și așa mai departe, până la y egal cu 10 (inclusiv). Se poate observa că dorim să facem cu y o serie de operații similare celor pe care le facem cu x. Prin urmare, întreg acest program de care vorbim va face parte din blocul de instrucțiuni al unei instrucțiuni while ce va testa condiția (y<=10). Hai să vedem cum ar arăta programul: var y; var x; y = 1; while (y <= 10) { x = 1; while (x <= 10) { Aprinde(x, y); x = x+1; } 74
y = y+1; } Pentru a-l testa, revino la simulator, apasă butonul Reseteaza afisajul , după care introdu acest program și apasă butonul Executa programul . A mers, nu? Ți s-au aprins toate punctele de pe ecran? Mă întreb dacă ai putea să îl modifici astfel încât să nu îți aprindă toate punctele, ci să deseneze un soi de tablă de șah formată din 10×10 pătrățele. (Idee: Folosește în while-uri if-uri; eventual unele în interiorul altora…)
Știu că acest capitol a fost mai solicitant decât cele de până acum. Așa că te rog să îl recitești dacă e nevoie și să nu treci mai departe cât timp nu înțelegi foarte bine ce se întâmplă în realitate într-un program ce conține instrucțiunea cât timp ( while), precum și cum se poate îmbina această instrucțiune cu instrucțiunea dacă (if) pentru a construi programe serioase. Cu lecţia aceasta se încheie modulul I. Acum știi tot ce este esențial în programarea calculatoarelor. Și lecțiile care urmează sunt extrem de importante, însă în cea mai mare parte ele sunt doar carne pusă peste scheletul pe care l-ai învățat până aici.
4.2 Exerciţii Lecţia 4 – Exerciţiul 1: 75
Scrie un program care să aprindă toate punctele de pe linia cu y egal cu 1 a ecranului de 10x10 puncte: 1.1) de la stânga la dreapta. 1.2) de la dreapta la stânga.
Lecţia 4 – Exerciţiul 2: Scrie un program care să deseneze pe ecran un dreptunghi negru (plin) care are: 2.1) colţul din stânga-jos la coordonatele (a, b), lăţimea de w puncte şi înălţimea de h puncte. 2.2) colţul din stânga-sus la punctul (x1, y1) şi colţul din dreapta-jos la punctul (x2, y2).
Lecţia 4 – Exerciţiul 3: Scrie un program care pune în variabila c numărul de cifre ale numărului întreg stocat în variabila n.
Lecţia 4 – Exerciţiul 4: Scrie un program care să deseneze o linie care are capetele la punctele (x1, y1) şi (x2, y2).
Lecţia 4 – Exerciţiul 5: Scrie un program care să deseneze un triunghi negru (plin) care are colţurile în punctele (a, b), (1, 1) şi (10, 1). 76
Lecţia 4 – Exerciţiul 6: Scrie un program care aprinde pe ecranul de 10x10 puncte toate punctele (x, y) pentru care numărul (y-1)*10+x este multiplu de 3, dar nu este multiplu de 5.
Lecţia 4 – Exerciţiul 7: Scrie un program care să aprindă toate punctele situate sub diagonala principală a ecranului de 10x10 puncte. (Diagonala principală este acea linie compusa din punctele (x, y) care au proprietatea că x este egal cu y.)
Lecţia 4 – Exerciţiul 8: În variabila nr se găseşte stocat un număr întreg. Să se scrie un program care aprinde punctul (8, 8) doar dacă: 8.1) suma divizorilor lui nr e 8. 8.2) cel mai mare divizor al lui nr (diferit de nr) este 8.
Lecţia 4 – Exerciţiul 9: Se dă numărul întreg stocat în variabila n. Să se scrie un program care îl reduce pe n la o singură cifră prin însumarea cifrelor sale (şi apoi prin însumarea cifrelor rezultatului, şi aşa mai departe până când se obţine un număr format dintr-o singură cifră).
Lecţia 4 – Exerciţiul 10: Se dă numărul întreg stocat în variabila n. Să se scrie un 77
program care are ca efect reprezentarea lui n cifră cu cifră pe ecranul de 10x10 puncte, fiecare cifră fiind reprezentată ca o coloană de puncte negre având ca înălţime atâtea puncte cât e cifra respectivă. Cifra unităţilor se va reprezenta pe coloana cu x egal cu 10, cifra zecilor pe coloana cu x egal cu 9, şi aşa mai departe.
4.3 Soluţii Lecţia 4 – Exerciţiul 1: 1.1) // Stânga --> dreapta var x = 1; while (x <= 10) { Aprinde(x, y); x = x+1; } 1.2) // Dreapta --> stânga var x = 10; while (x >= 1) { Aprinde(x, y); x = x-1; }
Lecţia 4 – Exerciţiul 2: 78
2.1) // (a, b), w, h var x; var y = b; while (y-b < h) { x = a; while (x-a < w) { Aprinde(x, y); x = x+1; } y = y+1; } 2.2) // (x1, y1), (x2, y2) var x; var y = y1; while (y >= y2) { x = x1; while (x < x2) { Aprinde(x, y); 79
x = x+1; } y = y-1; }
Lecţia 4 – Exerciţiul 3: var n = 31928; if (n < 0) { n = -n; } var c = 1; // Tai ultima cifra din numarul n: n = (n - (n%10)) / 10; // Si repet atata timp cat mai am cifre in numarul n: while (n > 0) { c = c+1; n = (n - (n%10)) / 10; }
Lecţia 4 – Exerciţiul 4: 80
// Soluţia pentru cazul în care: // modul(y2 - y1) <= modul(x2 - x1) şi // x2 >= x1
// Exemplu: linie de la (3, 2) la (9, 5) var x1 = 3; var y1 = 2; var x2 = 9; var y2 = 5;
var x = x1; while (x < x2) { var y = y2 - ( (x2-x)/(x2-x1) )*(y2-y1); if ( (y%1) < 0.5 ) y = y-(y%1); else y = y-(y%1)+1; Aprinde(x, y); x = x+1; }
81
// Pentru cazul în care modul(y2-y1)>=modul(x2-x1) şi y2>=y1 // s-ar fi făcut în while parcurgerea după y (între y1 şi y2) // şi s-ar fi calculat x cu expresia x2-( (y2-y)/(y2-y1) )*(x2-x1).
// Şi mai sunt încă două cazuri, care pot fi reduse la unul // dintre cele două cazuri pomenite (prin interschimbarea // coordonatelor (x1, y1) şi (x2, y2)).
Lecţia 4 – Exerciţiul 5: var a = 4; var b = 7; var y = b; while (y >= 1) { var x1 = a - ( (b-y)/(b-1) )*(a-1); if (x1%1 < 0.5) x1 = x1 - x1%1; else x1 = x1- x1%1 + 1; var x2 = a + ( (b-y)/(b-1) )*(10-a); if (x2%1 < 0.5) 82
x2 = x2 - x2%1; else x2 = x2- x2%1 + 1; var x = x1; while (x <= x2) { Aprinde(x, y); x = x+1; } y = y-1; }
Lecţia 4 – Exerciţiul 6: var y = 1; while (y <= 10) { var x = 1; while (x <= 10) { var val = (y-1)*10+x; if (val%3 == 0) { 83
if (val%5 != 0) { Aprinde(x, y); } } x = x+1; } y = y+1; }
Lecţia 4 – Exerciţiul 7: var x = 1; while (x <= 10) { var y = 1; while (y <= x) { Aprinde(x, y); y = y+1; } x = x+1; } 84
Lecţia 4 – Exerciţiul 8: 8.1) Dacă observ că singurul număr pentru care suma divizorilor e 8 este numărul 7, atunci pot scrie programul aşa: var nr = …; if (nr == 7) { Aprinde(8, 8); } În caz contrar, trebuie să parcurg toate numerele întregi de la 1 până la nr şi să adun toate acele numere care îl divid pe nr: var nr = ...; var suma = 0; var i = 1; while (i <= nr) { if (nr%i == 0) suma = suma+i; i = i+1; } if (suma == 7) Aprinde(8, 8); 8.2) var nr = 16; 85
var i = nr-1; while (i > 0) { if (nr%i == 0) if (i == 8) Aprinde(8, 8); else i = 1; i = i-1; }
Lecţia 4 – Exerciţiul 9: var n = 1984; var repeta = 1; while (repeta == 1) { var sumacifre = n%10; n = (n-(n%10)) / 10; while (n>0) { sumacifre = sumacifre + n%10; n = (n-(n%10)) / 10; 86
} if (sumacifre < 10) repeta = 0; else n = sumacifre; } //Aprinde(sumacifre, sumacifre);
Lecţia 4 – Exerciţiul 10: var n = 1984; var x = 10; while (n>0) { var cif = n%10; var y = 1; while (y <= cif) { Aprinde(x, y); y = y+1; } x = x-1; n = (n-(n%10)) / 10; }
87
Modulul II Bastonaşe După ce în primul modul ai învăţat să faci “liniuţele”, în acest al doilea modul vei învăţa “bastonaşele”. Adică vei învăţa să faci nişte “liniuţe” mai întortocheate puţin. Vei învăţa cum să lucrezi cu date organizate sub formă de vectori şi matrici şi vei învăţa să îţi defineşti propriile funcţii şi să foloseşti operatorii logici. De asemenea, vei mai învăţa şi să afişezi culori, numere şi cuvinte pe ecranul virtual. După parcurgerea cu succes a acestui modul deja vei avea (aproape) toate uneltele necesare pentru a putea construi în simulator jocuri ca Labirint, X&O şi Sokoban. (Îţi va mai trebui doar posibilitatea de a reacţiona la apăsări de taste, lucru pe care îl vei învăţa în primul capitol al modulului următor.)
88
5. Vectori 5.1 Lecţie Probabil că atunci când auzi de vectori și de matrici te gândești la lecțiile de fizică și de matematică din școală. Vectorii și matricile din programare, însă, sunt cu totul altceva. Și anume, ele sunt doar niște modalități de organizare a datelor. Ele constituie, în esență, bazele structurilor de date utilizate în programele complexe. (Iar acestea constituie fundamentul bazelor de date de care cel mai probabil ai mai auzit.) Hai să n-o mai lungim mult și să vedem ce sunt vectorii și matricile din programare.
Vectori Dar mai întâi: de ce am avea nevoie de o modalitate de structurare a datelor? Oare variabilele simple nu sunt de ajuns? Dacă am de memorat o dată, folosesc o variabilă. Dacă îmi trebuiesc două date într-un program, folosesc două variabile. Dar dacă am nevoie de 10 date? (Să zicem că aș vrea să memorez starea tuturor celor 10 beculeț e de pe linia cu y 89
egal cu 1 din ecranul virtual de 10×10 puncte utilizat în capitolele anterioare. Aș putea să consider valoarea 1 pentru starea aprins și valoarea 0 pentru starea stins.) Da, pot folosi 10 variabile. Dar dacă apoi aș vrea să schimb starea tuturor becurilor (adică pe cele aprinse să le sting, și pe cele stinse să le aprind)? Ar trebui, evident, să iau fiecare dintre cele 10 variabile în parte și să scriu pentru ea un fragment de program de genul următor:
dacă (variabila == 1) { variabila = 0 } altfel { variabila = 1 }
Imaginează-ți treaba asta (scrisă, desigur, cu instrucțiunea if în loc de dacă) repetată de 10 ori! (Of, și tocmai începuse să-ți placă programarea, nu?…) Ei bine, vestea bună e că datorită vectorilor nu va trebui să scrii de 10 ori aproape același lucru. Cu ajutorul vectorilor vei putea scrie ceva de genul următor:
if (variabila_numărul_n == 1) {
variabila_numărul_n = 0 } else {
variabila_numărul_n = 1 90
}
Acest grup de instrucţiuni va trebui să-l scrii o singură dată, în interiorul unei instrucțiuni cât timp (adică while) cu ajutorul căreia îl faci pe n să parcurgă toate valorile de la 1 la 10 (așa cum ai văzut în capitolul anterior). Bun, dar cum poți traduce variabila_numărul_n în cadrul unui program? Să zicem că variabila am denumit-o v. Atunci variabila v numărul n se va scrie v[n]. Super! Mai rămâne doar să vedem cum se definește o astfel de variabilă. O poți face cu ajutorul funcției Vector(număr_elemente), unde în loc de număr_elemente pui numărul de elemente dorit (deci câte variabile îți dorești să conțină variabila (adică vectorul) v). De exemplu, pentru 10 variabile în v (prima fiind v[0], iar ultima v[9]) poti scrie astfel: var v = Vector(10) Iată, deci, un vector!
Așadar, un vector e doar o variabilă ce conține mai multe variabile (care pot fi accesate prin expresii (ce pot conține numere, operatori și alte variabile) scrise între paranteze drepte după numele variabilei, așa cum am arătat în paragrafele anterioare). Să vedem acum cum ar arăta programul de inversare a stării celor 10 beculețe, de care vorbeam ceva mai devreme: var v = Vector(10) … var n = 0 91
while (n < 10) { if (v[n] == 1) { v[n] = 0 } else { v[n] = 1 } n = n+1 } … Pentru a face un program pe care să-l putem rula în simulator mai trebuie să inițializăm valorile din vectorul v după ce l-am definit. Așa cum am zis anterior, pentru starea aprins vom folosi valoarea 1, iar pentru starea stins valoarea 0. De asemenea, mai trebuie să aprindem beculețele de pe ecran atât înainte, cât și după rularea algoritmului de inversare a beculețelor. Putem face aceasta punând o pauză în program (cu ajutorul funcției Pauza()). Iată programul complet: // Program inversare culori (cu vector) // definire vector cu 10 elemente var v = Vector(10) // initalizare elemente vector v[0] = 0 v[1] = 0 v[2] = 1 v[3] = 1 v[4] = 1 v[5] = 1 v[6] = 1 v[7] = 0 92
v[8] = 1 v[9] = 1 // aprindere puncte de pe linia cu y = 1, // conform valorilor din vectorul v // (0 -- stins; 1 -- aprins) var n = 0 while (n < 10) { if (v[n] == 1) { Aprinde(n+1, 1) } else { Stinge(n+1, 1) } n = n+1 } // pauza… Pauza() // inversare stare puncte n=0 while (n < 10) { if (v[n] == 1) { v[n] = 0 } else { v[n] = 1 } n = n+1 } // aprindere puncte de pe linia cu y = 1, // conform valorilor din vectorul v // (0 -- stins; 1 -- aprins) 93
n=0 while (n < 10) { if (v[n] == 1) { Aprinde(n+1, 1) } else { Stinge(n+1, 1) } n = n+1 } A mers în simulator, da? Acum urmează partea cea mai frumoasă (și care te va ajuta cel mai mult în a deveni un bun programator) — partea de creație și experimentare. Modifică, te rog, programul anterior astfel încât doar starea punctelor de pe coloanele pare (coloanele 2, 4, 6, 8, 10) să se modifice (iar punctele de pe coloanele cu număr impar să rămână nemodificate).
Matrici Acum că e clar cum stă treaba cu vectorii, să vedem ce sunt matricile și la ce sunt ele bune. Dacă folosind un vector am putut manipula o linie din afișaj (și la fel de simplu putem manipula o coloană), cu ajutorul unei matrici putem manipula întreg ecranul de 10×10 puncte. Căci matricea este (ca și vectorul) o variabilă ce conține mai multe variabile (dar nu dispuse în șir (ca la vector), ci într-un pătrat sau dreptunghi (pe linii și coloane)). (Prin urmare, un vector este un caz particular de matrice (și anume o matrice ce conține fie doar o linie, fie doar o coloană). Cu alte cuvinte, vectorii sunt matrici. În ciuda 94
acestui fapt, în realitate matricile sunt implementate cu ajutorul vectorilor, folosindu-se faptul că o matrice este un vector de vectori (adică un vector în care fiecare element este un vector).) Când definim o matrice trebuie să îi specificăm numărul de linii și numărul de coloane, astfel: var m = Matrice(numar_linii, numar_coloane) Întreg afișajul îl putem, deci, considera un fel de matrice (de puncte, sau beculețe), de 10 linii și 10 coloane. Numerotarea liniilor începe, însă, de sus (deci linia cu y egal cu 10 este linia 0 din matrice, iar linia cu y egal cu 1 este linia 9 din matrice), iar numerotarea coloanelor începe de la stânga (deci coloana cu x egal cu 1 este coloana 0 din matrice, iar coloana cu x egal cu 10 este coloana 9 din matrice). Haide să definim o matrice în care să memorăm starea (aprins sau stins) tuturor celor 10×10 puncte de pe afişaj. La fel ca anterior, pentru starea aprins vom memora în matrice valoarea 1, iar pentru starea stins valoarea 0. Dacă denumim această variabilă m, atunci va trebui să scriem în program în felul următor pentru a o defini: var m = Matrice(10, 10) Pentru accesarea elementelor matricii m vom scrie similar ca la vectori, cu diferența că matricii trebuie să îi specificăm doi indecși (unul pentru linie, și celălalt pentru coloană) în loc de unul singur. Astfel, de exemplu, cu m[1][2] se accesează elementul de pe linia 1 și coloana 2 (care corespunde punctului de pe afișaj situat la coordonatele x egal cu 3 și y egal cu 9). (Îmi poți spune în clipa asta cum ai accesa elementul matricii m în care vom memora starea punctului din colțul din dreapta sus al afișajului?) 95
Hai să vedem acum cum ar arăta programul scris la secțiunea Vectori, dar de astă dată pentru întreg ecranul, folosind matricea m definită anterior. (Îți reamintesc că programul îşi propune să aprindă câteva puncte de pe ecran, să facă o pauză, după care să inverseze starea tuturor punctelor.) // Program inversare culori (cu matrice) // definire matrice cu 10 linii si 10 coloane var m = Matrice(10, 10) // initalizare elemente matrice var l = 0 var c while (l < 10) { c=0 while (c < 10) { if ( ((l+c)%2) == 0 ) { m[l][c] = 1 } else { m[l][c] = 0 } c = c+1 } l = l+1 } // aprindere puncte de pe ecran, // conform valorilor din matricea m // (0 -- stins; 1 -- aprins) l=0 while (l < 10) { c=0 96
while (c < 10) { if (m[l][c] == 1) { Aprinde(c+1, 10-l) } else { Stinge(c+1, 10-l) } c = c+1 } l = l+1 } // pauza… Pauza() // inversare stare puncte l=0 while (l < 10) { c=0 while (c < 10) { if (m[l][c] == 1) { m[l][c] = 0 } else { m[l][c] = 1 } c = c+1 } l = l+1 } // aprindere puncte de pe ecran, // conform valorilor din matricea m 97
// (0 -- stins; 1 -- aprins) l=0 while (l < 10) { c=0 while (c < 10) { if (m[l][c] == 1) { Aprinde(c+1, 10-l) } else { Stinge(c+1, 10-l) } c = c+1 } l = l+1 } După ce îl testezi, modifică, te rog, acest program astfel încât să își schimbe starea doar punctele aflate pe linii și coloane pare.
Gata și lecția despre vectori și matrici! Cu această lecție libertatea ta în materie de construit programe a crescut enorm. Nici nu îți dai seama cât de aproape ești de a-ți programa propriul joc în simulator.
5.2 Exerciţii Lecţia 5 – Exerciţiul 1: Construieşte un vector de 10 elemente şi pune în el 7 valori 98
oarecare. Apoi scrie un program care calculează (în variabila “m”): 1.1) valoarea minimă dintre cele 7 valori din vector. 1.2) valoarea maximă dintre cele 7 valori din vector.
Lecţia 5 – Exerciţiul 2: Construieşte un vector de 10 elemente şi pune în el 10 valori oarecare. Scrie apoi un program care aprinde punctul (x+1, y+1), unde x este poziţia (indicele) din vector a elementului de valoare minimă şi y este poziţia din vector a elementului de valoare maximă.
Lecţia 5 – Exerciţiul 3: Construieşte un vector de 30 de elemente şi completează primele n elemente cu valori oarecare (unde n este o variabilă ce poate avea valori între 3 şi 30). Scrie apoi un program care calculează: 3.1) suma elementelor pare din vector. (E pară, sau impară?) 3.2) produsul elementelor impare din vector. (E par, sau impar?)
Lecţia 5 – Exerciţiul 4: Se dă un vector numit “note” cu 30 de elemente în care sunt puse notele la desen ale elevilor unei clase. Scrie un program care calculează procentul notelor de trecere (adică “cât la sută dintre note sunt de cel puţin 5”).
99
Lecţia 5 – Exerciţiul 5: Î n vectorul “mate” se găsesc notele la matematică ale celor 25 de elevi ai unei clase, î n vectorul “fizica” se găsesc notele lor la fizică şi î n vectorul “info” se găsesc notele lor la informatică. Scrie un program care pune î n vectorul “media” media celor 3 note pentru fiecare elev în parte.
Lecţia 5 – Exerciţiul 6: Se dă o matrice numită “m”, cu 3 linii şi 4 coloane. Să se scrie un program care calculează diferenţa dintre valoarea maximă şi valoarea minimă din matrice.
Lecţia 5 – Exerciţiul 7: Se dă o matrice numită “ecran”, cu 10 linii şi 10 coloane, în care fiecare element corespunde unui pătraţel din ecranul de 10x10 puncte. Să se scrie în colţul din dreapta-jos valoarea 1, în colţul dreapta-sus valoarea 2, în colţul stânga-sus valoarea 3 şi în colţul stânga-jos valoarea 4, iar în toate celelalte elemente valoarea 0. (Se va ţine cont de faptul că la matrice liniile se numară (începând de la 0) de sus în jos şi coloanele de la stânga la dreapta, în vreme ce pe ecran coordonata x creşte (începând de la 1) de la stânga la dreapta şi coordonata y creşte de jos în sus.)
Lecţia 5 – Exerciţiul 8: Se dă o matrice de 5x5 elemente. Să se scrie un program care completează elementele matricii cu valori crescătoare cu 1, începând de la valoarea 1 în colţul stânga-jos şi mergând î n “spirală” pe marginile încă neocupate până când se ocupă cu valori toate elementele matricii. 100
Lecţia 5 – Exerciţiul 9: Se dă o matrice 7x7. Să se scrie un program care completează valoarea 1 în colţul din stânga-sus, valoarea 2 în cele două elemente din linia diagonală (orientată la 45 de grade faţă de orizontală la dreapta) situată imediat sub colţul stânga-sus, valoarea 3 în cele 3 elemente din următoarea diagonală de sub ea, şi aşa mai departe până când se ajunge în colţul din dreapta-jos (în care se va completa valoarea 13).
Lecţia 5 – Exerciţiul 10: Se dă o matrice de 10 linii şi 10 coloane. Să se completeze cu elemente de 0 şi 1 astfel încât desenul rezultat prin aprinderea punctelor corespunzătoare de pe ecran (acolo unde în matrice se găseşte valoarea 1) să rezulte o “tablă de şah” cu pătrăţele de 2x2 puncte.
5.3 Soluţii Lecţia 5 – Exerciţiul 1: var v = Vector(10); v[0] = 9; v[1] = 14; v[2] = -4; v[3] = 100; v[4] = -25; v[5] = 2; v[6] = 7; // 1.1) Valoarea minima: var min = v[0]; var i = 1; while (i<7) 101
{ if (v[i] < min) { min = v[i]; } i = i+1; } // 1.2) Valoarea maxima: var max = v[0]; i = 1; while (i<7) { if (v[i] > max) { max = v[i]; } i = i+1; }
Lecţia 5 – Exerciţiul 2: var v = Vector(10); v[0] = 9; v[1] = 14; v[2] = -4; v[3] = 100; v[4] = -25; 102
v[5] = 2; v[6] = 7; v[7] = 192; v[8] = -123; v[9] = 101;
var min = v[0]; var pozmin = 0; var max = v[0]; var pozmax = 0;
var i = 1; while (i<10) { if (v[i] < min) { min = v[i]; pozmin = i; } if (v[i] > max) { max = v[i]; pozmax = i; } i = i+1; } 103
Aprinde(pozmin+1, pozmax+1);
Lecţia 5 – Exerciţiul 3: var v = Vector(30); var n = 23; // Completez primele n elemente din vector // cu valori oarecare: 0, 5, 3, ... var i = 0; while (i
var suma = 0; // parcurg fiecare element din vector, i = 0; while (i<30) { // si atunci cand dau peste un element cu valoare para, if ((v[i]%2) == 0) { // il adun la suma. suma = suma + v[i]; } i = i+1; } // 3.2) Produsul elementelor impare din vector: // Initializez produsul cu 1, var prod = 1; // parcurg fiecare element din vector, i = 0; while (i<30) { // si atunci cand dau peste un element cu valoare impara, if ((v[i]%2) != 0) 105
{ // il inmultesc cu produsul. prod = prod * v[i]; } i = i+1; }
Lecţia 5 – Exerciţiul 4: var v = Vector(30); // Completez vectorul cu note oarecare: 4, 9, 7, ... var i = 0; while (i<30) { v[i] = 4+(i*19)%7; i = i+1; } // Numar cate note de trecere sunt: var nrNote = 0; i = 0; while (i<30) { if (v[i] >= 5) 106
{ nrNote = nrNote + 1; } i = i+1; } // Si apoi calculez procentul: var procentNoteTrecere = (nrNote / 30) * 100;
Lecţia 5 – Exerciţiul 5: var mate = Vector(25); var fizica = Vector(25); var info = Vector(25); // Completez vectorii cu note oarecare: var i = 0; while (i<25) { mate[i] = 4+(i*19)%7; fizica[i] = 3+(i*19)%8; info[i] = 2+(i*19)%9; i = i+1; } // Calculez media notelor: 107
var media = Vector(25); i = 0; while (i<25) { media[i] = (mate[i]+fizica[i]+info[i]) / 3; i = i+1; }
Lecţia 5 – Exerciţiul 6: var m = Matrice(3, 4); // Completez matricea cu valori oarecare: var i = 0; var j; while (i<3) { j = 0; while (j<4) { m[i][j] = i*j - (i+j); j = j+1; } i = i+1; 108
} // Calculez valoarea maxima si valoarea minima: var min = m[0][0]; var max = m[0][0]; i = 0; while (i<3) { j = 0; while (j<4) { if (m[i][j] < min) { min = m[i][j]; } if (m[i][j] > max) { max = m[i][j]; } j = j+1; } i = i+1; } 109
// Calculez diferenta dintre valoarea maxima si valoarea minima: var diferenta = max - min;
Lecţia 5 – Exerciţiul 7: var ecran = Matrice(10, 10); // Completez intreaga matrice cu valoarea 0: var i = 0; var j; while (i<10) { j = 0; while (j<10) { m[i][j] = 0; j = j+1; } i = i+1; } // Pun 1 in coltul dreapta-jos: m[9][9] = 1; // Pun 2 in coltul dreapta-sus: 110
m[0][9] = 2; // Pun 3 in coltul stanga-sus: m[0][0] = 3; // Pun 4 in coltul stanga-jos: m[9][0] = 4;
Lecţia 5 – Exerciţiul 8: var m = Matrice(5, 5); // Voi completa imaginea patrat cu patrat, // pornind de la patratul exterior (cel care // are latura de 5). var latura = 5; var val = 1; while (latura > 0) { // latura de jos, de la stanga la dreapta var i = 0; while (i < latura) { m[ 4 - (5-latura)/2 ][ i + (5-latura)/2 ] = val; ///Aprinde( i + (5-latura)/2 + 1 , 5 - (4 - (5-latura)/2) ); 111
///Pauza(); val = val+1; i = i+1; } // latura din dreapta, de jos in sus i = 1; while (i < latura) { m[ 4 - (5-latura)/2 - i ][ 4 - (5-latura)/2 ] = val; ///Aprinde( 4 - (5-latura)/2 + 1, 5 - (4 - (5-latura)/2 - i) ); ///Pauza(); val = val+1; i = i+1; } // latura de sus, de la dreapta la stanga var i = 1; while (i < latura) { m[ (5-latura)/2 ][ 4 - (5-latura)/2 - i ] = val; ///Aprinde( 4 - (5-latura)/2 - i + 1 , 5 - ((5-latura)/2) ); ///Pauza(); 112
val = val+1; i = i+1; } // latura din stanga, de sus in jos i = 1; while (i < latura-1) { m[ (5-latura)/2 + i ][ (5-latura)/2 ] = val; ///Aprinde( (5-latura)/2 + 1 , 5 - ((5-latura)/2 + i) ); ///Pauza(); val = val+1; i = i+1; } latura = latura-2; } Observatie: Prin de-“comentarea” liniilor care î ncep cu “///” se va putea vedea ordinea în care se parcug elementele matricii m (prin aprinderea pe ecran a punctului corespunzător din matrice (considerând elementul m[0][0] al matricii ca fiind plasat în punctul de coordonate (1, 5) de pe ecranul de 10x10).
Lecţia 5 – Exerciţiul 9: var m = Matrice(7, 7); 113
// Pornesc de la diagonala din coltul stanga-sus // (adica diagonala pe care voi pune numarul // (nrdiag) 1) si apoi avansez pana la ultima // diagonala (adica diagonala pentru care "nrdiag" // va avea valoarea 13. var nrdiag = 1; while (nrdiag <= 13) { var l; // linia curenta din matrice var c; // coloana curenta din matrice // Aleg ca punct de start pentru parcurgerea // diagonalei cu numarul "nrdiag" punctul // ei din stanga-jos. if (nrdiag <= 7) { c = 0; l = nrdiag-1; } else { c = nrdiag-7; l = 6; 114
} // Apoi parcurg diagonala "nrdiag" din matrice, // urcand la fiecare pas cu cate un punct in sus // si la dreapta pana cand punctul curent iese // in afara matricii. var e_in_afara = 0; if (l<0) e_in_afara = 1; if (c>6) e_in_afara = 1; while ( e_in_afara == 0 ) { m[l][c] = nrdiag; ///Aprinde(c+1, 7-l); ///Pauza(); // Ma mut la urmatorul punct. l = l-1; c = c+1; // Si verific daca a iesit in afara matricii. e_in_afara = 0; if (l<0) e_in_afara = 1; 115
if (c>6) e_in_afara = 1; } // Trec la urmatoarea diagonala. nrdiag = nrdiag+1; } Observatie: Prin de-“comentarea” liniilor care î ncep cu “///” se va putea vedea ordinea în care se parcug elementele matricii m (prin aprinderea pe ecran a punctului corespunzător din matrice (considerând elementul m[0][0] al matricii ca fiind plasat în punctul de coordonate (1, 7) de pe ecranul de 10x10).
Lecţia 5 – Exerciţiul 10: var m = Matrice(10, 10); var l; var c; l = 0; while (l<10) { c = 0; while (c<10) { var val = 0; 116
if ((l/2) % 2 == 0) { if ((c/2) % 2 == 0) { val = 1; } } else { if ((c/2) % 2 == 1) { val = 1; } } m[l][c] = val; m[l][c+1] = val; m[l+1][c] = val; m[l+1][c+1] = val; c = c+2; // sar din doua in doua coloane } l = l+2; // sar din doua in doua linii } 117
// Parcurg matricea m element cu element si // aprind punctele de pe ecran corespunzatoare // elementelor din matrice cu valoarea 1. l = 0; while (l<10) { c = 0; while (c<10) { if (m[l][c] == 1) { Aprinde(c+1, 10-l); } c = c+1; } l = l+1; }
118
6. Funcții 6.1 Lecţie Dacă ai parcurs atent capitolele anterioare, până la momentul de față deja stăpânești bazele programării calculatoarelor. Revino, te rog, la capitolul anterior și reia ultimul program pe care ți l-am oferit ca exemplu. După cum ai văzut, programul mai întâi inițializa cu anumite valori elementele unei matrici (binare — adică doar cu valori de 1 și de 0), după care aprindea pe ecran punctele corespunzătoare acelor elemente din matrice cu valoarea 1. Urma o pauză, după care elementele matricii erau inversate și apoi se repeta operațiunea anterioară (folosind de astă dată, bineînțeles, noile valori ale elementelor matricii). Ai remarcat, probabil, că programul rezultat este destul de lung. De asemenea, cred că ai remarcat faptul că este presărat cu comentarii care să ajute la înțelegerea rapidă a lui. În ciuda lor, însă, programul are un aer destul de complicat.
În capitolul aceasta vei afla cum să faci mai multe cu mai puține. Mai precis, vei învăța cum să dresezi calculatorul să execute alte funcții (comenzi ) față de cele pe care le cunoștea deja. Iar apoi, folosind aceste noi funcții definite de către tine, vei putea face programe mai scurte și mai puternice. Să vedem, mai întâi, la ce ne-ar ajuta definirea unei noi funcții 119
în cazul programului de care discutăm (și anume ultimul din capitolul anterior). Nu necesită un efort deosebit să observăm faptul că secvența de program ce realizează aprinderea tuturor punctelor de pe ecranul de 10×10 puncte conform valorilor (1 — aprins, sau 0 — stins) din matricea m (de 10 linii și 10 coloane, bineînțeles) se repetă. Ba chiar se repetă în mod identic această secvență de program. N-ar fi așadar, mult mai simplu să o scriem o singură dată, să -i dăm un nume, și apoi s-o apelăm folosind acel nume? Ba sigur că da. Exact asta facem cu instrucțiunea funcție (function). Ea se scrie într-un program în felul următor: function NumeleFunctiei () { // corpul functiei; instructiunile // … } Spre deosebire de celelalte instrucțiuni de până acum, instrucțiunea function nu are un efect imediat și nu intervine direct în funcționarea programului. Ci ea este o instrucțiune cu rol declarativ, care va spune calculatorului ca prin comanda NumeleFunctiei () mă voi referi în program la instrucțiunile care se regăsesc în corpul funcției. Pentru un exemplu concret, iată cum ar arăta ultimul program din capitolul anterior, scris de astă dată cu ajutorul unei funcții pe care aleg să o denumesc AprindeEcran: // Program inversare culori (cu matrice) // definire matrice cu 10 linii si 10 coloane var m = Matrice(10, 10) // Definesc noua functie pe care urmeaza // sa o folosesc in program. function AprindeEcran() 120
{ // Parcurg toate elementele matricii m si aprind // sau sting punctul corespunzator de pe ecran, // in functie de valoarea elementului curent // (0 -- stins; 1 -- aprins). var l = 0 var c while (l < 10) { c=0 while (c < 10) { if (m[l][c] == 1) { Aprinde(c+1, 10-l) } else { Stinge(c+1, 10-l) } c = c+1 } l = l+1 } } // Si acum urmeaza programul propriu-zis: // initalizare elemente matrice var l = 0 var c while (l < 10) { c=0 while (c < 10) { if ( ((l+c)%2) == 0 ) { m[l][c] = 1 121
} else { m[l][c] = 0 } c = c+1 } l = l+1 } // aprindere puncte de pe ecran, // conform valorilor din matricea m // (0 -- stins; 1 -- aprins) AprindeEcran() // pauza… Pauza() // inversare stare puncte l=0 while (l < 10) { c=0 while (c < 10) { if (m[l][c] == 1) { m[l][c] = 0 } else { m[l][c] = 1 } c = c+1 } l = l+1 } // aprindere puncte de pe ecran, // conform valorilor din matricea m // (0 -- stins; 1 -- aprins) 122
AprindeEcran() (Dacă îl testezi în simulator și pe acesta, vei vedea că efectul său este identic cu cel al vechiului program.) Ce parere ai de noul program? Nu e mai simplu să scrii doar AprindeEcran() decât să scrii (de două ori) întreg acel bloc de instrucțiuni? Bănuiesc că te astepți deja că urmează un exercițiu pentru tine. Ei bine, ia încearcă să faci și inversarea stării punctelor tot printr-o funcție (la fel cum am făcut pentru AprindeEcran). (Deci programul tău va conține în partea inițială pe lângă definirea variabilei m și a funcției AprindeEcran, și definirea acestei noi funcții — pe care o poți numi, de exemplu InverseazaPuncte.) Buuun, cu asta ai învățat și functiile, nu? Nu încă! De ce? Pentru că ceea ce am făcut până aici e doar un caz particular de utilizare a funcțiilor (și anume utilizarea lor ca pe un soi de macro-intrucțiuni). În cazul cel mai general, însă, o funcție poate să primească niște parametri și să returneze o valoare (exact ca în cazul unei funcții matematice).
Funcții cu parametri Hai să vedem mai întâi cum stă treaba cu funcțiile care au parametri. Să zicem că ne dorim să construim o funcție care să deseneze un dreptunghi pe ecranul nostru virtual de 10×10 puncte. Dorim, însă, ca funcția asta să fie capabilă să deseneze nu doar un anumit dreptunghi, ci orice dreptunghi. Prin urmare, este necesar să specificăm dreptunghiul dorit prin intermediul unor parametri. 123
Putem, de exemplu, să specificăm dreptunghiul cu ajutorul coordonatelor (pe axa x (orizontală) și pe axa y (verticală)) punctului din stânga- jos și pe ale celui din dreapta-sus. Să denumim aceste coordonate prin (x1, y1) și (x2, y2). În conditiile acestea, cum ar arăta o funcție (să-i zicem, de exemplu, DeseneazaDreptunghi) care să deseneze pe ecran dreptunghiul ce are colțul din stânga-jos la coordonatele (x1, y1) și coltul din dreapta-sus la coordonatele (x2, y2)? Iată o variantă mai jos: function DeseneazaDreptunghi(x1, y1, x2, y2) { var x = x1 while (x <= x2) { Aprinde(x, y1) Aprinde(x, y2) x = x+1 } var y = y1 while (y <= y2) { Aprinde(x1, y) Aprinde(x2, y) y = y+1 } } // Si iata si cateva exemple de utilizare a functiei: // (pot fi testate pe rand, unul cate unul) // 1) un patrat DeseneazaDreptunghi(1, 1, 10, 10) // 2) o linie orizontala DeseneazaDreptunghi(3, 3, 7, 3) // 3) o linie verticala DeseneazaDreptunghi(3, 5, 3, 7) // 4) un punct DeseneazaDreptunghi(7, 7, 7, 7) 124
// 5) nimic DeseneazaDreptunghi(8, 8, 2, 2) (Îmi poți spune de ce în cazul ultimului apel al funcți ei (varianta 5 de mai sus) nu se desenează nimic pe ecran?) Dupa cum ai observat dacă ai testat programul în simulator, funcția DeseneazaDreptunghi pe care am definit-o se folosește de valorile a patru parametri, și anume x1, y1, x2 și y2. Și dacă ne uităm în corpul ei vedem că mai întâi definește o variabilă locală (care va fi, deci, invizibilă în afara corpului funcției) numită x cu ajutorul căreia parcuge (folosind un while în combinație cu incrementarea cu 1 din final (adică instrucțiunea x = x+1)) toate valorile de la x1 la x2. Și apoi, în corpul while-ului (deci pentru fiecare valoare a lui x între x1 și x2 inclusiv) apelează de două ori funcția predefinită Aprinde — mai întâi pentru a aprinde punctul de la coordonatele (x, y1) și apoi pe cel de la coordonatele (x, y2). Prin urmare, după finalizarea execuției acestui prim while se vor fi desenat pe ecran două dintre laturile dreptunghiului (și anume cea de jos și cea de sus). În mod similar, partea a doua a funcției desenează celelalte două laturi ale dreptunghiului (și anume latura din stânga și latura din dreapta). Super, nu? Folosindu-ne de o funcție deja existentă (Aprinde) am construit o funcție mai complexă (DeseneazaDreptunghi). În felul acesta nu numai că putem scrie programe mai scurte și mai clare, ci putem scrie programe generice, a căror funcționare să poată fi ușor modificată prin modificarea valorilor unor parametri. (Menționez cu această ocazie că DeseneazaDreptunghi poate fi apelată și folosind variabile pe post de parametri. Cu alte cuvinte, pot scrie în program ceva de genul 125
DeseneazaDreptunghi(csjx, csjy, 8, 9), în condițiile în care variabilele csjx și csjy au fost în prelabil definite și li s-au atribuit niște valori.)
Funcții ce returnează valori A mai rămas un singur caz de funcții pe care aș vrea să-l discut acum. Este vorba de funcțiile care returnează o valoare. Care e treaba cu ele? Este cam același lucru pe care îl facem la matematică atunci când scriem y = f(x). f este funcția , x este parametrul cu care o apelez, iar y este variabila în care memorez valoarea returnată de funcția f pentru parametrul x . În esență, funcțiile ce returnează o valoare nu sunt diferite de de funcțiile de care am vorbit până acum. Ba chiar orice funcție poate să returneze o valoare. Cu condiția ca în corpul ei să existe intrucțiunea return valoare (unde în loc de valoare se poate pune fie o valoare numerică, fie o variabilă în care este memorată o valoare, fie o expresie matematică (ce poate cuprinde valori numerice, operatori, variabile)).
În momentul în care o astfel de intrucțiune returnează (return) este executată, execuția funcției se finalizează și valoarea returnată de ea este pusă în program acolo unde funcția a fost apelată. Aceste cuvinte poate că par mult mai complicate decât stau lucrurile în realitate, așa că haide să vedem un exemplu. Să zicem că desenăm pe ecran un dreptunghi (cu funcția DeseneazaDreptunghi făcută anterior), după care dorim să desenăm un punct în centrul lui. Ce-ar fi să facem o funcție cu ajutorul căreia să calculăm coordonatele acestui centru? În regulă. Hai să vedem ce ar trebui să facem pentru a obține centrul dreptunghiului, în condițiile în care avem coordonatele 126
(x1, y1) ale colțului din stânga- jos și (x2, y2) ale colțului din dreapta-sus. Păi, pentru a obține coordonata pe axa x a centrului ar trebui să facem media aritmetică între x1 și x2. Adică (x1+x2)/2. Și apoi ar trebui să rotunjim valoarea obținută pentru a obține o valoare întreagă pe care s-o putem folosi pentru a selecta una dintre cele 10 coloane de pe ecranul virtual. (Putem rotunji o valoare reală la un număr întreg folosind funcția predefinită Rotunjeste.) Și similar se poate obține coordonata centrului dreptunghiului pe axa y . Prin urmare, dacă am face o funcție care să calculeze media dintre două numere și să returneze rezultatul rotunjit la o valoare întreagă , atunci am putea folosi această funcție atât pentru a calcula coordonata pe axa x a centrului dreptunghiului, cât și coordonata sa pe axa y . Hai să vedem ce program ar ieși: // definesc functiile pe care le voi folosi function DeseneazaDreptunghi(x1, y1, x2, y2) { var x = x1 while (x <= x2) { Aprinde(x, y1) Aprinde(x, y2) x = x+1 } var y = y1 while (y <= y2) { Aprinde(x1, y) Aprinde(x2, y) y = y+1 } 127
} function CalculeazaMedia(a, b) { // calculeaza media numerelor a si b // si rotunjeste rezultatul var r = (a+b)/2 r = Rotunjeste(r) return r } // am definit functiile; acum iata programul: DeseneazaDreptunghi(2, 3, 8, 9) var xc = CalculeazaMedia(2, 8) var yc = CalculeazaMedia(3, 9) Aprinde(xc, yc) Înainte de a încheia această lecţie, menţionez faptul că instrucţiunea return se poate folosi şi într-o funţie care nu returnează valori. La întâlnirea acestei instrucţiuni execuţia funcţiei pur şi simplu se va opri şi se va reveni în program imediat după apelul ei. Spor la teste îți urez! (Simulatorul e prietenul tău cel mai bun.)
Cu acest capitol închei lecțiile serioase de programare din această primă parte. Dacă stăpânești bine toate conceptele pe care ți le-am prezentat până aici deja te poți numi programator. Așa că în următoarele două capitole din acest modul ne vom relaxa puțin cu niște detalii menite să îţi sporească expresivitatea în materie de programare.
128
6.2 Exerciţii Lecţia 6 – Exerciţiul 1: Scrie o funcţie numită Dreptunghi, care permite desenarea unui dreptunghi negru plin pe ecranul de 10x10 puncte prin specificarea coordonatelor colţului său din stânga-jos, a lăţimii sale şi a înălţimii sale. (Lăţimea se consideră pe orizontală, iar înălţimea pe verticală.)
Lecţia 6 – Exerciţiul 2: Scrie o funcţie numită Linie, care permite desenarea unei linii negre pe ecranul de 10x10 prin specificarea coordonatelor capetelor ei.
Lecţia 6 – Exerciţiul 3: Scrie o funcţie numită Maxim, care primeşte ca parametru un vector şi numărul de elemente din el şi returnează valoarea maximă din vector.
Lecţia 6 – Exerciţiul 4: Scrie o funcţie numită Ordoneaza, care primeşte ca parametru un vector şi numărul de elemente din el şi returnează un vector ce conţine aceleaşi valori ca vectorul primit ca parametru, dar aşezate în ordine crescătoare.
Lecţia 6 – Exerciţiul 5: Să se scrie o funcţie care primeşte ca parametru un vector şi numărul de elemente din el şi returnează o valoare ce 129
reprezintă de câte ori a fost găsită valoarea 10 în vector.
Lecţia 6 – Exerciţiul 6: Să se scrie o funcţie ce primeşte ca parametru două matrici de 10x10 ce conţin doar valori de 0 şi 1 şi returnează o matrice ce se obţine prin “suprapunerea” lor. (Adică dacă ele ar reprezenta imagini pe ecranul de 10x10 (unde valoarea 1 reprezintă punct negru şi valoarea 0 reprezintă punct transparent), ce imagine ar apărea pe ecran prin suprapunerea celor două imagini?)
Lecţia 6 – Exerciţiul 7: Să se scrie o funcţie ce primeşte ca parametru o matrice 10x10 ce conţine un “desen” binar (adică valoarea 0 acolo unde e punct alb în desen, şi valoarea 1 acolo unde e punct negru) şi returnează câte puncte are linia “neagră” orizontală continuă de lungime maximă din “desen”.
Lecţia 6 – Exerciţiul 8: Să se scrie o funcţie ce primeşte ca parametru o matrice de 3x3 elemente ce reprezintă starea unei table de X&O (unde valoarea 1 reprezintă piesa “X”, valoarea -1 reprezinta piesa “O”, iar valoarea 0 reprezinta loc gol) şi returnează 1 dacă a castigat X, -1 dacă a câştigat O şi 0 în toate celelalte cazuri.
Lecţia 6 – Exerciţiul 9: Să se scrie o funcţie ce primeşte ca parametru o matrice de 10x10 elemente ce reprezintă configuraţia curentă într-un joc de Tetris (cu 1 pentru piesă şi 0 pentru gol) şi returnează 130
numărul primei linii (de jos în sus) pline găsite (sau returnează -1 dacă nu a fost gasită nicio astfel de linie).
Lecţia 6 – Exerciţiul 10: Să se scrie o funcţie ce primeşte ca parametru o matrice 10x10 ce conţine o configuraţie dintr-un joc de Tetris şi elimină din ea toate liniile pline (cu valori de 1), coborând toate liniile de deasupra lor cu câte o poziţie (iar linia cel mai de sus va fi completată cu valoarea 0). Pentru identificarea liniilor pline se va apela funcţia realizată la exerciţiul anterior.
6.3 Soluţii Lecţia 6 – Exerciţiul 1: function Dreptunghi(xsj, ysj, lat, inal) { var adx = 0; while (adx < lat) { var ady = 0; while (ady < inal) { Aprinde(xsj+adx, ysj+ady); ady = ady+1; } 131
adx = adx+1; } }
Dreptunghi(1, 1, 10, 10);
Lecţia 6 – Exerciţiul 2: // Functie ajutatoare: function Modul(x) { if (x>=0) { return x; } else { return -x; } } // Solutia pentru cazul in care: // modul(y2-y1) <= modul(x2-x1) si // x2 >= x1 132
function Linie1(x1, y1, x2, y2) { var x = x1; while (x <= x2) { var y = y2 - ((x2-x)/(x2-x1))*(y2-y1); if ( (y%1) < 0.5 ) { y = y-(y%1); } else { y = y-(y%1)+1; } Aprinde(x, y); x = x+1; } } // Solutia pentru cazul in care: // modul(y2-y1) >= modul(x2-x1) si // y2 >= y1 function Linie2(x1, y1, x2, y2) 133
{ var y = y1; while (y <= y2) { var x = x2 - ((y2-y)/(y2-y1))*(x2-x1); if ( (x%1) < 0.5 ) { x = x-(x%1); } else { x = x-(x%1)+1; } Aprinde(x, y); y = y+1; } } function Linie(x1, y1, x2, y2) { var ok = 0; if (x2>=x1) { 134
if (Modul(y2-y1) <= Modul(x2-x1)) { Linie1(x1, y1, x2, y2); ok = 1; } } if (ok==0) { if (y2>=y1) { if (Modul(y2-y1) >= Modul(x2-x1)) { Linie2(x1, y1, x2, y2); ok = 1; } } } if (ok==0) { Linie(x2, y2, x1, y1); } } 135
Linie(1, 10, 10, 7); Linie(10, 7, 1, 5); Linie(1, 5, 3, 1); Linie(3, 1, 10, 4);
Lecţia 6 – Exerciţiul 3: function Maxim(v, n) { var max = v[0]; var i = 1; while (i
136
Lecţia 6 – Exerciţiul 4: function Ordoneaza(v_in, n) { // intai copiez vectorul v_in intr-un alt vector, v var v = Vector(n); var i = 0; while (i < n) { v[i] = v_in[i]; i = i+1; } // apoi aranjez elementele vectorului v // in ordine crescatoare i = 0; while (i < n-1) { var j = i+1; while (j < n) { if (v[i] > v[j]) { var temp = v[i]; 137
v[i] = v[j]; v[j] = temp; } j = j+1; } i = i+1; } // si la final returnez noul vector creat si ordonat, v return v; }
Lecţia 6 – Exerciţiul 5: function Numara10(v, n) { var nr = 0; var i = 0; while (i < n) { if (v[i] == 10) nr = nr+1; i = i+1; } 138
return nr; }
Lecţia 6 – Exerciţiul 6: function Suprapune(m1, m2) { var m = Matrice(10, 10); var l = 0; while (l < 10) { var c = 0; while (c < 10) { m[l][c] = m1[l][c]+m2[l][c] - m1[l][c]*m2[l][c]; c = c+1; } l = l+1; } return m; }
139
Lecţia 6 – Exerciţiul 7: function LinieMax(m) { var nr_max = 0; // Pentru fiecare linie din matrice: var l = 0; while (l < 10) { var nr_puncte = 0; // Parcurg fiecare coloana din linia respectiva: var c = 0; while (c < 10) { if (m[l][c] == 1) { // Si numar cate valori de 1 consecutive apar: nr_puncte = nr_puncte + 1; } else { // Iar cand dau de o valoare de 0, inseamna // ca s-a terminat linia: 140
if (nr_puncte > nr_max) { nr_max = nr_puncte; } nr_puncte = 0; } c = c+1; } l = l+1; } return nr_max; }
Lecţia 6 – Exerciţiul 8: function ACastigat(m) { var suma = Vector(8); // sumele pe linii: suma[0] = m[0][0] + m[0][1] + m[0][2]; suma[1] = m[1][0] + m[1][1] + m[1][2]; suma[2] = m[2][0] + m[2][1] + m[2][2]; // sumele pe coloane: 141
suma[3] = m[0][0] + m[1][0] + m[2][0]; suma[4] = m[0][1] + m[1][1] + m[2][1]; suma[5] = m[0][2] + m[1][2] + m[2][2]; // sumele pe diagonale: suma[6] = m[0][0] + m[1][1] + m[2][2]; suma[7] = m[2][0] + m[1][1] + m[0][2]; // verific daca vreuna dintre sume e 3 sau -3 var castigX = 0; var castigO = 0; var i = 0; while (i<7) { if (suma[i] == 3) { castigX = 1; } if (suma[i] == -3) { castigO = 1; } i = i+1; } 142
// si in functie de ce gasesc, returnez rezultatul if (castigX==1) { if (castigO==0) { return 1; } } if (castigO==1) { if (castigX==0) { return -1; } } return 0; }
Lecţia 6 – Exerciţiul 9: function NrLiniePlina(m) { // parcurg liniile matricii de jos in sus 143
var lin = 9; while (lin >= 0) { // si pentru fiecare linie parcurg coloanele; // daca gasesc cel putin un zero, linia nu e ok; var ok = 1; var col = 0; while (col <= 9) { if (m[lin][col] == 0) { ok = 0; } col = col+1; } // daca am gasit o linie plina, returnez numarul ei if (ok == 1) { return (9-lin); } lin = lin-1; } 144
// iar daca am verificat toate liniile si n-am gasit // linie plina, returnez -1 return -1; }
Lecţia 6 – Exerciţiul 10: function EliminaLiniiPline(m) { // mai intai vad daca e vreo linie plina var nr_lin = NrLiniePlina(m); while (nr_lin != -1) { // cobor pe rand fiecare linie de deasupra ei var lin = 9-nr_lin; var col; while (lin-1 >= 0) { col = 0; while (col<10) { m[lin][col] = m[lin-1][col]; col = col + 1; 145
} lin = lin - 1; } // si umplu linia 0 cu 0 col = 0; while (col<10) { m[0][col] = 0; col = col+1; } // apoi vad daca mai e vreo linie plina nr_lin = NrLiniePlina(m); } }
146
7. Operatori 7.1 Lecţie Și . Sau. Nu. Înainte de a ne apuca de treabă aș vrea să te gândești puțin la aceste trei cuvinte. Ce îți transmit ele? La ce te duc cu gîndul? Dacă în gândurile tale a răsărit ideea de logică , atunci lecția aceasta va fi una ușoară. Îți voi vorbi în acest capitol despre operatori. Vei revedea operatorii cu care te-ai întâlnit în lecțiile trecute și vei învăța alții noi. Accentul va fi pus pe trei operatori logici cu ajutorul cărora vei putea scrie cu ușurință programe mai scurte și mai clare. Practic, vei putea programa la fel de simplu cum gândești. Singura diferență e că gândurile tale le vei exprima nu în cuvinte, ci în limbajul simplu, concis și lipsit de ambiguitate al programării.
Culoare Și ca să îți infirm (la nivel subconștient) ideea că logica ar fi ceva plictisitor și lipsit de culoare, am ales ca în acest capitol să introduc puțină culoare în ecranul nostru de 10×10 beculețe (sau puncte, cum le-am mai numit). 147
Până acum ecranul nostru a fost unul binar, fiecare punct de pe el putând avea doar două stări — aprins și stins. Pentru a trece punctul de la coordonatele (x, y) în starea aprins ai folosit functia Aprinde(x, y), iar pentru a-l trece în starea stins, Stinge(x, y). Reduse la esență, ambele funcții (Aprinde și Stinge) fac același lucru — colorează un pătrățel de pe ecran. Prima îl colorează cu negru, iar cea de-a doua îl colorează cu alb. Acest mod binar de a opera cu lucrurile nu pare să aibă prea mare legătură cu realitatea cotidiană, care e plină de culoare, nuanțe, ambiguități si valori intermediare. (Însă calculatoarele așa lucrează. De asta am ales să te obișnuiesc în lecţiile de până acum cu un ecran alb-negru. Ca să simți la un nivel mai profund calculatorul pe care îl programezi. Cei care se jenează de astfel de intimități cu calculatorul nu cred că vor putea să ajungă programatori cu adevărat buni.) Dar de aici încolo am dat VERDE la culoare! Și ROSU, și ALBASTRU, și TURCOAZ, și GALBEN, și GRI. Plus NEGRU și ALB, care au fost folosite în mod implicit până acum. Deci în total ai la dispoziție 8 culori cu ajutorul cărora îți poți dezlănțui creativitatea pe care până acum ți-am încarcerat-o într-o temniță binară. Cum poți folosi aceste culori? La fel de simplu ca și până acum. Pentru a aprinde (deci pentru a-l colora cu NEGRU) punctul (x, y) folosești Aprinde(x, y). Cum până acum era posibilă o singură culoare, doar coordonatele punctului erau suficiente ca parametri pentru această funcție. Însă dacă vreau să specific și culoarea punctului îmi mai trebuie un parametru. Așa că am introdus un al treilea parametru în funcția Aprinde. Așadar, Aprinde(x, y) va avea același efect ca și Aprinde(x, y, 148
NEGRU). Dar ce efect crezi că vor avea următoarele instrucțiuni?: Aprinde(1, 1, NEGRU) Aprinde(2, 1, ALB) Aprinde(3, 1, GRI) Aprinde(4, 1, ROSU) Aprinde(5, 1, VERDE) Aprinde(6, 1, ALBASTRU) Aprinde(7, 1, GALBEN) Aprinde(8, 1, TURCOAZ) Dacă te-ai uitat pe imaginea de la începutul acestui capitol, ai observat pe linia de jos o serie de puncte colorate. Ei bine, ele sunt rezultatul rulării acestor 8 instrucțiuni. (A se remarca faptul că Aprinde(2, 1, ALB) este echivalent cu Stinge(2, 1).)
Operatori aritmetici Hai să încheiem paranteza asta lungă dedicată culorilor și să vedem care e treaba cu operatorii. Chiar dacă termenul poate îți sună prea matematic (sau, poate, muncitoresc), cu operatori ca + (plus) și – (minus) ai lucrat încă de mic și cu siguranță nu ți se par a fi lucruri dificile. Nici nu sunt. Voi face mai întâi o trecere în revistă a claselor de operatori pe care le-am folosit în capitolele de până acum, după care îți voi vorbi despre operatorii logici. Mai întâi, operatorii aritmetici. Ai folosit până acum următorii operatori aritmetici: +, –, % și /. Ca să completez lista, aș mai adăuga în plus operatorul *. Ce fac, pe scurt, fiecare dintre ei? 149
Operatorul + returnează rezultatul adunării celor doi operanzi ai lui. Operatorul – returnează rezultatul scăderii celui de-al doilea operand din primul operand. Operatorul % returnează restul împărțirii primului operand la cel de-al doilea. Operatorul / returnează rezultatul împățtirii primului operand la cel de-al doilea. Rezultatul este un număr real. Deci e posibil să dea cu virgulă. Îl poti rotunji la un număr întreg folosind funcția Rotunjeste (pe care am introdus-o în capitolul anterior). (Notă: Câtul împărțirii numărului A la numărul B poate fi obținut folosind următoarea expresie: (A-(A%B))/B. Adică mai întâi scad din A restul împărțirii lui la B (deci rezultatul va fi un număr divizibil cu B), după care împart rezultatul la B.) Iar operatorul * returnează rezultatul înmulțirii primului operand cu cel de-al doilea. În toate aceste explicații, prin primul operand mă refer la cel din stânga, în vreme ce prin al doilea operand mă refer la cel din dreapta. Gata! N-avem puteri, logaritmi sau radicali. (Iar dacă avem cumva nevoie de ele, nu ne rămâne altceva de făcut decât să le obținem aproximări folosind operatorii pe care îi avem la dispoziție. Și uite așa ajungem să vedem la ce sunt bune, de exemplu, seriile convergente… Dar asta e deja o altă discuție, care nu face obiectul acestei cărți.)
Operatori relaționali Hai să trecem la cea de-a doua clasă de operatori utilizați în programele noastre de până aici. Au fost cazuri în care a trebuit să comparăm între ele unele 150
valori (numerice, sau valori de variabile). În astfel de situații am avut nevoie de operatori care să ne spună dacă o valoare este egală cu o alta, sau dacă este mai mare sau mai mică. Cu asta se ocupă operatorii relationali. În lecțiile de până acum am folosit următorii operatori relaționali: ==, >, <, <=. Pentru a completa lista, mai adaug operatorii >= și !=. Să-i luăm pe rând și să vedem ce fac ei.
Operatorul == retunează adevărat dacă cele două valori comparate sunt egale. (Notă: A nu se confunda cu operatorul =, cu ajutorul căruia atribuim unei variabile o valoare numerică sau rezultatul unei expresii aritmetice.) Operatorul > returnează adevărat dacă prima valoare este mai mare decât cea de-a doua. Operatorul < returnează adevărat dacă prima valoare este mai mică decât cea de-a doua. Operatorul <= returnează adevărat dacă prima valoare este mai mică decât cea de-a doua sau egală cu ea. Operatorul >= returnează adevărat dacă prima valoare este mai mare decât cea de-a doua sau egală cu ea. Operatorul != returnează adevărat dacă prima valoare este diferită de cea de-a doua. În caz contrar, fiecare dintre acești operatori returnează fals. Ca și în cazul operatorilor aritmetici, oricare dintre operanzi poate fi o întreagă expresie (ce poate conține valori numerice, variabile și operanzi aritmetici). Pentru a controla ordinea în care se evaluează operanzii se folosesc ( ) (paranteze). Spre deosebire de operatorii aritmetici, pe care îi puteam 151
folosi pentru a calcula valori pe care să le memorăm eventual în variabile, operatorii relaționali îi folosim în expresii condiționale (deci acele expresii care intervin în instrucțiunile dacă (if) și cât timp ( while)).
Operatori logici Și iată că am ajuns şi la subiectul de care am pomenit încă de la inceputul acestui articol. Operatorii logici și , sau și nu. Evident, acești operatori nu fac calcule și nici nu compară valori. Atunci unde ar fi ei utili? În construirea expresiilor condiționale ce se folosesc în instrucțiunile if și while. În mod normal în cadrul acestor intrucțiuni se testează (dacă este adevărată sau nu) o singură condiție. Însă dacă dorim să testăm mai multe condiții ce facem? Să zicem că dorim să parcurgem pe rând toate punctele din linia cu y egal cu 1 de pe ecran și să aprindem doar acele puncte pentru care x este mai mare decât 3 și este mai mic decât 7. Cum am putea proceda? Să ne uităm încă o dată la condiția ce trebuie îndeplinită pentru ca punctul curent să fie aprins. Vedem că de fapt condiția aceasta este compusă din două subcondiții: (C1) x > 3 și (C2) x < 7. Prin urmare, am putea verifica aceste două condiții pe rând, cu ajutorul a două instrucțiuni if, astfel: if (x > 3) // (C1) { if (x < 7) // (C2) { Aprinde(x, y) } } Dacă în loc de două subcondiții am fi avut trei, ar fi fost 152
nevoie de 3 astfel de if-uri. În plus, nici la capitolul claritate acest fragment de program nu stă prea bine. N-ar fi frumos dacă am putea scrie într-un singur if întreaga condiție ((C1) și (C2))? Ar fi. Și chiar se poate! Cu ajutorul operatorului && (și ), care returnează adevărat dacă atât condiția din stânga lui, cât și cea din dreapta, sunt ambele adevărate . Prin urmare, fragmentul de program anterior poate fi rescris în felul următor: if ( (x>3) && (x<7) ) { Aprinde (x, y) } Altfel arată lucrurile acum, nu? Odată ce cunoști semnificația fiecărui semn de acolo, e clar că prima linie se citește în limbaj natural ca dacă valoarea variabilei x este mai mare decât 3 și valoarea variabilei x este mai mică decât 7 . Foarte similar stau lucrurile și pentru operatorul || (sau). Acest operator returnează adevărat dacă fie condiția din stânga lui, fie cea din dreapta, este adevărată . De asemenea, returnează adevărat și pentru cazul în care ambele condiții sunt adevărate. Să folosim în scop demonstrativ un exemplu similar cu cel anterior. Doar că de astă dată ne propunem să aprindem punctele pentru care x este mai mic sau egal cu 3 sau este mai mare sau egal cu 7. Dacă testez pe rând cele două subcondiții, fragmentul de program rezultat ar arăta astfel: if (x <= 3) { 153
Aprinde(x, y) } if (x >= 7) { Aprinde(x, y) } Dar dacă aș avea mai multe astfel de subcondiții? Evident, ar fi necesare mai multe if-uri. (Ca să nu mai zic că Aprinde(x, y) ar trebui scris în fiecare dintre aceste instructiuni de tip if.) Cum ar arăta acest fragment scris cu operatorul sau? Iată: if ( (x<=3) || (x>=7) ) { Aprinde(x, y) } Nu numai că am scris mai puțin, dar fragmentul rezultat este și mult mai clar. Hai să îl vedem acum și pe cel de-al treilea operator logic despre care ți-am promis că vom vorbi aici. Nu.
Nu nu, ci nu. Adică operatorul de negare. Acel operator prin care să pot testa dacă o anumită condiție este nu adevarată, ci falsă. Acest operator se scrie ! (simbol cu care te-ai întâlnit și în cadrul operatorului de testare a inegalității dintre două valori, și anume != (operatorul diferit )). Spre deosebire de ceilalți doi operatori logici discutați, operatorul de negare (operatorul !) nu are doi operanzi, ci unul singur (deci nu este un operator binar, ci este un operator unar). El returnează adevărat dacă condiția este 154
falsă, si returnează fals în caz contrar. Am putea să traim și fără el, căci orice condiție ar putea fi transformată în contrara ei. Iar în cazul intrucțiunii dacă (if) există și blocul opţional atfel (else). Prin urmare, dacă am dori să parcurgem toate cele 10×10 puncte și să aprindem toate punctele pentru care nu este adevărat că y e diferit de 1, am putea scrie astfel: if (y != 1) { } else { Aprinde(x, y) } Sau am putea folosi operatorul de negare, astfel: if ( ! (y!=1) ) { Aprinde(x, y) } Câștigul pe care îl oferă acest operator nu pare să fie unul major din punct de vedere al dimensiunii programului scris, însă el este important din motive de claritate. Nu este o raritate situația în care este mai simplu și mai clar să lucrezi cu contrara unei condiții decât cu condiția însăși.
După cum ai observat, în acest capitol (spre deosebire de capitolele anterioare) nu ți-am oferit ca exemplu programe complete, direct testabile. Însă dacă m-ai urmărit cu atenție până aici, cu siguranță că nu îți este dificil să completezi fragmentele de program discutate pentru a obține programe 155
ce pot fi rulate (sau executate) în simulator.
7.2 Exerciţii Lecţia 7 – Exerciţiul 1: Cum ai “traduce” în limbajul de programare folosit în lecţii condiţiile: 1.1) “dacă x este cuprins între 1 şi 100 şi este divizibil cu 5”? 1.2) “dacă x nu se găseşte în intervalul [a, b], cu excepţia cazului în care x e număr par”?
Lecţia 7 – Exerciţiul 2: Cum ai traduce în limbajul de programare folosit în lecţii condiţiile: 2.1) “dacă valoarea variabilei x nu depăşeşte 3”? 2.2) “dacă valoarea variabilei x nu depăşeşte 3 şi este pozitivă şi impară”?
Lecţia 7 – Exerciţiul 3: Cum ai traduce în limbajul de programare folosit în lecţii condiţiile: 3.1) “dacă al treilea element din vectorul x este mai mare decât cel puţin unul dintre vecinii săi imediaţi din vector (adică elementul din stânga sa şi elementul din dreapta sa; sau elementul de dinaintea sa şi elementul din urma sa)”? 3.2) “dacă cel puţin unul dintre cele 5 elemente ale vectorului 156
x este nenul”?
Lecţia 7 – Exerciţiul 4: Cum ai traduce în limbajul de programare folosit în lecţii condiţiile: 4.1) “dacă valoarea cel puţin uneia dintre variabilele a, b şi c este cuprinsa î n intervalul (x, y)”? 4.2) “dacă valorile a cel puţin două dintre variabilele a, b şi c sunt cuprinse î n intervalul (x, y)”?
Lecţia 7 – Exerciţiul 5: Cum ai face un program care să aprindă punctul (3, 7) folosind culoarea verde dacă exact 3 dintre elementele vectorului v de n elemente sunt divizibile cu 3 sau cu 7, f ără să fie pare? (Şi în caz contrar, punctul (3, 7) să fie aprins folosind culoarea roşu.)
Lecţia 7 – Exerciţiul 6: Cum ai face un program care să aprindă cu verde pe ecranul de 10x10 puncte toate punctele (x, y) pentru care x e divizor al lui y, cu excepţia punctelor situate pe marginile ecranului, care vor fi aprinse toate cu negru? (Şi toate celelalte puncte să fie aprinse cu roşu.)
Lecţia 7 – Exerciţiul 7: Scrie un program care să aprindă primele 8 linii (de jos în sus) din ecranul de 10x10 folosind câte o culoare diferită 157
pentru fiecare linie.
Lecţia 7 – Exerciţiul 8: Parcurge linie cu linie (de la stânga la dreapta, şi de jos în sus) toate punctele ecranului de 10x10 şi aprinde cu albastru fiecare al optulea punct parcurs.
Lecţia 7 – Exerciţiul 9: Să se deseneze pe ecranul de 10x10 puncte un pătrat albastru cu latura de 10 puncte, un pătrat galben cu latura de 8 puncte şi un pătrat roşu cu latura de 6 puncte.
Lecţia 7 – Exerciţiul 10: Să se aprindă cu roşu pe ecranul de 10x10 puncte cele două diagonale şi cu galben toate celelalte puncte.
7.3 Soluţii Lecţia 7 – Exerciţiul 1: 1.1) if ( ( (x>=1) && (x<=100) ) && (x%5 == 0) ) 1.2) if ( ( !( (x>=a) && (x<=b) ) ) || (x%2 == 0) )
Lecţia 7 – Exerciţiul 2: 2.1) if ( !( x > 3) ) 2.2) if ( (x<=3) && (x>=0) && (x%2 == 1) ) 158
Lecţia 7 – Exerciţiul 3: 3.1) if ( (x[2] > x[1]) || (x[2] > x[3]) ) 3.2) if ( (x[0] != 0) || (x[1] != 0) || (x[2] != 0) || (x[3] != 0) || (x[4] != 0) )
Lecţia 7 – Exerciţiul 4: 4.1) if ( ((a>x)&&(ax)&&(bx)&&(cx)&&(ax)&&(bx)&&(ax)&&(cx)&&(bx)&&(c
Lecţia 7 – Exerciţiul 5: //... var nr_elem = 0; var i = 0; while (i
i = i+1; } if (nr_elem == 3) { Aprinde(3, 7, VERDE); } else { Aprinde(3, 7, ROSU); }
Lecţia 7 – Exerciţiul 6: var x; var y = 1; while (y <= 10) { x = 1; while (x <= 10) { if ( (x==1) || (x==10) || (y==1) || (y==10) ) { Aprinde(x, y, NEGRU); 160
} else if ( y%x == 0 ) { Aprinde(x, y, VERDE); } else { Aprinde(x, y, ROSU); } x = x+1; } y = y+1; }
Lecţia 7 – Exerciţiul 7: var x; var y = 1; while (y <= 8) { x = 1; while (x <= 10) { 161
if ( y == 1 ) { Aprinde(x, y, NEGRU); } else if ( y == 2 ) { Aprinde(x, y, ALB); } else if ( y == 3 ) { Aprinde(x, y, ROSU); } else if ( y == 4 ) { Aprinde(x, y, VERDE); } else if ( y == 5 ) { Aprinde(x, y, ALBASTRU); } else if ( y == 6 ) { 162
Aprinde(x, y, GALBEN); } else if ( y == 7 ) { Aprinde(x, y, TURCOAZ); } else { Aprinde(x, y, GRI); } x = x+1; } y = y+1; }
Lecţia 7 – Exerciţiul 8: var nr = 0; var x; var y = 10; while (y>=1) { x = 1; 163
while (x<=10) { nr = nr+1; if (nr == 8) { Aprinde(x, y, ALBASTRU); nr = 0; } x = x+1; } y = y-1; }
Lecţia 7 – Exerciţiul 9: var i = 0; while (i<10) { Aprinde(1+i, 1, ALBASTRU); Aprinde(1+i, 10, ALBASTRU); Aprinde(1, 1+i, ALBASTRU); Aprinde(10, 1+i, ALBASTRU); i = i+1; 164
} i = 0; while (i<8) { Aprinde(2+i, 2, GALBEN); Aprinde(2+i, 9, GALBEN); Aprinde(2, 2+i, GALBEN); Aprinde(9, 2+i, GALBEN); i = i+1; } i = 0; while (i<6) { Aprinde(3+i, 3, ROSU); Aprinde(3+i, 8, ROSU); Aprinde(3, 3+i, ROSU); Aprinde(8, 3+i, ROSU); i = i+1; }
Lecţia 7 – Exerciţiul 10: var x; 165
var y = 10; while (y>=1) { x = 1; while (x<=10) { if ( (x==y) || (x==(11-y)) ) { Aprinde(x, y, ROSU); } else { Aprinde(x, y, GALBEN); } x = x+1; } y = y-1; }
166
8. Texte 8.1 Lecţie Mai tii minte cum se adună două numere? Da, le scrii pe hârtie unul sub celalalt şi apoi le aduni cifră cu cifră, începând cu cifra cel mai din dreapta (adică cifra unităţilor). Exact asta vom face în acest capitol. Doar că în loc de foaie de hârtie vom folosi ecranul din simulator şi în loc de creier vom scrie un program cu ajutorul căruia calculatorul va efectua adunarea celor două numere. Dar pentru asta ne trebuie o modalitate de a afişa numere (şi alte simboluri) în simulatorul nostru. Am introdus în acest scop funcţia Afiseaza, care are trei parametri obligatorii şi un parametru opţional: – text ◄ poate fi un număr între 0 şi 9 sau un caracter afişabil (scris între apostrofuri). – xx ◄ este coordonata pe axa x a căsuţei (sau pătrăţelului) în care se va face afişarea conţinutului lui text. – yy ◄ este coordonata pe axa y a căsuţei în care se va face afişarea conţinutului lui text. – (optional) cc ◄ este fie un cod de culoare (dintre ALB, NEGRU, ROSU, VERDE, ALBASTRU, GRI, GALBEN, 167
TURCOAZ), fie un număr între 0 şi 7 care identifică una dintre cele 8 culori menţionate (în aceeaşi ordine). (Dacă acest parametru lipseşte în apelul funcţiei, culoarea standard este NEGRU.) Gata cu introducerea. Hai să vedem programul de adunare la treabă. Deschide simulatorul, apasă butonul “Resetează afişajul”, introdu în caseta din stânga programul de mai jos şi apoi apasă butonul “Execută programul”): // Program de adunare a doua numere: // -------------------------------// primul numar: var nr1 = Vector(5); nr1[0] = 7; nr1[1] = 1; nr1[2] = 9; nr1[3] = 9; nr1[4] = 6; // al doilea numar: var nr2 = Vector(5); nr2[0] = 2; nr2[1] = 9; nr2[2] = 8; nr2[3] = 7; nr2[4] = 5; // rezultatul: var rez = Vector(6); rez[0] = 0; rez[0] = 0; rez[0] = 0; rez[0] = 0; rez[0] = 0; // -------------------------------// calculul sumei (rez = nr1+nr2): var transport = 0; var i = 0; while (i < 5) 168
{ var suma = nr1[4-i] + nr2[4-i] + transport; if (suma > 9) { transport = 1; suma = suma - 10; } else { transport = 0; } rez[5-i] = suma; i = i+1; } rez[5-i] = transport; // -------------------------------// afisarea numerelor si a rezultatului: Afiseaza('+', 9, 7, NEGRU); i = 4; while (i>=0) { Afiseaza(nr1[i], 8-(4-i), 7, ALBASTRU); Afiseaza(nr2[i], 8-(4-i), 6, ALBASTRU); Afiseaza('=', 8-(4-i), 5, NEGRU); Afiseaza(rez[i+1], 8-(4-i), 4, ROSU); i = i-1; } if (rez[i+1] > 0) { Afiseaza('=', 8-(4-i), 5, NEGRU); Afiseaza(rez[i+1], 8-(4-i), 4, ROSU); } După cum ai putut vedea dacă ai analizat cu atenţie codul sursă al programului, cele două numere de adunat sunt stocate ca vectori (nr1, respectiv nr2) în care fiecare element este o cifră din număr, iar adunarea lor se face cifră cu cifră, 169
rezultatul fiind pus într-un alt vector, rez. Un rol important îl are în program şi variabila transport (care “ţ ine minte 1 ” atunci când suma a două cifre depăşeşte valoarea 9). Nu e chiar greu, nu-i aşa? Dar nici foarte uşor nu e (mai ales acum, la început.) Te provoc să modifici programul astfel încât în loc de adunare să facă scăderea celor două numere. Ce zici? Accepţi provocarea? Având în vedere dificultatea exerciţiului, de astă dată îţi ofer eu soluţia. Îţi rămâne ca exerciţiu să o studiezi şi să o înţelegi: // Program de scadere a doua numere: // -------------------------------// primul numar: var nr1 = Vector(5); nr1[0] = 7; nr1[1] = 1; nr1[2] = 0; nr1[3] = 0; nr1[4] = 6; // al doilea numar: var nr2 = Vector(5); nr2[0] = 2; nr2[1] = 9; nr2[2] = 8; nr2[3] = 7; nr2[4] = 9; // rezultatul: var rez = Vector(5); rez[0] = 0; rez[1] = 0; rez[2] = 0; 170
rez[3] = 0; rez[4] = 0; // -------------------------------// afisarea numerelor: Afiseaza('-', 9, 7, NEGRU); var i = 4; while (i>=0) { Afiseaza(nr1[i], 8-(4-i), 7, ALBASTRU); Afiseaza(nr2[i], 8-(4-i), 6, ALBASTRU); Afiseaza('=', 8-(4-i), 5, NEGRU); i = i-1; } // -------------------------------// calculul diferentei (rez = nr1-nr2): i = 0; while (i < 5) { var diferenta = nr1[4-i] - nr2[4-i]; if (diferenta < 0) { // e necesar sa fac imprumut var cif = i+1; while (cif < 5) { if (nr1[4-cif] > 0) { nr1[4-cif] = nr1[4-cif] - 1; cif = 5; // si ies din while } else //if (cif == 0) { nr1[4-cif] = 9; cif = cif + 1; // si trec la cifra mai din stanga } } // si abia apoi fac diferenta 171
diferenta = diferenta + 10; } rez[4-i] = diferenta; i = i+1; } // -------------------------------// afisarea rezultatului: i = 4; while (i>=0) { Afiseaza(rez[i], 8-(4-i), 4, ROSU); i = i-1; } Nici nu realizezi acum cât de multe vei învăţa dacă vei lua o foaie de hârtie şi un pix şi vei începe să execuţi pe foaie programul.
Este interesant să poţi aprinde puncte (sau pătrăţele) colorate pe ecran, nu-i aşa? Dar şi mai interesant e să poţi afişa în acele pătrăţele litere, cifre şi simboluri, nu? Dacă ai învăţat să foloseşti funcţia prezentată în această lecţie, abilităţile tale de exprimare a rezultatelor programelor au crescut semnificativ. În modulul următor vei învăţa cum să introduci în programele tale mister , viaţă şi interactivitate. Cu ajutorul tuturor acestor elemente deja vei putea construi mici jocuri captivante.
8.2 Exerciţii Lecţia 8 – Exerciţiul 1: Scrie un program prin care să îţi afişezi numele pe ecranul de 10x10. 172
Lecţia 8 – Exerciţiul 2: Scrie un program prin care să parcurgi toate punctele de pe ecranul de 10x10 de la stânga la dreapta şi de jos în sus şi să afişezi în fiecare punct un număr î n ordinea 0, 1, 2, 3, …, 8, 9, 0, 1, 2, … .
Lecţia 8 – Exerciţiul 3: Scrie un program prin care să parcurgi toate punctele de pe ecranul de 10x10 de la stânga la dreapta şi de jos în sus şi să afişezi în fiecare punct un număr în ordinea 0, 1, 2, 0, 1, 2, 0, 1, … .
Lecţia 8 – Exerciţiul 4: Scrie un program prin care să parcurgi toate punctele de pe ecranul de 10x10 de sus în jos şi de la stânga la dreapta şi să afişezi în fiecare punct un număr în ordinea 1, 2, 3, …, 8, 9, 1, 2, 3, … .
Lecţia 8 – Exerciţiul 5: Scrie un program prin care să parcurgi toate punctele de pe ecranul de 10x10 de la stânga la dreapta şi de sus în jos şi să afişezi în fiecare punct un număr în ordinea 1, 3, 5, 7, 9, 1, 3, 5, … .
Lecţia 8 – Exerciţiul 6: Scrie un program prin care să parcurgi toate punctele de pe 173
ecranul de 10x10 de la dreapta la stânga şi de sus în jos şi să afişezi în fiecare punct un număr în ordinea 1, 2, 3, 6, 1, 2, 3, 6, 1, 2, … .
Lecţia 8 – Exerciţiul 7: Scrie un program prin care să parcurgi toate punctele de pe ecranul de 10x10 de la stânga la dreapta şi de sus în jos şi să afişezi în fiecare al şaptelea punct cifra 7 scrisă cu alb pe fundal verde.
Lecţia 8 – Exerciţiul 8: Scrie un program prin care să parcurgi toate punctele de pe ecranul de 10x10 de jos în sus şi de la stânga la dreapta şi să afişezi în fiecare punct un număr în ordinea 2, 4, 6, 8, 2, 4, 6, 8, 2, … .
Lecţia 8 – Exerciţiul 9: Scrie un program prin care să parcurgi toate punctele de pe ecranul de 10x10 de jos în sus şi de la dreapta la stânga şi să afişezi în fiecare punct un număr în ordinea 1, 2, 3, 4, ..., 8, 9, 1, 2, … .
Lecţia 8 – Exerciţiul 10: Scrie un program în care să parcurgi toate punctele de pe ecranul de 10x10 linie cu linie de sus în jos, prima linie parcurgând-o de la stânga la dreapta, a doua de la dreapta la stânga, a treia de la stânga la dreapta, şi tot aşa, completând în fiecare punct parcurs un număr în ordinea 1, 2, 3, 4, …, 8, 9, 0, 1, 2, 3, … . 174
8.3 Soluţii Lecţia 8 – Exerciţiul 1: Afiseaza('F', 3, 6, ALBASTRU); Afiseaza('l', 4, 6, ALBASTRU); Afiseaza('o', 5, 6, ALBASTRU); Afiseaza('r', 6, 6, ALBASTRU); Afiseaza('i', 7, 6, ALBASTRU); Afiseaza('n', 8, 6, ALBASTRU);
Lecţia 8 – Exerciţiul 2: var nr = 0; var x; var y = 1; while (y<=10) { x = 1; while (x<=10) { Afiseaza(nr, x, y); nr = (nr+1) % 10; x = x+1; } 175
y = y+1; }
Lecţia 8 – Exerciţiul 3: var nr = 0; var x; var y = 1; while (y<=10) { x = 1; while (x<=10) { Afiseaza(nr, x, y); nr = (nr+1) % 3; x = x+1; } y = y+1; }
Lecţia 8 – Exerciţiul 4: var nr = 1; var y; 176
var x = 1; while (x<=10) { y = 10; while (y>=1) { Afiseaza(nr, x, y); nr = nr+1; if (nr>9) { nr = 1; } y = y-1; } x = x+1; }
Lecţia 8 – Exerciţiul 5: var nr = 0; var x; var y = 10; while (y>=1) 177
{ x = 1; while (x<=10) { Afiseaza(2*nr+1, x, y); nr = (nr+1) % 5; x = x+1; } y = y-1; }
Lecţia 8 – Exerciţiul 6: var cif = Vector(4); cif[0] = 1; cif[1] = 2; cif[2] = 3; cif[3] = 6; var nr = 0; var x; var y = 10; while (y>=1) { 178
x = 10; while (x>=1) { Afiseaza(cif[nr], x, y); nr = (nr+1) % 4; x = x-1; } y = y-1; }
Lecţia 8 – Exerciţiul 7: var nr = 1; var x; var y = 10; while (y>=1) { x = 1; while (x<=10) { if (nr == 7) { Aprinde(x, y, VERDE); 179
Afiseaza(7, x, y, ALB); nr = 1; } else { nr = nr+1; } x = x+1; } y = y-1; }
Lecţia 8 – Exerciţiul 8: var nr = 0; var y; var x = 1; while (x<=10) { y = 1; while (y<=10) { Afiseaza(2*(nr+1), x, y); 180
nr = (nr+1) % 4; y = y+1; } x = x+1; }
Lecţia 8 – Exerciţiul 9: var nr = 0; var y; var x = 10; while (x>=1) { y = 1; while (y<=10) { Afiseaza(1+nr, x, y); nr = (nr+1) % 9; y = y+1; } x = x-1; }
181
Lecţia 8 – Exerciţiul 10: var nr = 1; var x; var y = 10; while (y>=1) { x = 1; while (x<=10) { if ( (11-y)%2 == 1 ) { Afiseaza(nr, x, y); } else { Afiseaza(nr, 11-x, y); } nr = (nr+1) % 10; x = x+1; } y = y-1; } 182
Modulul III Litere Acest al treilea modul încheie prima parte a cărţii, dedicată construirii unei fundaţii serioase pentru o gândire solidă de programator. Vei învăţa în lecţiile din acest modul cum să faci ca programele tale să reacţioneze la apăsări de taste şi la clickuri de mouse, cum să introduci elemente aleatoare în programe şi cum să generezi animaţii. Modulul acesta îţi va oferi (în completarea celor învăţate în modulele anterioare) toate uneltele necesare pentru a putea construi în simulator jocuri ca Mastermind, Clickomania, Snake şi Tetris.
183
9. Taste 9.1 Lecţie Îmbinând în moduri ingenioase toate cunoștințele și abilitățile deprinse până acum îți poți construi propriul joc (ce va rula, bineînţeles, în simulator). Da, ai (aproape) tot ce îți trebuie! Mai lipsește doar un aspect. Un aspect simplu, dar fără de care n-am fi putut vorbi niciodată de aplicaţii serioase pentru calculator. Este vorba despre interactivitate. Și cum putem asigura această interactivitate altfel decât lăsând utilizatorului programului nostru posibilitatea de a da comenzi programului? (Altfel am putea programa doar poze sau cel mult filme, și nicidecum jocuri sau orice alte aplicaţii care implică o interacţiune în timp real între calculator şi utilizator.) Cum cea mai utilizată metodă de a interacționa cu calculatorul este (încă) tastatura, îți voi arăta în acest capitol cum îți poți face programul să răspundă așa cum dorești la apăsarea anumitor taste. Lecția va fi scurtă, însă înțelegerea ei pe deplin va necesita exercițiu din partea ta. (Evident, această ultimă afirmație este valabilă și pentru celelalte lecții de până acum. Nu am scris această carte din dorința de a scrie literatură, ci din dorința de a-ți oferi în formă scrisă un antrenor personal care să alerge împreună cu tine până când îți atingi obiectivul.) Hai să vedem acum cum putem să îi spunem calculatorului 184
nostru virtual ce să facă atunci când se apasă o tastă. La prima vedere lucrurile sunt un pic mai complicate, căci îmbină practic majoritatea informațiilor de până aici. Însă dacă lecțiile trecute nu ți-au pus probleme, nici aceasta nu o va face.
În momentul în care calculatorul detectează apăsarea unei taste, el apelează în mod automat o funcție care a fost specificată în acest sens. În cadrul acelei funcții trebuie să afli tasta care a fost apăsată și să specifică modul în care calculatorul trebuie să reacționeze la acest eveniment. Întreg acest capitol l-am rezumat în paragraful anterior. Haide să reparcurgem paragraful respectiv pas cu pas și să detaliez acolo unde e cazul. La apăsarea vreunei taste calculatorul apelează în mod automat o funcție specificată de către tine. Buuun! E foarte bine că se ocupa el în mod automat de treaba asta. Mai rămâne să vedem: (1) cum îi poți spune ce funcție să apeleze și (2) ce caracteristici trebuie să aibă această funcție. Cât privește (1), răspunsul constă în funcția predefinită AscultaTaste. I-am dat un nume destul de sugestiv, nu? Această funcție așteaptă ca parametru numele unei funcții care să conțină funcționarea dorită în cazul apăsării tastelor. Prin urmare, în program o poți folosi în felul următor: AscultaTaste(FunctieTaste); În loc de FunctieTaste poți folosi orice nume valid de funcție. Singura constrângere este (și cu asta răspund la întrebarea (2)) că această funcție trebuie să primească un parametru. În acest parametru calculatorul (care, repet, va apela în mod 185
automat funcția FunctieTaste de fiecare dată când se apasă o tastă) va pune informații despre evenimentul care s-a produs (adică apăsarea tastei). Să zicem că denumim ev (de la eveniment ) parametrul functiei FunctieTaste. Prin urmare, definiția ei va arăta în felul următor: function FunctieTaste(ev) { // instructiunile din cadrul functiei, // ce vor fi executate in mod automat // la fiecare apasare de tasta detectata. … } Evident, așa cum am zis mai sus, simpla definiție a acestei funcții nu este suficientă pentru ca programul să răspundă la apăsarea tastelor. Este necesar ca după ce definești această funcție să-i spui calculatorului că pe ea dorești să o apeleze atunci când detectează vreo tastă apasată. Cum faci asta? Am spus mai sus: prin apelul AscultaTaste(FunctieTaste). Bun. Aproape am terminat. Ramane doar să vedem ce scriem în cadrul funcției FunctieTaste. Păi n-ar trebui să fie prea greu, nu? Doar avem parametrul ev, în care ni se trasmit (în mod automat) informații despre evenimentul care s-a produs. Da, chiar nu e greu. Singura mică problemă e că în ev nu ni se spune direct ce tastă a fost apasată. Dar nu e grav, căci avem la dispoziție funcția predefinită TastaApasata, care face exact acest lucru. Primește ca parametru variabila ev și returnează tasta ce a fost apasată, în format literal. (Vei vedea mai jos despre ce e vorba.) 186
Practic, dacă la începutul funcției FunctieTaste scriem: var tasta = TastaApasata(ev); în variabila tasta vom avea memorată în format literal tasta ce a fost apăsată. OK, dar ce e cu formatul ăsta literal? Până aici în variabile am memorat doar numere. Ei bine, în variabile se pot memora și caractere (adică litere, cifre şi simboluri). (De fapt, la nivel intern tot cu numere se lucrează, fiecărui caracter de pe tastatură corespunzându-i un cod numeric. Însă pentru programator este mai simplu să lucreze cu caractere.) Aceste caractere trebuie puse între apostrofuri. De exemplu, caracterul corespunzator apăsării tastei A este 'a' (iar 'A' corespunde apăsării combinației de taste SHIFT+A). Așadar, dacă doresc să verific dacă s-a apăsat tasta A, pot scrie în program (în cadrul funcției FunctieTaste, bineînțeles, și presupunând că în variabila tasta am tasta ce a fost apăsată) o condiție de genul următor: if (tasta == 'a') { // instructiuni ce trebuie executate daca s-a // apasat tasta A … } Cam asta e. Acum e timpul să punem cap la cap tot ce am zis până aici și să facem un mic program demonstrativ. Hai să facem un program în care să plimbam pe ecranul virtual un punct cu ajutorul tastelor A (stânga), S (jos), D (dreapta) și W (sus). Și pentru ca lucrurile să fie mai interesante, îți propun ca punctul să fie roșu dacă se află în centrul ecranului (considerând centru în acest caz pătratul cu colțul stânga-jos 187
la coordonatele (3, 3) și coltul dreapta-sus la coordonatele (8, 8)), și să fie negru în caz contrar (deci dacă se află pe primele două linii din marginile ecranului virtual). Fără alte comentarii preliminare, iată mai jos programul. (Înainte de a-l citi, te rog să îți schițezi tu însuți propriul program.) // Program deplasare punct din taste // 1) Initializari: var x = 1; var y = 1; Aprinde(x, y); // 2) Functia de tratare a apasarii tastelor: function FunctieTaste(ev) { var tasta = TastaApasata(ev); Stinge(x, y); if ( (tasta == 'a') && (x > 1) ) { x = x-1; } if ( (tasta == 'd') && (x < 10) ) { x = x+1; } if ( (tasta == 's') && (y > 1) ) { y = y-1; } if ( (tasta == 'w') && (y < 10) ) { y = y+1; } if ( (x>=3) && (y>=3) && (x<=8) && (y<=8) ) { Aprinde(x, y, ROSU); } else { Aprinde(x, y); } } AscultaTaste(FunctieTaste); N-a fost chiar greu, nu? După cum ai văzut, programul conține în esență două părți: 188
1) o parte de inițializări (în care nu fac altceva decât să definesc două variabile (x și y) în care voi memora coordonatele curente ale punctului pe ecran, coordonate pe care le inițializez cu (1, 1) (deci punctul cel mai din stânga-jos al ecranului), și să aprind (cu negru) punctul de la coordonatele (x, y)) și 2) o parte în care spun calculatorului ce să facă atunci când se apasă vreo tastă. Și dacă intrăm în detaliile funcției FunctieTaste, vei vedea că și acolo lucrurile sunt destul de clare. Mai întâi aflu ce tastă a fost apăsată, apoi sting punctul de la coordonatele (x, y), după care verific dacă a fost apăsată vreuna dintre tastele A, S, D sau W și modific în mod corespunzător coordonata x sau y (verificând, de asemenea, ca punctul să nu iasă în afara ecranului). Iar la final aprind punctul de la coordonatele (x, y) (care probabil au fost modificate) fie cu roșu, fie cu negru (în funcție de poziția în care se găsește punctul pe ecran). Ca exercițiu, te-aș ruga să modifici programul (după ce l-ai testat în simulator) astfel încât elementul care se plimbă pe ecran să nu mai fie doar un punct, ci un mic pătrățel format din 4 puncte.
Cu ajutorul celor învăţate în această lecţie deja programele tale pot trece la un nou nivel. Până acum, întreg comportamentul programului era predefinit. Dar de acum încolo poţi face programe care să răspundă instantaneu la comenzi pe care i le dai nu doar înainte (ca pâ nă acum), ci chiar în timpul rulării programului (prin apăsări de taste).
189
9.2 Exerciţii Lecţia 9 – Exerciţiul 1: Să se realizeze un program care la apăsarea tastei ‘s’ afişează pe ecranul de 10x10 la coordonatele (1, 1) cifra ‘1’, iar la apăsarea tastei ‘r’ afişează (î n acelasi loc) cifra ‘0’.
Lecţia 9 – Exerciţiul 2: Să se realizeze un program care aprinde cu verde pe ecranul de 10x10 punctul (3, 3) şi la fiecare apăsare a tastei ‘d’ punctul se va deplasa la dreapta cu o poziţie. Când atinge marginea din dreapta a ecranului, se va opri acolo.
Lecţia 9 – Exerciţiul 3: Să se realizeze un program care aprinde cu verde pe ecranul de 10x10 punctul (3, 3) şi la fiecare apăsare a tastei ‘d’ punctul se va deplasa la dreapta cu o poziţie. Când atinge marginea din dreapta a ecranului, punctul va dispărea automat şi va reapărea pe marginea din stanga a ecranului (pe aceeaşi linie).
Lecţia 9 – Exerciţiul 4: Să se realizeze un program care aprinde cu verde pe ecranul de 10x10 punctul (3, 3) şi la fiecare apasare a tastei ‘s’ punctul se va deplasa în sus cu o poziţie. Când atinge marginea de sus a ecranului, punctul va dispărea automat şi va reapărea pe marginea de jos a ecranului (pe aceeaşi coloană).
190
Lecţia 9 – Exerciţiul 5: Să se realizeze un program care afişează pe ecran simbolul ‘+’ şi permite deplasarea lui în cele 4 direcţii folosind tastele ‘a’ (stânga), ‘d’ (dreapta), ‘w’ (sus) şi ‘s’ (jos).
Lecţia 9 – Exerciţiul 6: Să se realizeze un program care afişează pe ecran simbolul ‘+’ şi permite deplasarea lui în cele 4 direcţii folosind tastele ‘a’ (stânga), ‘d’ (dreapta), ‘w’ (sus) şi ‘s’ (jos). În plus, atunci când se apasă tasta ‘=’, simbolul ‘+’ se va transforma în simbolul ‘=’ (iar la o altă apăsare a tastei ‘=’, va redeveni ‘+’).
Lecţia 9 – Exerciţiul 7: Să se realizeze un program care la fiecare apăsare a tastei ‘p’ va aprinde câte un punct albastru pe linia de jos a ecranului de 10x10, începând cu primul punct din stânga şi continuând spre dreapta până când se aprinde toată linia.
Lecţia 9 – Exerciţiul 8: Să se realizeze un program care la fiecare apăsare a tastei ‘p’ va aprinde câte un punct albastru pe linia de jos a ecranului de 10x10, începând cu primul punct din stânga şi continuând spre dreapta până când se aprinde toată linia. La apăsari ulterioare ale tastei ‘p’, punctele se vor stinge pe rând în aceeaşi ordine în care s-au aprins.
Lecţia 9 – Exerciţiul 9: Să se realizeze un program care la fiecare apăsare a unei taste va afişa pe ecran simbolul corespunzator ei în punctul (1, 1), care în prealabil va fi colorat cu verde. 191
Lecţia 9 – Exerciţiul 10: Să se realizeze un program care la fiecare apăsare a unei taste va afişa pe ecran simbolul corespunzător ei la poziţia curentă a cursorului (care va fi marcata prin colorarea cu galben a punctului respectiv de pe ecran). Iniţial cursorul este plasat în punctul (1, 10), iar la fiecare apăsare de tastă el se deplasează la dreapta cu o poziţie. Atunci când este plasat pe ultima coloană din dreapta, deplasarea lui nu se va face la dreapta, ci se va muta cu o linie mai jos, la capătul din stânga al ei.
9.3 Soluţii Lecţia 9 – Exerciţiul 1: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 's') { Aprinde(1, 1, ALB); Afiseaza('1', 1, 1); } else if (tasta == 'r') { Aprinde(1, 1, ALB); 192
Afiseaza('0', 1, 1); } } AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 2: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'd') { Aprinde(x, 3, ALB); // Stinge(x, 3); if (x < 10) { x = x+1; } Aprinde(x, 3, VERDE); } } var x = 3; Aprinde(x, 3, VERDE); AscultaTaste(FunctieTaste);
193
Lecţia 9 – Exerciţiul 3: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'd') { Stinge(x, 3); x = x+1; if (x == 10) x = 1; Aprinde(x, 3, VERDE); } } var x = 3; Aprinde(x, 3, VERDE); AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 4: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 's') 194
{ Aprinde(3, y, ALB); y = y+1; if (y == 10) y = 1; Aprinde(3, y, VERDE); } } // Programul propriu-zis: var y = 3; Aprinde(3, y, VERDE); AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 5: function FunctieTaste(ev) { var tasta = TastaApasata(ev); Aprinde(x, y, ALB); if (tasta == 'a') { x = x-1; } else if (tasta == 'd') 195
{ x = x+1; } else if (tasta == 'w') { y = y+1; } else if (tasta == 's') { y = y-1; } Afiseaza('+', x, y); } var x = 5; var y = 5; Afiseaza('+', x, y); AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 6: function FunctieTaste(ev) { var tasta = TastaApasata(ev); 196
Stinge(x, y); //Aprinde(x, y, ALB); if (tasta == 'a') { x = x-1; } else if (tasta == 'd') { x = x+1; } else if (tasta == 'w') { y = y+1; } else if (tasta == 's') { y = y-1; } else if (tasta == '=') { if (simbol == '+') { simbol = '='; 197
} else { simbol = '+'; } } Afiseaza(simbol, x, y); }
var x = 5; var y = 5; var simbol = '+'; Afiseaza(simbol, x, y); AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 7: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'p') { x = x+1; 198
if (x <= 10) { Aprinde(x, 1, ALBASTRU); } } }
var x = 0; AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 8: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'p') { x = x+1; if (x == 11) { x = 1; mod = 1-mod; } 199
if (mod == 1) { Aprinde(x, 1, ALBASTRU); } else { Stinge(x, 1); } } }
var x = 0; var mod = 1; // 1 - aprinde; 0 - stinge AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 9: function FunctieTaste(ev) { var tasta = TastaApasata(ev); Aprinde(1, 1, VERDE); Afiseaza(tasta, 1, 1); } 200
Aprinde(1, 1, VERDE); AscultaTaste(FunctieTaste);
Lecţia 9 – Exerciţiul 10: function FunctieTaste(ev) { var tasta = TastaApasata(ev); Stinge(x, y); Afiseaza(tasta, x, y); x = x+1; if (x == 11) { y = y-1; x = 1; } Aprinde(x, y, GALBEN); if (y == 0) { Aprinde(x, y, ALB); AscultaTaste(); } 201
}
var x = 1; var y = 10; Aprinde(x, y, GALBEN); AscultaTaste(FunctieTaste);
202
10. Aleatoare 10.1 Lecţie Ce simţi atunci când auzi cuvântul aleator ? Nu cumva simţi o oarecare conotaţie negativă în el? Te gândeşti, cel mai probabil, la ceva întâmplator, impredictibil, incert. Şi nu eşti departe de adevăr. Însă ceea ce probabil nu ştii în clipa asta este faptul că numerele aleatoare sunt nişte prieteni de nădejde pentru un programator. (Ele sunt necesare atât în jocuri, cât şi în algoritmi folosiţi în inteligenţa artificială.) Fără numere aleatoare jocurile de calculator, de exemplu, ar fi de o predictibilate tristă. Cum să joci ghiceşte numărul dacă ştii deja despre ce număr e vorba, cum să joci x şi zero dacă ştii deja unde va muta calculatorul, cum să joci Mastermind dacă deja ştii culorile şi poziţiile bilelor? Din fericire, există numere aleatoare. Hai să vedem ce sunt ele şi cum le poţi folosi.
Un numă r aleator este un numă r ce nu poate fi prezis. Dacă luăm lucrurile la modul foarte strict, nu există nimic cu adevărat aleator în lumea asta. Cine crede că realitatea în 203
care trăim se supune unor legi precise (chiar dacă necunoscute (de noi) în totalitate) va fi de acord cu această ultimă afirmaţie. Numerele aleatoare cu care operăm într-un program de calculator sunt în realitate doar numere pseudo-aleatoare. Adică doar par greu de prezis, însă în spatele lor stau nişte expresii matematice fixe şi lipsite de ambiguităţi. Hai să luam un exemplu simplu. Să considerăm o serie de numere în care primul număr este 3, iar următorul număr din serie îl obţinem calculând restul împărţirii la 13 a sumei dintre numărul curent şi 7. Prin urmare, primele 10 numere dintr-o astfel de serie vor fi următoarele: 3, 10, 4, 11, 5, 12, 6, 0, 7, 1, … Desi procedeul de calcul este foarte clar, seria de numere pe care o obţinem astfel pare (cel puţin la prima vedere) aleatoare. La o a doua vedere, însă, observăm că seria e departe de a fi impredictibilă. OK, exemplul a fost unul simplu şi a avut drept scop doar să te facă să simţi un pic din ceea ce stă în spatele metodelor de a obţine numere pseudoaleatoare. Metodele serioase din acest domeniu chiar produc serii de numere foarte greu (a se citi “practic imposibil”) de prezis. (Nu voi intra în detalii pe acest subiect în cartea de faţă.) O astfel de metodă este cea care la baza funcţiei predefinite NrAleator, pe care de aici înainte o poţi folosi în simulator pentru a produce numere aleatoare întregi din intervalul [0, Nmax], unde Nmax este un parametru pe care îl transmiţi funcţiei NrAleator atunci când o apelezi. Adică pentru a genera un număr aleator între 0 şi 9 (inclusiv) va trebui să scrii NrAleator(9). (Dar dacă vrei să produci un număr aleator între 1 şi 10? Evident, foloseşti expresia 1+NrAleator(9).) 204
Bun, acum hai să ne jucam!
Ce ai spune să coloră m aleator întregul ecran de 10×10 puncte? Dacă ai parcurs cu atenţie lecţiile anterioare, atunci exerciţiul acesta ar trebui să nu îţi pună mari dificultăţi. Hai să vedem pas cu pas ce ar trebui să facem. În primul rând, trebuie să parcurgem pe rând toate punctele de pe ecran. (Am mai făcut asta. Aici doar repet.) Şi cum putem face asta? Păi, ecranul e organizat ca o matrice, deci cu linii şi coloane. Ceea ce înseamnă că îl putem parcurge fie linie cu linie, fie coloană cu coloană (şi punct cu punct în cadrul liniei, respectiv al coloanei). Când auzi “să parcurgem”, la ce te gândeşti? La un while, desigur! Şi nu numai atât, ci la o structură de program de genul următor: i = 1; while (i<=10) { // instructiuni de executat … i = i+1; } Adică o structură de program în care variabila i ia pe rând valorile 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. Şi pentru fiecare dintre aceste valori ale variabilei i se va executa blocul de instrucţiuni ce va fi pus în loc de cele 3 puncte din secvenţa de program. Iar dacă vreau să parcurg ecranul linie cu linie, iar în cadrul fiecărei linii să parcurg pe rând toate punctele de la stânga la dreapta, atunci nu-mi rămâne de făcut altceva decât ca în cadrul unei secvente de program de genul celei de mai sus 205
să introduc (în locul punctelor de suspensie) o secvenţă similară (dar folosind o altă variabilă, şi nu tot variabila i). Adică ceva de genul următor: i = 1; while (i<=10) { j = 1; while (j<=10) { // instructiuni de executat … j = j+1; } i = i+1; } Ei bine, programul e acum aproape gata. Mai lipsesc doar instrucţiunile de declarare pentru variabilele i şi j, precum şi instrucţiunile prin care să aprind punctul curent (adică punctul de pe linia i şi coloana j de pe ecran) folosind o culoare selectată aleator dintre cele 8 disponibile (inclusiv culoarea alb). Cum pot, deci, selecta culoarea? Am funcţia NrAleator(Nmax), care îmi returnează un număr întreg aleator între 0 şi Nmax, inclusiv. Dar îmi trebuiesc aici numere între 0 şi 7 (corespunzător celor 8 culori, în ordinea următoare: ALB, NEGRU, ROSU, VERDE, ALBASTRU, GRI, GALBEN, TURCOAZ). Voi putea genera un astfel de număr cu apelul NrAleator(7). Şi voi putea aprinde punctul de la coordonatele (j, i) în culoarea respectivă astfel: AprindeNrCul(j, i, NrAleator(7));. Sau puteam să folosesc o variabilă temporară numită, de exemplu, culoare, şi să scriu aşa: 206
var culoare = NrAleator(7); AprindeNrCul(j, i, culoare); Funcţia AprindeNrCul este şi ea una dintre funcţiile predefinite şi are rolul de a-ţi permite să specifici culoarea folosind un număr de la 0 la 8 – după cum poţi vedea din definiţia ei: function AprindeNrCul(x, y, nc) { if (nc == 0) Aprinde(x, y, ALB); else if (nc == 1) Aprinde(x, y, NEGRU); else if (nc == 2) Aprinde(x, y, ROSU); else if (nc == 3) Aprinde(x, y, VERDE); else if (nc == 4) Aprinde(x, y, ALBASTRU); else if (nc == 5) Aprinde(x, y, GRI); else if (nc == 6) Aprinde(x, y, GALBEN); else if (nc == 7) Aprinde(x, y, TURCOAZ); } Hai să vedem acum întreg programul de colorare aleatoare a ecranului virtual din simulator: var i; var j; i = 1; while (i<=10) { j = 1; while (j<=10) 207
{ AprindeNrCul(j, i, NrAleator(7)); j = j+1; } i = i+1; } Simplu şi eficient, după cum poţi vedea testându-l în simulator. Ce e foarte interesant la acest program e că la fiecare apăsare a butonului de execuţie rezultatul este altul. Atenţie: Există riscul ca rularea lui în mod repetat să producă dependenţă :-)! Cum ai face ca ecranul virtual să conţină o repartiţie aleatoare nu de 8, ci de doar 3 culori (şi anume ROSU, GALBEN şi ALBASTRU)?
Fie că îţi plac sau nu surprizele, ai putut vedea în această lecţie cum poţi să introduci o doză de mister în programele tale.
10.2 Exerciţii Lecţia 10 – Exerciţiul 1: Să se aprindă un punct la întâmplare pe ecranul de 10x10. Dacă este pe marginea ecranului se va aprinde cu roşu, iar altfel se va aprinde cu albastru.
Lecţia 10 – Exerciţiul 2: Să se aprindă o linie de 10 puncte consecutive, verticală sau orizontală, la întâmplare pe ecranul de 10x10. 208
Lecţia 10 – Exerciţiul 3: Să se realizeze un program care la fiecare apăsare a tastei ‘p’ va aprinde un punct la întâmplare pe ecranul de 10x10.
Lecţia 10 – Exerciţiul 4: Să se realizeze un program care la fiecare apăsare a tastei ‘p’ va aprinde un punct la întamplare pe ecranul de 10x10. Culoarea lui va fi aleasă la întâmplare dintre ALBASTRU, GALBEN, ROSU şi VERDE.
Lecţia 10 – Exerciţiul 5: Să se realizeze un program care plasează un punct albastru la coordonatele (4, 4) şi la fiecare apăsare a tastei ‘m’ va deplasa punctul cu o poziţie fie la dreapta, fie la stânga. Alegerea între deplasare la stânga şi deplasare la dreapta se va face aleator.
Lecţia 10 – Exerciţiul 6: Să se realizeze un program care plasează un punct albastru la coordonatele (4, 4) şi la fiecare apăsare a tastei ‘m’ va deplasa punctul cu o poziţie fie la dreapta, fie la stânga, fie în sus, fie în jos. Alegerea între deplasare la stânga, deplasare la dreapta, deplasare în sus şi deplasare în jos se va face aleator.
Lecţia 10 – Exerciţiul 7: Să se realizeze un program care la fiecare apăsare a tastei ‘b’ va completa linia de jos a ecranului de 10x10 cu cifre de 0 209
sau 1, alese aleator.
Lecţia 10 – Exerciţiul 8: Să se realizeze un program care la fiecare apăsare a tastei ‘b’ va completa câte un punct din linia de jos a ecranului de 10x10 (începând cu punctul din colţul stânga-jos şi continuând către dreapta) cu cifre de 0 sau 1, alese aleator.
Lecţia 10 – Exerciţiul 9: Să se realizeze un program care la fiecare apăsare a tastei ‘n’ va afişa în centrul ecranului de 10x10 unul dintre textele ‘Aer’, ‘Bucurie’, ‘Caldura’, ‘Dragoste’. Alegerea textului afişat se va face aleator.
Lecţia 10 – Exerciţiul 10: Să se realizeze un program care parcurge de la stânga la dreapta şi de sus în jos toate punctele ecranului de 10x10 şi afişează în ele (caracter cu caracter, fără să lase spaţii) textele ‘aer’, ‘bucurie’, ‘caldura’, ‘dragoste’. Alegerea textelor afişate se va face aleator.
10.3 Soluţii Lecţia 10 – Exerciţiul 1: var x = 1+NrAleator(9); var y = 1+NrAleator(9);
210
if ( (x==1) || (x==10) || (y==1) || (y == 10) ) { Aprinde(x, y, ROSU); } else { Aprinde(x, y, ALBASTRU); }
Lecţia 10 – Exerciţiul 2: var poz = 1+NrAleator(9); var verticala = NrAleator(1); var i = 1; while (i<=10) { if (verticala == 1) Aprinde(poz, i); else Aprinde(i, poz); i = i+1; } 211
Lecţia 10 – Exerciţiul 3: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'p') { Aprinde(1+NrAleator(9), 1+NrAleator(9)); } } AscultaTaste(FunctieTaste);
Lecţia 10 – Exerciţiul 4: var culoare = Vector(4); culoare[0] = ALBASTRU; culoare[1] = GALBEN; culoare[2] = ROSU; culoare[3] = VERDE;
function FunctieTaste(ev) { var tasta = TastaApasata(ev); 212
if (tasta == 'p') { Aprinde(1+NrAleator(9), 1+NrAleator(9), culoare[NrAleator(3)]); } } AscultaTaste(FunctieTaste);
Lecţia 10 – Exerciţiul 5: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'm') { Stinge(x, 4); if (NrAleator(1) == 1) x = x+1; else x = x-1; Aprinde(x, 4, ALBASTRU); } } 213
var x = 4; Aprinde(x, 4, ALBASTRU); AscultaTaste(FunctieTaste);
Lecţia 10 – Exerciţiul 6: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'm') { Stinge(x, y); var dir = NrAleator(3); if (dir == 0) { y = y+1; } else if (dir == 1) { x = x+1; } else if (dir == 2) 214
{ y = y-1; } else //if (dir == 3) { x = x-1; } Aprinde(x, y, ALBASTRU); } }
var x = 4; var y = 4; Aprinde(x, y, ALBASTRU); AscultaTaste(FunctieTaste);
Lecţia 10 – Exerciţiul 7: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'b') { 215
var i = 1; while (i<=10) { Stinge(i, 1); Afiseaza(NrAleator(1), i, 1); i = i+1; } } } AscultaTaste(FunctieTaste);
Lecţia 10 – Exerciţiul 8: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'b') { Afiseaza(NrAleator(1), x, 1); x = x+1; } }
216
var x = 1; AscultaTaste(FunctieTaste);
Lecţia 10 – Exerciţiul 9: function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'n') { var i = 2; while (i <= 9) { Stinge(i, 6); i = i+1; } var nr_text = NrAleator(3); if (nr_text == 0) { Afiseaza('A', 4, 6); Afiseaza('e', 5, 6); Afiseaza('r', 6, 6); } else if (nr_text == 1) { Afiseaza('B', 2, 6); Afiseaza('u', 3, 6); 217
Afiseaza('c', 4, 6); Afiseaza('u', 5, 6); Afiseaza('r', 6, 6); Afiseaza('i', 7, 6); Afiseaza('e', 8, 6); } else if (nr_text == 2) { Afiseaza('C', 2, 6); Afiseaza('a', 3, 6); Afiseaza('l', 4, 6); Afiseaza('d', 5, 6); Afiseaza('u', 6, 6); Afiseaza('r', 7, 6); Afiseaza('a', 8, 6); } else //if (nr_text == 3) { Afiseaza('D', 2, 6); Afiseaza('r', 3, 6); Afiseaza('a', 4, 6); Afiseaza('g', 5, 6); 218
Afiseaza('o', 6, 6); Afiseaza('s', 7, 6); Afiseaza('t', 8, 6); Afiseaza('e', 9, 6); } } } AscultaTaste(FunctieTaste);
Lecţia 10 – Exerciţiul 10: var lungime = Vector(4); lungime[0] = 3; lungime[1] = 7; lungime[2] = 7; lungime[3] = 8;
var litere = Vector(32); litere[0] = 'a'; litere[1] = 'e'; litere[2] = 'r'; litere[8] = 'b'; litere[9] = 'u'; 219
litere[10] = 'c'; litere[11] = 'u'; litere[12] = 'r'; litere[13] = 'i'; litere[14] = 'e'; litere[16] = 'c'; litere[17] = 'a'; litere[18] = 'l'; litere[19] = 'd'; litere[20] = 'u'; litere[21] = 'r'; litere[22] = 'a'; litere[24] = 'd'; litere[25] = 'r'; litere[26] = 'a'; litere[27] = 'g'; litere[28] = 'o'; litere[29] = 's'; litere[30] = 't'; litere[31] = 'e';
var nr_text = NrAleator(3); 220
var index = 0;
var x; var y = 10; while (y >= 1) { x = 1; while (x <= 10) { Afiseaza(litere[nr_text*8 Afiseaza(litere[nr_tex t*8 + index], x, y); index = index+1; if ( index == lungime[nr_text] ) { nr_text = NrAleator(3); index = 0; } x = x+1; } y = y-1; }
221
11. Animaţii Animaţii 11.1 Lecţi Lecţie e A sosit timpul să să trecem la o lecţ lecţie foarte spectaculoasă spectaculoasă. Îţi Îţi voi ară arăta aici cât de uş uşor este să să faci o animaţ animaţie pe calculator. Prin programare, desigur. Îţi Îţi aminteş aminteşti programul fă făcut în lecţ lecţia trecută trecută? Cel în care aprindeam în culori alese aleator fiecare dintre cele 10×10 puncte ale ecranului nostru virtual. Ce-ar fi, însă însă, ca programul să să se ruleze singur, fă f ără să fie nevoie să să apeşi apeşi butonul “Execută programul”?
Hai să vedem vedem cum am putea face ca aprinderea ecranului în culori alese aleator să se se facă în în mod automat, o dată pe secundă . În primul rând, va trebui ca întreg programul anterior să îl punem în corpul unei funcţ funcţii. (Pe care o putem denumi, de exemplu, FunctieDesenare FunctieDesenare). ). Apoi va trebui ca în cadrul programului să s ă apelă apelăm funcţ funcţia predfinită predfinită StartAnimatie StartAnimatie,, careia îi vom da ca parametru numele funcţ funcţiei de desenare. Prin urmare, programul efectiv va consta în instrucţ instrucţiunea StartAnimatie(FunctieDesenare) StartAnimatie(FunctieDesenare).. Dacă Dacă te-ai gră grăbit să să testezi deja programul, ai observat cu siguranţă siguranţă că nu merge. Ecranul se aprinde în culori alese aleator, însă însă rămâne la fel şi după după o secundă secundă, şi după după două două, şi aş aşa mai departe. 222
Motivul este acela că că funcţ funcţia StartAnimatie StartAnimatie doar doar dă dă un imbold animaţ animaţiei, dar nu îi asigură asigură şi combustibilul pentru pentru a o susţ sus ţine în timp. Acest combustibil este asigurat prin apelul Animeaza(FunctieDesenare) chiar înainte de finalul funcţ Animeaza(FunctieDesenare) func ţiei FunctieDesenare.. Animeaza FunctieDesenare Animeaza este o altă altă funcţ funcţie predefinită predefinită, care are rolul de a-i spune calculatorului să să apeleze din nou funcţ funcţia FunctieDesenare FunctieDesenare,, după după ceva timp. Dacă Dacă testă testăm acum programul rezultat vom vedea deja rezultate. Şi chiar te rog s-o faci. Caseta de testare o vei gă g ăsi ceva mai jos. Ce pă părere ai? Animaţ Animaţia merge, nu-i aş aşa? Dar cum facem să să controlam timpul dintre cadre? Am zis mai sus că că dorim să să schimbam conţ conţinutul ecranului doar o dată dată pe secundă secundă. Aici te ajută ajută funcţ funcţia predefinită predefinită TimpScurs TimpScurs.. Ea primeş primeşte ca parametru un numă număr de milisecunde şi returnează returnează 1 (adevă (adevărat) dacă dacă s-a scurs acel timp de la ultima desenare, sau 0 (fals) în caz contrar. (O milisecundă milisecundă înseamnă înseamnă a mia parte dintr-o secundă secundă. Deci o secundă are 1000 de milisecunde.) milisecunde .) Prin urmare, dacă dacă dorim ca redesenarea ecranului să să se facă facă doar atunci când s-a scurs o secundă secundă (adică (adică 1000 de milisecunde) de la desenarea anterioară anterioară, va trebui ca în cadrul funcţ funcţiei FunctieDesenare FunctieDesenare desenarea ecranului să să o facem în corpul unui ifif în care testă testăm condiţ condiţia (TimpScurs(1000) == 1). 1). În continuare iată iată întregul program: // Program LUMINI DISCO // definitie functie: function FunctieDesenare() { 223
if (TimpScurs(1000)==1) { var i; var j; i = 1; while (i<=10) (i<=10) { j = 1; while (j<=10) (j<=10) { AprindeNrCul(j, i, NrAleator(7)); j = j+1; } i = i+1; } } Animeaza(FunctieDesenare); } // start program: StartAnimatie(FunctieDesenare); (Poţ (Poţi opri animaţia în animaţia în simulator apă apăsând sând butonul “Resetează afişajul”. Pentru ştergerea ecranului de 10x10 şi redesenarea grilajului va trebui să mai apeşi încă o dată acel buton.) buton. ) Îţi place animaţia rezultată? Acum gândeşte-te, gândeşte-te, te rog, cum ai face ca redesenarea ecranului să s ă se faca mai rapid (de exemplu, de 4 ori pe secundă secundă)?
Poate nu realizezi încă încă, dar ţi-am dat în această această lecţ lecţie o mină mină de aur. Ţie îţ ie îţii rămâne, însă însă, munca de a să s ăpa în ea pentru a scoate la iveală iveală aurul (sub forma a diverse animaţ animaţii sau joculeţe joculeţe interesante). interesante).
224
11.2 Exerciţii Lecţia 11 – Exerciţiul 1: Să se realizeze un program care face ca punctul (1, 1) din ecranul de 10x10 să “licăre” aprinzându-se şi stingându-se la fiecare o secundă. (Adică să stea aprins o secundă, stins o secundă, aprins o secundă, şi aşa mai departe.)
Lecţia 11 – Exerciţiul 2: Să se realizeze un program care face ca punctul (1, 1) să stea aprins o secundă şi să stea stins două secunde (iar apoi din nou aprins, şi aşa mai departe).
Lecţia 11 – Exerciţiul 3: Să se realizeze un program care face ca punctul (1, 1) să stea aprins o jumătate de secundă şi să stea stins două jumătăţi de secundă (iar apoi din nou aprins, şi aşa mai departe). Punctul (2, 1) se va aprinde în contratimp cu punctul (1, 1).
Lecţia 11 – Exerciţiul 4: Să se realizeze un program care aprinde pe rând toate punctele din ecranul de 10x10, începând din colţul stângasus şi mergând la dreapta (şi apoi în jos), fiecare punct aprinzându-se după ce trece o jumătate de secundă.
Lecţia 11 – Exerciţiul 5: Să se realizeze un program în care să îţi afişezi prenumele 225
(eventual prescurtat) pe linia cu y egal cu 3 din ecranul de 10x10, literele apărând pe rând una câte una de la stânga la dreapta şi timpul scurs între afişarea a două litere fiind de o secundă.
Lecţia 11 – Exerciţiul 6: Să se realizeze un program care face ca punctul (3, 3) să “licăre” prin aprinderea şi stingerea sa la fiecare 0.5 secunde. După apăsarea tastei ‘p’ licărirea va înceta (punctul rămânând fie aprins, fie stins, în funcţie de momentul apăsării tastei ‘p’), iar la o apăsare ulterioară licărirea va reîncepe.
Lecţia 11 – Exerciţiul 7: Să se realize un program care deplasează un punct negru pe ecranul de 10x10 începând din punctul (1, 1) şi mergând pe marginile ecranului în sens trigonometric cu viteza de un punct pe secundă.
Lecţia 11 – Exerciţiul 8: Să se realize un program care deplasează un punct negru pe ecranul de 10x10 începând din punctul (1, 1) şi mergând pe marginile ecranului în sens trigonometric cu viteza de un punct pe secundă. La apăsarea tastei ‘p’ se va pune “pauză” (adică punctul va “ îngheţa” în poziţia în care l-a găsit apasarea tastei), iar la o apăsare ulterioară a tastei ‘p’ deplasarea punctului pe ecran va continua de unde a fost oprită.
226
Lecţia 11 – Exerciţiul 9: Să se realizeze un program care plasează în partea de sus a ecranului de 10x10 o piesă de Tetris în formă de “L” şi o “coboară” cu câte o poziţie în jos la fiecare o secundă. Când piesa atinge linia de jos a ecranului, coborârea ei se va opri.
Lecţia 11 – Exerciţiul 10: Să se realizeze un program care plasează în partea de sus a ecranului de 10x10 o piesă de Tetris în formă de “T” şi o “coboară” cu câte o poziţie în jos la fiecare o secundă. La apăsarea tastei ‘a’ piesa se va deplasa la stânga cu o poziţie, iar la apăsarea tastei ‘d’ piesa se va deplasa la dreapta cu o poziţie. Când piesa atinge linia de jos a ecranului, coborârea ei se va opri.
11.3 Soluţii Lecţia 11 – Exerciţiul 1: function FunctieDesenare() { if (TimpScurs(1000)==1) { if (aprins==1) { Aprinde(1, 1); } 227
else { Stinge(1, 1); } aprins = 1-aprins; } Animeaza(FunctieDesenare); } // Programul propriu-zis: var aprins = 1; StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 2: function FunctieDesenare() { if (TimpScurs(1000)==1) { if (aprins==0) { Aprinde(1, 1); } else 228
{ Stinge(1, 1); } aprins = (aprins+1)%3; } Animeaza(FunctieDesenare); } var aprins = 0; StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 3: function FunctieDesenare() { if (TimpScurs(1000/2)==1) { if (aprins==0) { Aprinde(1, 1); Stinge(2, 1); } else { 229
Stinge(1, 1); Aprinde(2, 1); } aprins = (aprins+1)%3; } Animeaza(FunctieDesenare); } var aprins = 0; StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 4: function FunctieDesenare() { if (TimpScurs(1000/2)==1) { if (y>=1) { Aprinde(x, y); x = x+1; if (x==11) { y = y-1; 230
x = 1; } } } Animeaza(FunctieDesenare); }
var x = 1; var y = 10; StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 5: function FunctieDesenare() { if (TimpScurs(1000)==1) { if (x<=6) { Afiseaza(prenume[x-1], x, 3); } x = x+1; } 231
Animeaza(FunctieDesenare); }
var prenume = Vector(6); prenume[0] = 'F'; prenume[1] = 'l'; prenume[2] = 'o'; prenume[3] = 'r'; prenume[4] = 'i'; prenume[5] = 'n'; var x = 1; StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 6: function FunctieDesenare() { if (TimpScurs(1000/2)==1) { if (pornit==1) { if (aprins==1) { 232
Aprinde(3, 3); } else { Stinge(3, 3); } aprins = 1-aprins; } } Animeaza(FunctieDesenare); }
function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'p') { pornit = 1-pornit; } }
var aprins = 0; 233
var pornit = 1; StartAnimatie(FunctieDesenare); AscultaTaste(FunctieTaste);
Lecţia 11 – Exerciţiul 7: function FunctieDesenare() { if (TimpScurs(1000)==1) { Stinge(x, y); if (y==1) { x = x+1; if (x==11) { x = 10; y = 2; } } else if (x==10) { y = y+1; 234
if (y==11) { y = 10; x = 9; } } else if (y==10) { x = x-1; if (x==0) { x = 1; y = 9; } } else if (x==1) { y = y-1; if (y==0) { y = 1; x = 2; 235
} } Aprinde(x, y); } Animeaza(FunctieDesenare); }
var x = 0; var y = 1; StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 8: function FunctieDesenare() { if (TimpScurs(1000)==1) { if (pornit==1) { Stinge(x, y); if (y==1) { x = x+1; 236
if (x==11) { x = 10; y = 2; } } else if (x==10) { y = y+1; if (y==11) { y = 10; x = 9; } } else if (y==10) { x = x-1; if (x==0) { x = 1; y = 9; 237
} } else if (x==1) { y = y-1; if (y==0) { y = 1; x = 2; } } Aprinde(x, y); } } Animeaza(FunctieDesenare); }
function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'p') { 238
pornit = 1-pornit; } }
var x = 0; var y = 1; var pornit = 1; AscultaTaste(FunctieTaste); StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 9: function FunctieDesenare() { if (TimpScurs(1000)==1) { // Sterge piesa de la pozitia anterioara: Stinge(x, y); Stinge(x, y-1); Stinge(x, y-2); Stinge(x+1, y-2); // Coboara piesa cu o pozitie: y = y-1; 239
if (y == 3) // Daca piesa a ajuns jos: { // Opreste animatia: StartAnimatie(); } // Afiseaza piesa la noua pozitie: Aprinde(x, y); Aprinde(x, y-1); Aprinde(x, y-2); Aprinde(x+1, y-2); } Animeaza(FunctieDesenare); }
var x = 5; var y = 11; StartAnimatie(FunctieDesenare);
Lecţia 11 – Exerciţiul 10: function FunctieDesenare() { if (TimpScurs(1000)==1) 240
{ // Sterge piesa de la pozitia anterioara: Stinge(x, y); Stinge(x+1, y); Stinge(x+2, y); Stinge(x+1, y-1); // Coboara piesa cu o pozitie: y = y-1; if (y == 2) // Daca piesa a ajuns jos: { // Opreste animatia: StartAnimatie(); // Opreste ascultarea tastelor: AscultaTaste(); } // Afiseaza piesa la noua pozitie: Aprinde(x, y); Aprinde(x+1, y); Aprinde(x+2, y); Aprinde(x+1, y-1); } Animeaza(FunctieDesenare); 241
}
function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'a') { // Sterge piesa de la pozitia curenta: Stinge(x, y); Stinge(x+1, y); Stinge(x+2, y); Stinge(x+1, y-1); // Muta piesa cu o pozitie la stanga: x = x-1; // Afiseaza piesa la noua pozitie: Aprinde(x, y); Aprinde(x+1, y); Aprinde(x+2, y); Aprinde(x+1, y-1); } else if (tasta == 'd') { 242
// Sterge piesa de la pozitia curenta: Stinge(x, y); Stinge(x+1, y); Stinge(x+2, y); Stinge(x+1, y-1); // Muta piesa cu o pozitie la dreapta: x = x+1; // Afiseaza piesa la noua pozitie: Aprinde(x, y); Aprinde(x+1, y); Aprinde(x+2, y); Aprinde(x+1, y-1); } }
var x = 5; var y = 11; StartAnimatie(FunctieDesenare); AscultaTaste(FunctieTaste);
243
12. Clickuri 12.1 Lecţie Iată că am ajuns la ultima lecţie din prima parte a acestei cărţi. Am parcurs un drum destul de lung şi interesant până aici — ai deprins bazele programării structurate, ai învăţat să afişezi lucruri pe ecran, să asculţi taste şi să faci animaţii. E timpul să intrăm ca-n brânză într-o nouă lecţie — folosirea soarecelui :-). Vei vedea cum poţi asculta click-urile făcute cu mouse-ul pe ecranul nostru virtual şi cum poţi răspunde la ele. Dacă lecţia de utilizare a tastaturii nu ţi-a pus probleme, nici folosirea mouse-ului n-o va face. Şi aici ideea este similară:
(1) construiesc o funcţie în care îi spun calculatorului ce să facă în cazul în care s-a facut click pe ecran; (<-- funcţia aceasta va avea ca parametru o variabilă ce va conţine informaţii legate de evenimentul ce s-a produs (cum ar fi, de exemplu, punctul de pe ecranul virtual unde s-a făcut click)) (2) şi apoi îi spun calculatorului să asculte apă sarea mouseului şi să apeleze automat funcţia creată de mine la pasul anterior. (<-- calculatorul se va ocupa (în mod automat) şi de construirea şi trimiterea parametrului către funcţia mea) Mai precis, pentru a folosi mouse-ul într-un program care să ruleze în simulator trebuie o structură de program de genul următor: 244
function FunctieMouse(ev) { … } AscultaMouse(FunctieMouse); În loc de FunctieMouse puteam să scriu orice nume valid de funcţie. Singura constrângere este că această funcţie trebuie să accepte un parametru (pe care l-am denumit ev – de la eveniment ). Bun, dar ce scriu în funcţia FunctieMouse? Păi, scriu acţiunile pe care le vreau executate automat atunci când se detectează un click de mouse (realizat cu butonul din stânga). Dar dacă vreau să ştiu în care punct dintre cele 10×10 din ecranul meu virtual s-a făcut click? Pentru asta am construit funcţiile predefinite XMouse şi YMouse. Le voi apela în funcţia FunctieMouse, le voi da ca parametru pe ev (parametrul primit de FunctieMouse), iar ele îmi vor returna coordonatele (pe axa x , respectiv pe axa y ) punctului de pe ecranul virtual în care se găsea mouse-ul atunci când s-a făcut click. Aşa că la începutul funcţiei FunctieMouse va trebui să am ceva de genul următor: var xm = XMouse(ev); var ym = YMouse(ev); Apoi voi putea folosi cele două variabile – de exemplu, pentru a aprinde punctul de la coordonatele respective de pe ecranul virtual: Aprinde(xm, ym); Şi uite aşa a rezultat fără să ne dăm seama un mic program care ne permite să-l desenam pe Mickey nu prin instrucţiuni 245
(cum am făcut în prima lecţie), ci făcând click cu mouse-ul în punctele de pe ecran: function FunctieMouse(ev) { var xm = XMouse(ev); var ym = YMouse(ev); Aprinde(xm, ym); } AscultaMouse(FunctieMouse); Testează programul în simulator şi lasă artistul din tine să se exprime liber! :-)
Dacă ai ajuns până aici, te felicit! Eşti programator. Acum e timpul să pășești solid pe conceptele învățate, să-ți deschizi aripile creativității și să îți iei avânt.
12.2 Exerciţii Lecţia 12 – Exerciţiul 1: Să se realizeze un program care la un click cu mouse-ul aprinde punctul (1, 1) pe ecranul de 10x10, la un click ulterior îl stinge, şi aşa mai departe.
Lecţia 12 – Exerciţiul 2: Să se realizeze un program care la fiecare click cu mouse-ul aprinde (dacă era stinsă) sau stinge (dacă era aprinsă) prima coloană din stânga a ecranului de 10x10.
246
Lecţia 12 – Exerciţiul 3: Să se realizeze un program care la fiecare click cu mouse-ul pe ecranul de 10x10 aprinde punctul respectiv dacă era stins (şi îl stinge, în caz contrar).
Lecţia 12 – Exerciţiul 4: Să se realizeze un program care la fiecare click cu mouse-ul aprinde câte un punct din ecranul de 10x10, începând cu punctul din stânga-jos şi mergând spre dreapta şi apoi în sus.
Lecţia 12 – Exerciţiul 5: Să se realizeze un program care la fiecare click pe un punct situat pe bordura ecranului de 10x10 aprinde toate punctele de pe bordură (dacă erau stinse; sau le stinge, dacă erau aprinse).
Lecţia 12 – Exerciţiul 6: Să se realizeze un program care la fiecare click cu mouse-ul într-unul din punctele ecranului de 10x10 afişează în acel punct numărul coloanei (sau simbolul ‘x’ pentru coloana a zecea).
Lecţia 12 – Exerciţiul 7: Să se realizeze un program care la fiecare click cu mouse-ul într-unul din punctele ecranului de 10x10 va aprinde acel punct în culoarea curentă. Iniţial culoarea curentă este 247
ROSU, dar ea se poate modifica prin apăsarea tastelor ‘a’ (ALBASTRU), ‘g’ (GALBEN) sau ‘r’ (ROSU).
Lecţia 12 – Exerciţiul 8: Să se realizeze un program care la fiecare click cu mouse-ul în interiorul ecranului de 10x10 aprinde punctul respectiv în culoarea VERDE. În cazul în care click-ul s-a făcut în afara ecranului, întreg ecranul va fi aprins in culoarea ROSU.
Lecţia 12 – Exerciţiul 9: Să se realizeze un program care aprinde cu NEGRU un punct ales aleator pe ecranul de 10x10 şi îl tine aprins o secundă (după care aprinde un alt punct aleator, şi aşa mai departe). La click cu mouse-ul pe punctul negru, acesta se va colora în VERDE, iar la click în alta parte punctul se va colora în ROSU.
Lecţia 12 – Exerciţiul 10: Să se realizeze un program care la fiecare click cu mouse-ul pe ecranul de 10x10 va porni o “picatură de ploaie” sub forma unui punct ALBASTRU ce va “curge” cu viteza de două puncte pe secundă de sus în jos pe coloana unde s-a făcut click.
12.3 Soluţii Lecţia 12 – Exerciţiul 1: function FunctieMouse(ev) 248
{ if (aprinde == 1) { Aprinde(1, 1); aprinde = 0; } else { Stinge(1, 1); aprinde = 1; } }
var aprinde = 1; AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 2: // Definire funcţii: function FunctieMouse(ev) { var y; if (aprinde == 1) 249
{ y = 1; while (y<=10) { Aprinde(1, y); y = y+1; } aprinde = 0; } else { y = 1; while (y<=10) { Stinge(1, y); y = y+1; } aprinde = 1; } } // Program propriu-zis: var aprinde = 1; 250
AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 3: function FunctieMouse(ev) { var xm = XMouse(ev); var ym = YMouse(ev); // Daca s-a facut click pe un punct // din ecranul de 10x10: if ( (xm>=1) && (xm<=10) && (ym>=1) && (ym<=10)) { // Daca punctul era aprins, il sting: if (aprins[10-ym][xm-1] == 1) { Stinge(xm, ym); aprins[10-ym][xm-1] = 0; } // Altfel, il aprind: else { Aprinde(xm, ym); 251
aprins[10-ym][xm-1] = 1; } } }
// Memorez starea (aprins sau stins) tuturor // punctelor de pe ecranul de 10x10 intr-o matrice: var aprins = Matrice(10, 10); // Initializez toate elementele matricii // "aprins" cu valoarea 0 (adica "fals"): var linie = 0; var coloana; while (linie<10) { coloana = 0; while (coloana<10) { aprins[linie][coloana] = 0; coloana = coloana+1; } linie = linie+1; } 252
AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 4: function FunctieMouse(ev) { // La fiecare click cu mouse-ul // aprinde punctul curent: Aprinde(x, y); // Si apoi deplaseaza-te la dreapta: x = x+1; if (x==11) { // Iar cand s-a terminat linia, treci // cu o linie mai sus, la capatul din stanga: x = 1; y = y+1; if (y==11) { // Si cand ai terminat tot ecranul // inceteaza ascultarea mouse-ului: AscultaMouse(); 253
} } }
// Stabileste pozitia primului punct ce va fi aprins: var y = 1; var x = 1; // Si apoi porneste ascultarea evenimentelor mouse-ului: AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 5: function FunctieMouse(ev) { // La fiecare click cu mouse-ul // vezi daca e pe bordura: var xm = XMouse(ev); var ym = YMouse(ev) if ( (ym == 1) || (xm == 10) || (ym == 10) || (xm == 1) ) { // Si daca da, parcurge toate punctele // de pe bordura si aprinde-le (daca erau 254
// stinse) sau stinge-le (daca erau aprinse): var i = 1; while (i <= 10) { if (stinsa == 1) { Aprinde(i, 1); Aprinde(10, i); Aprinde(i, 10); Aprinde(1, i); } else { Stinge(i, 1); Stinge(10, i); Stinge(i, 10); Stinge(1, i); } i = i+1; } // Apoi memoreaza faptul ca starea bordurii // s-a modificat (adica in variabila "stinsa" 255
// pune 0 daca era 1, sau pune 1 daca era 0): stinsa = 1-stinsa; } }
// Initial bordura este stinsa: var stinsa = 1; // Porneste ascultarea evenimentelor mouse-ului: AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 6: function FunctieMouse(ev) { // La fiecare click cu mouse-ul // vezi mai intai daca e pe ecranul de 10x10: var xm = XMouse(ev); var ym = YMouse(ev); if ( (xm >= 1) && (xm <= 10) && (ym >= 1) && (ym <= 10) ) { if (xm < 10) { 256
Afiseaza(xm, xm, ym); } else { Afiseaza('x', xm, ym); } } }
AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 7: function FunctieMouse(ev) { // La fiecare click cu mouse-ul // vezi mai intai daca e pe ecranul de 10x10: var xm = XMouse(ev); var ym = YMouse(ev); if ( (xm >= 1) && (xm <= 10) && (ym >= 1) && (ym <= 10) ) { Aprinde(xm, ym, culoarea_curenta); 257
} }
function FunctieTaste(ev) { var tasta = TastaApasata(ev); if (tasta == 'a') { culoarea_curenta = ALBASTRU; } else if (tasta == 'g') { culoarea_curenta = GALBEN; } else if (tasta == 'r') { culoarea_curenta = ROSU; } }
var culoarea_curenta = ROSU; AscultaMouse(FunctieMouse); 258
AscultaTaste(FunctieTaste);
Lecţia 12 – Exerciţiul 8: function FunctieMouse(ev) { // La fiecare click cu mouse-ul // vezi mai intai daca e pe ecranul de 10x10: var xm = XMouse(ev); var ym = YMouse(ev); if ( (xm >= 1) && (xm <= 10) && (ym >= 1) && (ym <= 10) ) { Aprinde(xm, ym, VERDE); } // Si daca nu e, aprinde tot ecranul cu ROSU else { var x; var y = 1; while (y <= 10) { x = 1; 259
while (x <= 10) { Aprinde(x, y, ROSU); x = x+1; } y = y+1; } } }
AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 9: function FunctieDesenare() { if (TimpScurs(1000) == 1) { // Dupa ce a trecut o secunda // stinge punctul: Stinge(x, y); // Si apoi alege-i aleator alte coordonate: x = 1+NrAleator(9); 260
y = 1+NrAleator(9); // Si afiseaza-l la noua pozitie: Aprinde(x, y); } Animeaza(FunctieDesenare); }
function FunctieMouse(ev) { // La fiecare click cu mouse-ul // vezi daca s-a facut click pe punctul aleator: var xm = XMouse(ev); var ym = YMouse(ev); if ( (xm == x) && (ym == y) ) { Aprinde(x, y, VERDE); } // Daca nu e, aprinde-l cu ROSU else { Aprinde(x, y, ROSU); } 261
}
// Stabileste coordonatele initiale ale punctului aleator: var x = 1+NrAleator(9); var y = 1+NrAleator(9); // Porneste animatia: StartAnimatie(FunctieDesenare); // Porneste ascultarea evenimentelor mouse-ului: AscultaMouse(FunctieMouse);
Lecţia 12 – Exerciţiul 10: function FunctieDesenare() { // Dupa ce a trecut o jumatate de secunda: if (TimpScurs(1000/2) == 1) { // Parcurge toate coloanele ecranului: var i = 1; while (i<=10) { // Sterge picatura de pe coloana i: Stinge(i, y_picatura[i-1]); 262
// Coboara picatura cu o pozitie, // daca n-a ajuns pe linia 0: if (y_picatura[i-1] != 0) { y_picatura[i-1] = y_picatura[i-1] - 1; } // Aprinde picatura la noua pozitie, // daca n-a iesit din ecran: if (y_picatura[i-1] > 0) { Aprinde(i, y_picatura[i-1], ALBASTRU); } i = i+1; } } Animeaza(FunctieDesenare); }
function FunctieMouse(ev) { // La fiecare click cu mouse-ul // vezi daca s-a facut click pe ecran: 263
var xm = XMouse(ev); var ym = YMouse(ev); if ( (xm >= 1) && (xm <= 10) && (ym >= 1) && (ym <= 10)) { // Daca nu era picatura pe coloana xm: if (y_picatura[xm-1] == 0) { // Pune o picatura virtuala pe coloana xm: y_picatura[xm-1] = 11; } } }
// Initial toate picaturile de ploaie sunt pe linia 0: var y_picatura = Vector(10); var i = 0; while (i<10) { y_picatura[i] = 0; i = i+1; } 264
// Porneste animatia: StartAnimatie(FunctieDesenare); // Porneste ascultarea evenimentelor mouse-ului: AscultaMouse(FunctieMouse);
265
Partea a II-a: Noţiuni de programare adevărată Te felicit dacă ai ajuns până aici! Ai văzut în prima parte că programarea nu e atât de înfiorătoare pe cât credeai, dar nici banal de simplă (după cum ţi-au dovedit-o, poate, unele dintre exerciţii). După ce în prima parte te-ai jucat mai mult cu instrucţiunile şi cu modul de organizare a lor pentru a construi algoritmi, în această a doua parte vei învăţa să organizezi datele în structuri de date. Pentru experimentarea noţiunilor învăţate în primele două module din această parte te vei putea folosi de acelaşi simulator JavaScript pe care l-ai folosit şi în prima parte a cătţii. Tot în această parte vei învăţa (în ultimul modul, mai precis) şi cum se face programarea adevărată în prezent. Vei învăţa ce este un compilator, ce este un mediu de programare şi cum le poţi folosi pentru a construi programe ca cele pe care le utilizezi acum. De asemenea, vei învăţa pe ce se bazează şi cum arată principalele limbaje de programare şi ce loc ocupă ele în implementarea aplicaţiilor actuale.
266
Modulul IV Cuvinte Acest modul este dedicat “cuvintelor”; şi la fel cum cuvintele sunt formate din litere, vei vedea aici cum se î mbină noţiunile învăţate în prima parte a cărţii pentru a construi noţiuni mai complexe. Vei învăţa ce sunt pointerii şi tipurile de date, iar apoi vei învăţa să operezi cu şiruri de caractere şi cu structuri de date elementare, formate din îmbinarea mai multor variabile. Deja lucrurile încep să devină serioase. Bucură-te, căci în acest modul vei face paşi serioşi pe drumul tău către a -ţi construi o minte de programator adevărat.
267
13. Pointeri Mulţi programatori începători suferă de pointerofobie — adică o teamă inexplicabilă de pointeri. Şi cum nu se poate scăpa de teama de “Bau-bau”-ul din dulap decât mergând la dulap, deschizându-l şi văzând că nu e acolo nimic de speriat, în acest capitol te voi lua de mână şi te voi duce la “dulapul” cu pointeri. OK, dar mai întâi: Ce sunt pointerii (căci poate nici macar n-ai auzit vreodata de ei)? Şi la ce sunt ei buni? Încep cu î ntrebarea “La ce sunt buni?” — ca să înţelegi că sunt foarte importanţi şi că merită să studiezi cu mare atenţie explicaţiile ce urmează.
La ce sunt buni pointerii? Foloseşti Google şi Facebook? Ai văzut cât de repede caută informaţiile solicitate (site-uri, conturi) printre milioanele de informaţii disponibile? Cum crezi că se face asta? Oare la fiecare căutare cerută de tine Google îşi parcurge toate site-urile pe care le are înregistrate şi le compară unul câte unul cu textul căutat? N-ar fi prea plăcut să se întâmple aşa. Ţi-ar trebui răbdare. Multă răbdare. Chiar extrem de multă răbdare.
268
Secretul căutarilor Google (ca şi al oricărei aplicaţii serioase pe care o foloseşti (inclusiv aplicaţia din care citeşti rândurile astea, dacă acum citeşti versiunea electronică a cărţii)) sunt structurile de date. Adică modalităţi de organizare isteată a datelor. Adică (mai pe româneşte (într-o română specifică celor care deja ştiu bazele programării)) e vorba despre modalităţi de grupare isteaţă a variabilelor. Două modalităţi de grupare a variabilelor ai văzut deja în capitolul despre vectori şi matrici. Da, şi vectorii şi matricile sunt structuri de date şi ne permit să facem multe lucruri. Dar modalitatea prin care ele grupează variabilele e destul de limitată — le “aşeaza” pe linii sau pe linii şi coloane. Dar dacă am vrea să grupăm variabilele în moduri oarecare? Dacă am vrea, de exemplu, să le legăm între ele aşa cum oraşele dintr-o ţară sunt legate prin drumuri? Ne-ar trebui o modalitate de a “trasa drumuri” între variabile — sau de a spune “variabila aceasta e legată de variabila aceea”. Pointerii ne permit sa facem acest lucru. Datorită lor cresc arbori pe tărâmul programării. :-) (Nu e (chiar) o glumă. Arborii sunt o structură de date fără de care chiar nu îmi imaginez cum ar fi arătat utilizarea calculatoarelor în prezent. (Probabil mai mult ne-am fi uitat la ele şi le-am fi şters de praf decât să le utilizăm efectiv.))
Ce sunt pointerii? Am spus deja că pointerii sunt un fel de “legături” între variabile. Adică printr-un pointer putem spune că “variabila A e legată de variabila B” sau că “există un drum care leagă oraşul A de oraşul B” sau că “dosarul A se găseşte plasat în interiorul dosarului B”. 269
Deci ce e un pointer, mai precis? Uite figura asta:
Pointerul este “săgeata” care arată către variabila B. Uau! N-a fost greu, nu? Pointerii sunt “săgeti”, deci. (Nu e de mirare, atunci, că atât de mulţi programatori în devenire sunt speriaţi de ei.) Serios vorbesc. Aşa “trăiesc” pointerii în mintea mea. Dacă nu i-aş “vedea” sub formă grafică chiar nu ştiu dacă aş fi în stare să lucrez cu ei. (Caci eu nu sunt “matematician”, ci “desenator” (deci nu lucrez cu noţiuni abstracte, ci cu forme concrete).) Acum vine partea a doua: Cum “desenezi” o “săgeată” din asta într-un program? Ei bine… printr-o variabilă! (Şi uite cum iar te-am băgat în ceaţă…)
Cum implementezi practic un pointer? Am zis până aici că un pointer este un fel de săgeată care arată către o variabilă şi că el poate fi realizat practic într-un program cu ajutorul unei (alte) variabile. Deci e cazul să reluăm (un pic mai aprofundat acum) noţiunea de variabilă. Ce era o variabilă ?… (Îţi aminteşti?) O variabilă e o entitate (ca să nu zic “chestie”) care ţine în ea o valoare şi are un nume. 270
În programul pe care îl scriu îi folosesc numele, dar de fapt lucrez cu valoarea stocată în ea. De exemplu, dacă am variabila numită a var a; atunci când scriu a = 3; înlocuiesc valoarea care se găsea în variabila a cu valoarea 3. Iar dacă apoi scriu a = a+4; mai intâi calculez (valoarea expresiei din dreapta operatorului de atribuire, adică calculez) valoarea expresiei “a+4” (iar rezultatul este “(valoarea stocată în variabila a) plus (valoarea 4)”), adică 7 (dacă ţin cont de faptul că prin instrucţiunea precedentă (“a = 3;”) am pus în variabila a valoarea 3) şi apoi (realizez atribuirea “a = 7”, adică) în variabila a stochez valoarea rezultată prin evaluarea expresiei, adică valoarea 7; deci în urma executării acestei instrucţiuni variabila numită a va avea valoarea 7. Această instrucţiune (“a = a+4”) este interesantă pentru că îţi arată ambele moduri în care o variabilă poate fi folosită întrun program, adică pentru: – citire valoare (atunci când numele variabilei apare în expresii (iar la evaluarea acestor expresii numele va fi înlocuit automat cu valoarea conţinută de variabilă)) – scriere valoare (atunci când numele variabilei apare în stânga operatorului de atribuire (iar la executarea operaţiei de atribuire valoarea veche a variabilei va fi înlocuită cu noua valoare)). Hai să revenim. Ziceam că o variabilă e o chestie (entitate, pardon) care ţine în ea o valoare şi care are un nume… 271
Completez (în urma discuţiei anterioare) această definiţie cu: … pe care îl pot folosi fie pentru a citi valoarea memorat ă în variabilă , fie pentru a scrie o nouă valoare în variabilă . (Sau, altfel spus, variabila este ceva care conţine o valoare şi care are un nume cu ajutorul căruia poate fi accesată acea valoare.) Buuun, dar astea le ştiai deja, nu? Şi cred că mai ştiai şi că variabilele sunt depozitate în memoria calculatorului. De fapt, în realitate ele sunt mici “bucătele” din memoria calculatorului. Ceea ce probabil că nu ştiai până acum este faptul că memoria calculatorului seamana foarte mult cu un vector uriaş. Adică este formată din foarte multe bucăţi mici (numite locaţii de memorie) aşezate în linie una lângă alta şi care pot fi accesate prin indici. Concret, dacă memoria calculatorului ar fi un vector numit mem (de 1000 de elemente, să zicem (deşi în realitate un număr de la un miliard în sus ar fi mai veridic)) var mem = Vector(1000); atunci prima locaţie de memorie ar fi mem[0], ultima locaţie de memorie (adică cea de-a o mia locaţie) ar fi mem[999] şi orice locaţie de memorie ar putea fi accesată printr-un indice, astfel: mem[i] (unde în cazul ăsta indicele i poate avea valori între 0 şi 999 inclusiv). Heeei! Dar dacă-i aşa, asta nu înseamnă că variabilele mai au şi o altă caracteristică în afară de valoare şi nume — şi anume indicele la care se găsesc plasate în memorie? Pai da. Am zis că memoria e ca un vector în care locaţiile de memorie sunt elemente (deci pot fi accesate prin indici). Şi am mai zis că variabilele sunt în realitate locaţii de memorie. 272
Deci atunci când definesc o variabila a, ea va fi plasată în (vectorul) memorie la o anumită poziţie (adică la un anumit indice în vector). Indicele din memorie la care este plasată variabila a poartă numele de “adresa” variabilei a. Aşadar, variabila este o entitate caracterizată de: – valoarea stocata în ea (la momentul curent din execuţia programului) – numele (folosit pentru a-i accesa valoarea (fie pentru citire, fie pentru scriere)) – şi adresa la care este plasată variabila în memoria calculatorului (care este organizată ca un vector în care elementele pot fi accesate prin indici (sau adrese)). Oof, cam lungă discuţia, nu? Probabil ai şi uitat despre ce vorbeam. Era vorba despre pointeri şi despre faptul că un pointer este o “săgeată” care “arată către” o variabilă. Şi ziceam că poţi implementa un pointer folosind o variabilă.
Cum implementezi practic un pointer? (pe bune, acum) Faptul că variabila are şi adresă e ca şi cum ar avea un al doilea nume. Să zicem că variabila a este plasată în memorie la adresa 10. Atunci mă pot referi la ea în doua moduri echivalente: – 1) variabila numită a – 2) variabila care se găseşte în memorie la adresa 10. Pentru a putea vizualiza lucrurile, să zicem că memoria ar fi un vector numit mem (aşa cum am discutat mai sus) şi că mai am în program încă două variabile: variabila b, plasată la 273
adresa 13, variabila c, plasată la adresa 7 şi variabila p, plasată la adresa 2. Adică ceva de genul următor:
Asta înseamnă că atunci când accesez variabila a accesez de fapt locaţia de memorie de la adresa 10, adică elementul mem[10] din vectorul mem. Similar, variabila b este de fapt variabila c este elementul mem[7], elementul mem[2].
elementul mem[13], iar variabila p este
(Numele variabilelor este doar o simplificare pentru programator (căci în realitate calculatorul foloseşte adresele variabilelor pentru a accesa locaţiile de memorie corespunzătoare lor).) Hei, dar era vorba despre pointeri şi despre cum poţi face un pointer folosind o variabilă! Uită-te, te rog, la variabila p. E o variabilă ca oricare alta, doar că vreau s-o fac pointer — adică “săgeată” care să-mi “arate către” o altă variabilă (să zicem, către variabila a ). Cum pot face asta? Stocând în p adresa variabilei a , adică 10. Voilà !
274
Dar dacă voiam ca pointerul p să “arate către” variabila b? Evident, puneam în el adresa variabilei b, adică 13.
OK. Ai văzut cum poţi face ca pointerul p să arate către o variabilă. Dar cum poţi face să foloseşti acea variabilă? (Adică s-o accesezi.) Să zicem că vrei să pui în variabila c valoarea variabilei “pointate” de catre pointerul p. Păi, simplu: c = mem[p]; Dacă p “pointa” către a (deci în p era adresa variabilei a, adică 10), efectul intrucţiunii anterioare este “c = mem[10]” (adică “c = a”, practic). Iar dacă p pointa către b (adică în p era valoarea 13 (care este adresa lui b)), efectul instrucţiunii anterioare este “c = mem[13]” (adică, practic, “c = b”). Deci aşa faci citirea . Dar daca vrei să scrii î n variabila “pointată” de către pointerul p valoarea variabilei c? Tot simplu (dacă ai înţeles cum funcţionează vectorii): mem[p] = c; Dacă p pointa către a (deci în p era 10), efectul instrucţiunii e “mem[10] = c” (adică practic “a = c”). Iar dacă p pointa către b (deci în p era 13), efectul instrucţiunii e “mem[13] = c” (adică practic “b = c”). 275
În concluzie Pointerii din programare sunt o banalitate. Dar asta e valabil doar dacă ai înţeles bine ce e o variabilă şi mai ales ce e şi cum se foloseşte un vector de variabile. Deci nu pointerii sunt dificili, ci dificil e s ă îţi antrenezi mintea să lucreze cu variabile nu folosindu-le numele, ci folosindu-le adresele. Adică să nu le mai identifici folosind nume, ci să le identifici folosind numere. Altfel spus, dacă vrei să te împrieteneşti cu pointerii, e cazul să-ţi bagi variabilele la “puşcărie” şi să le vorbeşti pointerilor (care sunt un fel de gardieni docili, dar tăcuti, încruntaţi şi fără simţul umorului) pe limba lor — adică nu le spune despre “deţinutul” Andreea, ci despre “deţinutul” din celula 137. Javascripticul nostru limbaj de programare minimalist nu permite folosirea (explicită a) pointerilor (aşa cum o permit limbaje ca C şi C++), dar îţi poti face “antrenamentul” foarte bine folosind un vector mem (aşa cum am discutat mai sus). De exemplu, spune-mi, te rog, cât face suma variabilelor a şi b în urma rulării programului următor: var mem = Vector(100); // Variabila a e plasata in mem la adresa 75. // Variabila b e plasata in mem la adresa 13. var pointer; pointer = 13; mem[pointer] = 75; pointer = pointer+62; mem[pointer] = 13; mem[pointer] = mem[pointer] * 100; // Deci cat este acum a+b?… Recomand foaie şi pix. (Căci dacă reuseşti să înveţi pointerii fără elementele astea două, atunci poate că eu sunt cel care are nevoie de lecţii de la tine, şi nu invers.) 276
14. Tipuri Tot despre variabile vom vorbi și în acest capitol. Ele sunt elemente de bază ale programării, căci în esență programele sunt colecții de variabile și instrucțiuni ce operează cu aceste variabile. Am văzut până aici că o variabilă este ”ceva” care are: - nume - valoare - adresă Deci dacă memoria calculatorului ar fi un perete plin de sertare, o variabilă ar fi un astfel de ”sertar”. Numele variabilei ar fi eticheta lipită pe sertar, valoarea ar fi ceea ce se găsește în sertar (conţinutul lui), iar adresa mi-ar indica în mod unic unde este poziționat sertarul (de exemplu, adresa ar putea fi formată din numărul liniei şi al coloanei pe care se găseşte sertarul).
Valoare Prima oară când ți-am vorbit despre variabile m-am concentrat pe nume și valoare. Apoi (în lecția trecută) m-am concentrat pe adresă. Acum voi intra în detalii în ceea ce priveşte valoarea. Da, variabila are valoare. (Și nu este nevoie să asculți manele ca să î nțelegi asta). :-) 277
Dar ce înseamnă valoare în acest context? Înseamnă un număr sau orice lucru care poate fi redus la un număr. De exemplu, atât literele cum ar fi ’F’) cât şi simbolurile (cum ar fi ’&’) sunt codificate intern în calculator sub forma unor numere. (Caută pe internet informații despre codul ASCII și vei vedea concret ce valori corespund literelor și simbolurilor). Dar și numerele pot fi de mai multe tipuri: narurale, întregi, raționale, reale etc.. Care dintre aceste tipuri de numere pot constitui valori valabile pentru variabile? Răspunsul e: în realitate variabilele pot avea ca valori doar numere naturale. Serios?! Adică un calculator nu știe să calculeze rezultatul operației 1/4? Ba da, știe. Î ți va da ca rezultat numărul rațional 0.25. Sau nu ştie să calculeze rezultatul operației 1 - 4? Ba da, știe și asta. Îți va da ca rezultat numărul întreg -3. Păi, atunci de ce am zis că valorile variabilelor pot fi doar numere naturale? Fiincă asta e realitatea a ceea ce se întâmplă fizic în calculator. Se memorează doar numere naturare (0, 1, 2, 3, .... 123, 124,...... 65536, ....). Dar unele instrucțiuni sunt făcute astfel încât să interpreteze anumite numere ca și cum ar fi întregi sau raționale. Îți voi arăta exemple un pic mai târziu. Mai întâi, hai să vedem cum se memorează în calculator numerele naturale.
Numere in baza 10 Tu ce înțelegi atunci când auzi cuvântul ”număr”? De exemplu, 75 este un număr? 278
Ai zis ”da”? Probabil că te gândești la cantitatea exprimată de simbolurile grafice ”75”. Adică ”șaptezeci și cinci”. Dar ”7” și ”5” sunt cifre. În particular, și ele reprezintă numere – numărul ”șapte” și numărul ”cinci”. Alăturarea a două cifre formează prin convenție (că așa ai învățat la școală), un nou număr. Acest nou număr este egal cu numărul exprimat de cifra din stânga mărit de zece ori și adunat cu cifra din dreapta. Matematic, ar fi așa: numărul ab = a*10+b. Dacă ar fi vorba de 3 cifre, numărul rezultat din alăturarea lor ar fi egal cu numărul reprezentat de cifra cel mai din stânga mărit de o sută de ori și adunat cu numărul reprezentat de cifra din mijloc mărit de zece ori şi adunat cu numărul reprezentat de cifra din dreapta. Adică numărul abc = a*100+b*10+c. Pentru patru cifre ar fi așa: abcd = a*1000+b*100+c*10+d Adică: abcd = a*103+b*102+c*101+d*100 (Prin ”103” am notat 10 la puerea a 3-a, adică ”10*10*10”) Dacă am avea oricâte cifre, am putea generaliza formula asta pentru a putea calcula valoarea numărului rezultat din alăturarea acelor cifre. Hai să ne imaginăm că o variabilă ar putea memora în ea doar numere formate din două cifre. Câte numere diferite ar putea ea memora, deci? Cât ai zis? Nouăzeci de numere? 10, 11, 12, ....99. E adevărat că de la 10 la 99 sunt nouăzeci de numere, dar nu ne oprește nimeni să considerăm variabile și numerele 01, 279
02, 03, 04, 05, 06, 07, 08, 09. Ce dacă au un 0 în față? Zerourile din față nu contribuie cu nimic la valoarea numărului. De exemplu: 07 = 0*10+7 = 7 (Sau chiar: 007=0*100+0*10+7=7). Deci câte numere diferite pot memora într-o variabilă de două cifre? O sută de numere (100 = 102). Dar într-o variabilă de trei cifre? O mie de numere (1000 = 103). Dar pe 4 cifre? Zece mii de numere (10000 = 104). Este clar, deci, că într-o variabilă de n cifre pot memora 10 n numere, da?
Numere întregi Bun, dar cum se poate simula un număr negativ folosind doar numere naturale? Simplu – le schimbăm semnul! Numărul 75 înseamnă “șaptezeci și cinci” doar fiincă așa am hotărât noi (sau cei care ne-au învățat matematică). Dar putem lua decizia ca 75 să însemne altceva. De exemplu, ar putea să însemne numărul -25. Pentru a discuta pe concret, să zicem că lucrăm cu valori de câte două cifre (deci de la 00 până la 99), cărora le dăm sensul din tabelul următor: Valoare
Sens
00
+00
01
+01
.........
........ 280
49
+49
50
-50
51
-49
......
.......
99
-01
Adică numerele de la 50 încolo le considerăm negative (iar valoarea absolută (adică valoarea fără semn) le-o obținem scăzându-le din 100). Hai să vedem, acum, cum am putea face operații cu aceste numere. 12+35 = 47 (sens) Adunare: (pozitiv+pozitiv)
Adunare: (pozitiv+negativ)
12+35 = 47
(valoare)
12+(-35) = -23
(sens)
12+65 = 77
(valoare)
100 - 77 Cum a rezultat un număr mai mare de 50, înseamnă 23 că rezultatul operaţiei 12 + (-35) este un număr negativ (deci cu semnul minus în faţă) a cărui valoare o putem afla scăzându-l din 100. Se observă astfel cum cu ajutorul operaţiei de adunare putem efectua şi operaţia de scădere. (Discuția e prea largă pentru a intra în toate detaliile, dar aș vrea măcar să te fac să ”simţi” un pic cum stau lucrurile).
Numere raționale Și numerele raționale se pot modela la fel de simplu (ca și numerele întregi) folosind numerele naturale. Le schimbăm 281
“sensul”! De exemplu, aşa: Valoare
Sens
00
0.00
01
0.01
02
0.02
...
...
99
0.99
Observ că în acest caz operația de adunare rămâne nemodificată. 0.12+0.35 = 0.47 (sens) De exemplu: 12+ 35 = 47 (valoare)
Numere în baza 2 Cred că ai înțeles din discuția de pînă aici că o variabilă poate avea ca valoare un număr format dintr-un număr limitat de cifre. (În mod cert nu poate memora infinituri). Mai trebuie să îți spun că în realitate (fizic) în calculator numerele nu sunt stocate ca șiruri de cifre în baza 10 (adică cifre de la 0 la 9), ci ca șiruri de cifre în baza 2 (adică cifrele de la 0 la 1; adică cifrele 0 şi 1). Nu știu câtă experiență ai avut cu bazele de numeratie, dar îți garantez că numerele 75(10) și 1001011(2) reprezintă fix același număr (scris în baza 10, respectiv în baza 2). Ca să înțelegi ideea, hai să numărul 5.
luăm un exemplu simplu –
În baza 10 ai 10 degete la mâini (deci zece cifre: 0, 1, 2, 3,.... 282
9), așa că te apuci să numeri: 0, 1, 2, 3, 4, 5. Deci 5(10). În baza 4 ai patru degete la mâini (deci patru cifre: 0, 1, 2, 3) și numeri (de la 1 până la 5): 1, 2, 3, 4 – ai terminat degetele, așa că aduni 1 la cifra ”zecilor” și reiei numărătoarea de la unu pentru cifra ”unităților”: 1 (5). Deci ai numărul 11(4). În baza 2 ai două degete la mâini (deci două cifre: 0, 1) și numeri: 1, 2 – și deja ai terminat degetele, așa că adaugi 1 la cifra „zecilor” şi reiei numărătoarea de la unu pentru cifra ”unităţilor”: 1 (3), 2 (4) – şi iar ai terminat degetele, aşa că adaugi 1 la cifra zecilor (dar cum cifra zecilor deja era maximă, 1, ea devine 0 şi se adaugă 1 la cifra “sutelor”, care devine astfel 1) şi apoi reiei numărătoarea pentru cifra “unităţilor”: 1 (5). Deci ai obţinut numărul 101(2). Mai matematic, lucrurile ar sta așa: abcd(10) = a*103+b*102+c*101+d*100 abcd(4) = a*43 +b*42 +c*41 +d*40 abcd(2) = a*23 +b*22 +c*21 +d*20 Într-adevăr: 5(10)
= 5*100 = 5
11(4) = 1*41 + 1*40 = 5 101(2) = 1*22 + 0*21 + 1*20 = 5 Pot obține reprezentarea unui număr din baza 10 în baza 2 prin împărțirea (întreagă, fără virgulă) repetată a numărului la 2 până când ajung la zero; apoi citesc resturile în ordine inversă. De exemplu:
283
75 :2 74 37 :2 1 36 18 :2 1 18 9 :2 0 8 4 :2 1 4 2 :2 0 2 1 :2 0 0 0 1 75(10) = 1001011(2)
Octeți Ai auzit de ”bit”, de ”byte”, de ”kilobyte”, de ”megabyte” și de ”gigabyte”? Toate acestea exprimă dimensiuni de valori ce pot fi stocate în calculator. Prin ”dimensiuni de valori” nu mă refer la ”cât de mare este o valoare față de alta”, ci pe câte ”cifre” e ”scrisă” o valoare. Așa cum ți-am zis, în memoria calculatorului valorile sunt scrise sub formă de înșiruiri de cifre binare (adică 0 sau 1). O valoare scrisă sub forma unui număr format dintr-o singură cifră binară se zice că are dimensiunea de 1 bit (1b). Valorile reprezentate ca numere (binare) formate din 8 cifre (binare) au dimensiunea de 1 byte (1B). 1B = 8b (în română îi zice ”octet”). ”Bucățica” de memorie cea mai mică ce poate fi accesată (prin intermediul variabilelelor, desigur) nu area dimensiunea de 1 bit (cum ai putea crede) ci de 1 byte. Într-o astfel de ”bucățică” de memorie se pot reprezenta câte numere 284
(naturale) diferite? Păi, să vedem: 00000000(2), 00000001(2), 00000010(2), ..., 11111111(2). Cât ar însemna numerele astea în baza 10? 0, 1, 2, 3, 4, 5, ..., 255. Deci în total 256 de numere, adică 28. În mod tipic valorile cu care lucrăm în programare au dimensiuni de 1B, 2B, 4B sau 8B. Ca să îți faci o idee, pe 2 octeți putem reprezenta 65536 de valori diferite, iar pe 4 octeți – peste 4 miliarde. Kilobyte-ul, megabyte-ul și gigabyte-ul se folosesc pentru a exprima dimensiuni mari de date (stocate în general, sub formă de fișiere). Probabil te-ai aștepta că 1 kB=1000 B. Nu este așa! 1 kB = 210 B = 1024 B Similar un megabyte este: 1 MB = 210 kB = 210 *210 B = 220 B. Iar un gigabyte e: 1 GB = 210 MB = 210 *210 kB = 210 *210 *210 B = 230 B. (Despre TB nu îți mai zic, căci îți dai seama). Îți atrag atenția să nu confuzi 1 kB cu 1 kb. (1 kb=1024 biți; iar 1 kB = 1024 bytes, adică 1024*8 biți.)
Tipuri de date De ce toată introducerea asta? Ca să îți zic că o variabilă mai este caracterizată (pe lângă adresă, nume și valoare) și de tipul valorilor ce pot fi puse în ea. Adică de ”tipul de date” al ei. Iar principala caracteristică a tipului de date este dimensiunea valorilor ce corespund acelui tip. (Oricum asta nu înseamnă 285
că nu pot exista mai multe tipuri distincte care să aibă aceeași dimensiune). În limbajul de programare cu care te-ai ” jucat” aici nu prea teai lovit direct de tipuri de date. Căci toate variabilele au fost de ”tip” ”var” și ai putut pune în ele cam orice fel de valori, nu? (Numere, litere, vectori, matrici.) În limbaje cum e C, însă, în momenul în care definești o variabilă î i spui și de ce tip sunt valorile pe care le vei putea pune în ea. Știu că asta pare că îți complică viața (căci trebuie să te gândești din timp cam cât de mari vor fi numerele din programele tale), dar te va scuti de multe posibile erori extrem de greu de depistat din programe. Unele limbaje de programare sunt mai ”stricte” în ceea ce privește tipurile valorilor stocate în variabile (sau utilizate în operații aritmetice, sau transmise ca parametri funcțiilor) și generează erori dacă tipurile nu corespund, în vreme ce alte limbaje sunt mai „tolerante” din acest punct de vedere și realizează respectivele operaţii transformând valorile în mod dinamic dintr-un timp în altul. (De multe ori, această modificare a tipului valorii presupune practic doar o modificare a interpretări ei, adică a ”sensului”, și nu neapărat şi a ”valorii” efective.)
Alocare dinamică Dacă tot ți-am pomenit de valorile de tip vector și matrice utilizate în programele noastre, trebuie sa îți mărturisesc că atunci când scriu: var v = Vector(8); în variabila v nu se memorează, de fapt, un vector format din 8 elemente, ci un ”pointer” (”referință” se cheamă) către un vector de 8 elemente. 286
Prin apelul ”Vector(8)” al funcției ”Vector”, în memoria calculatorului se alocă spațiu pnru un vector de 8 elemente. Acest spaţiu va începe de la o anumită adresă în memorie. În variabila ”v” se va memora nu vectorul (adică cele 8 elemente ale sale), ci această adresă (de unde începe depozitarea vectorului în memorie). Limbajul (de programare) pe care l-am folosit în aceste lecții este (așa cum am zis) foarte permisiv în ceea ce privește utilizarea tipurilor de date, ceea ce ne oferă libertatea de a stoca într-o variabilă valori de orice tip. Adică este ”legal” să scriu ceva de genul: var v = Vector(8); v=3; Adică întâi memorează în ”v” (o referință către) un vector de 8 elemente, apoi memorează în ”v” valoarea numerică 3. (Dar ce s-a întânplat cu vectorul? Prin instrucţiunea “v = 3;” sa șters doar „pointerul” către el, dar elementele vectorului au rămas în memoria calculatorului! Ce se va întâmpla cu acea zonă de memorie alocată dinamic? Va rămâne ”blocată”, deci neutilizabilă pentru alte variabile? Răspunsul ar fi ”Da!” dacă programul ar fi fost realizat în C sau C++. Dar pentru programele scrise în limbajul JavaScript calculatorul ”eliberează” automat zonele de memorie despre care observă că nu mai sunt folosite. Mecanismul acesta se cheamă ”garbage collection”.
În următoarele două lecții din acest modul îți voi prezenta două tipuri de date mai complexe cu ajutorul cărora vei putea opera cu date în format text și cu date grupate.
287
15. Şiruri Ai văzut că în variabile putem pune ca valori atât numere, cât și litere sau simboluri. Ce ne facem, însă, dacă dorim să lucrăm cu nume de persoane sau cu paragrafe de text? Soluția e să folosim un șir de caractre. (Aici prin ”caracter” se înțelege o literă sau cifră sau un simbol.) Șirul de caractere e o strucură de date ce permite memorarea unei succesiuni de caractere și operarea cu aceasta. O variabilă de tip ”șir de caractere” ne va permite să memorăm în ea un număr variabil de caractere, ne va permite să accesăm în mod individual fiecare caracter, ne va permite să îi comparăm conţinutul cu al unei alte variabile de același tip și ne va permite și să căutăm dacă un șir este conținut în altul. Putem să implementăm practic un șir de caractere folosind un vector. Numărul elementelor vectorului va fi egal cu numărul caracterelor din șir, iar fiecare element din vector va conține, în ordine, caracterele șirului. Concret, putem lucra cu șirul de caractere ”igotopia” reprezentându-l sub forma unui vector astfel: var s = Vector(8); s[0] = ’i’; s[1] = ’g’; s[2] = ’o’; 288
s[3] = ’t’; s[4] = ’o’; s[5] = ’p’; s[6] = ’i’; s[7] = ’a’; Dacă dorim să comparăm două șiruri, putem construi o funcție de genul următor: function ComparaSiruri(s1, n1, s2, n2) // s1 e primul sir, de lungime n1 // s2 e al doilea sir, de lungime n2 // functia returneaza valoarea 1 daca sirurile sunt la fel { if (n1 != n2) return 0; var i = 0; while (i < n1) { if (s1[i] != s2[i]) return 0; i = i+1; } return 1; } Cum găsește o nepotrivire, funcția returnează 0 (adică ”cele două șiruri nu sunt egale”). Dacă se parcurg toate caracterele din cele două şiruri și nu s-a detectat diferențe, funcția returnează 1 – adică ”cele două șiruri sunt egale”. Să construim, acum, cu un alt șir de caractere – ”top”: var t = Vector(3); t[0] = ’t’; t[1] = ’o’; t[2] = ’p’; Putem compara cele două șiruri astfel: if (ComparaSiruri(s, 8, t, 3) == 1) { Afiseaza(’D’, 1, 1); Afiseaza(’a’, 2, 1); 289
} else { Afiseaza(’N’, 1, 1); Afiseaza(’u’, 2, 1); } Dar dacă dacă vrem să să extragem extragem dintr-un șir s sub subșșirul care începe la caracterul numărul n1 și ține până la caracterul cu numărul n2? n2 ? Putem face simplu o funcție și pentru treaba t reaba asta: function Subsir(s, n, i1, i2) // s e sirul, de lungime n, din care se va extrage... // ...subsirul care incepe de la caracterul al i1-lea... // .. pana la caracterul al i2-lea // functia returneaza subsirul extras { // Intai verific validitatea parametrilor: if (i1<0) i1 = 0; if (i1>=n) i1 = n-1; if (i2<0) i2 = 0; if (i2>=n) i2 = n-1; if (i2
Apoi putem verifica dacă a extras întrîntr -adevăr subșirul ”top” printr-o printro comparație cu șirul t (definit mai sus): if (ComparaSiruri(tt, 3, t, 3) == 1) Afiseaza(’D’, 1, 1); else Afiseaza(’N’, 1, 1); Putem, de asemenea, concatena concatena (adică ”alipi”) două șiruri de caractere – prin construirea construirea unui șir care să le ”încapă” pe amândouă și prin copierea celor două șiruri (unul după altul) în noul șir: function ConcatSiruri(s1, n1, s2, n2) // s1 e primul sir, de lungime n1 // s2 e al doilea sir, de lungime n2 // functia returneaza cele doua siruri concatenate { var rez = Vector(n1+n2); var i = 0; while (i < n1) { rez[i] = s1[i]; i = i+1; } i = 0; while (i < n2) { rez[n1+i] = s2[i]; i = i+1; } return rez; } Hai să folosim și această funcție: 291
var st = ConcatSiruri(s, 8, t, 3); Cum putem face să afișăm frumos șirul pe ecran? Îl parcurgem caracter cu caracter și afișăm fiecare caracter pe ecran (folosind functia Afiseaza Afiseaza din din simulator): function AfiseazaSir(s, n, x, y) // s e sirul de afisat // n e lungimea sirului s // x e coloana de pe ecran de unde incepe afisarea // y e linia de pe ecran pe care se afiseaza { var i = 0; while (i < n) { Afiseaza(s[i], x, y); x = x+1; i = i+1; } } Deci putem acum afișa pe ecran (pe linia de d e sus) conținutul conținutul şirului st st astfel: astfel: AfiseazaSir(s, 11, 1, 10); Ce zici – îți plac șirurile? Cum ai face o funcție AfiseazaSubsir (care să afișeze nu întreg șirul, ci un subşir din el (specificat prin indecşi))? indecşi))?
Șirurile de caractere sunt foarte utile în programare, așa că majoritatea limbajelor de programare le au ”încorporate” direct în limbaj (și nu ca niște structuri de date ”simulate”, construite de către programator (așa cum am am făcut (în scop didactic) aici)).
292
16. Structuri Am pomenit deja expresia ”structuri de date”. Nu despre despre asta vreau să îți vorbesc încă încă aici, ci despre o modalitate de ”structurare”, (de ”grupare”, de ”a ”alăturare”) a unor date diferite dar care se referă la un același lucru. Concret, dacă am un magazin de calculatoare și vreau să fac un program în care să tin evidența produselor, îmi poate fi util să pot ține la un loc diferite informații despre fiecare calculator, cum ar fi: - tipul (laptop, dektop) - frecvența procesorului - memoria - sistemul de operare preinstalat - prețul Pot face acest lucru folosind variabile de tip ”structură” – adică variabile în în care grupez grupez laolaltă mai multe variabile. ”Definiția” asta cam seamănă cu cea de la vectori, nu? Ba da, doar că la vectori ideea (implicită implicită)) era că toatele elementele sunt de același tip; tip; în schimb, la la ”structură” diferitele elemente pot fi de orice tip. Chestia e că în realitate ”vectorii ”vectorii”” din limbajul limbajul nostru ”de ”de jucărie” nu este chiar vector conform definiției definiției vectorilor din programare,, ci permit ca fiecare element să fie de orice tip programare (inclusiv de tipul vector). Prin urmare, pot implementa o 293
”structură” de tipul celei prezentate mai sus folosind u n vector, astfel: var produs = Vector(5); // Structura produs[0] = ’L’; // tip: laptop produs[1] = 2.2; // frecventa: 2.2 GHz produs[2] = 4; // memorie: 4 GB produs[3]] = ’L produs[3 ’L’; // sistem de operare: Linux produs[4] = 1500; // pret: 1500 lei E dificil, însă, însă, să să memorez memorez faptul faptul că la poziția 0 în vector este tipul calculatorului, calculatorului, la poziția 1 e frecvența procesorului, şi așa mai departe. Pot simplifica lucrurile din acest punct de vedere dând nume sugestive valorilor 0, 1, 2, ......4, astfel (folosind variabile, desigur): var TIP = 0; var FRECV = 1; var MEM = 2; var SO = 3; var PRET = 4; Așa că voi putem modifica, de exemplu, prețul produsului astfel: produs[PRET] = 1499; Dacă am mai multe produse disponibile în magazin magazin,, pot defini un vector în care fiecare element va fi o structură ca cea prezentată: var produse = Vector(3); // <-- 3 produse 294
produse[0] = Vector(5); // <-- primul produs produse[0][TIP]] = ’L’; produse[0][TIP produse[0][FRECV] = 2.2; produse[0][MEM] = 4; produse[0][SO] = ’L’; produse[0][PRET] = 1500; produse[1] = Vector(5); // <-- al doilea produs produse[1][TIP] = ’D’; produse[1][FRECV] = 3.1; produse[1][MEM] = 16; produse[1][SO] = ’W’; produse[1][PRET] = 2500; produse[2] = Vector(5); // <-- al treilea produs produse[2][TIP] = ’L’; produse[2][FRECV] = 2.2; produse[2][MEM] = 8; produse[2][SO] = ’M’; produse[2][PRET] = 5500; O astfel de organizare a datelor îmi permite să pot automatiza cu ușurință calcule de genul „Care este prețul mediu al unui produs din magazinul meu?” sau să fac căutări de genul ”C ”Câtă memorie are cel mai scump produs produs?”. ?”. Îți voi arăta în continuare aș putea implementa sub forma 295
unor funcții răspunsurile la aceste două întrebări: function PretulMediu(prod, nr) // prod e vectorul de produse // iar nr e numarul produselor din vectorul prod { // Vezi mai intai daca sunt produse: if (! (nr > 0)) return -1; // Eroare! Eroa re! // Calculeaza pretul total: var pret_total = 0; var i = 1; while (i <= nr) { pret_total = pret_total + prod[i][PRET]; i = i+1; } // Si apoi imparte-l la numarul de produse: return (pret_total/nr); } function MemPretMaxim(prod, nr) // prod e vectorul de produse // iar nr e numarul produselor din vectorul prod { // Vezi mai intai daca sunt produse: if (! (nr > 0)) return -1; // Eroare! Eroa re! // Cauta cel mai scump produs: produ s: var index_pret_max = 0; var pret_max = prod[index_pret_max][PRE prod[index_pret_max][PRET]; T]; var i = 1; while (i <= nr) { if (prod[i][PRET] > pret_max) { pret_max = prod[i][PRET]; index_pret_maxim = i; } i = i+1; 296
} // Si vezi cata memorie are: return prod[index_pret_max][MEM]; } Le pot apela apoi astfel: var pm = PretulMediu(produse, 3); Afiseaza((pm-pm%1000)/1000, 1, 10); // cifra mii Afiseaza(((pm-pm%100)/100)%10, 2, 10); // cifra sute Afiseaza(((pm-pm%10)/10)%10, 3, 10); // cifra zeci Afiseaza(pm%10, 4, 10); // cifra unitati var mem = MemPretMaxim(prod, 3); Afiseaza(((m-m%10)/10)%10, 1, 9); // cifra zeci Afiseaza(m%10, 2, 9); // cifra unitati Ca exercițiu, cum ai face o structură în care să ții informații despre cărțile din biblioteca ta?
Încheiem cu această lecție primul modul din partea a doua a cărții. Ai văzut deja că lucrurile s-au mai complicat puțin, dar în același timp ți-au fost relatate lucruri interesante privind modul cum lucrează în realitate calculatorul cu variabilele. Ba chiar ai început să îți faci idee despre cum operează bazele de date. În modulul următor vei studia principalele structuri de date cu care lucrează programatorii în lumea reală.
297
Modulul V
Propoziţii În acest modul vom îmbina “cuvintele” în “propoziţii”. Adică vei vedea cum poţi îmbina cele învăţate până aici în structuri de date – modalităţi de organizare a datelor care să faciliteze operarea cu ele şi realizarea de programe utile şi eficiente. Vei învăţa aici patru structuri de date esenţiale, ce vin în completarea vectorilor şi matricilor pe care le-ai studiat în prima parte a cărţii: lista, stiva, coada şi arborele. Mă bucur dacă ai ajuns până până în acest punct parcurgând cu atenţia cuvenită (şi mai ales cu exerciţiul practic absolut necesar) toate lecţiile. Asta arată că eşti cu adevărat dispus(ă) să ajungi să gândeşti ca un programator.
298
17. Liste Îți amintești exemplul din lecţia trecută în care am memorat produsele dintr-un magazin de calculatoare într-un vector de structuri? Aparent este OK, dar salvarea produselor într-un vector nu este foarte potrivită pentru a modela operarea obișnuită a unui magazin (în care produsele disponibile nu sunt fixe, ci pot apărea alte produse, la fel cum pot dispărea unele dintre produsele existente). Ar fi mai indicată o structură de date care să permită atât adăugarea cât și ștergerea ușoară de produse. (Vectorii au dimensiune fixă, stabilită la crearea lor. Deci dacă aș vrea să măresc un vector ar trebui să construiesc un nou vector (mai mare) și să copiez în el conținutul noului vector, operație care ar lua ceva timp). Lista este o astfel de strucură de date – ce permite adăugarea și ștergerea ușoară de elemente. Permite asta fiincă nu e construită toată o dată, ci element cu element. De asemenea, spre deosebire de vector, într-o listă elementele nu se găsesc la poziții fixe față de începutul vectorului, ci se pot găsi oriunde în memorie. Elementele listei pot fi, totuși, accesate datorită faptului că fiecare element are o legătură către următorul element din listă. Astfel, plecând de la primul element al listei și mergând din legătură în legătură putem parcurge toate elementele listei. 299
(O astfel de listă se zice că este ”simplu înlănţuită” – căci fiecare element conține o singură legătură (și anume, către următorul element din listă). Există și liste ”dublu înlănţuite”, în care fiecare element conține o legătură către elementul anterior din listă și o legătură către elementul următor din listă.) Cam multă teorie nu? Hai să vedem cum am putea implementa practic o listă (simplu înlănţuită). În primul rând, cum facem un element al listei? Un astfel de element trebuie să conțină două lucruri: o valoare și o legătură către următorul element din listă. Putem folosi deci, o structură ca cea din lecția trecută. Adică o ”variabilă” ce va conține două variabile: - valoarea (VAL) elementul cerut din listă - si legătura LEG către următorul element din listă. var VAL = 0; var LEG = 1; var element = Vector(2); element[VAL] = ... // <-- Aici voi pune structura aceea // cu 5 elemente (TIP, FRECV, // MEM, SO, PRET) ce defineste // un produs. element[LEG] = ...; // <-- Aici voi pune un “pointer” // catre urmatorul element din lista. Așa fac un element al listei. Ca să fac acea listă de trei produse din lecția trecută va trebui să construiesc trei astfel 300
de elemente și să ”leg” primul element de al doilea și pe al doilea să îl ”leg” de al treilea. De asemenea, ca să pot utiliza lista mai am nevoie de două lucruri. În primul rând, am nevoie de un ”pointer” către primul element al listei. De aici voi începe parcurgerea listei de fiecare dată când caut un element al listei sau adaug un element în listă sau șterg un element din listă. În al doilea rând, am nevoie de un „marcaj” al sfârșitului listei. Acest lucru se poate realiza foarte simplu prin a pune în ”subelementul” LEG din ultimul element din listă valoarea 0. (Astfel î i spun, practic, că următorul element din listă după ultimul element este ”nimic”.) Ca să vezi concret cum stau lucrurile, iată cum am putea reface lista de produse din lecția trecută folosind în loc de vector o listă pentru a memora cele trei elemente (adică informațiile despre cele trei tipuri de calculatoare disponibile în magazin): var VAL = 0; var LEG = 1; var element1 = Vector(2); // primul element al listei element1[VAL] element1[VAL][TIP] element1[VAL][FRECV] element1[VAL][MEM] element1[VAL][SO] element1[VAL][PRET]
= Vector(5); // <-- primul // produs = ’L’; = 2.2; = 4; = ’L’; = 1500;
var element2 = Vector(2); // al doilea element al listei element2[VAL]
= Vector(5); // <-- al doilea // produs 301
element2[VAL][TIP] element2[VAL][FRECV] element2[VAL][MEM] element2[VAL][SO] element2[VAL][PRET]
= ’D’; = 3.1; = 16; = ’W’; = 2500;
element1[LEG] = element2; var element3 = Vector(2); // al treilea element al listei element3[VAL] element3[VAL][TIP] element3[VAL][FRECV] element3[VAL][MEM] element3[VAL][SO] element3[VAL][PRET]
= Vector(5); // <-- al treilea // produs = ’L’; = 2.2; = 8; = ’M’; = 5500;
element2[LEG] = element3; element3[LEG] = 0; // <-- Marchez astfel sfarsitul listei. var produse = element1;
// <-- “pointer” catre primul // element din lista
Ca să vezi și cum ar diferi practic folosirea unei liste de folosirea unui vector, voi reface în continuare cele două funcții construite în lecția trecută pentru a calcula prețul mediu al unui produs și pentru a afla câtă memorie are cel mai scump dintre produse. function PretulMediu(prod) // prod e lista de produse { // Vezi mai intai daca sunt produse: if (prod == 0) return -1; // Eroare! // Calculeaza pretul total si numara produsele: var pret_total = 0; var nr = 1; 302
while (prod != 0) { pret_total = pret_total+prod[VAL][PRET]; nr = nr + 1; prod = prod[LEG]; // Treci la elementul // urmator. } // Si apoi returneaza rezultatul impartirii lor: return (pret_total/nr); } function MemPretMaxim(prod) { // Vezi mai intai daca sunt produse: if (prod == 0) return -1; // Eroare! // Cauta cel mai scump produs: var pret_max = prod[VAL][PRET]; var mem = prod[VAL][MEM]; while (prod != 0) { if (prod[VAL][PRET] > pret_max) { pret_max = prod[VAL][PRET]; mem = prod[VAL][MEM]; } prod = prod[LEG]; } // Si returneaza-i cantitatea de memorie: return mem; } Și le voi apela similar (revezi lecția trecută), doar că nu va trebui să le mai dau acestor funcții și cel de-al doilea parametru (adică numărul de elemente). Dacă la vector e absolut necesar să știu câte elemente are, la listă acest lucru nu mai e valabil, căci voi parcurge lista element cu element, legătură cu legătură, până când ajung la ultimul element (care ”se leagă” de 0 (adică de ”nimic”)). 303
Listele sunt structuri de date mai flexibile decât vectorii în ceea ce privește inserarea și eliminarea de elemente, dar au două dezavantaje: - consumă mai multă memorie, căci fiecare element trebuie să conțină în el și legătura (”pointerul”) către următorul element; - și nu îmi permit să accesez elementele direct, ca la vector; adică o listă nu îmi poate găsi, de exemplu, elementul al treilea din ea decât după ce parcurge primul element, apoi pe al doilea și apoi ajunge la al treilea. S-ar putea lungi foarte mult discuția despre liste, căci practic ele deschid ”poarta” către ”tărâmul” structurilor de date. Dar mă opresc aici, ca să îți ofer posibilitatea de a ”digera” bine informaţiile dobândite și de a experimenta cu ele în simulator.
Ai văzut în această lecție o structură de date ce îți poate fi utilă ca atare în aplicațiile tale (pentru eficientizarea anumitor programe), cât și ca element constructiv pentru structuri de date mai complexe. Îți voi prezenta în continuare două structuri de date foarte întâlnite în programare, ale căror implementări se pot realiza foarte natural cu ajutorul listei. Totusi, voi arăta acolo implementarile cu vector, și nu cu listă, ca să păstrez lucrurile cât mai clare și mai ușor de înțeles. (Știu că și înțelegerea vectorilor e o provocare destul de serioasă, așa că nu mă aștept să stăpânești deja listele după doar o parcurgere rapidă a subiectului (ci este nevoie de exercițiu (atât în simulator, cât cu pixul pe hârtie (pentru a ”vizualiza” cum se compun elementele de tip ”structură” unele din altele și cum se ”leagă” între ele prin ”pointeri”))).)
304
18. Stive Un aspect al programării care nu este deloc clar celor care sunt la început este acela că programarea nu înseamnă doar instrucţiuni prin care îi spui calculatorului ce acţiuni să efectueze. Bineînţeles că acesta este aspectul cel mai important şi care e responsabil de rezultatele pe care le vezi atunci când execuţi un program. Dar asta nu e totul. Programarea mai înseamnă şi altceva . Un altceva foarte important, fără de care acţiunile de care vorbim nu ar avea putere. Acel altceva sunt structurile de date. Daca ar fi să scriu într-un soi de formul ă matematică simplă ce înseamnă un program, ar ieşi ceva de genul:
Program = Date + Instrucţiuni Deja ai început să lucrezi cu structuri de date. Vectorii sunt cele mai simple structuri de date. (Iar matricile reprezintă de fapt generalizarea vectorilor pentru cazul bidimensional (adica în două dimensiuni – linii şi coloane).) De asemenea, 305
listele (studiate în capitolul anterior) sunt şi ele structuri de date. În capitolul acesta o să te joci cu o structură de date puţin mai restrictivă decât un vector, dar care poate fi implementată cu mare uşurinţă folosind un vector şi o variabil ă (simplă).
Stiva Cu siguranţă ştii ce e o stivă . Dacă nu, ia o cutie de carton şi pune în ea o carte. Apoi mai pune o carte peste prima carte. Şi încă o carte peste a doua. OK? Acum ai trei cărţi (situate una peste alta) în stivă. Scoate o carte. Evident, vei scoate ultima carte pusă (adică cea care e situată în vârful stivei). Te-ai prins cum functionează o stivă?: (1) Poţi pune elemente în ea doar în vârf (peste ultimul element pus). (2) Şi poţi scoate elemente din ea doar de la vârf. Ultimul element introdus în stivă este primul care va fi scos (atunci când va fi cazul să scoţi un element din ea). În limba engleză la treaba asta îi zice LIFO (Last In, First Out).
Cum se implementează o stivă Cel mai simplu poţi implementa o stivă folosind un vector S (cu un număr de elemente suficient de mare pentru a încapea în el toate elementele care se vor adăuga în stivă la vreun moment dat) şi o variabilă v (în care se va memora 306
indicele poziţiei din vector de deasupra ultimului element din stivă). Desenul următor vrea să explice cele spuse în paragraful anterior.
Prin urmare, atunci când vrei să adaugi un element în stivă faci aşa:
S[v] = element_nou; v = v+1; Iar atunci când vrei să scoţi un element din stivă , faci aşa:
v = v-1; element_scos = S[v]; Iniţial, când stiva e goală, v va avea valoarea 0. (Adică vârful stivei va indica baza stivei.) 307
Valoarea lui v poate fi interpretată şi ca fiind numărul de elemente existente în stivă. Cel mai bine înveţi jucându-te, aşa că iată mai jos un program pe care îl poţi copia şi rula în simulatorul nostru. După ce introduci codul sursă de mai jos şi apeşi butonul “Execută…” din simulator, vei vedea pe ecran 10 puncte colorate dispuse aleator. Prin click pe un punct colorat, acel punct se va adauga în stiva din dreapta (pe coloana cu coordonata x egală cu 11, deci imediat în afara ecranului de 10×10 puncte). (În felul acesta vei putea vedea la lucru operaţiunea de adă ugare în stivă .) Prin click pe un punct alb , acel punct se va completa cu un element din stivă. (Vei vedea astfel cum lucrează operaţiunea de scoatere din stivă .) Iată şi codul sursă pe care îl poţi copia şi rula în simulator: // Joc pentru intelegerea ideii de stiva // Datele programului: var tabla = Matrice(10, 10); var stiva = Vector(10); var varf_stiva = 0; // Functiile ajutatoare: function InitializeazaTabla() { // parcurge toate elementele // din matricea "tabla" si // pune in ele valoarea 0 var x = 0; while (x < 10) { 308
var y = 0; while (y < 10) { tabla[x][y] = 0; y = y+1; } x = x+1; } } function InitializeazaStiva() { // parcurge toate elementele // din vectorul "stiva" si // pune in ele valoarea 0 var y = 0; while (y < 10) { stiva[y] = 0; y = y+1; } } function ImprastieCuburi() { // plaseaza in pozitii aleatoare // pe tabla 10 cuburi de culori // aleatoare var nr = 0; while (nr < 10) { // genereaza un numar aleator cuprins // intre 0 si 99-nr var pozitie = NrAleator(99-nr); // apoi parcurge toate punctele de pe // tabla si gaseste al "pozitie"-lea // punct liber 309
var contor = 0; var x = 0; while (x < 10) { var y = 0; while (y < 10) { if (tabla[x][y] == 0) { contor = contor+1; if (contor == pozitie) { // pune in al "pozitie"-lea // punct liber de pe tabla // o culoare aleatoare (intre // 1 si 7) tabla[x][y] = 1+NrAleator(6); } } y = y+1; } x = x+1; } nr = nr+1; } } function DeseneazaEcran() { // parcurge fiecare punct de pe tabla // si in in pozitia pozitia corespunzatoare corespunzatoare de pe // ecran.... var x = 0; while (x < 10) { var y = 0; while (y < 10) 310
{ // ....aprinde punctul folosind culoarea // memorata in matricea "tabla" AprindeNrCul(x+1, AprindeNrCul(x+ 1, y+1, tabla[x][y]); y = y+1; } x = x+1; } // parcurge fiecare element din stiva // de la baza spre varf si in pozitia pozitia // corespunzatoare de pe ecran... var y = 0; while (y < 10) { // ....aprinde punctul folosind culoarea // memorata in vectorul "stiva" AprindeNrCul(11, y+1, stiva[y]); y = y+1; } } function FunctieMouse(ev) { var xm = XMouse(ev); var ym = YMouse(ev); if ( (xm>=1) && (xm<=10) && (ym>=1) && (ym<=10) ) { // daca s-a facut click pe un punct // gol de pe tabla if (tabla[xm-1][ym-1] == 0) { // daca stiva nu era goala, scoate // elementul din varful varful ei si pune-l pune-l // in pozitia pozitia pe care s-a facut click if (varf_stiva > 0) { tabla[xm-1][ym-1] = stiva[varf_stiva-1]; 311
varf_stiva = varf_stiva - 1; // si deseneaza pe ecran AprindeNrCul(xm, AprindeNrCul(x m, ym, tabla[xm-1][ym-1]); AprindeNrCul(11, 1+varf_stiva, 0); } } // altfel (deci daca s-a facut click // pe un punct ocupat de un cub colorat) colorat) else { // pune cubul in stiva stiva[varf_stiva] stiva[varf_stiv a] = tabla[xm-1][ym-1]; varf_stiva = varf_stiva + 1; tabla[xm-1][ym-1] = 0; // si deseneaza pe ecran AprindeNrCul(xm, AprindeNrCul(x m, ym, 0); 0 ); AprindeNrCul(11, varf_stiva, stiva[varf_stiva-1]); } } } // Programul propriu-zis: InitializeazaTabla(); InitializeazaStiva(); ImprastieCuburi(); DeseneazaEcran(); AscultaMouse(FunctieMouse);
Privită astfel e amuzant e amuzantă ă stiva, nu-i aş aşa? Am o veste bună bună: stiva nu e singura structură structur ă de date amuzantă amuzantă, aşa că te aşteaptă teaptă un drum foarte interesant în continuare.
312
19. Cozi Am început în capitolul trecut o incursiune interactivă interactivă în lumea structurilor de date. (Ai vă văzut acolo cum poţ poţi implementa o stivă stiv ă folosindu-te de un vector şi de o variabilă variabilă.) O vom continua în acest capitol, în care îţ îţii voi ară ar ăta cum poţ poţi implementa o coadă coad ă folosind un vector şi două două variabile.
Coada Când te gândeş gândeşti la structura de date numită numit ă coadă nu nu te gândi la coada vreunui animal. Gândeş Gândeşte-te la coada care se formează formeaz ă la casa unui supermarket. Adică Adic ă un şir de oameni în care pe la un capă capăt se intră intră şi pe la celă celălalt capă capăt se iese. Capătul pe la care se intr ă se nume Capă numeşşte coada cozii, iar capă cap ătul pe la care se iese se numeş numeşte capul cozii. Dacă la stivă Dacă stivă ultimul element introdus era primul ce urma a fi scos, la coadă coadă primul element introdus este primul ce va fi scos. (În engleza la treaba asta îi zice FIFO (First In, First Out).) Altfel spus, la stivă stivă elementele sunt scoase în ordine inversă invers ă 313
faţă de cum au fost introduse, iar la coadă faţă coad ă elementele sunt scoase în aceeaş aceeaşi ordine în care au fost introduse.
Cum se implementează o o coadă Cel mai simplu poţ po ţi implementa o coadă coadă folosind un vector (numit, de exemplu, coada) (cu un numar de elemente suficient de mare pentru a încă încăpea în el toate elementele care se vor adă adăuga în coadă coadă la vreun moment dat) şi două variabile — capul_cozii şi coada_cozii (în care se vor memora poziţ poziţia din care va fi scos urmă urm ătorul element şi, respectiv, poziţ poziţia în care se va insera urmă urm ătorul element în vectorul coada ). ). Desenul urmă următor vrea să să explice cele spuse în paragraful anterior:
Prin urmare, atunci când vrei să adaugi un element în coadă faci faci aş aşa:
coada[coada_cozii] = element_nou; coada_cozii = coada_cozii+1; Pare OK, nu? Adică Adică adaugi noul element în vectorul coada în poziţţia specificată pozi specificată prin valoarea memorată memorată în variabila coada_cozii , iar apoi muţ muţi coada cozii cu o poziţ poziţie mai la dreapta. Dar ce se întâmpl întâmplă ă atunci când valoarea din variabila coada_cozii ajunge să să dep depăş ăşeasc ească ă num numă ărul de elemente 314
disponibile din vector? Se va produce o eroare atunci când voi încerca să să adaug un nou element în coadă coadă. Problema se poate rezolva transformând vectorul coada întrun vector circular. Astfel, dacă dacă de exemplu vectorul coada are 10 elemente, se va considera că c ă elementul coada[0] îl are în stânga lui pe elementul coada[9] (ş (şi că elementul coada[9] îl are în dreapta lui pe elementul coada[0]). Practic, lucrurile s-ar scrie aş a şa:
coada[coada_cozii] = element_nou; coada_cozii = (coada_cozii+1) % 10; Rezultatul operaţ operaţiei 10%10 (adică (adică restul împă împărţirii lui 10 la 10) este 0, ceea ce înseamnă înseamnă c că ă dup după ă 9 nu va urma 10, ci 0.
scoţi un element din coadă , faci aş Similar, când vrei să sco aşa: element_scos = coada[capul_cozii]; capul_cozii = (capul_cozii+1) % 10; Iniţial, când coada e goală Iniţ goală, atât capul_cozii cât cât şi coada_cozii vor avea aceeaş aceeaşi valoare. (Nu contează contează care valoare, că căci vectorul e circular.) Numă Num ărul de elemente existente în coadă coad ă este egal cu:
nr_elemente = coada_cozii-capul_cozii; Pare corect, nu? Ups! Dar ce te faci când valoarea din capul_cozii e e mai mare decat valoarea din coada_cozii (ceea ce e posibil să să se întâmple – c că ăci vectorul e circular, știi?)? Problema poate fi remediată remediată astfel (în ideea că că vectorul coada are 10 elemente): 315
nr_elemente = coada_cozii-capul_cozii; if (nr_elemente < 0) { nr_elemente = 10-capul_cozii + coada_cozii; } Ca şi până până acum, cred ca cel mai bine înveţ înveţi jucându-te cu ceva concret – aşa că că iat iată ă mai jos un mic “joc” pe care îl care îl poţ po ţi rula în simulatorul nostru. Copiază Copiază şi introdu codul sursă sursă în simulator şi apoi apasă apasă butonul "Execută "Execută...". Vei vedea pe ecran 10 puncte colorate dispuse aleator. Prin click pe un punct colorat, acel punct se va adăuga în coada de jos (pe linia cu coordonata y egală cu 0, deci imediat în afara ecranului de 10x10 puncte). (În felul acesta vei putea vedea la lucru operaţiunea operaţ iunea de adă ugare .) ugare în coadă .) Prin click pe un punct alb , acel punct se va completa cu un element din coadă coadă. (Vei vedea astfel cum lucrează lucrează operaţţiunea de scoatere din coadă .) opera .) // Joc pentru intelegerea ideii de coada // Datele programului: var tabla = Matrice(10, 10); var coada = Vector(10); var capul_cozii = 0; // pozitia elementului ce va fi // eliminat din coada var coada_cozii = 0; // pozitia unde va fi f i inserat // un nou element in coada // Functiile ajutatoare: function InitializeazaTabla() 316
{ // parcurge toate elementele // din matricea "tabla" si // pune in ele valoarea 0 var x = 0; while (x < 10) { var y = 0; while (y < 10) { tabla[x][y] = 0; y = y+1; } x = x+1; } } function InitializeazaCoada() { // parcurge toate elementele // din vectorul "coada" si // pune in ele valoarea 0 var y = 0; while (y < 10) { coada[y] = 0; y = y+1; } } function ImprastieCuburi() { // plaseaza in pozitii aleatoare // pe tabla 10 cuburi de culori // aleatoare var nr = 0; while (nr < 7) 317
{ // genereaza un numar aleator cuprins // intre 0 si 99-nr var pozitie = NrAleator(99-nr); // apoi parcurge toate punctele de pe // tabla si gaseste al "pozitie"-lea // punct liber var contor = 0; var x = 0; while (x < 10) { var y = 0; while (y < 10) { if (tabla[x][y] == 0) { contor = contor+1; if (contor == pozitie) { // pune in al "pozitie"-lea // punct liber de pe tabla // o culoare aleatoare (intre // 1 si 7) tabla[x][y] = 1+NrAleator(6); } } y = y+1; } x = x+1; } nr = nr+1; } } function DeseneazaEcran() { // parcurge fiecare punct de pe tabla 318
// si in in pozitia pozitia corespunzatoare corespunzatoare de pe // ecran.... var x = 0; while (x < 10) { var y = 0; while (y < 10) { // ....aprinde punctul folosind culoarea // memorata in matricea "tabla" AprindeNrCul(x+1, AprindeNrCul(x+ 1, y+1, tabla[x][y]); y = y+1; } x = x+1; } // parcurge fiecare element din coada // de la cap spre coada si in pozitia pozitia // corespunzatoare de pe ecran... x = 0; while (x < 10) { // ....aprinde punctul folosind culoarea // memorata in vectorul "coada" AprindeNrCul(1+x,, 0, coada[x]); AprindeNrCul(1+x x = x+1; } } function FunctieMouse(ev) { var xm = XMouse(ev); var ym = YMouse(ev); if ( (xm>=1) && (xm<=10) && (ym>=1) && (ym<=10) ) { // daca s-a facut click pe un punct // gol de pe tabla if (tabla[xm-1][ym-1] == 0) 319
{ // daca coada nu era goala, scoate // elementul din capul capul ei si pune-l // in pozitia pozitia pe care s-a facut click if (capul_cozii != coada_cozii) { tabla[xm-1][ym-1] = coada[capul_cozii]; // si deseneaza pe ecran AprindeNrCul(xm, AprindeNrCul(x m, ym, tabla[xm-1][ym-1]); AprindeNrCul(1+capul_cozii, AprindeNrCul(1+capul_c ozii, 0, 0); capul_cozii = (capul_cozii+1) % 10; } } // altfel (deci daca s-a facut click // pe un punct ocupat de un cub colorat) colorat) else { // pune cubul in stiva coada[coada_cozii] coada[coada_coz ii] = tabla[xm-1][ym-1]; tabla[xm-1][ym-1] = 0; // si deseneaza pe ecran AprindeNrCul(xm, AprindeNrCul(x m, ym, 0); 0 ); AprindeNrCul(1+coada_cozii, AprindeNrCul(1+c oada_cozii, 0, coada[coada_coz coada[coada_cozii]); ii]); coada_coziii = (coada_cozii+1) % 10; coada_cozi 1 0; } } } // Programul propriu-zis: InitializeazaTabla(); InitializeazaCoada(); ImprastieCuburi(); DeseneazaEcran(); AscultaMouse(FunctieMouse);
320
Sper că şi coada ţi s-a pă părut amuzantă amuzantă şi interesantă interesantă şi te invit să să faci cunoş cunoştint tintă ă în capitolul urmă următor cu o structură structură de date chiar şi mai interesantă interesantă, numită numită arbore.
321
20. Arbori Stivele și cozile au foarte multe aplicații în programare, dar ele nu pot modela structuri de date complexe cum ar fi cele necesare pentru organizarea datelor în dosare pe hard-disk. Pentru acest din din urmă caz se folosesc arborii. Poate că numele de ”arbore ”arbore”” îți sună cam sună cam dubios. Vei vedea imediat, însă, că el este foarte bine b ine ales (așa cum a fost cazul şi la stive și la și la cozi). Hai să luăm luăm niste exemple de arbore. Mai întâi, un exemplu cu care te-ai obiş obişnuit. nuit. Să zicem că pe hard-disk-ul hard-disk-ul unui calculator avem trei dosare: - Programe - Documente - Poze În dosarul Programe Programe să zicem că avem subdosarele: subdosarele: - CodeBlocks - NetBeans În dosarul Documente avem avem patru subdosare: - Carti - Acte - Temporare - Ciorne 322
În dosarul Poze nu avem niciun subdosar, iar în subdosarul Carti din dosarul Documente Documente avem două subdosare: - Tehnica - Literatura Este greu de urmărit această descriere. Deja te-am te -am pierdut, nu? Hai să să prezint lucrurile mai compact, într-o într-o formă care nu îți e străină: - Programe - CodeBlocks - NetBeans - Documente - Carti - Tehnica - Literatura - Acte - Temporare - Ciorne - Poze Acesta este un arbore. Nu mă crezi? Hai să țiți -l ”redesenez” așa: Lite ratu ra
Teh nica Cod eBlo cks
NetB eans
Progr ame
Car ti
Act e
Docu ment e
Rădă cină
323
Tem pora re
Cior ne Poz e
Acum seamănă cu un arbore, nu? Din rațiuni practice, însă, însă, în programare arborii se desenează în mod tipic cu ”rădăcina” î ”rădăcina” î n sus și cu ”ramurile ”ramurile”” în jos. (Deci ar trebui să „ogli „oglindesc” ndesc” vertical imaginea de mai sus). ”Cerculețele” se cheamă ”noduri ”noduri”, ”, iar ”liniuțele” se cheamă ”arce”. Un nod poate fi ”părinte” pentru noduri ”fii”. (De exemplu, nodul ”Carti” este părinte părinte pentru nodurile-fii ”Tehnica” şi ”Literatura”, şi şi este în acelasi timp fiu pentru nodul ”Documente”.) ”Documente”.) Nodurile din arbore care nu au fii f ii se numesc ”frunze”. Un caz particular de arbori sunt arborii arborii binari. În acești arbori fiecare are nod fie zero, fie f ie doi fii. Un arbore binar este cel din exemplul următor: x
+ 2
7
5
4
L-am reprezentat (așa (așa cum am zis că se face), cu rădăcina (nodul ”x”) în sus și cu ”frunzele” în jos. Acest arbore modelează modelează o expresie matematică formată din numere, operatori binari și paranteze. Mai precis, este vorba de expresia (2+5)*(7-4). Hai să vedem cum am putea să stocăm în memoria calculatorului acest arbore. Dacă mm-ai urmărit cu atenție când ți-am ți -am vorbit despre liste, atunci n-ar n-ar trebui să fie prea greu. greu. În primul rând, ne trebuie o structură în care să memorăm un nod din arbore. din arbore. Ea poate fi 324
de felul următor: // Definesc constantele pentru accesarea // elementelor structurii: var VAL = 0; var FIU_ST = 1; var FIU_DR = 2; // Creez structura pentru nod: var nod = Vector(3); // Si ii scriu valori in elemente: nod[VAL] = ...; // <-- Valoarea nodului nod[FIU_ST] = ...; // <-- “Pointer” catre fiul din stanga nod[FIU_DR] = ...; // <-- “Pointer” catre fiul din dreapta Pentru a memora arborele ne trebuiesc șapte astfel de noduri, legate corespunzător unele de altele. // Definesc constantele pentru accesarea // elementelor unui nod: var VAL = 0; var FIU_ST = 1; var FIU_DR = 2; // Creez nodurile: var nod_x = Vector(3); nod_x[VAL] = ’x’; var nod_p = Vector(3); nod_p[VAL] = ’+’; var nod_m = Vector(3); nod_m[VAL] = ’’-’; var nod_2 = Vector(3); 325
nod_2[VAL] = 2; var nod_5 = Vector(3); nod_5[VAL] = 5; var nod_7 = Vector(3); nod_7[VAL] = 7; var nod_4 = Vector(3); nod_4[VAL] = 4; // Si fac legaturile intre ele: nod_x[FIU_ST] = nod_p; nod_x[FIU_DR] = nod_m; nod_p[FIU_ST] = nod_2; nod_p[FIU_DR] = nod_5; nod_m[FIU_ST] = nod_7; nod_m[FIU_DR] = nod_4; nod_2[FIU_ST] = 0; nod_2[FIU_DR] = 0; nod_5[FIU_ST] = 0; nod_5[FIU_DR] = 0; nod_7[FIU_ST] = 0; nod_7[FIU_DR] = 0; nod_4[FIU_ST] = 0; nod_4[FIU_DR] = 0; Hai să vedem acum cum am putea face o funcție care să parcurgă acest arbore și să î l afișeze (pe ecranul de 10x10) într-o formă similară celei pe care am arătat-o în exemplul cu dosare și subdosare (adică să afișăm informaţiile din noduri unele sub altele, cu indentări diferite în funcție de nivelul în care ne găsim în arbore): 326
var y = 10; // <-- Numarul liniei de unde incepe afisarea function AfisArbore(nod, nivel) // nod e nodul curent din arbore // iar nivel arata cati pasi am coborat de la radacina { // Daca nodul curent nu exista, iesi: if (nod == 0) return; // Altfel, afiseaza-i valoarea (si coboara o linie): Afiseaza(nod[VAL], 1+nivel, y); y = y - 1; // Si apoi fa aceleasi lucruri pentru fiecare fiu: AfisArbore(nod[FIU_ST], nivel+1); AfisArbore(nod[FIU_DR], nivel+1); } ... // Iar in program o voi apela astfel: AfisArbore(nod_x, 0); Deși ar fi multe lucruri de vorbit despre arbori, mă opresc aici (căci totuși acesta trebuie să fie doar un „abecedar”, nu?). Îți mai zic doar că arborii sunt un caz particular de grafuri (care sunt niște structuri de date formate din noduri conectate între ele în moduri arbitrare – ca o rețea de orașe legate între ele prin autostrăzi).
Am încheiat și modulul dedicat introducerii în ceea ce cheamă structuri de date. Sper că ți-am dat o idee despre cum arată lucrurile în acest domeniu și că ți-am trezit interesul pentru a aprofunda. Cu acest modul se încheie partea ”practică” a cărţii. În următorul modul îţi voi prezenta un pic de “filosofie”.
327
Modulul VI Fraze Felicitări! Acum nu doar că ştii să gândeşti ca un programator, ci ştii şi să “vorbeşti” calculatorului într-o “limbă” pe care el e capabil s-o “înţeleagă”. (E vorba de miniJavaScript-ul în care ai programat pe măsură ce ai parcurs capitolele de până aici.) Dar asta înseamnă de fapt programarea în prezent? te întrebi, poate. Ce e cu toate acele limbaje de programare de care tot aud? Şi cum se fac programele şi aplicaţiile pe care le folosesc zilnic? Vei primi răspunsurile în acest modul, care îşi propune să îţi facă o trecere uşoară de la statutul de ”proaspăt absolvent al abecedarului de programare” la cel de “începător în învăţarea unui limbaj de programare”.
328
21. Compilatoare Ceea ce ai citit și experimentat până aici ți-a dat o idee destul de bună despre ce î nseamnă programarea în general. Dacă ai studiat atent lecțiile și ți-ai acordat suficient timp pentru a lucra cu noțiunile învățate ar trebui să știi până acum să gândești algoritmi prin care să împarți soluționarea problemelor în succesiuni logice de pași mărunți. Ar trebui, de asemenea, să te simți în largul tău când lucrezi cu structurile de date de bază. Cu toate acestea, programele pe care le-ai făcut până acum au toate un aer de "jucărie”. Nu prea seamănă cu programele cu care ești obișnuit(ă), nu-i așa? Nici nu e de mirare, căci până acum ai lucrat doar într-un simulator care ți-a ascuns destul de multe lucruri despre cum stă treaba în programarea adevărată. Motivul a fost acela de a te proteja de potențialul efect copleșitor al noțiunilor "introductive” clasice de programare și de a te putea concentra pe a "gusta" programarea și a deprinde un model de gândire care să îți fie o bază solidă pentru a putea intra și avansa cât mai ușor î n acest domeniu. E ca și cum ți-am dat să bei apă ca să vezi ce gust are și apoi te-am învățat cum poți să o treci din stare lichidă în stare gazoasă și în stare solidă – fără să îți spun nimic despre formula ei chimică. A venit, deci, momentul să îți spun "formula chimică ” a unui 329
programator de calculator. Ți-am tot zis că un calculator e ca un "roboțel” care știe să execute comenzi. Ce nu ți-am zis până acum e că "creierul” (sau "motorul”, dacă preferi așa) unui calculator este un circuit electronic digital numit microprocesor. Și calculatoarele de tip desktop, și laptop-urile, și tabletele, și telefoanele au în interior circuite de acest gen. Ba chiar și camerele foto, televizoarele și chiar telecomenzile au așa ceva. Diferența e că acestea din urmă nu sunt gândite pentru a lăsa utilizatorului posibilitatea de a le programa. Și ele rulează programe, dar în mod obișnuit utilizatorul nu are posibilitatea de a modifica aceste programe. Pe un calculator (inclusiv pe tabletă și telefon), însă, utilizatorul are posibilitatea de a-și construi și rula propriile program. Pentru a face asta, el trebuie să știe să "vorbească" pe "limba" microprocesorului respectiv și să scrie programul sub forma unei serii de numere pe care procesorul "știe" să le " înțeleagă". Evident că acest lucru este dificil de făcut (căci oamenii gândesc mult mai usor în cuvinte decât în numere), motiv pentru care s-a inventat limbajul de asamblare. Deși este doar o formă de a scrie cu litere instrucțiunile numerice pe care le cunoaște microprocesorul, limbajul de programare a simplificat mult sarcina programatorilor. Să zicem că am avea un microprocesor ipotetic care înțelege cele două instrucțiuni din tabelul următor (unde Ri sunt regiștri, adică un fel de variabile): 7
6 Codul instrucțiunii 0 1 1 1 0 1
5
4 Registrul destinație 0 0 0 0 0 1
3
2
1
0
Registrul sursă 0 0 1
0 0 0 330
0 1 0
1 0 0
Numerele
biților
Intrucțiunea
move R0, R1 add R0, R2 move R3, R0
Cum ți-ar fi mai ușor să scrii programul? În "limbaj mașină " microprocesorului)?:
(adică
limbajul
numeric
al
01 000 001 11 000 010 01 011 000 Asta e în binar. În baza 16 (sau în hexazecimal) ar arăta așa: 0x41 0xC2 0x58 Iar î n baza 10 ar arăta astfel: 65 194 88 Sau ți-ar fi mai simplu să zici așa (în limbaj de asamblare, adică)?: move R0, R1 add R0, R2 move R3, R0 Sigur că varianta în limbaj de asamblare e mult mai lizibilă. Dar n-ar fi fost și mai simplu să scrii așa?: R3 = R1 + R2 Ba normal că da! 331
Acesta este motivul pentru care au apărut limbajele de nivel înalt. Adică limbaje (cum este și cel cu care te-ai " jucat” în această carte) ce permit expresii matematico-logice și instrucțiuni de genul "dacă” și "cât timp”. Problema este că microprocesorul nu știe de la sine să " înțeleagă” astfel de limbaje. De exemplu, microprocesorul ipotetic de mai sus nu înțelege așa cum trebuie comanda "R3 = R1 + R2". El știe să execute seria de comenzi "65 194 88”, dar "R3=R1+R2" nu știe ce înseamnă. Așa că programatorul trebuie să traducă instrucțiunile din limbajul de nivel înalt in instrucțiuni în limbaj mașină. (Eventual se poate folosi de limbajul de asamblare ca etapă intermediară în această "traducere”.) Programator
Limbaj de nivel înalt Limbaj de asamblare Limbaj mașină Calculator
Din fericire "traducerea” limbajului de nivel înalt în limbaj masină este o sarcină de rutină, care este suficient de clară și neambiguă pentru a putea fi transformată într-o serie de pași mărunți pe care procesorul știe să îi execute. Prin urmare, dacă am scrie un astfel de program capabil să traducă alte programe din limbajul de nivel înalt în limbaj mașină, acel program ne-ar scuti pe viitor de dificultatea scrierii de programe în limba j mașină. 332
Ba chiar am putea să scriem programe fără să fie nevoie să cunoaștem vreodată limbajul mașină al microprocesorului din calculatorul pe care îl programăm. Vestea bună e că astfel de programe există – nu trebuie să le construim noi. Se numesc compilatoare. Un compilator este un program care traduce programe. Le traduce din limbajul de nivel înalt (pe care îl ințelege omul (programatorul)) în limbajul mașină, de nivel scăzut (pe care îl înțelege calculatorul) Cea mai mare parte dintre programele care rulează pe calculator au fost realizate cu ajutorul compilatoarelor. Adică programatorul a scris programul sub forma unui text în limbaj de nivel înalt (text ce poartă denumirea de cod sursă al programului), iar compilatorul a tradus acest text într-un fișier ce conține comenzi pe care procesorul le înțelege direct. Acest fisier este de fapt programul pe care îl rulăm. Un exemplu standard de limbaj (de programare de nivel înalt) compilat este limbajul C++.
Interpretoare O categorie aparte de "compilatoare” o constituie interpetoarele. Și ele (ca și compilatoarele) au ca intrare codul sursă al programului (scris în limbaj de nivel înalt). Dar (spre deosebire de compilatoare) interpretoarele nu traduc programul în limbaj mașină, ci "simulează” rularea programului și generează efectele acestei rulări. Altfel spus, din punct de vedere al programatorului interpretorul pare că realizează o compilare a programului și apoi o rulare a lui. În realitate, compilarea aceasta nu se produce (cel puțin, nu în întregime). Ca să îți faci o idee despre ce vorbesc, iată etapele compilării unui program: 333
Program (cod sursă)
Analiză lexicală => Listă de atomi lexicali
Analiză sintactică => Arbore sintactic
Generare cod în limbaj mașină
Program executabil Interpretorul nu mai realizează cel de-al treilea pas, ci în locul acelui pas el parcurge arborele sintactic și execută instrucțiunile întâlnite acolo. (Traducerea din cod sursă (fisier text) în arbore sintactic îi este necesară pentru a " înțelege” sensul fiecăruia dintre "cuvintele” întâlnite în codul sursă. Căci unele sunt variabile, altele sunt funcții, altele sunt parametri, altele sunt operatori etc.. (La fel cum la gramatică avem subiecte, atribute, predicate, complemente.) Interpreoarele au avantajul major că permit niște limbaje mai flexibile, cu instrucțiuni mai ușor de scris și de înțeles de către oameni și mai puțin vulnerabile la erori grave. Dezavantajul principal este, însă, viteza mai scăzută a programelor interpretate față de cele compilate. (În orice caz, în ultima vreme interpretoarele s-au dezvoltat atât de mult încât această diferență este adesea insesizabilă.) 334
Un exemplu standard de limbaj (de programare) interpretat este limbajul JavaScript. Cam orice browser web are încorporat în el un interpretor de JavaScript.
Mașini virtuale O situatie intermediară între programele compilate și cele interpretate o au programele care rulează pe o mașină virtuală ce rulează pe calculaor. Mașina virtuală acționează ca un intermediar între programator și microprocesor. Ea este de fapt un program care rulează pe microprocesor și îl face să pară că ar fi capabil să execute alte instrucțiuni decât cele pentru care a fost proiectat. Rularea unui program pe o mașină virtuală are două avantaje majore: 1) Siguranța sporită: Programatorul nu mai are acces direct la instrucțiunile procesorului, ci mașina virtuală acționează ca un "tampon” ce poate absorbi "șocul” produs de eventualele "accidente”. 2) Portabilitatea sporită: Există mai multe tipuri de sisteme de operare, care la rândul lor rulează pe mai multe tipuri de procesoare. Un program compilat pentru un anumit procesor nu va putea rula pe altul (decât dacă se dispune de codul sursă și se compilează pentru acel procesor). Dar programele făcute pentru o mașină virtuală rulează nemodificate pe orice calculator pe care în prealabil rulează respectiva mașină virtuală. Este, de exemplu, cazul programelor scrise in limbajul Java. Ele sunt "traduse” ("compilate”) în mod tipic într-un "bytecode” (așa este denumit) a cărui executare este apoi simulată de către mașina virtuală Java (JVM – Java Virtual Machine), 335
Nu cred că este o surpriză pentru tine să afli că implementări ale mașinii virtuale Java se găsesc practic pentru orice sistem de operare. Rulează inclusiv pe telefoanele mobile. Într-o manieră similară stau lucrurile și cu programele scrise in limbajul C#.
Medii de dezvoltare Am vorbit până acum despre codul sursă al programului (pe care vrem să îl scriem) ca și cum ar fi fost vorba despre un singur fișier text ce conține toate instrucțiunile din program. În realitate, programul nu e conținut în doar un fișier – cu exceptia programelor foarte simple. În majoritatea programelor serioase codul sursă este distribuit în mai multe fișiere, ceea ce permite o gestionare mult mai facilă a lui (precum și posibilitatea de a reutiliza (în alte proiecte) părți mai generice de cod sursă). În plus, pe lângă codul sursă propriu-zis, construirea unui proogram mai poate presupune și realizarea de alte fișiere (cum ar fi, de exemplu, acele fișiere în care este descrisă poziționarea elementelor pe interfața grafică a aplicației). Gestionarea tuturor fișierelor pe care implică realizarea unui proiect softare complex nu e o sarcină chiar ușoară. În plus, ea presupune numeroase sarcini repetitive (care consumă timp și predispun la erori). Așa că au fost construite programe care să realizeze toate aceste lucruri. Ele se numesc medii de dezvoltare integrate (IDE – Integrated Development Environment). Astfel de medii sunt, de exemplu, Code::Blocks (pentru C și C++), NetBeans (penru Java) și Visual Studio (pentru C#). Principalele lucruri pe care ele le permit sunt: + Împărțirea facilă a codului sursă în mai multe fișiere, ținerea laolaltă a acestor fișiere și gestionarea automată a legăturilor 336
dintre ele. + Compilarea întregului proiect și rularea (printr-o simplă apăsare de buton a) programului rezultat. + Rularea programului în modul de depanare, pentru facilitarea găsiri erorilor din codul sursă.
Cam atât despre compilatoare. Urmează să vorbim în continuare despre principalele limbaje de programare utilizate în prezent și despre rolurile pe care le ocupă ele în cadrul tehnologiilor software actuale. Dar mai întâi să vedem ce sunt paradigmele de programare și cum ne pot ajuta ele să "facem ordine” în multitudinea de limbaje existente.
337
22. Paradigme Poate crezi că un termen atât de dubios ca "paradigmă” n-ar avea ce căuta într-un "abecedar” de programare. Și adevărul e că nici nu îl găsești în vreun alt curs introductiv de programare. De ce am ales, totuși, să vorbesc (atât de devreme) despre paradigmele de programare? Pentru că există foarte multe limbaje de programare. Pentru că cei care vor să își facă intrarea în acest domeniu sunt pur și simplu copleșiti de această multitudine de limbaje. Și pentru că un pic de ordine încă de la început te poate ajuta să vezi mai clar poza de ansamblu și să știi cum te poziționezi in contactul global al programări. Ce inseamnă "paradigmă de programare”, practic? Înseanmnă un mod de a gândi nu doar programele, ci î ntreaga idee de programare. Paradigma stabilește "forma" și "compoziția” "cărămizilor” din care sunt compuse programele, ca și modul cum se îmbină ele. Îți voi vorbi în continuare despre principalele paradigme de programare utilizate în prezent.
Programarea imperativă Tot ceea ce ai învățat despre programare din această carte se găsește sub paradigma programării imperative. Adică 338
programarea bazată pe comenzi (ca cele pe care le dai unui "roboțel”, așa cum am mai zis). Aceste comenzi mai poartă mai poartă numele de "proceduri” (motiv pentru care poți să dai și peste denumirea de "programare procedurală”). Prin urmare "cărămida” de bază din care e construit un program în programarea imperativă este procedura (adică "instrucțiunea” sau "comanda” (sau "funcția” – în sensul în care am definit-o în modulul al doilea)). Un program este format dintr-o înșiruire de astfel de proceduri, care se execută una după alta în ordinea în care au fost scrise (și ținând cont, eventual, de îndeplinirea anumitor condiții) Aceste proceduri pot comunica între ele prin intermediul datelor, stocate sub formă de variabile în memoria calculatorului.
Procedurile și datele sunt două entități (sau "chestii”, ca să nu te sperie termenul) distincte, separate. Privit din acest punct de vedere, un program în programarea procedurală este un ansamblu format dintr-o (1) succesiune (adică o listă, o serie) de proceduri și niște (2) date (pe care procedurile le pot citi si scrie). Adică așa: Program procedural Date
Proceduri
Programarea procedurală a avut un atât de mare succes până recent (și încă este utilizată foarte mult și în prezent) fiincă e cea mai apropiată de de modul real de funcționare a unui microprocesor. De asemenea, e și cea mai apropiată de ideea de algoritm. 339
Prin termenul algoritm se face referire la o succesiune bine determinată de operații prin care pe baza unor date de intrare se obțin niște date de ieșire: Date de intrare Algoritm Date de
ie ire
Un program procedural atunci când este rulat primește de la utilizatr niște date (adică niște valori (prin intermediul variabilelor din memorie)) și efectuează asupra lor o succesiune de proceduri, una câte una, pe rând, pentru pentru a calcula datele de ieșire. Ai văzut deja în lecțiile trecute că există și unele cazuri particulare de programe (simple) care nu respectă această definiție. Mă refer la faptul că unele programe nu primesc de la utilizator date de intrare. Dar asta nu trebuie să te tulbure, căci poți considera că (1) datele de intrare sunt deja "încrustate” în program, așa că nu mai e nevoie să fie primite din afară sau poți considera că (2) datele de intrare sunt ø (adică mulțimea vidă (din matematică); adică „nimic”). De asemenea, pot exista și programe care nu produc date de ieșire. Un exemplu ar fi programul: var a = 0; a = a+1; El calculează ceva, dar nu îi arată utilizatorului nimic. Deci nu scoate date de ieșire. (E și acesta tot un program, dar un program inutil.) Programarea imperativă se întâlnește în limbaje ca: C, C++, Java, C#, PHP, JavaScript, Python și altele. 340
Separarea atât de puternică între date și proceduri din programarea imperativă face destul de dificilă gestionarea programelor de dimensiuni mari. (Un exemplu foarte simplu este acela că într-un program mare e foarte posibil să ai nevoie (pentru claritatea programului) să folosești același nume pentru mai multe variabile sau pentru mai multe funcții.) Din acest motiv a apărut programarea orientată pe obiecte.
Programarea orientată pe obiecte În programarea orientată pe obiecte (POO) "cărămida” de bază din care este construit un program este obiectul. Un program în POO este un ansamblu format din obiecte ce comunică între ele prin "mesaje”. La modul general, un obiect este un fel de "variabilă” mai complicată, ce conține în interiorul ei variabile și proceduri (sau funcții , așa cum le-ai găsit denumite în acastă carte). Astfel spus, un obiect este o "variabilă” de tip "structură" care poate conține atât variabile (sau câmpuri ), cât și funcții (sau metode): Obiect Varibile sau obiecte (date) Funcții (proceduri)
"câmpuri"
"metode"
(Prin urmare, variabilele simple cu care am lucrat până acum pot fi considerate niște obiecte. De exemplu, în programul ... var a = 3; ... variabila a este un (fel de) obiect care conține un singur câmp 341
(cu valoarea 3) și nicio metodă.) Deci un obiect este format din câmpuri și metode ce pot opera asupra câmpurilor (adică le pot citi și scrie). Prin apelarea unei metode a unui obiect zicem că i-a transmis un "mesaj” acelui obiect. Metodele operează ca și funcțiile obișnuite (din programarea imperativă) – deci li se pot da ca parametri obiecte și pot returna obiecte. Diferența constă în faptul că metodele unui obiect și nu au acces (în mod normal) la câmpurile altor obiecte. Un program orientat pe obiecte ar putea arăta (conceptual) cam așa: Program orientat pe obiecte Obiect Câmpuri Metode
Obiect Câmpuri Metode
Obiect Câmpuri Metode
POO nu este o paradignmă complet diferită de programare imperativă. De fapt, corpurile metodelor arată ca niște programe procedurale în care în loc de variabile se folosesc obiecte și în loc de apeluri de funcții se apelează metode ale obiectelor. Această paradigmă de programare a avut foarte mult succes pentru că permite o organizare mai facilă a programelor mari, permite o reutilizare mai ușoară a unor porțiuni de programe 342
în alte programe și duce la evitarea unor posibile erori. Programarea orientată pe obiecte este suportată de aproape toate limbajele moderne, ca: C++, Java, C#, PHP, Python, etc.. Ba chiar unele dintre ele (Java, C#) nu pot fi practic înțelese fără POO, căci au fost concepute din start pe "calapodul” acestei paradigme de programare.
Programarea funcțională O paradigmă mai "exotică” dar din ce în ce mai întâlnită în prezent este programarea funcțională. "Cărămida” fundamentală din care este construit un program funcțional e funcția (adică o procedură care primește perametri și returnează valori). În programarea funcțională nu există explicit "date”, deci nu există practic variabile. (Există un fel de "variabile”, dar cărora li se poate face o singură atribuire (adică sunt folosite doar pentru a memora un rezultat și apoi nu mai pot fi rescrise, ci doar citite pentru a utiliza respectivul rezultat în alte calcule).) Conceptual, putem considera că un program funcțional arată cam așa: Program funcțional Funcție Funcție Funcție
Funcție
Funcție
343
Programarea funcțională este mai dificil de înțeles și de utilizat decât programarea imperativă și programarea orientată pe obiecte. Există mulți programatori care, deși sunt buni în aceste două paradigme de programare, au dificultăți în a înțelege programarea funcțională. Am ales să îți vorbesc și despre ea, totuși, pentru că începe să își facă simțită prezenta tot mai intens în limbajele de programare moderne (C#, JavaScript, PHP, Python). În plus, există și limbaje de programare care sunt în pricipal (sau chiar total) funcționale (cum ar fi Haskell, Scheme și Erlang), dar nu sunt atât de populare (ci sunt folosite doar în anumite aplicații specifice).
344
23. Limbaje O să iți prezint în continuare limbajele de top utilizate în prezent. Bineî nțeles că nu o să intru în detalii – căci pentru fiecare dintre aceste limbaje se poate scrie (și chiar s-au scris) cărți întregi. Scopul meu e să îți ofer o "hartă” după care să te poți ghida în continuare pe tărâmul programării.
JavaScript Motivul pentru care încep aici cu JavaScript este acela că "experimentele" de programare pe care le-ai realizat pe măsură ce ai parcurs această carte au folosit toate acest limbaj de programare. JavaScript este un limbaj foarte complex (suportând toate cele trei paradigme de programare discutate: imperativă, orientată pe obiecte și funcțională) și nu este chiar indicat pentru începători. L-am ales, totuși, ca limbaj introductiv pentru tine din cauza faptului că rulează practic pe orice calculator (care are un brouwser web instalat). În plus, am construit niște funcții ajutătoare de care te-ai putut folosi fără să știi ce e în spatele lor și am prezentat în lecții doar unele dintre aspectele limbajului. Porțiunea din JavaScript pe care ți-am arătat-o în această carte seamănă destul de mult (cel puțin ca sintaxă (ca aspect al codului sursă, adică)) cu limbajele C, C++, Java și C#. Vei 345
avea plăcuta surpriză să simți că aceste limbaje îți sunt familiare atunci când te vei apuca să le studiezi. O surpriză și mai mare s-ar putea să ai când vei reveni (cu "forțe" de programare sporite) la JavaScript și vei realiza că nu e nicidecum un limbaj de " jucărie”, ci este un limbaj puternic și foarte utilizat. Aproape că nu există site pe internet care să nu folosească și JavaScript. În general codul sursă JavaScript se folosește în interiorul paginilor web (alături de codul sursă HTML) și este interpretat de către bro wser. (Există, însă, și posibilitatea de a-l rula în mod independent, cu ajutorul Node.js.) Închei prin a puncta foarte clar faptul că JavaScript este (în ciuda numelui) foarte diferit de limbajul Java. (La fel cum "lava" și "lavanda" nu prea au multe în comun (în afară de primele patru litere).)
C Cel mai tare limbaj de programare este limbajul C. Nu fiincă ar fi cel mai utilizat sau cel mai modern sau cel mai cuprinzător (ba chiar suportă doar una singură dintre paradigmele de programare discutate – cea procedurală). Ci este cel mai tare fiindcă e cel mai apropiat de microprocesor dintre limbajele actuale. Chiar este considerat de către uni i limbaj nu de nivel înalt (ca JavaScript, de exemplu), ci limbaj de nivel mediu (căci este mai apropiat de limbajul de asamblare al procesorului decât limbajele de nivel înalt tipice). Din aceste motiv el se folosește pentru a implementa sisteme de operare (gen Linux) și drivere pentru diverse dispozitive externe (gen plăci de rețea). Se mai folosește și pentru a programa sisteme cu microcontrolere (cum este Arduino). De asemenea, se folosește pentru a implementa compilatoare și interpretoare pentru alte limbaje. Fiind mai 346
apropiat de structura interna a calculatorului, codul sursă C produce în general programe mult mai rapide decât alte limbaje de programare. Consider că după ce ai învățat bazele programării, e o idee foarte bună să te apuci să înveți C. Este destul de răspândită părerea că orice programator adevărat știe C (chiar dacă lucrează de fapt în cu totul alte limbaje). C e un limbaj compilat care te va expune la ce înseamnă cu adevărat programarea și te ajuta să relaționezi cu calculatorul tău în moduri nebănuite. De exemplu, pe lângă efectuarea de calcule, el îți va permite să accesezi sistemul de fișiere al calculatorului. Adică vei putea citi și modifica fișiere, precum și crea fișiere noi. Principalul dezavantaj e că programele pe care le vei face vor rula doar în terminalul de comenzi (deci datele de intrare și de ieșire vor putea fi doar în format text). (Adică fără grafică și click-uri de mouse. Se pot face și astfel de lucruri în C, dar într-un mod mult prea complicat penru începători. Limbaje ca Java și C# sunt mult mai adecvate pentru astfel de aplicații.)
C++ După ce ai ajuns să te descurci cu limbajul C, următorul pas natural către a deveni programator serios este limbajul C++. Spre deosebire de cazul JavaScript - Java, între C++ și C există multe asemănări. Văzut ca instrucțiune în C numele "C++” ar însemna "C = C+1”, adică o incrementare cu 1, adică o îmbunătățire. Întradevăr, limbajul C++ este o îmbunătățire semnificativă a limbajului C din perspectiva programatorului. Principala îmbunătățire pe care o aduce C++ față de C este introducerea suportului pentru programarea orientată pe obiecte. Simplificând lucrurile, putem spune că C++ = C+POO. 347
C++ este, deci, un limbaj modern, orientat pe obiecte și este compilat (ca și C). Păstrează din C mare parte din apropierea față de structura hardware a calculatorului, motiv pentru care programele rezultate sunt aproximativ la fel de repide ca programele C (dar sunt usor de programat). Multe dintre aplicațiile pe care le folosești pe calculatorul tău sunt programate în limbajul C++. Jocurile, la fel. (Mă refer la cele serioase, cu grafică 3D). Un dezavantaj al limbajului C++ este acela că a fost gândit ca o îmbunătățire a limbajul C și ca un limbaj complet nou. Din acest motiv, el lasă programatorului posibilitatea să amestece POO cu programarea procedurală în moduri care pot predispune la erori. (Limbajele Java si C#, în schimb, au fost gândite din start din perspectiva POO.)
Java Programele scrise în Java rulează în prezent pe aproape orice dispozitiv care conține un microprocesor (laptop, tabletă, telefon). Acest limbaj a fost gândit încă de la cearea lui pentru realizarea de programe portabile. Adică programe care să funcționeze la fel indiferent de sistemul de operare (Windows, Linux, Mac) si de tipul microprocesorului (pe 32 sau pe 64 de biți). Acest lucru a fost posibil prin utilizarea conceptului de mașina virtuală. Adică programele Java nu sunt compilate pentru procesorul calculatorului pe care se face compilarea, ci sunt traduse în instrucțiuni pe care le înțelege mașina virtuală Java (JVM – Java Virtual Machine). Aceasta este un program care simulează un calculator virtual ce rulează pe calculatorul real. Astfel, atâta timp cât există o JVM pentru Wimdows, programele Java vor rula pe Windows. Dacă vrem să ruleze nemodificate (adică fără a fi recompilate) și pe Linux, ne trebuie doar o JVM pentru Linux. Din fericire, există JVM-uri 348
pentru cam toate sistemele de operare. Limbajul Java îl regăsim și în contextul sist emului de operare Android. Mașina virtuală Dalvik (pe care rulează aplicațiile de Android) seamănă mult cu o JVM, iar limbajul de bază pentru realizarea de aplicații pentru Android este Java. Așa cum ziceam, în Java paradigma programării orientate pe obiecte este impregnată în însăși structura limbajului, ceea ce obligă programatorul să gândească programele în acest mod – ceea ce le face mai clare, mai ușor de gestionat și de reutilizat și mai puțin predispuse la erori. În plus, faptul că programele Java nu rulează direct pe microprocesor, ci pe o mașină virtuală, permite o securitate sporită (în sensul că programatorul nu mai poate face chiar tot ce vrea cu calculatorul; deci Java nu e chiar așa indicat pentru scrierea de viruși cum e C++ (și mai ales C)). De asemenea, mașina virtuală poate simplifica munca programatorului prin realizarea automată a unor sarcini care în C și C++ îi revin lui – cum ar fi eliberarea memoriei alocate dinamic. În programele Java această eliberare se face automat de către "colectorul de gunoi" (garbage collector).
C# Numele limbajului C# se citește "C sharp” (adică ”si șarp" (fonetic)). Adică C# este un "C ascuțit”. Nu este doar un C îmbunătățit (cum este C++), ci este este un C total regândit. Ideea companiei Microsoft a fost să facă un limbaj și mai bun decât Java. Dacă s-a reușit asta sau nu este o problemă încă intens dezbătută. Ca și programele Java, programele C# rulează pe o mașină virtuală (CLR – Common Language Runtime, care face parte din .NET Framework). Spre deosebire de mașina virtuală Java, mașina virtuală C# nu este disponibilă chiar pe toate platformele. Dar acest lucru este pe cale să se schimbe – au apărut versiuni pentru Linux si pentru Mac OS (dar nu sunt 349
100% compatibile cu versiunea pentru Windows). Pe lângă asta, e de notat faptul că au apărut instrumente software care permit construirea de aplicații Android folosind limbajul C#. Limbajul C# suportă – pe lângă programarea imperativă și programarea orientată pe obiecte – și unele aspecte de programare funcțională. Din acest punct de vedere nici Java nu este mai prejos, căci începând cu versiunea Java 8 au fost introduse în limbaj unele aspecte din programarea funcțională). Atât Java, cât și C#, se folosesc intens în prezent pentru realizarea de aplicații web – adică aplicații al căror "motor” rulează pe un server, iar interfața prin care utilizatorul folosește aplicația rulează pe browsere web de pe alte calculatoare din rețea (sau din internet). Cei care lucrează la astfel de aplicații trebuie să cunoască, deci, și limbajul browserelor web – HTML. (Î n plus, aplicațiile serioase își organizează datele în baze de date, deci programatorii trebuie să cunoască și limbajul prin care pot comunica cu acesta – SQL).
HTML Limbajul internetului este HTML. Indiferent ce site accesezi prin intermediul oricărui browser web (Chrome, Firefox, Opera etc.), ceea ce vezi pe ecran are în spate un text ce conține un cod sursă HTML. HTML nu este propriu-zis un limbaj de programare. (Nu suportă niciuna dintre paradigmele de programare discutate). Ce este, atunci, HTML? Este un limbaj de specificare a formatului. Adică este un limbaj de descriere în format text a documentelor (sau a paginilor) ce pot conține texte (de diferite culori, fonturi, dimensiuni și poziționări î n pagină) și imagini (de diferite dimensiuni și poziționări în pagină). Limbajul HTML nu permite folosirea variabilelor și nici a unor 350
instrucțiuni de tip "dacă” și "cât timp”. E foarte posibil să stârnești indignarea (sau măcar zâmbetul îngăduitor) programatorilor avansați dacă vei denumi HTML-ul "limbaj de programare”. Repet: nu este un limbaj de programare, ci este un limbaj de descriere a conținutului documentelor. Paginile descrise în HTML pot conține texte, imagini și legături către alte pagini. Învățarea acestui limbaj este foarte facilă pentru începători (căci le dă cât de cât o imagine asupra ce î nseamnă programarea fără să-i introducă cu adevărat în programare) și este practic obligatorie pentru avansați. În general, limbajul HTML nu este folosit singur, ci împreună cu limbajul CSS (pentru specificarea stilului diverselor componente ale paginii web) și cu limbajul JavaScript (pentru adăugarea de "logică” și "dinamism” paginii). Dacă folosirea JavaScript-ului împreună cu HTML pare destul de naturală (căci HTML-ul singur nu permite programare, ci permite doar realizarea de pagini statice, fără calcule și alternative), utilitatea limbajului CSS poate naște intrebări, nu? Cum adică prin CSS specifici stilul elementelor din pagină? Nu asta face și HTML? Ba da, dar ce te-ai face dacă ai vrea să modifici întreaga "față” a site-ului rapid? Cum ai face, de exemplu, să modifici culoarea tuturor subtitlurilor din pagină? Ai căuta manual în fișierul HTML toate subtitlurile și ai face acolo modificările necesare? N-ar fi (incomparabil) mai ușor să modifici o singură valoare în fișierul CSS atașat HTML-ului acelei pagini?
SQL Bazele de date sunt nelipsite în orice aplicație de anvergură. Ele oferă o modalitate facilă de depozitare și căutare a unei cantități foarte mari de date organizate sub formă de tabele cu legături între ele. 351
SQL este un limbaj ce permite interacționarea cu astfel de baze de date. Modalitatea fizică de stocare și căutare a datelor nu este accesibilă direct utilizatorului bazei de date. Utilizatorul sau aplicația ce lucrează cu respectiva bază de date interacționează cu ea prin intermediul unei aplicații numită "sistem de gestiune a bazelor de date" (cum este, de exemplu, MySQL) care " înțelege" limbajul SQL. În SQL se pot adăuga/șterge tabele la/din baza de date, se pot adăuga/modifica date la/din tabelele existente și se pot căuta date simultan în toate tabelele în funcție de indeplinirea unor criterii complexe (permițând, deci, selectări, filtrări și ordonări). SQL este mult mai apropiat de un limbaj de programare decât este HTML, dar este un limbaj foarte specific, nefiind nici pe departe la fel de puternic ca limbajele de programare discutate aici.
PHP Dacă tot am vorbit despre HTML, CSS, JavaScript și SQL, simt nevoia să pomenesc și limbajul PHP. Programele PHP rulează pe serverul web, adică pe acel calculator care trimite către browserul din calculatorului tău date în format HTML atunci când browserul îi cere să vadă ce e la adresa http://www.pagina-pe-care-o-vrei.com. În loc de un fișier HTML, gen "pagina.html”, pe server se poate găsi un fișier "pagina.php", care poate conține atât cod HTML, cât și instrucțiuni în limbajul PHP. PHP este un limbaj de programare în adevăratul sens al cuvântului (nu ca HTML, CSS și SQL), așa că fișierele PHP pot conține programe complexe care generează și "asamblează” în mod dinamic fișiere HTML (aunci când browserele clienților le cer) ținând cont de condiții complexe și putând interacționa cu baze de date prin intermediul utilizării de comenzi SQL. 352
PHP este un limbaj imperativ, procedural, dar suportă și POO (ca și unele elemente de programare funcțională). Nu este compilat, ci fișierele PHP sunt interpretate pe server la momentul în care un client solicită serverului respectivul fisier PHP. Majoritatea magazinelor online și a site-urilor pe care te poți loga cu un cont de utiulizator și parolă au în spate PHP și baze de date. În particular, platforma ”Wordpress” (care permite dezvoltarea ușoară de bloguri și site-uri complexe) este bazată pe PHP și MySQL.
Python Știu că ți-am dat deja suficiente subiecte de studiu până aici, dar nu strică să pomenesc și limba jul Python. Un limbaj interpretat, multi-paradigmă, puternic și concis. Mulți îl recomandă ca limbaj de inițiere în programare datorită clarității și conciziei sale. A fost gândit astfel încât să nu lase programatorului mai multe variante de a realiza un același lucru, ci în Python există, în general, o singură variantă (corectă și clară) de a efectua orice acțiune. E un limbaj foarte utilizat (inclusiv de către Google (Alphabet)) așa că este foarte probabil să fi auzit deja de el. Deși în România nu este la fel de căutat ca în afară, nu strică să cunoști și limbajul Python (dar asta doar după ce știi C++ și/sau Java și/sau C# și/sau JavaScript – care sunt mult mai căutate la noi în țară).
Erlang Închei această discuție despre limbaje de programare cu un limbaj de care e mai puțin probabil să fi auzit. Este vorba despre Erlang – un limbaj funcțional dedicat construirii de aplicații extrem de fiabile. 353
Deși nu îți recomand nicidecum învățarea acestui limbaj ca o prioritate (mai ales că (așa cum am mai spus) programarea funcțională poate pune probleme și unora dintre programatorii avansați), ea este o direcție pe care nu strică să o ai în vedere pe termen lung. Erlang e un limbaj interpretat ce permite realizarea de aplicații distribuite cu un înalt grad de fiabilitate. (Adică părți din program pot fi î nlocuite în timpul rulării, fără a fi necesară oprirea și repornirea lui. De asemenea, dacă un anumit subsistem "pică” (cedează), sistemul per ansamblu continuă să funcționeze). Limbajul Limbajul a fost gândit pentru a permite realizarea de programe în care o eroare să nu ducă la blocarea întregului program sau la comportamente impredictibile. Dacă în celelalte limbaje programatorul trebuie să acorde o atenție deosebită tratării tuturor erorilor posibile ce pot apărea pe parcursul rulării programului; în Erlang se încurajează filosofia "Let it crash” – "Lasă-l să cedeze”. Programele Erlang sunt în mod tipic compuse din numeroase procese ce rulează în pararel și pot comunica unele cu altele. Dacă un proces cedează, un alt proces (care îl monitorizează) îl poate reporni cu ușurință. (Rularea în paralel a proceselor este, de fapt, o altă paradigmă de programare – programarea concurentă (sau programarea paralelă). Ea se referă la considerarea programelor ca ansambluri formate din mai multe "procese” sau "fire de execuție” sau "sarcini” ce rulează în paralel, în mod independent unele față de altele (dar putând fi pornite/oprite unele de către altele și putând comunica între ele). Elemente de programare concurentă se întâlnesc nu doar în Erlang, ci și în alte limbaje (ca Java, C#, C++, Python).) Cu toate că nu e o prioritate pentru tine acum, învățarea limbajului Erlang îți poate oferi niște satisfacții intelectuale 354
deosebite după ce vei intra mai adânc în tărâmul programării. (Faptul că tu (poate că) nu ai auzit încă de acest limbaj, nu înseamnă că Facebook nu îl folosește pentru a-și implementa chat-ul prin care comunici cu prietenii tăi online direct din browser.)
355
24. Aplicaţii Cu toate că unele lucruri ți le-am mai spus pe parcurs, consider necesar să pun aici cap la cap și să completez informațiile sporadice si separate referitoare la aplicațiile practice ale limbajelor de programare prezentate (și ale programării, în general). Cu alte cuvinte, vreau să îți răspund aici la întrebarea următoare: Ce poți să faci cu ajutorul programări? Răspunsul este simplu: (Cam) orice. De fapt, orice program pe care l-ai folosit sau văzut sau de care ai auzit vreodată. Ba mai mult – cu suficientă creativitate și istețime poți construi programe care nu s-au mai văzut niciodată. Mă voi limita aici la a-ți face o clasificare a tipurilor de programe cel mai întâlnite în prezent.
Sisteme de operare Sistemul de operare este acel "program” care rulează atunci când îți pornești calculatorul (sau telefonul) și îți permite să-l folosești (adică să rulezi programe, să le instalezi sau să le dezinstalezi). În realitate, sistemul de operare nu este un singur program, ci este o îmbinare a mai multor "programe speciale”. Sunt mai speciale fiincă au un "contact” mult mai direct cu calculatorul decât celelalte programe pe care le folosești în mod normal. Sistemul de operare oferă celorlalte programe un cadru în care să ruleze în bune condiții unele alături de altele. 356
Cele mai întâlnite sisteme de operare în prezent sunt: Windows, Linux, Mac OS (pentru calculatoare) și Android și iOS (pentru telefoane). O categorie de programe foarte apropiate de sistemele de operare sunt driverele. Ele sunt niște programe care intermediază comunicarea dintre alte programe și dispozitivele hardware conectate la calculator. De exemplu, placa de rețea este un simplu circuit conectat la calculator și n-ar folosi la nuimic dacă n-ai avea instalat un driver care să îi "vorbească” pe "limba" ei. Programarea de sisteme de operare și mai ales de drivere este o treabă mai apropiată de electronică decât de informatică (căci necesită și unele cunoștințe despre hardware-ul calculatorului și despre modul lui de funcționare fizică). Limbajul de programare cel mai utilizat pentru acest gen de programe este limbajul C.
Aplicații de sine stătătoare În această categorie intră acele aplicații de calculator pe care dai dublu click și pornesc. În general ele deschid o "fereastră” prin intermediul căreia utilizatorul poate reacționa cu aplicația. Unele sunt direct executabile de către sistemul de operare, (cum este cazul aplicațiilor realizate în C++), iar altele sunt rulate de către o mașină virtuală (cum este cazul aplicațiilor realizate în Java sau C#). Ca să î ți fie și mai clar aici la ce se refer, îți zic că programe care se încadrează în această categorie sunt browserele web, aplicațiile de tip Office, editoarele de imagini (gen Photoshop), cititoarele de PDF-uri (ca Acrobat Reader) și jocurile complexe. Cele mai utilizate limbaje pentru realizarea acestui gen de programe sunt C++, Java și C#. 357
Aplicații web Spre deosebire de aplicațiile discutate anterior, aplicațiile web sunt compuse din două în două părți: - o parte care rulează pe server (care este un calculator central la care se pot conecta (eventual) mai mulți clienți) - și o parte care rulează pe client (adică pe calculatorul utilizatorului). Cea mai mare parte a "muncii” aplicației o face partea de pe server, partea de pe client ocupându-se de sarcini mai ușoare, cum ar fi afișarea și introducerea datelor, precum și lansarea comenzilor către server și afișarea rezultatelor primite de la acesta. Având în vedere cantitatea redusă de muncă pe care trebuie să o facă clientul, în ultima vreme s-a renunțat aproape total la realizarea aplicațiilor de tip client, rolul lor fiind preluat de către browserele web. Prin urmare, partea de client se găsește tot pe server, sub forma unui server web si a unor fișiere HTML (+CSS, +Javascript). Generarea acestor fișiere se poate face folosind programe PHP, dar poate fi făcută și cu programe scrise în Java sau în C#. La fel cum există site uri care au " în spate” doar PHP, exista site-uri care au în spate doar Java sau doar C#. Domeniul acesta s-a dezvoltat extrem de mult, dar ai mari șanse să te adaptezi rapid dacă stăpânești HTML, CSS, Javscript (plus biblioteci ca jQuery si framework-uri gen AngularJS), PHP, SQL, Java și/sau C#.
Aplicații mobile În această categorie intră aplicațiile pe care le folosești pe telefonul tău. Bineî nțeles că atâta timp cât pe telefon ai un browser web poți accesa aplicații web de genul celor despre care am vorbit mai sus. Dar mă refer aici la echivalentul 358
aplicațiilor de sine stătătoare ce ruleazp pe calculatoare, doar că pentru telefoane mobile. Pentru sistemul de operare Android astfel de aplicații se realizeaza folosind limbajul Java. (Exista și posibilitatea de a utiliza alte limbaje (C#), dar varianta "naturală” este Java). Pentru sistemul de operare iOS limbajul utilizat pentru realizarea de aplicații a fost până recent ObjectiveC, fiind înlocuit acum cu Swift. Nu ți-am recomandat acest limbaj fiincă nu este nici pe departe atât de general utilizat ca limbajele pe care ți le-am pomenit.
Uau! O adevărată " junglă” programarea asta, nu? (Ca să nu îți mai zic că ceea ce ți-am prezentat în acest modul a fost doar un "schelet”, o "hartă” cu pricipalele "autostrăzi”, și nicidecum o tratare în adâncime a subiectului). Nu lăsa multiudinea asta de limbaje și tehnologii să te copleșească. Nu trebuie neapărat să le știi pe toate pentru a fi un bun programator. Trebuie doar să știi bazele și apoi să î ți lași pasiunea și interesul să te ghideze în alegerea drumului personal de urmat. Sper ca "harta” pe care ți-am oferit-o în acest modul să te ajute să faci cea mai bună alegere pentru tine.
359
Concluzie Gata. S-a încheiat abecedarul de programare. Așa că acum... Abracadabra! Ești programator! Ba nu. Nu neapărat! Știi bine că lucrurile în lumea reală nu se mișcă așa. Nu prin formule magice și nici prin scurtături leneșe sau prin fugi de efort harnice ajungi acolo unde îți dorești. Ci prin acțiune constantă și răbdare. Iar pe astea două doar joaca le poate ține în picioare atunci când uraganul tăcut al deznădăjduirii dă peste tine. Așa că te rog (din nou) să te joci. Ia în serios programarea, dar învățarea ei transform-o într-o joacă. Te-ai uitat vreodată la copiii captivați de vreun joc? Copii foarte energici și gălăgioși în rest, dar care în momentele acelea sunt atât de absorbiți de ceea ce construiesc încât nu mai sunt conștienți parcă de nimic din jurul lor. O astfel de pasiune captivantă pentru programare îți urez și ție. Nu te aștepta, însă, ca starea asta să-ți vină din senin, fără niciun efort din partea ta. Un efort va trebui să faci (cel puțin la început), dar efortul ăsta va fi mic dacă îți vei lega învățarea programării de alte pasiuni, interese sau motivații interioare. (De exemplu, pe mine m-a împins către acest domeniu pasiunea pentru grafică și pentru jocuri.) 360
Poate că acum, la finalul acestui abecedar de programare, cel mai bun sfat pe care ți-l pot da e să-l parcurgi încă o dată și să te asiguri că stăpânești toate noțiunile de programare (și cele câteva noțiuni de matematică) prezentate în el. Sunt lucruri esențiale, de care nu vei scăpa indiferent de limbajele de programare pe care vei alege să le studiezi în continuare. Spor și fascinație îți urez!
361