We weten nu hoe we zelf een functie kunnen bouwen. Echter merken we iets speciaals op als we naar de functies kijken die we zoveel gebruiken: de functie ellipse() bijvoorbeeld, kan je helemaal niet gebruiken als je geen getallen invult tussen de () haken. Deze functie moet namelijk weten waar en hoe groot die ellips moet worden.
//dit kan niet: ellipse(); //maar wel mét parameters: ellipse(50,20, 10,10);
Als we die getallen aanpassen, verandert de plaats en de vorm van onze ellips, maar de functie blijft hetzelfde. Ze tekent nog steeds ellipsen. Deze functie werkt dus met variabelen, die wij bij het aanroepen van de functie vullen.
Onze eigen functie accepteert tot nu toe nog géén getallen of andere data. Wanneer dit wel het geval wordt, winnen we weer aan flexibiliteit.
van zonder naar mét parameters
De functie drawBlackCircle() werkt goed, maar als we bewegende zwarte cirkels willen tekenen met verschillende grootten, zullen we een variabele moeten introduceren die de diameter van de cirkel bevat. Het enige alternatief is anders voor elke cirkel een nieuwe functie schrijven, en dat kost alleen maar werk en maakt het geheel onoverzichtelijk.
Als we drawBlackCircle() in de draw() loop aanroepen, willen we dus eigenlijk dat we dit kunnen doen:
drawBlackCircle(10,50,100,100);
De opgegeven waarden zouden dan kunnen staat voor de x,y, breedte en hoogte van onze zwarte cirkel.
Maar laten we beginnen met 1 variabele, voor de diameter.
In de draw loop willen we dus:
void draw(){ drawBlackCircle(16); }
Dat wil zeggen dat we in het ontwerp van onze functie met dat getal moeten rekening houden. Als we onze functie aanmaken, geven we binnen de ( en ) haak aan dat we hier een int verwachten, en we geven deze een naam (ik kies voor de naam “diam”) :
void setup(){ size(500,500); } void draw(){ drawBlackCircle(16); } void drawBlackCircle(int diam){ //lokale variabele diam stroke(255); fill(0); ellipse(50,50,diam, diam); //we gebruiken de lokale variabele diam als diameter van de cirkel }
and that’s it! Op deze manier wordt de “16” die we hebben ingegeven bij het aanroepen van onze functie gebruikt IN de functie.
Hier gebeurt erg veel, dus een overzicht van de flow is wel handig:
Als we de code van de bewegende cirkel met onze nieuwe functie aanvullen en een aantal keer drawBlackCircle() aanroepen met verschillende waardes, krijgen we:
int x = 0; int speed = 2; void setup(){ size(500,500); } void draw(){ background(255); drawBlackCircle(50); drawBlackCircle(20); drawBlackCircle(10); } void drawBlackCircle(int diam){ x = x + speed; if(x > width || x < 0){ speed = -speed; } stroke(255); fill(0); ellipse(x,100,diam,diam); }
Oefening
breidt bovenstaand voorbeeld uit, zodat naast de diameter, ook de y-positie kan variëren
Lokale variabelen en functies
Wanneer we bovenstaand diagram van de flow bekijken, zien we duidelijk dat de waarde “16” die we meegeven als we de functie aanroepen in een lokale variabele in de functie terecht komt. Dat wil zeggen dat deze variabele enkel voor de duur dat de functie wordt uitgevoerd bestaat (zie programming principles).
Natuurlijk kunnen we niet alleen “hardcoded” waarden meegeven als we een functie aanroepen. Diezelfde 16 kunnen we in een globale variabele “diameter” stoppen, en ook dan werkt onze functie nog:
drawBlackCircle(diameter);
int diameter = 16; void setup(){ size(500,500); } void draw(){ drawBlackCircle(diameter); } void drawBlackCircle(int diam){ fill(0); ellipse(width/2, height/2, diam, diam); }
Waar echter vaak verwarring rond ontstaat is het volgende: Wat gebeurt er als ik in mijn functie 3 bij diam optel ?
int diameter = 16; void setup(){ size(500,500); } void draw(){ drawBlackCircle(diameter); } void drawBlackCircle(int diam){ diam = diam + 3; println(diam); }
Als we kijken naar de output van deze code, zien we dat de print van de waarde die in diam zit altijd hetzelfde blijft, namelijk 19. Wat gebeurt hier precies ?
Een overzicht :
Het is dus belangrijk om te onthouden dat de waarde uit de variabele die je in een functie meegeeft wordt gekopieerd naar de tijdelijke variabele(n) van de functie.
Return type
Dit bezorgt ons wel een probleem. Wat dan als we een functie willen schrijven waar wél een waarde wordt veranderd of berekend en weer terug moet naar een globale variabele ? Daarvoor is het eerste woord van de functie van groot belang. Deze geeft namelijk het “return type” aan.
Een functie die een waarde teruggeeft begint altijd met als eerste woord het type van die waarde en eindigt met het woord “return“, met daarachter hetgeen er moet worden teruggegeven. Een voorbeeld:
int som(int a, int b, int c){ return a+b+c; }
Bovenstaande functie telt 3 gehele getallen met elkaar op, en geeft het resultaat terug.
Let op:
- de functie begint met het woord “int“. Dit wil zeggen dat de waarde die uit de functie zal terugkomen een geheel getal is
- de functie bevat als laatste zin “return a+b+c”. Dit is de waarde die de functie teruggeeft
- een functie die niet “void” als type heeft, moet altijd als laatste zin “return” bevatten
Deze functie geeft dus de som van 3 getallen terug. We roepen ze alsvolgt aan in setup() of draw():
som(3,4,5);
Op deze manier wordt de functie wel uitgevoerd, maar wordt er niets met de waarde gedaan die wordt teruggegeven. We kunnen deze waarde opslaan in een globale variabele:
int mijnTotaal; void setup(){ size(500,500); } void draw(){ mijnTotaal = som(3,4,5); println(mijnTotaal); } int som(int a, int b, int c){ return a+b+c; }
Ook hier duiden we de flow aan om duidelijkheid te scheppen:
Oefening
// Schrijf een functie die 1 argument accepteert: F voor Fahrenheit // De functie berekent vervolgens de Celcius waarde // C = (F - 32) * (5 / 9) ______ tempConverter(float ________) { _____ _______ = ______________ _____________________________ }