cum iti dresezi calculatorul sa raspunda la comenzi noi
Daca ai urmarit atent lectiile din articolele anterioare, pana la momentul de fata deja stapanesti bazele programarii calculatoarelor. Am ajuns sa facem impreuna programe destul de complexe si suntem la doar un pas de a construi primul joc.
Revino, te rog, la lectia anterioara si reia ultimul program pe care ti l-am oferit ca exemplu. Dupa cum ai vazut, programul mai intai initializa cu anumite valori elementele unei matrici (binare — adica doar cu valori de 1 si de 0), dupa care aprindea pe ecran punctele corespunzatoare acelor elemente din matrice cu valoarea 1. Urma o pauza, dupa care elementele matricii erau inversate si apoi se repeta operatiunea anterioara (folosind de asta data, bineinteles, noile valori ale elementelor matricii).
Ai remarcat, probabil, ca programul rezultat a fost destul de lung. De asemenea, cred ca ai remarcat faptul ca este presarat cu comentarii care sa ajute la intelegerea rapida a lui. In ciuda lor, insa, programul are un aer destul de complicat.
In lectia aceasta vei afla cum sa faci mai multe cu mai putine. Mai precis, vei invata cum sa inveti calculatorul sa execute alte functii (comenzi) fata de cele pe care le cunostea deja. Iar apoi, folosind aceste noi functii definite de catre tine, vei putea face programe mai scurte si mai puternice.
Sa vedem, mai intai, la ce ne-ar ajuta definirea unei noi functii in cazul programului de care discutam (si anume ultimul din lectia despre vectori si matrici).
Nu necesita un efort deosebit sa observam faptul ca secventa de program ce realizeaza aprinderea tuturor punctelor de pe ecranul de 10×10 puncte conform valorilor (1 — aprins, sau 0 — stins) din matricea m (de 10 linii si 10 coloane, bineinteles) se repeta. Ba chiar se repeta in mod identic aceasta secventa de program. N-ar fi asadar, mult mai simplu sa o scriem o singura data, sa-i dam un nume, si apoi s-o apelam folosind acel nume?
Ba sigur ca da. Exact asta facem cu instructiunea “functie” (function). Ea se scrie intr-un program in felul urmator:
function NumeleFunctiei()
{
// corpul functiei; instructiunile
// …
}
Spre deosebire de celelalte instructiuni de pana acum, instructiunea function nu are un efect imediat si nu intervine direct in functionarea programului. Ci ea este o instructiune cu rol declarativ, care va spune calculatorului ca prin comanda NumeleFunctiei() ma voi referi in program la instructiunile care se regasesc in corpul functiei.
Pentru un exemplu concret, iata cum ar arata ultimul program din articolul anterior, scris de asta data cu ajutorul unei functii pe care aleg sa 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()
{
// 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
}
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)
AprindeEcran()
Iata si caseta de testare a programului.
Ce parere ai de noul program? Nu e mai simplu sa scrii doar AprindeEcran() decat sa scrii (de doua ori) intreg acel bloc de instructiuni?
Banuiesc ca te astepti deja ca urmeaza un exercitiu pentru tine. Ei bine, ia incearca sa faci si inversarea starii punctelor tot printr-o functie (la fel cum am facut pentru AprindeEcran). (Deci programul tau va contine in partea initiala pe langa definirea variabilei m si a functiei AprindeEcran, si definirea acestei noi functii — pe care o poti numi, de exemplu InverseazaPuncte.)
Buuun, cu asta ai invatat si functiile, nu? Nu inca!
De ce? Pentru ca ceea ce am facut pana aici e doar un caz particular de utilizare a functiilor (si anume utilizarea lor ca pe un soi de macro-intructiuni).
In cazul cel mai general, insa, o functie poate sa primeasca niste parametri si sa returneze o valoare (exact ca in cazul unei functii matematice).
Hai sa vedem mai intai cum sta treaba cu functiile care au parametri.
Sa zicem ca ne dorim sa construim o functie care sa deseneze un dreptunghi pe ecranul nostru virtual de 10×10 puncte. Dorim, insa, ca functia asta sa fie capabila sa deseneze nu doar un anumit dreptunghi, ci orice dreptunghi. Prin urmare, este necesar sa specificam dreptunghiul dorit prin intermediul unor parametri.
Putem, de exemplu, sa specificam dreptunghiul cu ajutorul coordonatelor (pe axa x (orizontala) si pe axa y (verticala)) punctului din stanga-jos si ale celui din dreapta-sus. Sa denumim aceste coordonate prin (x1, y1) si (x2, y2).
In conditiile acestea, cum ar arata o functie (sa-i zicem, de exemplu, DeseneazaDreptunghi) care sa deseneze pe ecran dreptunghiul ce are coltul din stanga-jos la coordonatele (x1, y1) si coltul din dreapta-sus la coordonatele (x2, y2)? Iata o varianta 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:
// 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)
// 5) nimic
DeseneazaDreptunghi(8, 8, 2, 2)
(Imi poti spune de ce in cazul ultimului apel al functiei (varianta 5 de mai sus) nu se deseneaza nimic pe ecran?)
Dupa cum ai observat (daca ai testat programul), functia DeseneazaDreptunghi pe care am definit-o se foloseste de valorile a patru parametri, si anume x1, y1, x2 si y2.
Si daca ne uitam in corpul ei vedem ca mai intai defineste o variabila locala numita x cu ajutorul careia parcuge (folosind un while in combinatie cu incrementarea cu 1 din final (adica instructiunea x = x+1)) toate valorile de la x1 la x2. Si apoi, in corpul while-ului (deci pentru fiecare valoare a lui x intre x1 si x2 inclusiv) apeleaza de doua ori functia predefinita Aprinde — mai intai pentru a aprinde punctul de la coordonata (x, y1) si apoi pe cel de la coordonata (x, y2). Prin urmare dupa finalizarea executiei acestui prim while se vor fi desenat pe ecran doua dintre laturile dreptunghiului (si anume cea de jos si cea de sus).
In mod similar partea a doua a functiei deseneaza celelalte doua laturi ale dreptunghiului (si anume latura din stanga si latura din dreapta).
Super, nu? Folosindu-ne de o functie deja existenta (Aprinde) am construit o functie mai complexa (DeseneazaDreptunghi). In felul acesta nu numai ca putem scrie programe mai scurte si mai clare, ci putem scrie programe generice, a caror functionare sa poata fi usor modificata prin modificarea valorilor unor parametri.
(Mentionez cu aceasta ocazie ca DeseneazaDreptunghi poate fi apelata si folosind variabile pe post de parametri. Cu alte cuvinte, pot scrie in program ceva de genul DeseneazaDreptunghi(csjx, csjy, 8, 9), in conditiile in care variabilele csjx si csjy au fost in prelabil definite si initializate cu niste valori.)
A mai ramas un singur caz de functii pe care as vrea sa-l discut acum. Este vorba de functiile care returneaza o valoare.
Care e treaba cu ele? Este cam acelasi lucru pe care il facem la matematica atunci cand scriem y = f(x). f este functia, x este parametrul cu care ea este apelata, iar y este variabila in care memorez valoarea returnata de functia f pentru parametrul x.
In esenta functiile ce returneaza o valoare nu sunt diferite de de functiile de care am vorbit pana acum. Ba chiar orice functie poate sa returneze o valoare. Cu conditia ca in corpul ei sa existe intructiunea return valoare (unde in loc de valoare se poate pune fie o valoare numerica, fie o variabila in care este memorata o valoare, fie o expresie matematica (ce poate cuprinde valori numerice, operatori, variabile)).
In momentul in care o astfel de intructiune “returneaza” (return) este executata, executia functiei se finalizeaza si valoarea returnata de ea este pusa in program acolo unde functia a fost apelata.
Aceste cuvinte poate ca par mult mai complicate decat stau lucrurile in realitate, asa ca haide sa vedem un exemplu.
Sa zicem ca desenam pe ecran un dreptunghi (cu functia DeseneazaDreptunghi facuta anterior), dupa care dorim sa desenam un punct in centrul lui. Ce-ar fi sa facem o functie cu ajutorul careia sa calculam acest centru?
In regula. Hai sa vedem ce ar trebui sa facem pentru a obtine centrul dreptunghiului, in conditiile in care avem coordonatele (x1, y1) ale coltului din stanga-jos si (x2, y2) ale coltului din dreapta-sus.
Pai, pentru a obtine coordonata pe axa x a centrului ar trebui sa facem media aritmetica intre x1 si x2. Adica (x1+x2)/2. Si apoi ar trebui sa rotunjim valoarea obtinuta pentru a obtine o valoare intreaga pe care s-o putem folosi pentru a selecta una dintre cele 10 coloane de pe ecranul virtual. (Putem rotunji o valoare reala la un numar intreg folosind functia predefinita Rotunjeste.)
Si similar pentru y.
Prin urmare, daca am face o functie care sa calculeze media dintre doua numere si sa returneze rezultatul rotunjit la o valoare intreaga, atunci am putea folosi aceasta functie atat pentru a calcula coordonata pe axa x a centrului dreptunghiului, cat si coordonata sa pe y.
Hai sa vedem ce ar iesi:
// 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
}
}
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)
Gata si lectia asta. La fel ca si cele de pana acum, nici ea nu se invata singura, insa. Este nevoie de ceva efort din partea ta. Dar daca ai in minte un proiect interesant pe care iti doresti sa il aduci la lumina, acest efort va deveni simplu si captivant ca o joaca.
Spune-mi, te rog, in comentariul tau daca ai neclaritati. De asemenea, spune-mi daca deja a inceput sa ti se contureze in minte un joc pe care l-ai putea face folosind calculatorul nostru virtual.
Multumindu-ti ca esti alaturi de mine in lupta pentru o Romanie mai logica si mai creativa,
Al tau prieten,