Python: variable bindings in recursive functions
I stumbled upon some oddity in Python using a function similar to the following:
def foo(x):
if int(x)!=4:
x = raw_input("Wrong guess, please enter new value: " )
foo(x)
else:
print "Good Job! %s was right"%x
return x
value = foo(x=raw_input("Guess a Number between 1 and 10: "))
print value
If I enter, for example: "1" then "2" then "3" then "4" I get the following printout:
Good Job! 4 was right
2
This is confusing as the function seems to successfully identify the correct answer, but then returns the value that was the second answer instead of the most recent answer.
Can anyone explain what is going on with the "x" binding in this recursive function?
source to share
We'll see!
value = foo(raw_input())
# foo is the same as in the question, I won't repeat it here
print value
Inside your foo, you get this:
# foo(1) calls foo(2) and sets x to 2
# foo(2) calls foo(3) and sets x to 3
# foo(3) calls foo(4) and sets x to 4
# foo(4) does:
print "Good Job! 4 was right"
return 4 # to foo(3)
# foo(3) does:
return 4 # to foo(2)
# foo(2) does:
return 3 # to foo(1)
# foo(1) does:
return 2 # to main
Since the return value is main (from the outermost recursion) 2
, that's what value
remains.
To fix this, you can make it iterative:
def iter_foo(x):
while int(x) != 4:
x = raw_input("Wrong guess. Try again! ")
print "Good Job! %s was right" % x
return x
Or make EACH recursion return the result of a new function
def recurse_foo(x):
if int(x) != 4:
return foo(raw_input("Wrong guess. Try again! "))
else:
print "Good Job! %s was right!" % x
return x
source to share
So mainly because you are missing return
in
def foo(x):
if int(x)!=4:
x = raw_input("Wrong guess, please enter new value: " )
foo(x) # <-- need return
else:
print "Good Job! %s was right"%x
return x
value = foo(x=raw_input("Guess a Number between 1 and 10: "))
print value
What happens is it calls foo(1)
which is not 4, assigns x
equal to 2 and this is the last value that is assigned x
, so
- Recursive
foo(x)
returns a value, but it is not attached to anything and is not returned from the recursive call. - The last value obtained
x
was 2, so the initial onefoo(1)
returns
So either
x = foo(x)
or
return foo(x)
On the highlighted line
source to share
Try using the following code:
def foo(x):
if int(x)!=4:
x = raw_input("Wrong guess, please enter new value: " )
return foo(x)
else:
print "Good Job! %s was right"%x
return x
value = foo(x=raw_input("Guess a Number between 1 and 10: "))
print value
You might think that every call to foo creates a new variable x. Thus, recursive calls form a sequence of variables x1, x2, x3, etc. This way, when the function call returns, your local variable x is not changed. This is why you are still getting 2 instead of the assignment from the last recursive call (4). If you want to change a variable passed to a function, you need to pass it by reference (or object), not by value. [Cm. See this article for more details: http://uvesway.wordpress.com/2013/02/18/passing-arguments-to-a-function-by-value-by-reference-by-object/]
source to share