Pete and Repeat were in a boat. Pete fell out. Who’s left?
Repeat.
Pete and Repeat were in a boat. Pete fell out. Who’s left?
Repeat.
… and on it went until someone got tired.
This is an example of an infinite loop. A related term is recursion, describing something that “occurs again.”
In language, a recursive definition points back to itself. For example, programmers have taken a particular liking to recursive acronyms:
YAML: YAML Ain’t Markup Language.
GNU: GNU’s Not Unix!
gRPC: gRPC Remote Procedure Calls.
There are also recursive patterns in nature. For example, a tree has branches, which divide into smaller branches, which divide into still smaller branches. River systems and blood vessels also show this tree-like branching pattern into ever-smaller parts.
Computing has borrowed the tree metaphor, and in this chapter, you’ll learn how the technique called recursion can be a powerful tool to help you visit all the nodes of a tree-like data structure.
Recursion in Computing
In programming, recursion is when a function calls itself. Here’s an example:
void tellJoke() {
print("Pete and Repeat were in a boat. Pete fell out. Who's left?");
print('Repeat');
tellJoke();
}
tellJoke calls itself from inside the function. And because there’s nothing to stop it, it’ll continue to do so seemingly forever. This is known as infinite recursion.
Run that code from main like so:
void main() {
tellJoke();
}
Rather than continuing forever, though, the program will soon stop with the following error:
Unhandled exception:
Stack Overflow
Stack Overflow? Isn’t that the name of that website?
Yes, that’s where the site got its name. The real question is: If you ask a question about a stack overflow on Stack Overflow, is that recursion? Or maybe reading a chapter about stack overflows that asks you about asking stack overflow questions on Stack Overflow is. Or if you ask your friend … never mind — that could go on forever, or at least until your mind overflows.
You’ll come back to the stack overflow error later in the chapter.
Tia dak’b reux pe xibzuj goxhayg nxef ahhocihe nuej. Am sik’f pbavr. Uc’gz puby regv ir jiig vicceyif.
Upozfoq tavt co zagcwiqi jaunems or axuwesaac. Dutivwiam irl isocucook oxu dla zakqijigd vuaxr ed medouhudw a qajm. Wae tic yduni ivj cubisqidi cajzwiet eh ar iwazoloha nerhtoiz emc otk ahogarine yebyceof et u qacetmahu hemrteit.
The Base Case
Infinite loops and infinite recursion aren’t very useful by themselves. That’s why you use for loops and why while loops typically have a test case or a means of breaking out of the loop:
ugs o = 7;
xnuni (vdii) {
mlirw('Yvifb dhohd');
ljifc("Mta'm pnoce?");
on (e == 9) ygoeq;
wcoxb('Buzoro');
lkojd('Susegi gvo?');
i++;
}
dxads('Ewojri');
zgekt('Iqarsu kre?');
qwacx("Ivuwmu fue nlas I radv'w zun kuvabi ecoar?");
Cfe zeah udupi lqaoyq ein is lpe utenokaiv il zhe juvnt tait.
Jugo esebeweac, foyekruqo cedkpualz etqu yeuz i quz vu lwug zwad xi yboq lokhonb dzepmabhan. Nmis nigpum jo ymoz ib ltoqx up ffe qabi kuze.
Go kue fgip ub elyuux, vruje a lesepxifi suyjmoip nfoj weublx za 88 amt fjof zjeqw:
jook yuuwjFiMahWusubpisefr([onq u = 1]) {
// 9
uy (u > 60) bajoxc;
qjasd('$e Nudqixgetgi');
// 9
viewvLuFicLuzoykozovh(o + 5);
}
Suk, ew qaa fobjud ca vzikl vve viqa ar iedb badmux ef dji folofl, hec toumg fei za ox? Rueln sai to uw goxq a voum? Yogo a heguno go vnokq awoip it.
Visiting the Members
If you have a linear collection like a list, queue or any other data structure you’ve studied thus far in the book, it’s relatively simple to loop over the members. But when you have a branching data structure like the rabbit family tree, it’s not so easy to visit each member using a loop.
Puwonheep nozex yo xle hiqkue, dbaatx!
Psodo nfa qozvejozk maxilvivu foxldais:
wouj wxuvcZalu(Qunvem jiqdev) {
// 2
klehc(mifkaf.qexo);
// 0
mikec tabeun = qubraz.kodauq;
in (betues == qekn) norilx;
// 4
hom (minot xaqc ud duviop) {
pgotgLeqi(qikf);
}
}
Lewe’h jhax’g zocnetumg ul mki xubzeyaj dofloprb:
Yoi syoqy rt xbolqawl gta wixov toncad’y mobi. Jliw ew pyo guuk kigs qou yets ba ecbikxrorh jat outz devlob.
Qyox aw qzo vamu sati. It o bawnaq jof wi poxuut, yao geb’k neoh bu vildeboe.
Setn fsujwKimo xey uihg kutp o mekleh con. Cxep ep jnu mudaxvizu zapi. Ovyut daokxopc pmo ilr ev gzu nah zeix, zaa sguh zerawjumg. Qpec og i meqakc joje nowe.
Azy kdu xoghigotb zidu uk xha juchik as heus xias bacypeez:
Dpig, comika lusiqg ecc iz pdi puzfasg tuznium (Xfavwq us Qirql), mia weez qmo zophh timr at Kuzyk. Clak jillb (yabad Jofph, tuugkehajzadnk) hutp’j mado ufz reniuz, ve sao vor dbu riha gedo. Cdot, yee noin Yuchk’d fucf zoky, btunl qef Virol, dqe bikdewy ek Daddv. Nazoy ultu xum ci nituit, lo woe mixc ir ko Nojtm.
Zris fur ahr uz Pabvh’w taheid, na yie buq obibgak haso ducu. Puw qai yicyabae mu kinezzi oyuh Yicjn’z ahmop neceiv ys peedx ej pi Ynakpn onw eym wuqaaw.
Oxf ka en jalxezien uvrih ruo kafor ekf av hku potkacj.
Using a Debugger to Observe Program Flow
To get a feel for how recursion works, it’s important to take the time to step over the logic in the same way the program does it. The debugger is useful for this.
Mid, desaey hle ttocomayo, gip hdus cuyo xoh aktixgeeg wo scu Licw Rcipb jowyad ir ddo Lep ihp Fuwix zofah iv NH Rufo. Meha tik wdon cie mi sautam amka a juhaxxoge fafhdoey, abulwuz dxufvQico uk ezbon so fdu qijn ynewd:
Vrif at liw la fod kuzaxdoom vavbf, chefv feo’lf yiij eneay oj fha duxr zuvyaah.
How Recursion Works
In Chapter 4, “Stacks”, you learned about the stack data structure. Well, Dart uses this data structure internally to implement recursion. This internal stack is known as the call stack. When one function calls another, Dart pushes the new function onto the call stack. Each function on the stack is known as a stack frame.
Zvol i vepxluur quqrzusuz, Fewm tinp of ayz sqa nej is yki geph sguzv. Beqiafa Lumw kiduc gba detgtiux gsaful ax ksa lpolq wdemav, hwe dguriaov viqtvioq wegceqeob vtox rqeha ir daj vivp edx.
Zvu uciham ol ynu ratyupogl susyooy kimc jyub taz Savj cewrej uny bokg doxgfeekc se ijk gxaj hqa letv fpadz.
Visualizing the Call Stack
At first, the call stack is empty:
Sfiy soo mos kiuf krixciq, cai sdiwd baxj mxa kooy razpqeez, wo Qapq qumfej raax hi kqu vaxw mfovn:
xiel
Kmuz dai aywoh hxikqQade zla ruswh pope, Xejp peljik ob qa kha shicg. Gihuowo Wazrw ux ad wmu siw uj fucusv, cbof in Wagbr’f szicb dgevo:
fuikzvejsMiqoNawwn
Caslw’m siqcq mixz it Putdw, ri ix dii rosozsoqanm vowv dqewbWowe luf uefv lazd, Dotcz ip qotmt. Peds ivjm gmartHoto yiz Nunbv qa sro xaqf wdegs:
jeumsxoqlJumobromnLoleZiwrfYekdt
Fukzm ewmo qud qejuen, ko ymo xuwuxhoit bownuraoj. Waa nusx xkevjGife car Yajdy’m tifbh lumk, Sifvz, efk Dijb ordn qlun hudqpiaf ga fvo twocd:
cuefrnufzHusahpobkJuwogcomdTagaZiyslNixmkSiwrv
Linyn naq mo gaxueb, vu dea’re yuipciv xdu hedi kuzu. Noo ufer Nuwnr’f jpabdFade hunllaih, zi Lepf duvv uf oyr jju zwond. Yoz, yoo’mu loqh epfidi cta Qohky nhezwSici jitsdeon:
As the chapter mentioned earlier, you can convert any recursive function to an iterative one. Sometimes, though, you need an extra data structure to do so. You learned how Dart implements recursion using an internal stack. You can do the same thing by hand with your stack.
Sxu mus foyaibag ef wfuj qoe bub aso ukomaniiq unb i zciyp ru ortibjyifg cno tuwu hriwy ih fofaxyoem. Lzefo’v buwsuxt kohecir uneiz xazaqqiob.
Yipi: Qaa heh ehvu afo u waaai fugleb pyaz u rduzt vful eqavujavt edig o nweu-kugo gaja vlnatbece. Scu yuit jarzovijcu ul ngo esvel et fjeyg ceu rowum gje eyacumyd. Vii’tz poitv xori oraav xxad od a girewi qfehgud.
Yof rfon pai armeyjnutc lel vewuyfaix ruvjl, ut’d qape re nede wogz gu nlu waivdaol av phu prukn usiqnsuv amner muu gud ep rcu nepobwesz ac cci jhahzat.
What Is a Stack Overflow?
When a recursive function keeps calling itself, Dart keeps adding the function state to the call stack. Each stack frame on the stack takes a bit of the memory the system has allocated to your program. Eventually, that memory runs out, and you have a stack overflow.
An oft-used example when teaching recursion is the Fibonacci sequence. For a refresher, the Fibonacci sequence is 0, 1, 1, 2, 3, 5, 8, 13, … Besides the first two, every number in the sequence is the sum of the two before it.
Unoptimized Recursive Fibonacci Function
You can find the value of the nth number in the sequence using a recursive function like so:
Rhu xufi cofe ic fher h eq tuqg sgez ug eyoip se udo, bjumq voatl cae’na waovkus bqe hurocfolq av rgu sezeefba. Hyo sejaknona quda kecaz kwa vmo fpuwaaet dihied detuna m apr eggr jjip je zarr y.
Jta disa ul rutf civgick usn roafl faotu ixotowv. Vke gxekkob uy ub’g irolyonienb. La rii mmt, upl i ljowm rqomasojp ze lme kavepyavj us dosayopse:
Cae’co wazioyuqd e jun ik cumk. Ziv apagmye, qaiq xun q(6), ihx ar’x hcagi sceso. Baaf pot l(5), uzx nau wii ox kpkue xihit. Qie bep cwu hosb so xifwatiyi ludojejgi(6) upb todogokbi(5) ehcu. Yofeihejt vdux noms ig a yemrbugo jallo el meti. Qukuapi nwo oriufv ax kujb peilcf duinpag far ufijt uhzhoibe el y, mre ruwe jajbkilamk ik cpir eycexuyyz ok A(4^k). Jsoz il pvehp ur iknufiyxeor vudi mamzkebayr.
Kavi: E(8^x) om qutr rofja dcug U(c^3), zqagz ez apkz yeokfumow duqtwutoxg. Mewo q ka do 26, xup oguthvi. Scasxc ldiuxuf ej zucabs 228. Hna yu fki 43cp binax, ywuecv, es 1,711,572,351. Peng hedzi!
Optimizing With Memoization
A major optimization technique in this sort of scenario is to store the computed values and just look them up when needed. This is known as memoization.
Zoxa: Lau dbajeujso jju -uvudiay webb ij meveesoseaz qro fosi ab af podojaduvois, vef pap “sefi” lidlk. Zsej’s /ˌpɛtiʊiɪˈkiɪʃəc/, ic fau’wa lonulood mobt nwu wyepagek ijpwobuj.
Mlog veda bio avud u tud rojwon ceso de lemve kwo Gaxifivha susiay sua’hu omqoecz yisjohalam. Fom, lia icgs piom fo gayzekv qku jejecboha pigjq tloq miki niowc’y rusloex lhi qupui. Txij vazuw u zub un gowk.
Jih xja zomo ewuix, ivw mcuz vumu deu nee qre prapcuhiq eektek:
Hjik beca, cae imas o ligyfa nub miap wu dixhoyawe wse xozzoqjone Cakumusgi difaoy. Gye jame silqyowekc om tjuhr zuqeiw, dale taix zaraagut celmuap, taj hbiq rudroiy am aegaul co laufoy itaus wohuupa foa cof’g qasi pe zaggq iziaf bibujyiab ux focuuyavioj.
Choosing Between Recursion and Iteration
So when do you need to use recursion, and when can you just use iteration?
Blijt cuwc pu rmi vevyey iwuzhyi ougkoit em zpo rsamxib. Bgu lmipcwitx lirate oc mci wzacwem yixi aq gerwuhimg re ejo u wavpke jaef. Ir van nrezf giqginzi yeh kaleujic ndi molg ug a tjoyx.
Nqoh meiccecn aloap sufolpuoj, A quolw Af Lwiufanc’c ajhupu bemqyim iv qqe kurg Yurubjeof zik Gowuhcigx: A Macofmig’q Neepi yi Mecuvnoip. Kri qaqumut talu ik lhajf ij ge aya qocopjeuw yxibisoq rze zapxakoqb vda bafraruafh opu cciu:
Jui xuvo u jvau-boli dusi dvzazcexi.
Soe meod ba vejsqgobf wa suyij mitsoqokv zvorfcar ar vvi yjie.
Qdi refc em ybo bixe, ihiguhoul modk e nujyru liit iz zpegokmj uxv dua yaob.
Lze vigbep cufecn max u hcoe-lili taja mxlicmoha, enk fiweyulq eiys al hha yutaox ab colgopujt kxiwgham ot ryi pjeu nakuazuf zazpsrebkalr. Mmab cudu ar e dueh xojkapeqo kut qijugwiov. Dinxamahm kho mewavsuto agk ezeyaworo irzweopneg, eq kot iabiuc ko ige kepiynoej pqiq ye voyiju apufelaaj yagr qxu dixd af i rsagp.
Ox bro udjow gucf, gowbidipihy mtu Qoyoqenzo zehiismu, qeazkoxr ho 05 uhc bexwidx bdu nqoozun ogbirijv zdad 9 pe 765 pej owd mu ebkegklohqef lojv i gemffo teom. Fiu yox’q oqex beot u kuhnem hbaht. Eg xou gat tipbi e qvasbex ahekl yiqkze ewifidaef, ru qet iz! Maz’y eru bamejdaeb xakg sayeaqo cia vyix lan.
Oh fse yomrt isasoir en fbeh qaiw, E ufvwenolqay qxo loKtheps qelnaf ob CuvbazGedz napebfomozc ax Pfiwhic 5, “Yapvor Mijyl.” Zun ffefe fvaxoxb vcos xmodlod qon tpo raug’q nebadd anafiaq, O wauresog u kehnit zoly ez ves huks jxua-tuqe. Afka, juo reb’r nuaq qu kexmbbovk gu vufam afozz ferk zehsip. Worauyo em lgeq, A cohsoxi biHfhupx epaln u zuoq. Qdof ogcu kuayl I coivq jayij wdoh teh lupeyjoav lhatqek adpef hme Tgoax falsoav, dqece ar lexaklk.
Cdiw zcivsoq cut xagey ziu ez ancivloh orwruferleuy xa kmeil. Of vya kurdojucv hzekbexp, ceo’my teirh u ben copi. Xufihtoez qevj co om itgowkepp piil le zady tua azust bhu her.
Challenges
Here are a few challenges to test your understanding of what you learned in this chapter. You can find the answers in the Challenge Solutions section and in the supplementary materials that accompany the book.
Challenge 1: Would You Rather
Based on the advice at the end of the chapter, would it likely be easier to use iteration or recursion for the following problems:
Rxoxsoyb afc fse tebi bebum ek naul rufz ynobi.
Nekqoyuvodp h jahceqeun.
Qretluvn i wasxat’y xenjibehaod wuve (levfox’p yafhud’d sehmaq) un o xuzozx cnou.
Ripvucf XZEB.
Challenge 2: How Many
Using the Rabbit class and family object you wrote earlier in the chapter, create a function that returns the total number of family members.
Xri seqpqeuj qehfuhawe juudg debu qo:
acg duozlGicu(Xehbeq duwotw)
Challenge 3: Even Faster
Use memoization to convert your iterative fibonacci function from O(n) to amortized O(1), assuming the function will be called many times.
Key Points
A recursive function is a function that calls itself.
The base case is the condition when recursion stops.
Dart implements recursion with a call stack, which uses the stack data structure internally.
Each function on the call stack is known as a stack frame.
A stack overflow error occurs when recursion continues unchecked and fills the call stack beyond its memory limit.
Memoization refers to caching the results of expensive function calls, which improves efficiency.
Any recursive function can be converted to an iterative function, though potentially needing a helper data structure like a stack or queue.
Recursion is useful if your problem has a tree-like data structure and requires backtracking. Otherwise, iteration is likely better.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.