Reputation: 108
I had a student give me the following code:
def addtwo():
numb=input("enter any number")
add=int(numb)+2
print(add)
inp = input("do you wish to enter again")
while inp=="yes":
addtwo()
After the first iteration, if the user inputs anything other than 'yes', the body of the while loop doesn't execute, as expected. But if we enter 'yes', it executes (again, as expected) and prompts "enter any number", but in the second iteration, even if the user enters anything other than 'yes' on "do you wish to enter again", the body of the while loop STILL executes. Now I ran the debugger on it, and found out that the value of inp
just changes to 'yes'
when executing line 5. Before execution of the line, the value of inp
, according to the debugger is still whatever the user entered. Why is that so?
Now I looked it up here, but couldn't find any explanation, though I found a way to work around it by adding a return before the call to addtwo()
in the body of the while loop (I found it here: Calling a function recursively for user input) but couldn't understand why the value of inp
just change in the local stack, and how the return
statement fixes it?
Here's the working code:
def addtwo():
numb=input("enter any number")
add=int(numb)+2
print(add)
inp = input("do you wish to enter again")
while inp=="yes":
return addtwo()
And, to add to my puzzle, the code just works fine if we use an if
statement instead of while
.
Upvotes: 3
Views: 2966
Reputation: 9998
Calling a recursive function works a bit like as if you had copied and pasted the inner function there. There are some differences between copy/paste and true recursion:
Despite these differences, if you're struggling to understand a recursive call, copy and paste is a good way to think about it. Let's try it for your function. The following code is equivalent to what you wrote, so long as you don't type "yes" too many times:
def addtwo0():
numb=input("enter any number")
add=int(numb)+2
print(add)
inp = input("do you wish to enter again")
while inp=="yes":
return addtwo1()
def addtwo1():
numb=input("enter any number")
add=int(numb)+2
print(add)
inp = input("do you wish to enter again")
while inp=="yes":
return addtwo2()
def addtwo2():
numb=input("enter any number")
add=int(numb)+2
print(add)
inp = input("do you wish to enter again")
while inp=="yes":
raise Exception("tried to recurse too deeply!")
Maybe it will be even clearer if we put these inline in one function. We'll have to rename the variables so they don't overwrite each other.
def addtwo():
numb0=input("enter any number")
add0=int(numb0)+2
print(add0)
inp0 = input("do you wish to enter again")
while inp0=="yes":
numb1=input("enter any number")
add1=int(numb1)+2
print(add1)
inp1 = input("do you wish to enter again")
while inp1=="yes":
numb2=input("enter any number")
add2=int(numb2)+2
print(add2)
inp2 = input("do you wish to enter again")
while inp2=="yes":
raise Exception("tried to recurse too deeply!")
You can now see the immediate cause of the problem: setting inp2
to "yes" doesn't set inp1
or imp0
to yes. Or in the original code, setting inp
to "yes" in a nested call to addtwo
doesn't set the outer imp
to yes, because each function has its own set of variables.
You can also now see the root cause of the problem: you don't need multiple loops to do this. Either recursion (with an if
) OR a while
loop suffices to enable repeated checking of a condition. By having both, even if you fix the immediate problem, you are making things unnecessarily complicated. In this case, a while
loop would actually be simplest; put the input
statement in that and get rid of the recursive call altogether.
Upvotes: 2
Reputation: 3520
Summary
This is because once you have entered yes once, that function will loop forever, regardless of what the next recursive call does.
You can fix your code by changing:
while inp == "yes":
add_two()
to
if inp == "yes":
add_two()
Walk through
The return statement
By default in python, functions which do not return anything, actually return None. This means you call add_two, and if the user does not enter "yes" when prompted, it will return, which will then force the first call to also return.
Upvotes: 4