Pagina 1 van 1

Arduino Programmeren voor Beginners – Deel 6: Functies

Arduino Programmeren voor Beginners – Deel 6: Functies
   9

In deel 6 van Arduino Programmeren voor Beginners, gaan we kijken naar functies (Engels: functions). We hebben al een paar functies gebruikt en gezien, maar in dit deel gaan we kijken naar functies die we zelf kunnen maken.

Functies worden vaak gebruikt om een reeks stappen, welke herhaald of hergebruikt kunnen worden, samen te voegen naar een nieuwe instructie. Dit kan bijdragen aan efficiënter programmeren, maar ook aan een beter leesbaar programma.

Deze reeks, richt zich hoofdzakelijk op het Arduino Programmeren voor beginners, en dan specifiek voor m’n neefjes Bram en Max – gebrek aan kennis voor wat betreft de Engelse taal en wiskundige achtergrond hoeft waarschijnlijk geen probleem te zijn. Het gebruik van extra electronica componenten blijft beperkt tot een minimum en bewaren we voor een volgende reeks.




Overzicht van dit Hoofdstuk

  Een volledig overzicht van de cursus vindt je hier: Cursus Overzicht.

Wat zijn Functies en Waarom hebben we ze nodig?

Als eerste hebben we theoretisch geen functies nodig voor een programma, echter onze code zou dan super lang worden om maar te zwijgen over de beroerde leesbaarheid van onze code. Daarnaast besparen functies ons een hoop werk – zeker als we de functie kunnen hergebruiken.

We hebben al een aantal functies gezien en we hebben er zelfs al twee verplichte functies gedefinieerd in onze Arduino programma’s: “setup()” en “loop()”.

Maar wat zijn dan die functies (functions)? En waarom willen we ze gebruiken?

Een functie (ook wel subroutine genoemd) kunnen we zien als een groep instructies, welke samen een specifieke taak uitvoeren.
We maken deze functies om ons programma beter georganiseerd te houden, beter leesbaar te maken, en vaak omdat we deze “taak” elders willen hergebruiken. En dat “elders” kan zelfs in een ander programma zijn.

In bepaalde talen noemt men een functie ook wel een “subroutine”, en dat beschrijft precies wat het is: een soort klein programma in ons programma. En net als bij een programma, kan een functie ook functies gedefinieerd hebben in de functie – een functie is echter ook onderhevig aan de eerder genoemde “scope”. Dus een functie die in een functie gedefinieerd wordt is dus alleen maar bekend in de functie waar het gedefinieerd is – dus net zoals we zagen bij variabelen!

Ik kan me voorstellen dat dit net even te ver ging om te bevatten, het is iets waar we later vanzelf mee te maken gaan krijgen, hou het dus in gedachten.

We kunnen twee soorten functies maken: een functie die een waarde terug geeft, en een functie die geen waarde terug geeft.
Die laatste is wat men, in sommige programmeertalen, een “procedure” noemt. De taal C echter, noemt beiden gewoon een functie, of in het Engels: een “function”.

Een functie, is een groep instructies met een bepaalde taak in gedachten.

Wanneer definiëren we een functie?

  • Wanneer code meerder keren herhaald wordt in een programma,
  • Als onze code beter leesbaar wordt door het gebruik van functies,
  • Als we onze code beter kunnen beheren met functies
  • Als we onze code willen hergebruiken, b.v. in andere programma’s.

Laten we eens een voorbeeld doorlopen zodat we het concept beter begrijpen.

Stel we hebben een hond, en we moeten die vier keer per dag uitlaten: Om 8:00, 12:00, 17:00, en om 21:00.

De taak “hond uitlaten” kan omschreven worden met de volgende instructies:
– Jas aandoen
– Hondenriem bij de hond aandoen
– Naar buiten gaan
– 15 minuten rond lopen
– Terug naar binnen gaan
– Hondenriem afdoen
– Jas uitdoen.

Dus voor een hele dag zou onze “code” er zo uitzien:

Als het 8:00 is doe dan:
– Jas aandoen
– Hondenriem bij de hond aandoen
– Naar buiten gaan
– 15 minuten rond lopen
– Terug naar binnen gaan
– Hondenriem afdoen
– Jas uitdoen.

Als het 12:00 is doe dan:
– Jas aandoen
– Hondenriem bij de hond aandoen
– Naar buiten gaan
– 15 minuten rond lopen
– Terug naar binnen gaan
– Hondenriem afdoen
– Jas uitdoen.

Als het 17:00 is doe dan:
– Jas aandoen
– Hondenriem bij de hond aandoen
– Naar buiten gaan
– 15 minuten rond lopen
– Terug naar binnen gaan
– Hondenriem afdoen
– Jas uitdoen.

Als het 21:00 is doe dan:
– Jas aandoen
– Hondenriem bij de hond aandoen
– Naar buiten gaan
– 15 minuten rond lopen
– Terug naar binnen gaan
– Hondenriem afdoen
– Jas uitdoen.

Zo wordt ons programma best lang he? En we zien aardig wat herhaling van dezelfde instructies …

We zouden echter de functie “HondUitlaten()” kunnen definiëren, bijvoorbeeld:

HondUitlaten():
– Jas aandoen
– Hondenriem bij de hond aandoen
– Naar buiten gaan
– 15 minuten rond lopen
– Terug naar binnen gaan
– Hondenriem afdoen
– Jas uitdoen.

Nu wordt ons programma een stuk overzichtelijker:

Als het 8:00 is dan
  HondUitlaten()

Als het 12:00 is dan
  HondUitlaten()

Als het 17:00 is dan
  HondUitlaten()

Als het 21:00 is dan
  HondUitlaten()

Zoals je ziet is onze code nu een stuk netter en beter te lezen. We hebben nu de programmeertaal uitgebreid, voor ons programma, met de functie “HondUitlaten”.

In de meeste scenario’s, vooral als een functie meerdere keren gebruikt wordt in een programma, zal dit zelfs leiden tot een kleiner programma (na compileren) voor de Arduino of Computer. Het is efficiënter.

Maar functies hebben nog een groot voordeel. Stel we moeten de instructies voor het hond uitlaten aanpassen, bijvoorbeeld door “deur van het slot halen” ene “deur weer op slot doen” toe te voegen. Bij een functie hoeven we dat dus maar op 1 plaats te doen: in de functie definitie. In tegenstelling tot het eerste voorbeeld waar we dat op 4 plaatsen moeten veranderen. Onze code wordt dus beter beheerbaar.

Stel we hebben een meer generieke functie gemaakt, dan zouden we deze in een zogenaamde bibliotheek (librarys) kunnen zetten zodat we deze functie in andere programma’s ook weer kunnen gebruiken – en we dus tijd besparen wanneer we weer een ander programma gaan maken. Dit noemt met “re-use” of “hergebruik” van code. We gaan nog even niet in op het gebruik van libraries, dat is voor een stadium waarin we gevorderder zijn.

Vergeet niet:

 

  • Een functie is net als een klein programma … binnen een programma
  • Een functie kan zelf ook functies bevatten …
  • Een functie heeft een scope” – zoals we zagen bij variabelen.

Zelf Functie(s) maken

In de programmeertaal C, zoals we die voor  Arduino programmeren gebruiken, is gelukkig redelijk eenvoudig. De basis structuur ziet er zo uit:


datatype FunctieNaam ( FunctieParameters ) {
  // functie code
}

Ik hoop dat je nog iets van DataTypes hebt onthouden zoals we dat in Deel 3 bespraken. Mocht dit niet zo zijn, dan kun je altijd nog even terug kijkenen ik zal een paar details hier weer vermelden.

Het datatype geeft aan wat voor antwoord we terug mogen verwachten van de functie. Echter … niet alle datatypes zijn bruikbaar hier voor, en dan moeten we vooral denken aan arrays!

De meest gebruikte datatypes voor functies zijn boolean, int, char, float en void (andere datatypen zoals double, long en de “unsigned” types werken ook).

Uiteraard moet onze functie ook een naam krijgen, de FunctieNaam, voor welke we dezelfde regels moeten volgens als namen voor variabelen en constanten:

Functie naam:

  • Moeten beginnen met een letter (a, b, …, z, A, B, … , Z) of een underscore ( _ )
  • Mag letters bevatten 
  • Mag underscore(s) bevatten
  • Mag nummers bevatten
  • MAG GEEN speciale tekens, symbolen of spaties bevatten
  • is hoofdletter gevoelig!

Een functie kan nul of meer Functie Parameters accepteren. Het aantal parameters is echter vast en voorgedefinieerd voor onze functie!

In een aantal C programmeertaal dialecten, moeten we een functie declareren (aankondigen) voor we ze kunnen gebruiken. In C voor Arduino Programmeren is dit echter niet perse noodzakelijk en daarom gaan we daar hier niet op in.

We hebben overigens ook al een aantal functies gebruikt en aangeroepen, ook al waren we ons daar misschien niet van bewust, waarbij we parameters naar de functie sturen door ze tussen ronde haakje te zetten. Een voorbeeld: Serial.print("Hallo");

Hier roepen we de functie “print()” aan van object “Serial” en geven het de parameter “Hallo” (een string). Meer over objecten later.

De code block, tussen accolades en na de functie naam en parameters, bevat de instructies voor onze functie – net zoals we dat ook bij “if” en de verschillende loops zagen (“for”, “while” en “do … while …”).

Een simpele (en niet zo zinvolle) functie definitie zou kunnen zijn:


1
2
3
void ZegHallo() {
  Serial.println("Hallo");
}

Deze functie, “ZegHallo()”, gebruikt geen parameters, omdat er niks tussen de ronde haakjes () staat.
Het stuurt ook geen antwoord terug omdat we het datatype “void” gebruiken – wat zoveel betekend als “niks, nada, noppes”.

De “body” van de functie (de code blok), bevat de instructies van de functie, en in dit geval voert het alleen maar de tekst “Hallo” uit.

Een voorbeeld hoe we dit kunnen gebruiken:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  for(int A=1; A<=5; A++) {
    ZegHallo();
  }
}

void loop() {
  // leave empty for now
}

void ZegHallo() {
  Serial.println("Hallo");
}

Dit zou er een beetje vertrouwd uit moeten zien.

Aan het einde definieren we de functie “ZegHallo()”.
In de “for”-loop roepen we de functie 5 keer aan, wat zoiets als dit oplevert

Hallo
Hallo
Hallo
Hallo
Hallo

Makkelijk toch?

Waarden doorgeven aan een Functie

Dat laatste voorbeeld was natuurlijk bijzonder simpel. Laten we eens kijken hoe we de functie kunnen gebruiken in ons lampen voorbeeld door 5 lampen aan te zettenmet behulp van een “for”-loop.

Hiervoor maken we de functie “ZetLampAan”, welke we een lamp nummer gaan geven voor de lamp die aangezet moet worden.

We weten eigenlijk wel dat de lamp nummers van het datatype “int” gaat worden (zie ook de definitie van “A” in de “for”-loop).

We weten ook dat de functie niets terug hoeft te geven.

Al dat gecombineerd:


void ZetLampAan(int LampNummer) {
  Serial.print("Zet de volgende lamp AAN: ");
  Serial.println(LampNummer);
}

De parameter “LampNummer” is dus gedefineerd als een “int”. Je ziet dat we de variabele “LampNummer” in onze functie definiëren. Het is belangrijk om te weten dat deze variable de waarde aan gaat nemen van de parameter die we aan de functie doorgeven als we de functie aanroepen. Deze waarde wordt gekopieerd – dus als we de waarde van LampNummer veranderen, dan wordt de originele parameter (wat ook een variabele kan zijn) daardoor niet veranderd!

Om nog eens terug te komen op “scope”: de variabele “LampNummer” is natuurlijk niet bekend buiten de functie “ZetLampAan”.

Als we in regel 6, de functie aanroepen, geven we het de waarde van de variabele “A” mee. Deze waarde wordt de gekopieerd in de variabele “LampNummer”.

Alles even bij elkaar gezet:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  for(int A=1; A<=5; A++) {
    ZetLampAan(A);
  }
}

void loop() {
  // leave empty for now
}

void ZetLampAan(int LampNummer) {
  Serial.print("Zet de volgende lamp AAN: ");
  Serial.println(LampNummer);
}

Zie je hoe de variabele “LampNummer” wordt gebruikt in de functie?

Dit is natuurlijk maar een eenvoudig voorbeeld. Wat gebeurt er nu als we meerdere waarden aan de functie door willen geven. Bijvoorbeeld door een boolean toe te voegen welke aangeeft of de lamp nu aan (true) of uit (false) gezet dient te worden?

Als we meerder parameters willen gebruiken, dan moeten we ze scheiden met komma’s ( , ).

Parameters in een functie worden gescheiden gehouden door een komma,
zowel bij functie definitie als bij functie aanroep.

Zoals met alle waarden die we door willen geven bij de parameters, moeten we ook die extra parameters voorzien van een naam (LichtAan) en een data type (boolean) zodat de functie weet wat voor waarde het kan verwachten.

Uiteraard moeten we even een “if” statement in de functie hangen, zodat afhankelijk van de boolean waarde, de lamp AAN of UIT geet gaat worden. Dit laat zien dat we functies zodanig kunnen schrijven dat ze voor meerdere situaties inzet baar kunnen zijn.

Het resultaat:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  for(int A=1; A<=5; A++) {
    ZetLampAan(A, true);
  }
 
  for(int A=1; A<=5; A++) {
    ZetLampAan(A, false);
  }
}

void loop() {
  // leave empty for now
}

void ZetLampAan(int LampNummer, boolean LampAan) {
  if(LampAan) {
    Serial.print("Zet de volgende lamp AAN: ");
  }
  else
  {
    Serial.print("Zet de volgende lamp UIT: ");
  }
 
  Serial.println(LampNummer);
}

Zoals je zietL de “for”-loop, gaat door 5 iteraties, waarbij 5 lampen AAN gezet gaan worden.
De doorgegeven waarden zijn dus ook weer apart gehouden door een komma te plaatsen!
In de volgende “for”-loop, weer 5 iteraties, waar we de lampen UIT zetten.

De output ziet er ongeveer zo uit:

Zet de volgende lamp AAN: 1
Zet de volgende lamp AAN: 2
Zet de volgende lamp AAN: 3
Zet de volgende lamp AAN: 4
Zet de volgende lamp AAN: 5
Zet de volgende lamp UIT: 1
Zet de volgende lamp UIT: 2
Zet de volgende lamp UIT: 3
Zet de volgende lamp UIT: 4
Zet de volgende lamp UIT: 5

Bedenk wel dat deze voorbeeld wel een beetje simpel zijn, maar zodra je jouw eigen programma’s gaat schrijven, die wat groter zijn, dan zul je snel leren hoe handig functies kunnen zijn.

Antwoord van een Functie krijgen

We weten nu dus dat we een functie waarden kunnen meegeven door middel van parameters. Maar hoe krijgen we een antwoord terug van een functie – bijvoorbeeld voor een complexe berekening?

Herinner je de “void” in onze voorgaande voorbeelden? Dat is waar we definiëren wat voor datatype we als antwoord mogen verwachten.

In de functie zelf moeten we echter duidelijk aangeven wat de “return” waarde is. De term “return” is Engels voor “terug”, dus de waarde die we “terug” sturen.

Als we een functie definiëren welke een waarde terug gaat geven (dus niet “void”!) dan moeten we de instructie “return” gebruiken om een antwoord terug te sturen. Dat antwoord moet van hetzelfde datatype zijn als in de functie definitie!

We zouden bijvoorbeeld ons geld voorbeeld, dat we eerder hebben gebruikt, als voorbeeld kunnen gebruiken. Ook dit is natuurlijk geen ingewikkelde situatie, maar het dient slechts ter illustratie hoe een functie werkt.

Stel we maken een functie “TelAmMijnGeld”, welke we twee parameters geven. Ik heb deze parameters expres een andere naam gegeven om aan te geven dat de waarden van de parameters gekopieerd worden. Maar … deze namen mogen gerust dezelfde zijn, want de waarde die als parameter wordt opgegeven is een ander variabele dan de varaiabele met dezelfde naam in de functie – denk terug aan de “scope”van variabelen.

In de functie dus 2 parameters, en als antwoord het “Totaal”. De “return” waarde wordt vervolgens toegewezen aan de variabele “AlMijnGeld”.

Probeer de volgende code maar eens.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variables
  int ZakGeld;
  int SpaarGeld;
  int AlMijnGeld;

  // assign the values
  ZakGeld = 4;
  SpaarGeld = 12;
  AlMijnGeld = TelAmMijnGeld(ZakGeld, SpaarGeld);

  // print the values to the serial monitor
  Serial.print("ZakGeld = ");
  Serial.println(ZakGeld);

  Serial.print("SpaarGeld = ");
  Serial.println(SpaarGeld);

  Serial.print("AlMijnGeld = ");
  Serial.println(AlMijnGeld);
}

void loop() {
  // leave empty for now
}

int TelAmMijnGeld(int OpZak, int OpDeBank) {
  int Totaal;

  Totaal = OpZak + OpDeBank;

  return Totaal;
}

Een functie die terug komt met een waarde zouden we kunnen zien als een variabele welke een waarde bevat. Uiteraard kunnen we niets toewijzen aan deze zogenaamde variabele, maar we kunnen de waarde wel “uitlezen” en gebruiken waar we vaak ook een variabele of constante kunnen gebruiken. Kijk in et volgende aangepaste voorbeel maar eens naar regel 21 … We hebben als de “parameter” voor de functie “Serial.println() ” gewoon onze nieuwe functie gebruikt … en dat werkt prima! We hoeven dus de “return” (terug) waarde van een functie echt niet eerst in een variabele op te slaan – tenzij we deze waarde meerdere keren nodig hebben, want in zo’n geval willen we de berekening maar 1x doen, en iedere keer dat we de functie aanroepen, wordt de functie uitgevoerd.

Een functie welke een antwoord (return) waarde geeft kan gebruikt worden alsof het een variabele of constante is …

Elke keer als een functie wordt aangeroepen, zullen de instructies in de functie opnieuw uitgevoerd worden. Als een antwoord dus meerdere keren gebruikt wordt, overweeg dan om deze waarde in een variabele op te slaan in plaats van herhaaldelijk aanroepen van de functie.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  // define our variables
  int ZakGeld;
  int SpaarGeld;

  // assign the values
  ZakGeld = 4;
  SpaarGeld = 12;

  // print the values to the serial monitor
  Serial.print("ZakGeld = ");
  Serial.println(ZakGeld);

  Serial.print("SpaarGeld = ");
  Serial.println(SpaarGeld);

  Serial.print("AlMijnGeld = ");
  Serial.println( CalculateMyMoney(ZakGeld, SpaarGeld) );
}

void loop() {
 // leave empty for now
}

int TelAmMijnGeld(int OpZak, int OpDeBank) {
 int Totaal;

 Totaal = OpZak + OpDeBank;

 return Totaal;
}

Ik adviseer weer om wat te gaan spelen met functies, bedenk een eigen functie, voeg wat extra parameters toe, etc.

Functies die Zichzelf aanroepen (Recursie)

  Deze paragraaf is wat lastig te bevatten en zeker niet super belangrijk – sla dit dus gerust over als het onduidelijk wordt.

Een functie kan zichzelf aanroepen, en men noemt dit “Recursie“.

Je denkt vast dat je nu aangekomen bent bij het gekkenhuis, en ik kan me voorstellen dat dit onderwerp een beetje te complex is voor de meeste beginners en zelfs ervaren programmeurs hebben soms moeite met dit onderwerp. Je hebt recursie zelden nodig en ik vermeld het hier alleen maar om een beetje meer volledig te zijn.

Voor wie interesse heeft, lees gerust verder, als je even niet meer snapt waar dit over gaat: ga gerust naar Deel 7.

Recursie is een zeer krachtig, ook al is het soms moeilijk te bevatten, hulpmiddel voor programmeurs, waarmee je met weinig code toch iets moois voor elkaar kunt krijgen. Hieronder twee resultaten: een recursief getekende boom en de zogenaamde zeef van Sierpinski (een driehoek in een driehoek in een driehoek, etc). Afbeeldingen bron: Wikipedia – Tree door Brentsmith101, Sierpinski door Wereon.

Recursieve Boom

Recursieve Boom

Zeef van Sierpinski

Zeef of Driehoek van Sierpinski

OK, laten we een makkelijker voorbeeld bedenken om recursie uit te leggen.

Stel we hebben de nummers 1 tot en met 5 en we willen ze allemaal optellen, dus 1+2+3+4+5 (= 15).
We kunnen dit heel simpel doen natuurlijk, met een eenvoudig “for”-loop, maar vandaag gaan we lekker moeilijk doen en gaan we recursie gebruiken.

We gaan een functie maken die we het nummer “5” geven, welke daarbij “4” gaat optellen. Daarna geven we dezelfde functie het nummer “4” en tellen daar “3” bij op, etc.


1
2
3
4
5
6
int VoegVorigeNummerToe(int Nummer){
    if(Nummer==0)
       return Nummer;
    else
       return Nummer+VoegVorigeNummerToe(Nummer-1);
}

Wat deze functie doe is de waarde van “Nummer” pakken en kijken of dit nul is. Als het nul is, stuur dan nul terug (return). Als dit niet nul is, roep dan de functie “VoegVorigNummerToe” aan met “Nummer – 1”.

Maf he?

In andere woorden: deze functie begint met een nummer, en blijft er steeds het voorgaande nummer aan toevoegen tot het nul bereikt heeft.

Laten we dit eens van dichtbij gaan bekijken met een voorbeeld:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void setup() {
  // set the speed for the serial monitor:
  Serial.begin(9600);
 
  Serial.println(VoegVorigeNummerToe(5));
}

void loop() {
  // leave empty for now
}

int VoegVorigeNummerToe(int Nummer){
    if(Nummer==0)
       return Nummer;
    else
       return Nummer+VoegVorigeNummerToe(Nummer-1);
}

We starten dus met VoegVorigeNummerToe(5).

De waarde van “Nummer” is niet nul, dus we doen 5 + “VoegVorigeNummerToe(5-1)”. We hebben nog geen return waarde of antwoord omdat eerst de aanroep van VoegVorigeNummerToe(5-1) afgewerkt moet worden.
In deze tweede aanroep van “VoegVorigeNummerToe” is “Nummer” gelijk aan 4, en dus nog steeds niet nul. We doen dus 4 + “VoegVorigeNummerToe(4-1)” en weer moeten we eerst de aanroep van “VoegVorigeNummerToe(4-1)” eerst afwerken voor we een antwoord terug kunnen sturen.
In de derde aanroep is “Nummer” gelijk aan 3 en dus weer niet nul, en we doen dus weer 3 + “VoegVorigeNummerToe(3-1)” – aan.
In de vierde aanroep is “Nummer” gelijk aan 2 en dus weer niet nul, en we doen dus weer 2 + “VoegVorigeNummerToe(2-1)” – aan.
In de vijfde aanroep is “Nummer” gelijk aan 1 en dus weer niet nul, en we doen dus weer 1 + “VoegVorigeNummerToe(1-1)” – aan.
De zesde aanroep echter is “Nummer” wel gelijk aan nul en we sturen dus nul als antwoord terug.

Omdat we nu in de scope van de vijfde aanroep terug komen, en we dus een return waarde kunnen bepalen, gaan we weer een stap terug met het antwoord (1+0 = 1).
Vervolgens komen we terug in de scope van de vierde aanroep, met als resultaat 2+1 = 3.
Dit herhaald zich voor de scope van de derde functie aanroep, met als resultaat 3+3 = 6,
en de scope van de tweede aanroep met als resultaat 4+6 = 10,
en de scope van de eerste aanroep wat uiteindelijk het antwoord 5+10 = 15 levert.

Lekker verwarrend he?

Misschien helpt het als ik het zo laat zien:


1
2
3
4
5
6
7
8
9
10
11
12
13
1ste aanroep:     VoegVorigeNummerToe(5)    // in de setup() functie
2de aanroep:     VoegVorigeNummerToe(4)    // in de VoegVorigeNummerToe(5) functie aanroep
3de aanroep:     VoegVorigeNummerToe(3)    // in de VoegVorigeNummerToe(4) functie aanroep
4de aanroep:     VoegVorigeNummerToe(2)    // in de VoegVorigeNummerToe(3) functie aanroep
5de aanroep:     VoegVorigeNummerToe(1)    // in de VoegVorigeNummerToe(2) functie aanroep
6de aanroep:     VoegVorigeNummerToe(0)    // in de VoegVorigeNummerToe(1) functie aanroep, Nummer is nu NUL!
terug naar de 5de aanroep:     Return = Nummer + resultaat van de 6de aanroep = 1 + 0 = 1
terug naar de 4de aanroep:     Return = Nummer + resultaat van de 5de aanroep = 2 + 1 = 3
terug naar de 3de aanroep:     Return = Nummer + resultaat van de 4de aanroep = 3 + 3 = 6
terug naar de 2de aanroep:     Return = Nummer + resultaat van de 3de aanroep = 4 + 6 = 10
terug naar de 1ste aanroep:     Return = Nummer + resultaat van de 2de aanroep = 5 + 10 = 15

Eindresultaat: = 15

Ik weet dat dit concept moeilijk te bevatten is, zelfs voor ervaren programmeurs, omdat dit niet de gebruikelijk manier is zoals onze grijze cellen hier mee omgaan. Je hoeft alleen maar te onthouden dat de functie zichzelf kan aanroepen en zichzelf daarbij niet in de weg zit, maar dat iedere aanroep een eigen “scope” heeft.

Als vuistregel voor recursie: zorg er altijd voor dat de functie een “uitgang” heeft, anders blijft de functie eindeloos zichzelf aanropen wat als gevolg heeft dat de computer of de Arduino geen geheugen meer heeft en vast loopt. Iedere scope wordt namelijk opgeslagen voor wanneer de functie weer “terug” komt …

Bij Recursief gebruik van functies:

ALTIJD zorgen dat er een “uitgang” is voor de functie!

 

Als je vragen hebt: stel ze dan hieronder, en bedenk dat er geen domme vragen zijn, behalve dan natuurlijk de vraag die niet gesteld is. We zijn allemaal een keer bij nul begonnen!

Volgende hoofdstuk: Arduino Programmeren voor Beginners – Deel 7: Strings

Donatie Opties


Donaties worden zeer gewaardeerd, maar zijn zeker niet vereist. Donaties worden gebruikt voor het dekken van kosten voor web-hosting en project materialen, en eventueel voor een drankje of een snack. Voor wie al gedoneerd heeft: Hartelijk dank! Het is werkelijk geweldig om te zien dat men onze artikelen en applicaties waardeert.

Reacties


Er zijn 9 reacties welke je hieronder kunt lezen.
Je kunt jouw eigen opmerkingen plaatsen m.b.v. dit formulier, of een reactie op een opmerking plaatsen door op de "Reageer" knop te klikken.

  • 27 jun 2016 - 01:22 - peter bakkers Reactie Link

    Hallo mensen,

    Een hoop info opgedaan alhier, dank daarvoor!! Ik ben weer een stukje Arduino-wijzer.

    Met vr gr

    Peter Bakkers

    Beantwoorden

    peter bakkers

    • 27 jun 2016 - 10:31 - hans - Auteur: Reactie Link

      Hi Peter!

      Dank je wel dat je de moeite hebt genomen voor een bedankje! 
      Dat wordt zeer gewaardeerd.

      Mocht je vragen hebben over de onderwerpen, vraag dan gerust! 

      Beantwoorden

      hans

  • 31 mrt 2017 - 22:26 - Nico Kwaak Reactie Link

    Ik vind het super interessant en leerzaam. Recursie is inderdaad ingewikkeld.

    Is er nog een boek over C programmeren wat je me aan kunt raden?

    En de belangrijkste vraag: komt er nog een vervolg op deze cursus?

    Beantwoorden

    Nico Kwaak

    • 1 apr 2017 - 02:25 - hans - Auteur: Reactie Link

      Hoi Nico,

      Erg leuk om te horen! 

      Nou een vervolg zou weleens kunnen gebeuren ja. Maar het e.e.a. hangt af van de tijd die ik beschikbaar heb. Deze kleine cursus schreef ik toen ik een jaartje vrij nam van werk. Niet dat het een heel jaar duurde om te schrijven haha

      Wat boek betreft kan ik eigenlijks zo 1-2-3 niets aanraden, omdat ik zelf nooit boeken gebruikt heb voor het leren programmeren. Het meeste heb ik zelf geleerd door gewoon te proberen, en (tegenwoordig) het door snuffelen van het Internet.
      Ik leer zelf altijd het snelste als ik een doel heb, daarom vindt ik het schrijven van een cursus ook best moeilijk, maar wel leuk.

      Wat recursie betreft; ik wil daar best nog weleens wat over schrijven, met name omdat het zo’n interessante techniek is die soms erg efficient kan zijn.

      Beantwoorden

      hans

  • 11 sep 2017 - 20:09 - SmlyTim Reactie Link

    Beste Hans,

    Zeer interresant dit artikel, ik ben net begonnen met arduino en dit is DE manier om te beginnen.
    Ik wou gewoon nog een kleine fout melden (denk ik) die voor andere beginners misschien verwarrend zou kunnen zijn. Er staat namelijk;

    datatype FunctieNaam ( FunctieParameters } {

      // functie code

    }

    Wat natuurlijk 2 ronde haakjes moeten zijn->      ( FunctieParameters ) {

    Bedankt voor deze cursus!

    Beantwoorden

    SmlyTim

  • 11 sep 2017 - 23:06 - bob Reactie Link

    Hoi,
    Wat een goede tutorial zeg!Ik heb wel een probleem met de functie’s: Als ik mijn code probeer te compileren krijg ik de foutmelding “function not declared in this scope”Hoe kan ik dit oplossen?

    Beantwoorden

    bob

  • 13 okt 2017 - 18:27 - Peer Strik Reactie Link

    Dag,   Alhoewel het eenvoudig zou moeten zijn, valt het me niet mee. Ik ben niet zo talig en ook hier durf ik niet zo snel de grens over om me aldaar onverstaanbaar te maken. Toch heb ik hoop. Dankzij de cursus begin ik er toch een beetje vertrouwen in te krijgen. Waarvoor mijn dank.

    Opmerking: Fouten in voorbeeld voor onderwerp recursie expres gemaakt? Bv OpZak ipv Zakgeld

    Beantwoorden

    Peer Strik

    • 15 okt 2017 - 11:13 - hans - Auteur: Reactie Link

      Hallo Peer,

      als eerste leuk om te horen dat dit het e.e.a. een beetje toegankelijk heeft gemaakt voor je, en natuurlijk hartelijk dank voor het compliment.

      Wat betreft de fout die je vond: Dit is geen fout.
      Als je in een functie een naam voor een variable definieert (zoals hieronder), dan hoef je die naam niet te hanteren als je de functie aanroept. De naam wordt namelijk alleen maar binnen de functie gebruikt om de waarde op te vangen die meegegeven werd door de aanroep van de functie.

      int TelAmMijnGeld(int OpZak, int OpDeBank) {
       int Totaal;
       Totaal = OpZak + OpDeBank;
       return Totaal;
      }

      In deze functie definieren we de variable OpZak en OpDeBank. Als de functie aangeroepen wordt, dan geven we een waarde door voor ieder van deze variabelen. Dit kan een andere variabele zijn (b.v. ZakGeld) of gewoon een waarde (b.v. het nummer “3”). Binnen de functie wordt dan de doorgegeven waarde gekopieerd in de variabelen OpZak en OpDeBank.
      Het is zelfs beter om andere variabele namen te gebruiken binnen de functie zodat je verwarring in de code op die manier minimaliseert.|Stel we zouden hier ook de variabele naam “ZakGeld” gebruiken dan kan de waarde buiten de functie (vanwaar de aanroep plaats vindt) veranderd worden, maar in de functie kan het weleens zijn dat deze niet overeenkomt met die waarde.
      Dit komt omdat iedere variabele een “scope” heeft – het gebied waarin de variabele toegankelijk is. 
      Misschien een beetje lastig om de eerste keren te bevatten en misschien is mijn uitleg niet helemaal duidelijk, dus vraag gerust! 

      Beantwoorden

      hans



Jouw Reactie ...

Vriendelijk verzoek om hier geen lange teksten te plaatsen (zoals source codes, log files of config files). Gebruik daarvoor het Forum.

Deel met anderen:
*
*
Houd me op de hoogte van nieuwe reacties (email).
       Gebruik jouw RSS reader om reacties te volgen.


Tweaking4All uses the free Gravatar service for Avatar display.
Tweaking4All zal nooit jouw email adres met anderen delen.