Adding through recursion

M

martin.clausen

There is problaly a really simple answer to this, but why does this
function print the correct result but return "None":

def add(x, y):
if x == 0:
print y
return y
else:
x -= 1
y += 1
add(x, y)

print add(2, 4)

result:
6
None

Martin
 
S

Simon Brunning

There is problaly a really simple answer to this, but why does this
function print the correct result but return "None":

def add(x, y):
if x == 0:
print y
return y
else:
x -= 1
y += 1
add(x, y)

print add(2, 4)

result:
6
None

Every function returns a value. If you don't use an explicit return
keyword, None is returned implicitly. You print the answer that you
are looking for from within your function, then print the return value
from that function, which, as I've explained, will be None.
 
M

Mark Jackson

There is problaly a really simple answer to this, but why does this
function print the correct result but return "None":

def add(x, y):
if x == 0:
print y
return y
else:
x -= 1
y += 1
add(x, y)

print add(2, 4)

result:
6
None

Perhaps this hint will help:
6
6
 
P

Peter Tillotson

basically the other two points :)

you create a string of add functions

add(2,4)--add(1,5)--add(0,6)

only in the last ( add(0,6) ) explicitly returns y, in the else of
add(1,5) you ignor it. If you want the starting add to return something
sensible you need to find a way to pass it back up the function chain.

not tested
def add(x, y):
if x == 0:
sum = y
print sum
else:
x -= 1
y += 1
sum = add(x, y)
return sum

print add(2, 4)
 
M

martin.clausen

I still don't get it. I tried to test with x = 0 and found that to
work. How come since the value of y is right and it is printed right it
"turns into" None when returned by the return statement ?
 
S

Simon Brunning

I still don't get it. I tried to test with x = 0 and found that to
work. How come since the value of y is right and it is printed right it
"turns into" None when returned by the return statement ?

There is no return statement in your else block. That's where the
Nones are coming from.
 
G

Gerard Flanagan

I still don't get it. I tried to test with x = 0 and found that to
work. How come since the value of y is right and it is printed right it
"turns into" None when returned by the return statement ?


Martin,

-a function should either return something or not. Your function has
two exit points, one explicitly returns a value, one doesn't (and so
defaults to returning None).

- trace through the function with pencil and paper for small values of
x and y

def add(x, y):
if x:
x -= 1
y += 1
add(x,y)
else:
print y


def ADD(x, y):
if x:
x -= 1
y += 1
return ADD(x,y)
else:
return y
11


Gerard
 
F

Fredrik Lundh

I still don't get it. I tried to test with x = 0 and found that to
work. How come since the value of y is right and it is printed right it
"turns into" None when returned by the return statement ?

because you're not returning the value to the print statement;
you're returning it to your own function, which throws it away.

if you add "raise Exception" to the "x == 0" path, you get this
traceback:

$ python add.py
6
Traceback (most recent call last):
File "add.py", line 10, in ?
print add(2, 4)
File "add.py", line 8, in add
add(x, y)
File "add.py", line 8, in add
add(x, y)
File "add.py", line 4, in add
raise Exception
Exception

which shows that you're a couple of levels down when you find
the right value. and since the "add(x, y)" call at line 8 throws
away the result, the "print" statement at line 10 will never see
it.

</F>
 
R

Rocco Moretti

There is problaly a really simple answer to this, but why does this
function print the correct result but return "None":

def add(x, y):
if x == 0:
print y
return y
else:
x -= 1
y += 1
add(x, y)

print add(2, 4)

One of the best things to do when you don't understand how a function is
working is to geneously sprinkle the code with tracing print statements:
params = (x, y)
print "Starting Function", params
if x == 0:
print "x is zero", params
print y
return y
print "After Return", params
else:
print "Non-zero x", params
x -= 1
y += 1
print "Updated x & y", params, '->', (x,y)
add(x, y)
print "Should I be here?", params
print "Falling off end.", params

Starting Function (2, 4)
Non-zero x (2, 4)
Updated x & y (2, 4) -> (1, 5)
Starting Function (1, 5)
Non-zero x (1, 5)
Updated x & y (1, 5) -> (0, 6)
Starting Function (0, 6)
x is zero (0, 6)
6
Should I be here? (1, 5)
Falling off end. (1, 5)
Should I be here? (2, 4)
Falling off end. (2, 4)
None
 
B

Ben Finney

def add(x, y):
if x == 0:
print y
return y
else:
x -= 1
y += 1
add(x, y)

To add to the other good advice in this thread:

This is just one of many reasons why I advocate always having a
*single* return statement, at the *end* of the function. I usually
start out writing my function setting a default return value, and the
return statement immediately below.

In your case, the default return value is None, so let's make that
explicit.

def recursive_add(x, y):
result = None
return result

Then, the rest of the function's responsibility is about changing that
default value if necessary.

def recursive_add(x, y):
result = None
if x == 0:
print y
result = y
else:
x -= 1
y += 1
recursive_add(x, y)
return result

With this structure, it becomes quickly obvious what's gone wrong: one
of the branches is not changing the default return value.

def recursive_add(x, y):
if x == 0:
print y
result = y
else:
x -= 1
y += 1
result = recursive_add(x, y)
return result

I find this much less error-prone than hiding return statements in
branches throughout the function; if the only return statement is at
the very end of the function, it becomes much easier to read.
 
D

Donn Cave

Ben Finney said:
....
def recursive_add(x, y):
if x == 0:
print y
result = y
else:
x -= 1
y += 1
result = recursive_add(x, y)
return result

I find this much less error-prone than hiding return statements in
branches throughout the function; if the only return statement is at
the very end of the function, it becomes much easier to read.

Well, it's sure clearer where it returns. On the other
hand, now you have to analyze the block structure to
know that the 3rd line assignment is still going to be
in effect when you get to the return. That's easy in
this case, of course, but make the structure more complex
and add a loop or too, and it can be hard. Where if you
see a return statement, you know for sure.

State variables are analogous to goto in a way, similar
sort of spaghetti potential. It may or may not help to
have all the strands come out at the same spot, if the
route to that spot could be complicated.

Donn Cave, (e-mail address removed)
 
M

Micah Elliott

On Nov 19, Ben Finney wrote:
...
This is just one of many reasons why I advocate always having a
*single* return statement, at the *end* of the function.

Agreed that it's a good *general* practice, but sometimes early exit
is useful and clear.

This is somewhat of a religious topic. A good discussion is:
http://c2.com/cgi/wiki?SingleFunctionExitPoint

pychecker warns of fall-off-the-end functions.
 
B

bonono

Ben said:
To add to the other good advice in this thread:

This is just one of many reasons why I advocate always having a
*single* return statement, at the *end* of the function. I usually
start out writing my function setting a default return value, and the
return statement immediately below.

In your case, the default return value is None, so let's make that
explicit.

def recursive_add(x, y):
result = None
return result

Then, the rest of the function's responsibility is about changing that
default value if necessary.

def recursive_add(x, y):
result = None
if x == 0:
print y
result = y
else:
x -= 1
y += 1
recursive_add(x, y)
return result

With this structure, it becomes quickly obvious what's gone wrong: one
of the branches is not changing the default return value.

def recursive_add(x, y):
if x == 0:
print y
result = y
else:
x -= 1
y += 1
result = recursive_add(x, y)
return result

I find this much less error-prone than hiding return statements in
branches throughout the function; if the only return statement is at
the very end of the function, it becomes much easier to read.

I don't see this as clearer than multiple return. But then it I believe
it is really preference or style, rather than obvious
advantage/disadvantage.

Interestingly, this kind of error is easy to spot by the compiler in C.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,270
Messages
2,571,353
Members
48,038
Latest member
HunterDela

Latest Threads

Top