Skip to content

ይህ ትምህርት ገና ወደ አማርኛ አልተተረጎመም፤ ስለዚህ በእንግሊዝኛ ቀርቧል። የእንግሊዝኛውን ገጽ ክፈቱ

Recursion

4 min read

A function can call any function, including itself. That sounds like a loop, and most beginners picture it as one, which makes recursive code impossible to predict. By the end of this lesson you'll see what really happens: each call runs the same code but with its own separate names, like a fresh copy, and each one hands its answer back to the call that made it.

A function that calls itself

Predict what this prints.

ውጤቱን ገምቱ

Decide before you look. Guessing wrong is how this sticks.

def countdown(n):
    if n == 0:
        print("Go!")
        return
    print(n)
    countdown(n - 1)

countdown(3)
Pick the output

Python prints

3
2
1
Go!

countdown(3) prints 3 and calls countdown(2), which prints 2 and calls countdown(1), and so on. countdown(0) prints Go! and returns without calling again. Each call prints before it calls the next, so the numbers come out in order. A function can call itself: by the time any call runs, def has already made it.

A function calling itself is . It needs two parts. The answers without calling again: here, n == 0. The calls the function on a smaller problem, n - 1, so every call gets closer to the base case. A bare return ends the call without a value (it returns None).

Each call has its own n

The factorial of 3, written 3!, is 3 × 2 × 1 = 6. Notice that 3! is 3 × 2!, 2! is 2 × 1!, and 1! is just 1. That's a recursive definition, and it turns straight into code. Base case first:

def fact(n):
    if n == 1:
        return 1
    return n * fact(n - 1)

print(fact(3))
print(fact(5))

In the editor, press Escape then Tab to move on.

fact(3) can't finish until it knows fact(2), which waits for fact(1). Step through it, slowly; this diagram is the whole lesson. When line 4 runs fact(n - 1), a new frame appears below the one whose line it is, with its own n. When a call reaches a return, the tab under its frame shows the value going back up.

fact(3): four frames, then three returns

Press Next to run one line at a time. Each arrow shows the object a name refers to. Each call gets its own frame of names, below the frame that called it.

def fact(n):
if n == 1:
return 1
return n * fact(n - 1)
r = fact(3)

Frames, oldest first: global.

Step 1 of 11

Step 1 of 11. Line 1: def fact(n):. Nothing in memory changed.

At step 6 there are three different ns alive at once, one in each frame, and none of them ever changes. If you pictured a loop, you pictured one n going 3, 2, 1. The frames pile up on the way down; that pile is the .

On the way back, each frame hands its value to the frame that called it, and that frame finishes its own line with its own n. fact(1) returns 1 to fact(2). fact(2)'s n is still 2, so it works out 2 × 1 and returns 2 to fact(3). fact(3)'s n is still 3, so it works out 3 × 2 and returns 6, which r gets. Python's error messages list the call stack too, oldest call first, which is why they say "most recent call last".

The way back

A loop runs its lines once per round and moves on. A recursive call comes back. Predict:

ውጤቱን ገምቱ

Decide before you look. Guessing wrong is how this sticks.

def show(n):
    if n == 0:
        return
    print("before", n)
    show(n - 1)
    print("after", n)

show(2)
Pick the output

Python prints

before 2
before 1
after 1
after 2

show(2) prints before 2, then waits inside show(1), which prints before 1 and waits inside show(0). show(0) returns at once. Then show(1) carries on and prints after 1, and only then does show(2) print after 2.

Every call finishes its own lines after the call inside it returns, and the innermost call finishes first. If you pictured a loop, you expected after 2 right after before 2.

ጥያቄ

Now the call is show(3). Which after line prints first?

Without a base case, the calls never stop. Python allows only so many frames on the stack, then stops the program:

When to use recursion

Honestly, for counting down or multiplying, a loop is simpler and faster. Recursion is here because it shines on data that has the same shape inside itself: folders in folders, a family tree, a list of lists of lists. You'll meet it again in data structures, algorithms, and job interviews. Here's a total that works however deep the lists go:

def total(items):
    result = 0
    for item in items:
        if type(item) == list:
            result = result + total(item)
        else:
            result = result + item
    return result

print(total([1, [2, 3], [4, [5]]]))

In the editor, press Escape then Tab to move on.

Each plain number is simply added. A list inside the list makes total call itself on that smaller list, and a list with no lists inside is the base case: the loop just adds its numbers. A loop alone couldn't know in advance how many levels to go down.

መልመጃ

Digit sum

Write digit_sum(n) recursively, for a whole number n of 0 or more. The base case: a number under 10 is its own digit sum. Otherwise, the last digit is n % 10 and the rest of the number is n // 10 (lesson 1.2).

Output
12
7

Your code

def digit_sum(n):
    # Base case first, then the recursive case
    pass

print(digit_sum(2019))
print(digit_sum(7))

In the editor, press Escape then Tab to move on.

መፍትሄውን አሳይ

This is one way to solve it, not the only one. If yours prints the same thing, it works.

def digit_sum(n):
    if n < 10:
        return n
    return n % 10 + digit_sum(n // 10)

print(digit_sum(2019))
print(digit_sum(7))

ዋና ዋና ነጥቦች

  • Recursion is a function calling itself on a smaller problem.
  • Write the base case first; without one, you get a RecursionError.
  • Each call is a separate copy with its own names, in its own frame on the call stack.
  • Each call returns its value to the call that made it, and the innermost call finishes first.
  • Loops are usually simpler; recursion is for data nested inside itself.