Pagina 2 din 6

BGT Curs 4 - Functiile - Partea 1

Scris: 19 Apr 2012, 03:47
de Manu
Marti, 10 aprilie 2012, ne-am intalnit pentru a patra oara in cadrul cursului nostru de programare in BGT.

Am apucat sa vorbim despre o parte din problemele legate de functii.

Deja am utilizat in intalnirile anterioare functii existente in BGT precum exit(), alert(), wait() sau random(). Aceste functii fac un anumit lucru: efectueaza o actiune sau returneaza o valoare. De exemplu alert() face o actiune, afiseaza o fereastra cu buton Ok, exit() scoate tot ceea ce tine de BGT din memorie etc, pe cand functia random() returneaza o valoare, un numar aleatoriu.
In BGT exista cateva sute de functii built-in, acestea fiind gasibile cu documentatie si exemple in manualul care devine disponibil in urma instalarii kitului de la Blastbay Studios.

Explicand cat mai simplu posibil, o functie este un nume care se refera la un bloc de cod care face ceva:
- ori o actiune fara returnarea unei valori,
- ori o actiune cu returnarea unei valori,
- ori doar returneaza o valoare fara o actiune perceputa de noi.
De exemplu functia random() care returneaza o valoare are si un bloc de cod care face ca numarul aleatoriu sa fie returnat, dar fiind o functie nativa / built-in, noi nu putem vedea acest cod care sta la baza.

Am observat ca atunci cand apelam o functie in cadrul unui script, numele acesteia este urmat de paranteze rotunde, deschisa si inchisa. Intre aceste paranteze pot fi sau nu parametri.
Parametrii pot fi oricati, inclusiv niciunul, intre ei punandu-se "," (virgula).
Acesti parametri sunt practic niste valori de care are nevoie o functie, dupa caz, pentru a se achita de sarcina asa cum vrem noi. De exemplu functia random() are nevoie de doi parametri, numarul minim si numarul maxim intre care sa fie generat numarul aleatoriu de returnat. Functia alert() are nevoie de doi parametri de tip string, unul reprezentand titlul, al doilea reprezentand corpul ferestrei cu buton Ok. Functia wait() are nevoie doar de un parametru de tip int (numar intreg) care sa specifice numarul de milisecunde pentru care rularea scriptului sa fie intrerupta. Functia exit() nu are nevoie de parametri, insa parantezele nu lipsesc.

BGT / C++ fiind un limbaj static typed, este foarte important tipul valorii returnate.
Avem functii care returneaza un numar intreg, aceasta inseamna ca functia este definita de tip int, avem functii care returneaza un string, deci functie definita string, la fel double sau bool, dar avem si functii care nu returneaza nimic, acestea fiind definite ca void.

De exemplu functia random() este de tip int pentru ca returneaza un numar intreg, numarul aleatoriu generat intre parametri dati. Inseamna ca putem atribui valoarea returnata de random() doar unei variabile de tip numar; asadar putem considera corect urmatorul cod:

Cod: Selectaţi tot

void main()
{
int numar_aleatoriu = random(1, 6);
alert(“Salut!”, “Un numar aleatoriu este ”+numar_aleatoriu+”.”);
exit();
}
Nu este insa corect urmatorul cod care la prima vedere ar induce in eroare, fiind asemanator:

Cod: Selectaţi tot

void main()
{
string numar_aleatoriu = random(1, 6);
alert(“Salut!”, “Un numar aleatoriu este ”+numar_aleatoriu+”.”);
exit();
}
Eroarea in cazul celui de-al doilea exemplu este ca tipul variabilei nu se potriveste cu tipul returnat de functie, am incercat sa atribuim unei variabile de tip string o valoare de tip int.

Un exemplu de functie care returneaza string ar fi string_reverse(). Aceasta functie returneaza un text inversat, scris de la coada la cap. Are nevoie de un parametru, string-ul de inversat:

Cod: Selectaţi tot

void main()
{
// Cream o variabila cu numele textul si ii dam o valoare:
string textul = “Manu”;
// Atribuim variabilei textul valoarea pe care o avea, dar inversata:
textul = string_reverse(textul);
alert(“Salut!”, textul);
/*
In corpul ferestrei alert va aparea string-ul “unaM”, de remarcat ca litera “M” ramane majuscula, chiar daca ajunge sa fie la capat de string.
*/
exit();
}
Referitor la codul de mai sus, trebuie deja sa fim convinsi ca nu am fi putut scrie ca a doua instructiune:
int text = string_reverse(textul);
nu am fi putut atribui valoarea string returnata de string_reverse unei variabile de tip int cum ar fi fost int text.

Functia exit() este de tip void, aceasta nu returneaza nici o valoare. Inseamna ca nu putem scrie o instructiune de tipul:
int x = exit();


Putem defini si noi functii pentru diferite actiuni, calcule. Avand functii definite pentru anumite sarcini, ne vom putea folosi in viitor de ele apelandu-le oriunde in cadrul scriptului nostru.
Avem sa zicem nevoie de o functie care sa adune doua numere si sa returneze rezultatul sub forma de int (numar intreg). Putem crea pentru aceasta o functie numita adunare(), functie pe care o vom putea utiliza apoi oriunde.

Pentru a defini o functie, sablonul este urmatorul:

Cod: Selectaţi tot

tip nume_functie(parametri)
{
// Aici un cod oarecare.

// Urmatorul rand doar daca tip nu este egal cu void:
return valoare;
}
Luand-o pas cu pas, explicam fiecare element al sablonului de mai sus, dupa care cream functia adunare() de care vorbeam.

Primul rand este:
tip nume_functie(parametri)

tip poate fi egal cu string, int, float, double sau bool, depinde ce tip de valoare vrem sa returneze in final functia. Daca nu vrem sa returneze vreo valoare ci doar sa faca o actiune, vom scrie void.

nume_functie poate fi orice text, important e ca nu poate incepe cu o cifra si poate contine doar litere, cifre sau semnul “_” (underline). E bine sa denumim cat mai sugestiv functiile, astfel incat sa ne amintim usor de ele; de exemplu daca cream o functie care sa faca o adunare, aceasta se poate numi… cum altfel decat “adunare”.

Intre parantezele rotunde vor aparea parametrii daca e cazul. Acestia vor fi definiti precum niste variabile. Daca avem de exemplu nevoie de doi parametri de tip int, am putea scrie intre paranteze (int nr1, int nr2).

Urmeaza blocul de cod la care se refera numele functiei pe care o cream, desigur acesta fiind intre acolade.
In acest bloc de cod pot fi oricate instructiuni, oricate alte functii built-in sau definite de noi, important e ca in final, daca functia nu a fost de tip void, sa apara instructiunea pentru returnarea valorii.
Pentru a returna valoarea scriem:
return val;
val poate fi ori continutul unei variabile, ori un calcul facut pe acelasi rand, ori un text scris intre ghilimele in cazul unei functii de tip string etc.

Sa presupunem ca am fi creat noi functia random(), incercam sa modificam sablonul cu date mai concrete.
Stim ca este o functie care returneaza int si stim ca are doi parametri de tip int, atunci sablonul va arata astfel:

Cod: Selectaţi tot

int random(int numar_minim, int numar_maxim)
{
/*
In locul acestui comentariu, ar putea fi codul pentru generarea numarului aleatoriu.
Variabilele care vor prelua parametrii au fost denumite la intamplare, nu conteaza numele pe care le dam.
In cadrul blocului de cod al acestei functii, cand vrem sa ne referim la valoarea primului parametru vom folosi numar_minim ca pe o variabila normala, la fel si cu al doilea parametru, numar_maxim.
Aici se termina comentariul.
*/

// Presupunand ca mai sus am generat numarul aleatoriu atribuindu-l unei variabile numite x, scriem mai jos:
return x;
}
Sa cream acum o functie care sa adune doua numere, un exemplu cam stupid, dar util pentru a intelege mai exact cum stau lucrurile.
Vrem asadar sa adunam doua numere de tip int (numar intreg) si apoi sa fie returnat rezultatul sub forma tot de int.
Avem nevoie de doi parametri, cele doua numere de adunat:

Cod: Selectaţi tot

int adunare(int nr1, int nr2)
{
// cream o variabila locala numita temp:
int temp;
// Atribuim acestei variabile rezultatul adunarii:
temp = nr1+nr2;
// Returnam valoarea variabilei temp:
return temp;
}
Avand asadar functia adunare, vom putea crea urmatorul script:

Cod: Selectaţi tot

void main()
{
int rezultat;
rezultat = adunare(1, 5);
alert(“Salut!”, “Rezultatul este ”+rezultat+“.”);
// ar trebui sa apara 6.
exit();
}
Pentru ca scriptul de mai sus sa functioneze, trebuie asadar sa avem definita functia adunare(), aceasta inseamna ca trebuie sa existe in acelasi fisier cu void main() sau intr-un fisier inclus. Despre includerea fisierelor vom discuta ulterior.

Mai cream o ultima functie de tip void in doua exemplare.
Sa zicem ca avem nevoie in unele locuri in script de o functie care sa inchida programul. O prima idee va fi: putem folosi exit() oricand, o functie deja existenta in BGT. Dupa o analiza mai atenta, vom vedea ca o inchidere brusca a unui joc nu este atat de placuta, poate vrem sa fie afisat si un mesaj de ramas bun.
Asadar, am avea nevoie de doua randuri de cod, una cu alert-ul care sa afiseze mesajul si unul cu functia exit() care sa inchida totul.
Am putea scrie cele doua randuri direct in cod fara sa cream o functie pentru aceasta, dar, poate in viitor dorim sa mai adaugam o instructiune, sa se auda si un sunet. Ar insemna sa cautam toate locurile din program / joc unde este posibila inchiderea si sa adaugam randul pentru sunet. Avand insa functia definita de noi pentru inchidere, aceasta este apelata peste tot unde este necesar, dar instructiunile care ii alcatuiesc blocul de cod sunt scrise doar intr-un loc, in locul definirii, fapt care va face sa ne fie foarte usor sa modificam cum si cand vrem actiunile care sa se intample la inchiderea programului.
O prima intrebare care ar putea aparea in aceasta faza la un incepator ar fi: “Cum adica sa fie in mai multe locuri nevoie de inchiderea programului? Nu se inchide la apasarea Alt + F4?”.
Raspunsul este: inchiderea programului poate surveni in nenumarate situatii: a fost apasata combinatia Alt + F4 daca aceasta este definita, s-a terminat jocul, a expirat timpul de testare intr-o versiune demo, a aparut o eroare etc. Peste tot in aceste cazuri noi am folosi functia creata si s-ar intampla acelasi lucru.

O prima varianta foarte simpla ar fi:

Cod: Selectaţi tot

void inchidere_program()
{
alert(“La revedere!”, “Te mai asteptam si altadata!”);
exit();
}
Apeland in toate exemplele anterioare codului de mai sus functia inchidere_program() in locul lui exit(), vom avea mesajul de bun ramas urmat de inchiderea propriu-zisa.

Presupunand totusi ca avem nevoie de mesaje diferite in functie de situatia in care trebuie sa se inchida programul, putem crea functia cu un parametru pentru mesaj:

Cod: Selectaţi tot

void inchidere_program(string mesajul)
{
alert(“La revedere!”, mesajul);
exit();
}
Astfel vom specifica intre ghilimele ca parametru sau printr-o variabila de tip string mesajul care sa fie afisat in corpul ferestrei generata de alert(), titlul ramanand de fiecare data “La revedere!”:
Pentru ca sa aiba acelasi efect ca prima varianta, atunci cand apelam functia inchidere_program() in cea de-a doua varianta scriem:
inchidere_program(“Te mai asteptam si altadata!”);

In final ar mai fi de mentionat cate ceva pentru a nu exista confuzii:
- Parametrii pot fi oricati, inclusiv niciunul.
- Tipul parametrilor nu au legatura cu tipul functiei. O functie care returneaza int poate avea ca parametri stringuri.
- Parametrii pot fi de toate felurile la aceeasi functie, de exemplu definirea unei functii amestec_litere() ar fi corecta si in felul urmator:
string amestec_litere(string tx, int nr)
desigur ca randul de mai sus este doar un exemplu pentru o functie ipotetica in care string tx ar fi textul de amestecat, iar acel int nr ar putea specifica de cate ori sa fie amestecat inainte de returnare.
- De-a lungul acestui rezumat nu am folosit cele mai scurte posibilitati cand am creat functii, intentionat am definit uneori variabile fara a le atribui pe acelasi rand o valoare sau invers, intentionat am creat variabile temporare desi nu era cazul si altele, acestea pentru a se putea observa toate modurile de exprimare.
Referitor la scrierea celui mai scurt cod posibil, redefinesc acum functia adunare():

Cod: Selectaţi tot

int adunare(int nr1, int nr2)
{
return nr1+nr2;
}
Practic nu am mai atribuit suma celor doua variabile parametru unei variabile, ci am adunat direct la return cele doua valori, rezultatul fiind in cele din urma acelasi.

Cam atat ar fi de inteles in prima faza despre functii, data viitoare vom continua cu alte probleme legate de acelasi subiect.


Ca tema de casa as cere:
Sa se creeze o functie cu numele int_to_string().
Aceasta sa aiba un parametru de tip int si sa returneze acel int sub forma de string.
Dupa ce este creata functia, urmatorul cod copiat in acelasi document ar trebui sa nu dea eroare:

Cod: Selectaţi tot

void main()
{
int x=29;
string z=int_to_string(x);
alert(“Salut!”, z+“ este egal cu 31 minus 2. Asa-i?”);
exit();
}
Asadar, dupa ce este creata functia int_to_string(), se poate lua codul de mai sus si verifica daca apare rezultatul corect, daca nu da vreo eroare.
O tema rezolvata corect este considerata cea care va contine atat functia definita, cat si codul de mai sus, astfel incat tot scriptul sa fie functional.

Imprimarea acestei intalniri este la link-ul urmator:
Download Fisier MP3 04._Curs_BGT_-_Functiile_Partea1.mp3 (55,5 MB)

modulul 4 tema

Scris: 20 Apr 2012, 10:01
de Eddy

Cod: Selectaţi tot

void main()
{
int x=29;
string z=int_to_string(x);
alert("Salut!", z+" este egal cu 31 minus 2. Asa-i?");
exit();
}

string int_to_string(int nr)
{
return ""+nr;
}

Scris: 20 Apr 2012, 21:17
de Manu
De data asta pot spune ca ai rezolvat perfect, exact asa as fi facut si eu, nici un caracter in plus sau in minus.
Ai ales cea mai simpla varianta, probabil ca dupa cum am facut la curs, cei mai multi ar fi mers in functia int_to_main() printr-o variabila intermediara ca in exemplul:

Cod: Selectaţi tot

string int_to_string(int nr)
{
string temp=""+nr;
return temp;
}
Ar fi fost la fel de bine, insa asa s-a economisit un timp de nivelul microsecundei si memorie pentru acelasi timp de 4 bytes.
De ce? pentru ca o variabila de tip int ocupa 4 bytes, iar int temp fiind o variabila locala nu ar mai exista dupa ce functia int_to_string() isi face treaba. De asemenea, sa nu uitam ca fisierul cu scriptul ar mai ocupa pe hard cativa bytes in plus cu acel rand. Normal ca la ce calculatoare avem acum nu ar conta, dar era o vreme cand orice byte era important, de asemenea si acum cand se programeaza pentru diverse sisteme embedded, puterea de calcul, memoriile fiind mici, se cauta variantele cu cel mai mic consum de resurse.

Sa inteleg ca urmaresti si tu cursul in paralel sau ca stii dinainte cate ceva despre programare? Intreb doar asa din curiozitate.

Scris: 20 Apr 2012, 23:26
de Eddy
urmaresc cursul in paralel si am participat si la cursul de jaws scripting.
inafara de ce predai tu nu mai stiu nimic despre programare.

Scris: 21 Apr 2012, 16:57
de Manu
O tema suplimentara pentru cine vrea:

Sa se creeze pe baza doar a cea ce am facut la curs o functie numita randoms() care sa returneze sub forma de string un numar aleatoriu intre doua numere decise de noi.

tema suplimentara

Scris: 22 Apr 2012, 07:42
de Eddy
/*
solutia mea cum spunea si un participant la curs mi se pare un artificiu
cred ca tu te gandeai la alta rezolvare dar...
*/

void main()
{
string x=randoms(10, 60);
alert("", x);
exit();
}

string randoms(int nrminim, int nrmaxim)
{
return ""+random(nrminim, nrmaxim);
}

randoms()

Scris: 22 Apr 2012, 12:18
de Cornel
din cate inteleg, se cere o returnare doar intre doua numere alese de noi, or tie iti returneaza random din intervalul 10 60; daca am inteles bine, alertul ar trebui sa sune ceva de genu: dintre opt si 12, calculatorul a ales: si apoi unul din numere.

Scris: 22 Apr 2012, 14:36
de Manu
Eddy a rezolvat bine. Pana la urma trebuie sa alegi numerele undeva, si anume ca parametri in functia randoms().
A definit mai jos functia string randoms(int nrminim, int nrmaxim), deci e totul in regula, e solutia cea mai scurta.

tema

Scris: 22 Apr 2012, 15:41
de Cornel
ok! am inteles gresit cerinta in cazul acesta; de fapt, cred ca, utilizand ceea ce s-a parcurs pana acuma, nici nu e posibil sa facem in asa fel ca doar doua numere (prestabilite( sa fie afisate; ceva de genu: o variabila sa fie cinci si alta noua si doar pe una sau pe alta sa o aleaga (ca un fel de cap si pajura);

Scris: 22 Apr 2012, 17:00
de Manu
Cerinta era clara, sa returneze un numar intre doua numere.
Pentru ce zici tu Cornel, ar trebui sa folosim If-ul. Ar trebui sa facem un random(1, 2), apoi daca a iesit 1 sa fie luat primul numar, daca iese 2 sa fie considerat al doilea numar. Asadar, suntem inca inainte cu doua cursuri de realizarea unei astfel de functii.
In prezent am putea face o functie care sa returneze unul din doua numere intregi, doar daca cele doua numere ar fi consecutive.
Pentru a alege unul din mai multe numere anume stabilite, ar trebui sa stim cum se lucreaza cu array-uri, adica un sir de valori in cazul de fata, iar random() sa aleaga un numar intre 0 si x, unde x este numarul total de valori / elemente gasite in array. Daca rezultatul ar fi 3 sa zicem, atunci al treilea numar din sir ar fi cel rezultat. Vom vedea noi toate acestea la momentul potrivit.

Si daca tot am dat teme suplimentare, ia sa completam noi cu o functie random2() putin diferita. Noi cand dam parametri functiei random(), stim ca va putea rezulta inclusiv numarul minim sau maxim.
Sa se faca o functie care sa fie sigur ca va returna sub forma de int doar numere intre valorile parametrilor.

tema suplimentara 2

Scris: 22 Apr 2012, 21:05
de Eddy
void main()
{
int x=random2(1, 4);
alert("", ""+x);
exit();
}

int random2(int a, int b)
{
a+=1;
b-=1;
return random(a, b);
}

Scris: 22 Apr 2012, 21:53
de Manu
Da, este bine, dar o varianta ar fi si urmatoarea:

Cod: Selectaţi tot

void main() 
{ 
int x=random2(1, 4); 
alert("", ""+x); 
exit(); 
} 

int random2(int a, int b) 
{ 
return random(a+1, b-1); 
}
Am aratat si acest exemplu pentru a vedea ca un parametru al unei functii poate sa conste si intr-o valoare rezultata in urma unui calcul, intr-o o valoare returnata de o functie, chiar de aceeasi functie.

Daca este inteles codul de mai jos e perfect, inseamna ca lucrurile devin tot mai clare. Daca nu este inteles, poate il explicam mai amanuntit.
Sa presupunem ca vrem sa fie generat un numar intre 2 si 9, putem scrie codul:

Cod: Selectaţi tot

void main() 
{ 
int x=random2(random2(1, 3), random2(8, 10)); 
alert("", ""+x); 
exit(); 
} 

int random2(int a, int b) 
{ 
return random(a+1, b-1); 
}
Asadar, interpretorul proceseaza intai functiile random2() situate ca parametri la functia random2() de dupa egal. Interpretorul rezolva lucrurile de la dreapta la stanga, altfel spus din adang spre suprafata, suprafata fiind ceea ce este imediat dupa egal.

O alta informatie importanta este ca se pot face calcule ca in aritmetica, folosind paranteze sau paranteze in paranteze.
De exemplu daca vrem sa inmultim pe 5 cu 6 plus 3, scriem:
int rezultat=5*(6+3);
6 plus 3 fiind intre paranteze, va fi calculat inainte, apoi va fi inmultit cu 5. Fara paranteza ar fi fost intai inmultit 5 cu 6, rezultatul fiind adunat cu 3.

random

Scris: 23 Apr 2012, 22:55
de Cornel
Tare exemplu asta ultimu: prin urmare o functie se poate avea pe sine ca parametri; care parametri, la randul lor pot fi exprimati sub forma unui calcul.

random

Scris: 23 Apr 2012, 23:11
de Cornel
In exemplul urmator rezultatul este constant zero; asta e din cauza ca functia random are nevoie de doi parametri in care primul sa fie mai mic decat al doilea?

Cod: Selectaţi tot

void main()
{
int x = randoms(4, 3);
alert("", "" + x);
exit();
}

int randoms(int a, int b)
{
return random(a+b, a-b);
}
Desigur daca pun sa returneze a-b, a+b atunci merge pe intervalul 1 sapte;

Scris: 24 Apr 2012, 00:10
de Manu
Da, este neaparat si logic ca numarul minim sa fie mai mic sau egal cu numarul maxim, dau mai jos un citat din manual de la explicatiile functiei random() din sectiunea Math:
Return value:
A random number between min and max on success, or 0 on failure.

Remarks:
This function only works with numbers (positive or negative), without decimals. If either min or max contain decimals, these will simply be removed without rounding. If max is less than or equal to min, min will be returned.
Se pare totusi ca este o mica inadvertenta in manual:
se spune ca 0 este returnat daca este o eroare, iar daca numarul maxim este mai mic sau egal cu cel minim, este returnat cel minim, adica valoarea primului parametru.
Ar fi trebuit in exemplu tau, Cornel, sa iasa 7.
Chiar daca nu conteaza asa mult practic, ci doar asa ca sa fim rigurosi, ii voi semnala si lui Philip.
Stiu ca la inceputuri, pe la versiunea 0.5 beta era un alt algorimt de generare a numarului random, apoi a trecut la algoritmul curent care cica este Mersenne twister. Probabil ca in varianta anterioara era returnare dupa cum spune manualul, iar dupa ce a modificat da de fiecare data 0, chiar daca nu e o eroare interna, ci o plasare ilogica a valorilor per parametri.