Joan Soler-Adillon || Tutorial de Processing
Despatx 52.803 (Campus de la comunicació, UPF) || 93 542 1238
----------------------------------------------------------------
----------------------------------------------------------------
TUTORIAL DE PROCESSING || Secció 9: Funcions
1. Funcions
Les funcions ens serveixen per organitzar el codi i poder-lo reciclar després. Una funció és una estructura que en un moment donat en el flux del programa és invocada. Quan això passa, les instruccions que estiguin continguda en la funció s'executaran.
Podem tornar a l'exemple de la llum del segon dia, on el programa decidia si era jo o un alumne, segons la distància de cadascun cap a la porta, qui havia d'anar a tancar-la. Una vegada presa la decisió, no n'hi ha prou, de fet, perquè la porta sigui tancada. Anar a tancar la porta és una acció complexa que consta de diverses accions concretes, que quan estem parlant de programació caldrà explicitar:
Aquestes accions es poden explicitar en el codi principal, el que en Processing seria el DRAW o bé pot crear-se una funció que les englobi (obrirLaPorta), i que en el codi principal només calgui invocar aquesta funció cada vegada que s'hagi de tancar una porta.
Així, podem dibuixar el Rebitxo que vam veure en la segona sessió amb una sola línia al DRAW, i tot el codi del dibuix contingut en una sola funció, de manera que el DRAW ens queda així:
Source code: bitxo
Aquí, dibuixaReBitxo(); invoca una funció que hem creat per a dibuixar e l'animaló. Quan cridem la funció, ho fem pel seu nom, seguit de parèntesi. En el cas d'una funció que no rep paràmetres (com aquest), simplement obrim i tanquem parèntesi sense més per a indicar a Processing que ens estem referint a una funció.
Per a crear la funció, cal tenir en compte dues coses. Si hi ha cap retorn (si retorna o no algun valor), i si ha de rebre paràmetres. Com que dediquem a aquestes dues coses sengles apartats en aquesta sessió, de moment ens centrarem en funcions que no retornen res i no reben paràmetres.
També seria el cas següent, on utilitzem una funció per fer un altre dibuix, una mica més senzill:
void setup(){ size(200,200); } void draw(){ background(0); smooth(); //invoquem la función, que definim més endavant: dibuixaUnSputnik(); } //Heus ací una funció que dibuixa un SPUTNIK: void dibuixaUnSputnik(){ strokeWeight(3); stroke(255); fill(255,0,0); line(70,70,130,130); line(70,130,130,70); ellipse(100,100,35,35); }
Aquí podem veure com declarem una funció:
El void hauria de sonar del SETUP y el DRAW. És la paraula clau que indica que la funció no retorna res. De la mateixa manera, els parèntesis en blanc indiquen que la funció no rep paràmetres.
El que aquestes dues coses signifiquen és que una funció així s'invocarà de la següent manera:
Simplement cridant la funció pel seu nom i els parèntesis en blanc provocarem que en el punt en el codi on la funció és invocada, el flux d'accions salta cap a la clau on comencen les instruccions de la funció, segueix fins que la funció es tanca, i continua després cap a la línia que segueix la invocació. En l'exemple, torna a començar el DRAW.
2.- Paràmetres
El que hem vist fins a ara no passa de ser una millor organització del codi. Les funcions comencen a ser molt més interessants quan comencem a utilitzar els paràmetres.
Per exemple, podem tenir una funció que ens dibuixi alguna cosa allí on hi ha el mouse:;
Source code: sptnk01
...allí on cliquem...
Source code: sptnk02_clic
...o segons una variable de posició:
Source code: sptnk03_pos
Aquí la gràcia està en què en ambdós casos utilitzem exactament la mateixa funció:
void dibuixaUnSputnik(int _x, int _y){ strokeWeight(3); stroke(255); fill(255,0,0); line(_x-30,_y-30,_x+30,_y+30);
line(_x-30,_y+30,_x+30,_y-30);
ellipse(_x,_y,35,35); }
és a dir, la reutilitzem. La mateixa funció exactament en dos programes (sketches) diferents. La diferència està en el moment que la funció és invocada. En el primer exemple, en el DRAW, i en el segon, en altra funció de sistema: MOUSEPRESSED, que s'executa quan es clica el mouse.
Però aquí la funció té una característica diferent a les del punt anterior. La funció dibuixaUnSputnik, aquí, rep paràmetres. Concretament en rep dos. Dos enters. D'aquí que al declarar la funció no deixem el parèntesi en blanc:
El que està dins els parèntesi són doncs els paràmetres, que es reben en forma de variable. Unes variables que cal declarar com veiem, i que anomenem en aquest cas _x i _y.
Els valors que s'assignaran a aquestes variables seran els dels paràmetres que especifiquem a l'assignar la funció. Així,
Assignaria a _x el valor 87 i a _y el valor 349 en aquesta execució de la funció. En els nostres exemples, el que se'ls assigna és altra variable, en aquest cas variable de sistema, però que no deixa de ser un enter: mouseXimouseY.
3.- Retorn
És una altra cosa que poden fer les funcions. I aquí, encara que pot semblar que la cosa es complica, és quan les coses es tornen interessants. A més de poder o no rebre paràmetres, una funció pot o no retornar valors. Fins a ara hem vist funcions que mai retornen res. Aquest tipus de funcions simplement s'invoquen, amb o si paràmetres, sense més.
Però què passa si una funció retorna un valor? A qui l'hi retorna i com ho gestionem? Doncs de fet és ben senzill. Quan invoquem una funció que retorna un valor, hem d'assignar aquest valor a una variable (o utilitzar-lo directament). És, de fet, el que ja hem estat fent amb random.
Per exemple, si tenim una funció anomendada calculsMoltComplexes que ens realitza els dificilíssims càlculos de fer la mitjana entre dos valors, l'assignaríem a una variable anomenada, per exemple, "resultat":
int resultat; resultat = calculsMoltComplexes(32,439);
I la funció seria quelcom com això:
int calculsMoltComplexes(int _a, int _b){
int aRetornar;
aRetornar = (_a+_b)/2; return aRetornar; }
Un altre aspecte molt important ara és la declaració de la funció. I aquí per fi cobrarà sentit el famós void: Quan declarem una funció que retorna un valor, hem d'especificar el tipus de dades que retorna al declarar-la. Així, una funció que retorna un enter es declararia per exemple així:
int fesCalculsQueResultenEnUnEnter(){ //instrucciones }
I una que ens ha de retornar un float:
float iAraUnsQueResultinEnUnFloat(){ //instrucciones }
I, finalment, cal tenir SEMPRE, en una funció que retorna quelcom, una instrucció de retorn al final. Aquesta instrucció s'anomenta, molt encertadament, return i va seguida d'un valor que es retorna, (valgui la redundància) que sempre haurà de ser del mateix tipus de dades que hem especificat al declarar la funció:
float iAraUnsQueResultinEnUnFloat(){ //instruccions on "flotador" és una variable del tipus float return flotador; }
Com sempre, millor amb exemples: Imagineu que tenim dos equips de programadors. Un ha de fer un dibuix segons la repidesa a què es mogui el ratolí. L'altre, comptar els píxels blancs de la pantalla. Vaja, el més normal del món...
Doncs amb funcions cada equip podria treballar per separat amb tant complexes tasques, i al final integrar-ho tot en un sol sketch:
Source code: countdraw2
I un altre exemple, amb enviament de paràmetres i retorn en una sola funció. Podem fer servir matriu, bucles i funcions per fer coses com calcular i dibuixar la posició mitjana d'un grup d'elements:
Source code: mitja
4.- Més exemples
No, no ens oblidarem de les pilotetes, però ara podem utilitzar una funció per fer que el que reboti no sigui una simple pilota, sinó per exemple el rebitxo. A més mirant el codi veurem com amb funcions és qüestio d'organitzaicó.
Source code: bitxorebota
I finalment, un altre exemple de funcions, i de pas introduim més idees de cara a obtenir resultats més interessants a nivell d'interactivitat:
Source code: follow
---------------------------------------------------------------------------------------