10.3 Functions & Recursion

Key Takeaways

  • A function packages parameters, a body, and an optional return value; calling it transfers control, then resumes after the call with the returned result
  • The call stack grows with each nested call and shrinks on return — each frame holds its own local variables and return address
  • Recursion means a function calls itself; every correct recursive design needs a base case that stops and a recursive case that moves toward that base
  • Missing or unreachable base cases cause infinite recursion / stack overflow; wrong base cases yield wrong answers even if the stack stays finite
  • Tracing recursion means expanding calls until the base case, then substituting returned values on the way back up the stack
Last updated: July 2026

Functions let you name a reusable chunk of logic. Recursion is the special case where that chunk calls itself. Cyber Test items on this topic are almost always trace-the-calls problems: what does f(3) return, how many times does a body run, or what goes wrong when the base case is missing?

Functions: Inputs, Body, Output

A function (also called a procedure or method in related contexts) has:

PieceRole
NameHow you call it
ParametersPlaceholders for incoming values
BodyStatements that run on each call
Return valueResult handed back to the caller (optional for "void" actions)

Language-agnostic:

FUNCTION add(a, b)
  RETURN a + b
END FUNCTION

SET s TO add(2, 5)   # s becomes 7

Python:

def add(a, b):
    return a + b

s = add(2, 5)

C++:

int add(int a, int b) {
  return a + b;
}
int s = add(2, 5);

Parameter Binding

On a call add(2, 5), parameter a gets 2 and b gets 5 for that invocation. Those names are local: they do not permanently overwrite same-named variables in the caller unless the language uses explicit reference/pointer semantics (advanced for this exam). For aptitude items, assume pass-by-value: the function works on copies; assigning to a parameter inside does not change the caller's variable.

Return Ends the Function

When return executes, the function stops. Statements after an executed return in the same path do not run. Multiple return points (often inside if branches) are normal; the first one hit wins for that call.


Call Stack Intuition

Think of the call stack as a stack of plates. Each function call pushes a frame (locals, parameters, where to resume). Each return pops a frame.

FUNCTION main()
  SET x TO double(3)
  RETURN x
END

FUNCTION double(n)
  RETURN n * 2
END
  1. main starts → frame: main
  2. double(3) called → stack: main | double (n=3)
  3. double returns 6 → pop double; main resumes with x = 6
  4. main returns → stack empty

You do not need OS-level detail. You need: deeper call = more frames, return = resume caller with a value.

Nested Calls

SET r TO add(double(2), 4)

Order: evaluate double(2) first (returns 4), then add(4, 4) (returns 8). Inner calls complete before the outer call that needs their results.


Recursion: Functions That Call Themselves

Recursion solves a problem by reducing it to a smaller instance of the same problem until a trivial case appears.

Every correct recursive function needs:

  1. Base case — condition where you return a known answer without calling yourself again
  2. Recursive case — call yourself on a smaller input, then combine the result

Classic: Factorial

Definition: n! = 1 when n is 0 or 1; otherwise n! = n * (n-1)!.

FUNCTION fac(n)
  IF n <= 1 THEN
    RETURN 1
  ELSE
    RETURN n * fac(n - 1)
  END IF
END FUNCTION

Trace fac(4) (expand downward):

fac(4) → 4 * fac(3)
fac(3) → 3 * fac(2)
fac(2) → 2 * fac(1)
fac(1) → 1          (base case)

Collapse upward:

fac(2) = 2 * 1 = 2
fac(3) = 3 * 2 = 6
fac(4) = 4 * 6 = 24

Call stack at deepest point: frames for fac(4), fac(3), fac(2), fac(1) stacked. After base case, frames pop in reverse order.

Python flavor:

def fac(n):
    if n <= 1:
        return 1
    return n * fac(n - 1)

C++ flavor:

int fac(int n) {
  if (n <= 1) return 1;
  return n * fac(n - 1);
}

Countdown Trace (Side Effects)

Not every recursive example returns a product; some exams ask how many prints occur.

FUNCTION boom(n)
  IF n == 0 THEN
    PRINT "done"
    RETURN
  END IF
  PRINT n
  boom(n - 1)
END FUNCTION

boom(3)

Output order: 3, then 2, then 1, then done. Prints happen before the recursive call, so they appear descending. If PRINT n were placed after boom(n-1), you would see done first, then 1, 2, 3 on the way back up — placement relative to the recursive call is a frequent trap.


Common Recursion Traps

TrapWhat happensFix / recognition
Missing base caseInfinite recursion; stack grows until failureEvery path must eventually hit a non-recursive return
Base case never reachede.g. always call fac(n) instead of fac(n-1)Argument must move toward the base
Wrong base valuee.g. fac(1) returns 0Yields wrong numeric answer with finite stack
Off-by-one in basen == 0 vs n <= 1 for factorialCheck the mathematical definition used in the stem
Confusing with loopsSame problem often solvable iterativelyRecursion questions want stack/base-case reasoning

Broken example (infinite):

FUNCTION bad(n)
  RETURN n * bad(n)   # never decreases; no base case
END

Broken example (finite but wrong):

FUNCTION bad_fac(n)
  IF n == 0 THEN RETURN 0   # should return 1 for 0!
  RETURN n * bad_fac(n - 1)
END

bad_fac(3)3 * 2 * 1 * 0 = 0. The stack behaved, but the base case lied.


Worked Mixed Example: Recursion + Control Flow

FUNCTION sum_odd(n)
  IF n <= 0 THEN
    RETURN 0
  END IF
  IF n % 2 == 0 THEN
    RETURN sum_odd(n - 1)
  ELSE
    RETURN n + sum_odd(n - 1)
  END IF
END FUNCTION

Trace sum_odd(5):

  • 5 odd → 5 + sum_odd(4)
  • 4 even → sum_odd(3)
  • 3 odd → 3 + sum_odd(2)
  • 2 even → sum_odd(1)
  • 1 odd → 1 + sum_odd(0)
  • 0 base → 0

Collapse: 1+0=1, then 3+1=4, then 5+4=9. So sum_odd(5) = 9 (1+3+5).

This blends modulo checks from operators, branching from control flow, and stack unwinding from recursion — representative of harder Cyber Test programming items.


Connecting Back to Earlier Sections

  • Variables/types: parameters are local variables; return types matter when mixing ints and bools in conditions.
  • Operators: recursive cases are often n * f(n-1) or n + f(n-1); modulo selects branches.
  • Control flow: base cases are ifs; without them, recursion never stops — the same discipline as loop termination conditions.

Exam Habits

  1. Find the base case first; evaluate it cold.
  2. Expand calls until the base, then substitute returns upward.
  3. Count stack depth only if asked; otherwise focus on returned values or printed order.
  4. Ask whether the recursive argument shrinks toward the base every time.
  5. Watch print/return placement before vs after the recursive call.

With variables, control flow, and functions/recursion in place, you have the language-agnostic core of the programming domain. Language-specific syntax and debugging patterns build on this foundation in the following chapter.

Test Your Knowledge

What does fac(3) return for: fac(n) returns 1 if n <= 1, else returns n * fac(n - 1)?

A
B
C
D
Test Your Knowledge

Which statement best describes a correct recursive design?

A
B
C
D
Test Your Knowledge

For boom(n): if n == 0 print "done" and return; else print n then call boom(n-1). What is the output of boom(2)?

A
B
C
D
Test Your Knowledge

When main calls helper(5), and helper calls util(5) before returning, which call-stack picture is correct at the deepest moment?

A
B
C
D