views:

139

answers:

3

Hello All. I'm currently trying to wrap my head around learning Python and I've come to a bit of a stall at recursive functions. In the text I'm using (Think Python), one of the exercises is to write a function that determines if number a is a power of number b using the following definition:

"A number, a, is a power of b if it is divisible by b and a/b is a power of b. Write a function called is_power that takes parameters a and b and returns True if a is a power of b."

The current state of my function is:

def isPower(a,b):
    return a % b == 0 and (a/b) % b == 0

print isPower(num1,num2)

As it is, this produces the result I expect. However the chapter is focused on writing recursive functions to reduce redundancy and I'm not quite sure how I can turn the final "(a/b) % b == 0" into a recursion. I've attempted:

def isPower(a,b):
    if a % b != 0:
        return False
    elif isPower((a/b),b):
        return True

But that just returns None.

What is the proper way to recurse this function?

+2  A: 

you need an additional case, for when both conditionals return false

def isPower(a,b):
    if a % b != 0:
        return False
    elif isPower((a/b),b):
        return True
    else
        return False
GSto
Such a simple fix. Thanks for the speedy reply!
bobby
Bob
This will always return false.
Niki Yoshiuchi
You need to account for the base case too, actually. I just edited away the typo.
Platinum Azure
this still does not work for me, at some point the function should return True in a non-recursive condition.
tokland
Yeah, after returning home and testing this out a bit it's consistently returning False. Back to the drawing board!
bobby
I'd shorten that to `return bool(a % b) or isPower((a//b), b) or False`. In general the `if isTrue: return True; if isFalse: return False`-type construction is ugly
Daenyth
+1  A: 
def isPower (a,b):
    return a==1 or (a!=0 and b!=0 and b!=1 and isPower((a/b),b))
Javier
I thought Python was supposed to be all about readability and avoiding redundancy? (-1 for that and lack of explanation)
Platinum Azure
Moreover abusing and/or short circuiting to squeeze everything in one line is not considered a good Python style.
Adam Byrtek
@Platinum Redundancy? Where do you get that? Readability - this is the best and most readable answer posted so far. +1.
NealB
@NealB: Are you high? How is this readable!? As for redundancy, you could say "b>1" instead of "b!=0 and b!=1", to name JUST ONE possible change. The rest of my complaints has to do with this being a one-liner that's just hard to read.
Platinum Azure
Also: If the guy is JUST learning about recursion, he's probably new to writing code, and it's a TERRIBLE idea to encourage one-liners. If anything, code should be overly verbose first, THEN you can learn how to shorten it with cool tricks.
Platinum Azure
@Platinum Azure You _can't_ say `b > 1` because that rules out the case that `b < 0`. At best you could do `b not in (0, 1)`.
aaronasterling
This will always return true in the event that a is initially 1, however that's only the case if b is 1 or -1.
Niki Yoshiuchi
Thanks for your help, Javier. Your and Niki's answers combined were exactly what I needed for everything to click in my head. I understand the importance of scaffolding and writing overly verbose code with plenty of tests along the way, but once I'm comfortable with the syntax and logic I would like to be able to produce the shortest code possible that produces the same effect. This answer was great to see what Niki's answer looks like as a one-liner for future reference. Too bad I can't have two accepted answers!
bobby
@Niki Yoshiuchi: 1 is a power of any number ( x^0 = 1 for any x )
Javier
@Javier - Duh! I'm an idiot.
Niki Yoshiuchi
+2  A: 

You are forgetting a base case, when a == 1:

def isPower(a,b):
    if a == 1:
        return True
    if a % b != 0:
        return False
    elif isPower((a/b),b):
        return True
    else
        return False

However this has some other problems - if a is 0 then it will never finish and if b is 0 then you will get a divide-by-zero.

Here is a verbose solution that as far as I can tell will work for all integer combinations:

def isPower(a,b):
    if a == 0 or b == 0:
        return False
    def realIsPower(a, b):
        if a == 1:
            return True
        elif a%b != 0:
            return False
        elif realIsPower((a/b), b):
            return True
        else:
            return False
    return realIsPower(a, b)

EDIT: My code didn't work for cases when both a and b are negative. I'm now comparing their absolute values.

EDIT2: Silly me, x^0 == 1, so a == 1 should ALWAYS return true. That also means I don't have to compare a to b before the recursion. Thanks @Javier.

Niki Yoshiuchi
Thank you. This clarified exactly what I was missing. After tinkering around some more with it earlier I had reached a maximum recursion loop, but couldn't figure out how to stop it. I guess I assumed the interpreter would know I just wanted it to stop after performing itself once more. Now I understand that, when writing a recursive call, one of the tests to even allow that ELIF branch to be reached has to be able to stop it. This was just a practice function so allowing for a base of 1 would've been fine, but thanks for going all out to help!
bobby
Hey, glad I could help! Always remember that there are two things required for all recursive functions - a base case(s) and a recursive call. Sometimes (such as in this case) there is more than one base case.
Niki Yoshiuchi