Debugging challengeBeginner
The memo that remembered too much
Scenario
A staircase-paths counter with memoization returns correct answers — the first time. Called again with a *different* set of allowed step sizes, it returns the answer from the previous configuration. A tree helper has the same disease: collect_leaves(tree) returns [2, 3] on the first call and [2, 3, 2, 3] on the second, with the same tree. Each function works in isolation and fails only across calls. Find the bug.
1def count_paths(n, steps, memo={}):2 """Number of ways to climb n stairs taking any step size in steps."""3 if n == 0:4 return 15 if n < 0:6 return 07 if n in memo:8 return memo[n]9 memo[n] = sum(count_paths(n - s, steps, memo) for s in steps)10 return memo[n]11 12 13def collect_leaves(node, out=[]):14 """Leaf values of a binary tree, left to right."""15 if node is None:16 return out17 if node["left"] is None and node["right"] is None:18 out.append(node["val"])19 return out20 collect_leaves(node["left"], out)21 collect_leaves(node["right"], out)22 return out23 24 25print(count_paths(4, [1, 2])) # 5 — correct26print(count_paths(4, [1])) # 5 — expected 1: only 1+1+1+1 is possible27 28tree = {"val": 1,29 "left": {"val": 2, "left": None, "right": None},30 "right": {"val": 3, "left": None, "right": None}}31print(collect_leaves(tree)) # [2, 3]32print(collect_leaves(tree)) # [2, 3, 2, 3] — grows on every callYour task
- When is the expression
{}inmemo={}evaluated — at each call, or somewhere else? Where does the resulting dict live between calls? - Explain the wrong
5: which entries are inmemowhen the second call starts, and why is keying the memo bynalone already a design bug oncestepscan vary? - Trace the two
collect_leavescalls and explain the doubling. - Write the standard fix with a
Nonesentinel, and an alternative usingfunctools.lru_cache. What doeslru_cacherequire of the arguments? - When is a mutable default actually the intended behaviour, and how would you make that intent unmistakable?
- State the complexity of the fixed
count_paths.
DebuggingEdge CasesImplementation
Work it out
Write your analysis before revealing anything. The self-check below compares it against what a strong answer contains.
Reveal
Progressive — each section builds on the previous one.
The bug
Why it happens
The fix
Edge cases
Complexity
Self-check
Tick what your analysis covered. Be honest — this feeds your readiness profile.