4.1.1.15–4.1.1.16 - Stack frames and recursion (Python)

4.1.1.15–4.1.1.16 - Stack frames and recursion (Python)

Learn how pending subroutine calls retain their own state, how a recursive call reaches a base case, and how to trace the results as calls return.

Stack frames

When one subroutine calls another, the caller must retain enough information to continue after that call finishes.

Stack frame

A record for one active subroutine invocation containing its parameters, local variables and return address: where execution should resume in its caller.

The call stack follows last-in, first-out order. A new call places a new frame on top. When that call returns, its frame is removed and the previous caller resumes. The return address identifies a position in the caller's code; it is different from the returned data value. A language runtime may also save other implementation details, but these three items explain the required model.

Suppose report() is waiting for double(4), and double calls increment(4) twice. The active increment frame stores its parameter 4, its own working values and the point in double to resume. It returns before the next increment invocation begins. This ordinary nesting uses the same stack mechanism as recursion; recursion is not the only reason a program needs frames.

Every recursive invocation receives a separate frame, even when all frames execute the same function definition. Its local n can therefore be 3 while a deeper invocation's n is 2. One invocation does not overwrite the other's local value.

Base and general cases

A recursive solution expresses a problem using a smaller instance of the same problem and a case that needs no further recursive call.

Recursion

A subroutine calling itself, directly or indirectly, to solve a problem. A base case finishes without recursion; a general case reduces the problem and combines the smaller result as needed.

For a non-negative integer nn, the sum of integers from 1 to nn is 0 when n=0n=0. Otherwise it is nn plus the sum from 1 to n−1n-1. Three conditions make this terminate: a reachable base case, progress toward it at each general step, and an input within the function's stated domain.

Observe calls and returns

{"code":"def triangular(n):\n print("enter", n)\n if n == 0:\n print("base", 0)\n return 0\n smaller = triangular(n - 1)\n result = n + smaller\n print("leave", n, result)\n return result\n\nprint("answer", triangular(int(input())))\n","instructions":"Predict the order of output for input 3, then run. Run input 0 as the boundary case. Modify only the combination to result = n * n + smaller so the function returns the sum of squares from 1 to n. Run inputs 3 and 0 again. Inputs are small non-negative integers.","files":[],"original_filename":"main.py","runtime_profile":"python-console-v1","file_mode":"fresh"}

With input 3, entry messages appear for 3, 2, 1, 0; the base returns 0; leave messages show 1 1, 2 3, 3 6; the answer is 6. Calls descend before pending additions finish. The square-sum modification instead returns 14, with leave results 1, 5, 14. Both versions return 0 for input 0.

Continue this lesson.

You’re previewing NovaLearn without an account. Create a free account to read the rest.

Create free account