简体   繁体   中英

Trying to understand recursion in Python

I've been trying to understand the following code, it's a recursion example from my Python book:

def mysum(L):
    if not L:
        return 0
    else:
        return L[0] + mysum(L[1:])


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

output: 15

I have a very hard time to understand how this works, and how it is returning 15 .

I've tried to rewrite the code as:

def mysum(L):
    if not L:
        return 0
    else:
        temp = L[0] + mysum(L[1:])
        print(temp)
        return temp


mysum([1, 2, 3, 4, 5])

this outputs:

5
9
12
14
15

But i'm still not sure how this works, it's like it starts to sum backwards. 5 + 4 + 3 + 2 + 1

return L[0] + mysum(L[1:])
I know that functions on the right get executed before the function returns anything. In this case it's recursive, it calls itself until L has no elements in it. But if it calls itself again, wouldn't that mean that it again doesn't return anything? This is very confusing to me.

L[0] is the head of the list and L[1:] is the rest. In each call the function adds the first element and the sum of the remaining list.

So what is happening is:

mysum([1, 2, 3, 4, 5]) => 1 + mysum([2, 3, 4, 5])
mysum([1, 2, 3, 4, 5]) => 2 + mysum([3, 4, 5])
mysum([1, 2, 3, 4, 5]) => 3 + mysum([4, 5])
mysum([1, 2, 3, 4, 5]) => 4 + mysum([5])
mysum([1, 2, 3, 4, 5]) => 5 + mysum([])
mysum([]) => 0

After the last call everything everything returns.

Maybe it would be helpful for you to print not only your temp but also L.

it's like it starts to sum backwards.

Well that's because it kind of does, this is an example of tail recursion which is not optimized in python, imagine replacing the mysum(L[1:]) with the result in brackets, you would get something like this:

#L[0] + mysum(L[1:])
mysum([1,2,3,4,5])
1 + mysum([2,3,4,5])
1 + (2 + mysum([3,4,5]))
1 + (2 + (3 + mysum([4,5])))
1 + (2 + (3 + (4 + mysum([5]))))
1 + (2 + (3 + (4 + (5 + mysum([])))))
1 + (2 + (3 + (4 + (5 + 0))))

The inner most level of recursion must finish evaluating before the above levels can, so it only actually starts adding the numbers together once the list has been exhausted and then starts will last recursive call. (the end of the list)

But if it calls itself again, wouldn't that mean that it again doesn't return anything?

Well yes, but only until it can return something without requiring another recursive call, then it can return something, and then the level above can return, then the level above...

Let's follow your first part of code, we call it with the list [1,2,3,4,5]

The first call has L = [1,2,3,4,5] so gets into the second portion and does:

return 1 + mysum([2,3,4,5])

mysum is called again, now with a smaller list, which does:

return 2 + mysum([3,4,5])

Next up:

return 3 + mysum([4,5])

Then:

return 4 + mysum([5])

One more time with the normal flow:

return 5 + mysum([])

This time our passed list is empty and our function returns 0. This cascades back on the stack, which means mysum([5]) now evaluates to 5 (5+0=5), this leads to an evaluation of mysum([4,5]) etcetera all the way up to our first call which returns 15.

Consider this code:

def mysum0(L):
    return 0

def mysum1(L):
    return L[0] + mysum0(L[1:])

def mysum2(L):
    return L[0] + mysum1(L[1:])

def mysum3(L):
    return L[0] + mysum2(L[1:])

def mysum4(L):
    return L[0] + mysum3(L[1:])

print(mysum4([1, 2, 3, 4]))

Each function mysum[n] sums a list of length n and delegates to mysum[n-1] to help it out. Can you understand how that works? The recursive mysum function is like all the mysum[n] functions combined into one. It only needs to know how to handle a length of list 0 and how to take care of one layer.

You can test the hypothetical question you raised: What happens when the recursion gets to the point that the list has only one element in it?

>>>print(mysum([5]))

returns:

5

Because it executes:

return L[0] + mysum(L[1:])

L[0] returns 5, and mysum(L[1:]) (because L[1:] doesn't exist with a list of length 1) returns 0.

So, now that the function has done that, it can compute the next mysum in the recursion, for L = [4, 5], which turns into:

return L[0] + mysum[L[1:]

which is equivalent to:

return 4 + mysum[5]

which, since we calculated mysum[5] = 5, is equivalent to:

return 4 + 5

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM