Umair Rafique
Umair Rafique

Reputation: 108

Recursive function in while loop changing variable [python]

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

Answers (2)

Arthur Tacca
Arthur Tacca

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:

  • Each function gets its own set of variables
  • When you copy and paste a function into itself, the function can only call (a copy of) itself as many times as you pasted, whereas recursion automatically goes aribtrarily deep. (That's the whole point of 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

dangee1705
dangee1705

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

  • add_two is called
  • the user enters a number
  • they are asked if they wish to continue
  • they enter "yes"
  • while imp == "yes" will now always be true
  • it calls add_two
    • they enter a number
    • they enter "no"
    • the loop doesn't run, so returns back to the first call of add_two
  • back into the while loop. it still evaluates to true, so continues calling add_two

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

Related Questions