Cursus Raku / Functioneel, concurrent, reactief en webprogrammeren / Functioneel programmeren / Recursie
Recursie met multi-subroutines
Tot nu toe was het basisgeval een regel binnen de subroutine
— een ternaire uitdrukking of een vroege return die het
argument controleert. Raku biedt een expressievere manier om hetzelfde
te schrijven. Omdat een subroutine meerdere
multi-kandidaten kan hebben, kun je het basisgeval en de
recursieve stap elk hun eigen subroutine geven en
meervoudige dispatch voor elke aanroep de juiste laten kiezen.
Denk terug aan de faculteit. Met multi worden haar twee
gevallen twee subroutines:
multi fact(0) { 1 }
multi fact($n) { $n * fact($n - 1) }
say fact(5); # 120De eerste kandidaat matcht alleen wanneer het argument precies
0 is — die literal in de signatuur is het
basisgeval. Elke andere aanroep gaat naar de tweede kandidaat, die
vermenigvuldigt en recursief aanroept. Wanneer fact($n - 1)
eindelijk 0 bereikt, schakelt de dispatch naar de eerste
kandidaat en wikkelt de keten van aanroepen zich af. Het basisgeval is
niet langer een tak die diep in de body verstopt zit; het is een
subroutine die voor één enkele waarde bestaat.
Waarom 0 en niet 1? Omdat elke stap er één
aftrekt, komt elk beginnend getal uiteindelijk precies op 0
uit, en 0! is gedefinieerd als 1 — dus
0 is waar de afdaling werkelijk eindigt. Een
literalkandidaat matcht één exacte waarde, dus een basis
multi fact(1) zou fact(1) correct berekenen
maar fact(0) naar multi fact($n) laten
doorvallen en eeuwig voorbij nul recursief blijven aanroepen. Bij
0 stoppen houdt de subroutine correct voor elk
niet-negatief geheel getal, fact(0) inbegrepen.
Dit leest bijzonder goed wanneer er meer dan één basisgeval is. Fibonacci heeft er twee nodig:
multi fib(0) { 0 }
multi fib(1) { 1 }
multi fib($n) { fib($n - 1) + fib($n - 2) }
say fib(10); # 55Elk basisgeval is een eigen kandidaat van één regel, en de recursieve kandidaat handelt al het overige af — geen geneste voorwaarden.
Een literal als 0 matcht alleen die exacte waarde.
Wanneer het basisgeval een bereik omvat — “$n is
1 of minder” — gebruik je in plaats daarvan een
where-beperking:
multi fact($n where * <= 1) { 1 }
multi fact($n) { $n * fact($n - 1) }
say fact(6); # 720De beperkte kandidaat is specifieker, dus Raku probeert hem eerst; de
kale $n-kandidaat vangt al het andere op.
Dezelfde discipline als eerder geldt nog steeds: elk recursief pad
moet een basisgevalkandidaat bereiken. De faculteit met de literal
0 is bijvoorbeeld alleen veilig voor niet-negatieve gehele
getallen — fact(-1) zou voorbij 0 stappen en
eeuwig recursief blijven aanroepen, omdat er nooit een kandidaat matcht.
De gevallen over multi-subs verdelen neemt de noodzaak van
een basisgeval niet weg; het geeft dat basisgeval alleen een eigen naam
en een eigen plek.
Praktijk
Maak de 1 quiz over de inhoud van dit onderwerp.
Cursusnavigatie
← Quiz — Het basisgeval | Quiz — Recursie met multi-subroutines →
💪 Of ga direct naar de oefeningen in deze
sectie.