1.2.37 · D5Introduction to Programming (Python)

Question bank — Recursion — call stack visualization, base case, recursive case

1,452 words7 min readBack to topic

True or false — justify

A recursive function must always have exactly one base case.
False. It needs at least one, but may have several — e.g. Fibonacci returns directly for both n == 0 and n == 1. The rule is "every path down must hit some base case", not "there is exactly one".
If the input gets smaller on every call, the recursion is guaranteed to stop.
False. Smaller-input alone is not enough — a value must actually be caught and returned by a base case. Without if n == 0: return ..., the shrinking sails right past into fact(-1), fact(-2), … forever.
Every recursive function can be rewritten as a loop.
True. Recursion and iteration are equally powerful; any recursion can be simulated with an explicit stack and a while loop (see Iteration — for and while loops). Recursion is often just closer to the maths, not more capable.
All the stack frames for fact(3) share one copy of the variable n.
False. Each call gets its own independent frame, so n = 3, n = 2, n = 1, n = 0 all coexist at the deepest moment. Same name, different boxes.
During fact(3), the call fact(3) returns its value before fact(0) does.
False. The stack is LIFO: the last frame pushed (fact(0), the base case) finishes first, then the others unwind outward. fact(3) returns last of all.
Recursion is always slower or more memory-hungry than the equivalent loop.
Usually true on memory (each frame costs stack space, capped near depth 1000), but "always" is too strong — with memoization a recursive solution can match or beat a naive loop. Clarity, not speed, is recursion's main win.
A function that calls a different function which then calls the first one back is not recursion.
False. That is mutual (indirect) recursionis_even(n) calling is_odd(n-1) calling is_even(n-2). Recursion means "eventually calls itself", directly or through a chain.
The return keyword is optional in the recursive case as long as you print the answer.
False for computing a value. Without return, the sub-call's result is discarded and the function yields None. Printing shows a number on screen but hands nothing back to the caller to combine.

Spot the error

def f(n): return n * f(n-1) — what's missing and what happens?
No base case, so it never stops. Python pushes frames until it hits the depth limit → RecursionError: maximum recursion depth exceeded. Add if n == 0: return 1.
def f(n): if n==0: return 1; return n * f(n) — why does this still crash?
The recursive call uses f(n), not f(n-1), so the input never shrinks. It re-asks the identical problem forever and never reaches the base case despite having one.
def summ(L): if not L: return 0; L[0] + summ(L[1:]) — what does it return and why?
It returns None. The final line computes a sum but has no return, so the value is thrown away. Fix: return L[0] + summ(L[1:]).
def cd(n): print(n); if n==0: return; cd(n-1) for cd(2) — is this correct?
It works but by luck of placement. print is before the check, so it prints 2, 1, 0 then stops. Cleaner is to test the base case first; here a negative call is avoided only because n==0 returns before cd(-1).
def fib(n): if n<=1: return n; return fib(n-1) + fib(n-1) — spot the bug.
Both branches recurse on n-1; the second should be fib(n-2). As written it computes the wrong sequence (and still terminates, so no crash to warn you — a silent logic bug). See Fibonacci and overlapping subproblems.
def f(n): if n==0: return 1 else: return n*f(n-1) called as f(2.5) — problem?
The input decreases by 1 each time (2.5 → 1.5 → 0.5 → −0.5 → …) but never equals exactly 0, so the base case is skipped forever → RecursionError. Base cases must be reachable from the starting input.
def down(n): if n==0: return; down(n-1); print(n) for down(3) — what prints?
1 2 3, not 3 2 1. The print sits after the recursive call, so it runs during unwinding (coming back up), reversing the visible order.

Why questions

Why does the base case have to come before the recursive call in the code?
So the stopping condition is checked first, on every entry. If the recursive call ran before the check, you'd descend one level too deep (or infinitely) before ever asking "should I stop?".
Why does the deepest recursive call finish first?
Because the call stack is LIFO — the last frame pushed is the first popped. Each caller is paused waiting on its callee, so nobody above can resume until the one below returns.
Why can two frames both have a variable named n without clashing?
Each function call creates a fresh frame with its own local namespace. The name n is a label inside that frame's box, so fact(3)'s n and fact(2)'s n are entirely separate storage.
Why does moving a print from before to after the recursive call reverse the output order?
A print before the call fires during winding (top-down); after the call it fires during unwinding (bottom-up). The two phases visit values in opposite orders.
Why is naive recursive Fibonacci slow even though each call shrinks the input?
Because it re-solves the same subproblems many times — fib(3) is recomputed all over the tree. The work explodes exponentially; Memoization and Dynamic Programming fixes it by caching each answer once.
Why is recursion natural for walking a folder tree or parsing nested data?
Those structures are self-similar: a folder contains folders, each handled the same way. Recursion mirrors that shape directly — see Tree and Graph Traversal.
Why does Python cap recursion depth near 1000 instead of allowing unlimited calls?
Every pending frame occupies real stack memory; unbounded depth would exhaust it and crash the interpreter unpredictably. The cap turns a silent memory blow-up into a catchable RecursionError. See Big-O and Recursion Depth.

Edge cases

What does fact(0) return, and why is that the whole point?
It returns 1 immediately with no recursive call. That direct answer is the base case — the ground the whole staircase of calls stands on; remove it and everything collapses.
What happens when you call summ([]) (empty list)?
The base case if not L: return 0 fires at once. An empty problem has a known trivial answer, so no recursion is needed — this is exactly why base cases exist.
If a recursive call is the very first thing that runs (before any base check), what breaks?
The function recurses before ever testing whether to stop, so it descends indefinitely and overflows the stack. The stop condition must be reachable before the next call.
For a single-element list summ([7]), does recursion happen at all?
Yes — [7] is not empty, so it computes 7 + summ([]). The empty-list base case is reached on the next call, not this one. One-element inputs are still a recursive case, not a base case.
Can a base case itself contain a recursive call?
No — by definition the base case returns without recursing. If it recursed it wouldn't stop anything, and there'd be no guaranteed floor to the descent.