Een recursieve functie is een functie die zichzelf opnieuw oproept, tot er aan één of meerdere voorwaarden wordt voldaan. Je kan het ook zien als een bijzondere soort herhaling: door zichzelf telkens opnieuw aan te roepen, ontstaat er immers een herhaling van dezelfde code.
Dat klinkt misschien vreemd: een functie die zichzelf oproept? Zou die dan niet eeuwig blijven doorlopen? Dat zou gebeuren als we niet oppassen. Daarom heeft elke recursieve functie twee delen.
Elke goede recursieve functie bestaat uit twee delen:
| Deel | Wat het doet |
|---|---|
| Basisgeval | De eenvoudigste situatie, waarvoor het antwoord meteen gekend is. Hier roept de functie zichzelf niet meer op: het basisgeval stopt de recursie. |
| Recursief geval | Het algemene geval, dat het probleem een stukje kleiner maakt en de functie opnieuw oproept op dat kleinere probleem. |
Het basisgeval is dus onmisbaar: zonder basisgeval stopt de recursie nooit en loopt het programma vast.
Bekijk de functie SomGetallen. Ze berekent de som van alle gehele getallen van
0 tot en met een gegeven getal. Zo is SomGetallen(3) gelijk aan
3 + 2 + 1 + 0.
static int SomGetallen(int getalTot)
{
int uitkomst;
if (getalTot == 0)
{
uitkomst = 0;
}
else
{
uitkomst = getalTot + SomGetallen(getalTot - 1);
}
return uitkomst;
}
De functie heeft een if-statement dat controleert of de waarde van de parameter
gelijk is aan 0:
getalTot gelijk aan 0, dan is de uitkomst meteen 0. Dit is het
basisgeval: de functie roept zichzelf niet meer op.SomGetallen(getalTot - 1)). Dit is het recursieve geval.Telkens onderbreekt de functie zichzelf en wordt ze opnieuw opgeroepen met een
iets kleinere waarde, tot het basisgeval bereikt is. Voor SomGetallen(3)
gebeurt het volgende:
SomGetallen(3) = 3 + SomGetallen(2)
= 3 + (2 + SomGetallen(1))
= 3 + (2 + (1 + SomGetallen(0)))
= 3 + (2 + (1 + 0))
= 6
Voor kleine getallen verloopt dit probleemloos. Geef je een heel groot getal in (bijvoorbeeld 500000), dan stapelen er zo veel openstaande oproepen op dat het programma foutloopt (een stack overflow). Recursie werkt dus best op problemen die snel kleiner worden.
printFun(3)Een recursieve oproep heeft eigenlijk twee momenten: het stuk code vóór de recursieve oproep, en het stuk code erna. Het volgende voorbeeld maakt dat mooi zichtbaar.
static void printFun(int test)
{
if (test >= 1)
{
Console.Write(test + " ");
printFun(test - 1);
Console.Write(test + " ");
}
}
Roep je deze functie aan met printFun(3), dan drukt het programma als resultaat
af:
3 2 1 1 2 3
Op het eerste zicht een vreemd resultaat. Stap voor stap gebeurt het volgende:
printFun(3).test gelijk is aan 3 en groter dan of gelijk aan 1, wordt eerst
het getal 3 afgedrukt.printFun opnieuw aangeroepen met test
verminderd met 1, dus printFun(2).printFun(2) wordt opnieuw het getal 2 afgedrukt, omdat test nog
steeds groter is dan of gelijk aan 1.printFun opgeroepen met test - 1, wat resulteert in
printFun(1).printFun(1) wordt het getal 1 afgedrukt.printFun opnieuw opgeroepen met test - 1, wat dit keer
printFun(0) oplevert.test gelijk is aan 0, wordt het basisgeval bereikt en wordt
de recursie gestopt.Tot hier is dus 3 2 1 afgedrukt. Maar de functie is nog niet klaar: bij elke
oproep moet immers ook het tweede Console.Write(test + " ") nog uitgevoerd
worden. De openstaande oproepen worden nu, in omgekeerde volgorde, afgewerkt:
printFun(0) is voltooid en de controle keert terug naar de
oproep van printFun(1) waar we gebleven waren.printFun(1) uitgevoerd, waarbij opnieuw het
getal 1 wordt afgedrukt.printFun(1) afgerond en de controle keert
terug naar de oproep van printFun(2).printFun(2) wordt het tweede gedeelte uitgevoerd, waarbij opnieuw het
getal 2 wordt afgedrukt.printFun(2) afgerond en de controle keert terug
naar de oproep van printFun(3).printFun(3) wordt het tweede gedeelte uitgevoerd, waarbij voor de
laatste keer het getal 3 wordt afgedrukt.Uiteindelijk zijn alle functieoproepen afgerond en zijn de getallen afgedrukt in
zowel oplopende als aflopende volgorde, wat de uitvoer 3 2 1 1 2 3 verklaart.
Het stuk code vóór de recursieve oproep voert uit op de heenweg (van de grote naar de kleine waarde); het stuk code erna voert uit op de terugweg (van de kleine waarde terug naar de grote). Hou dat in het achterhoofd bij de oefeningen.