6. is_prime - Where am I getting this wrong?


#1

So I've got this code down:

def is_prime(x):
    if x < 2:
        return False
    elif x == 2:
        return True
    else:
        for n in range(1, x-1):
            if x % n == 0:
                return False
            else:
                return True
print is_prime

And, well, it works great to tell apart most of the prime numbers, I've tested it on the Python shell program I have, but........ 3 isn't popping up as a prime number, and the compiler's giving this error:

Oops, try again.
Your function fails on is_prime(3). It returns False when it should return True.

And I've been mulling on it for hours, but I still can't make heads or tails of what's throwing my program off. Probably some stupid mistake I am overseeing. Anyone care to help?


#2

Your loop isn't a loop because it doesn't repeat. Why is that? Why does it stop?
Add prints to find out what a program is doing, there's no use in guessing. Find out.


#3

Well, thanks for the tip, figures I was just off indentation with my else statements, and the range was off too.

This is my final code, which worked great:

def is_prime(x):
    if x < 2:
        return False
    for n in range(2, x-1):
        if x % n == 0:
            return False
    else:
        return True
print is_prime

Thanks for the tip!


#4

What's the purpose of your else? You have to be able say why each part of your code should be there.


#5

So that, if the number doesn't meet either conditions before, it is then a prime?


#6

Now you're just guessing. You can't write code that way. Intent comes first, then you can implement your intentions. You de-indented it to obtain something that you don't know what it does!

If you remove the else and de-indent the last return statement, then that's completely equivalent to what you have now. And then you can ask yourself again what that else possibly could have been doing. It certainly does not interact with the if-statement! So what did you mean for it to do? What does for-else do?

Code is instructions to the machine. You are describing what to do. If you use "words" that you don't know what they mean, then you also don't know what you're saying. You need to know what you are saying. Either use different "words" or find out what that new word means.


#7

Well, to be honest, I had realized originally that the else with the return True statement was in the wrong indentation, it was literally telling the loop to evaluate any number as a prime if they did have a remainder in the first iteration, so I did push it out because of that realization, but the rest, I do I admit I tried to do some guesswork, because I was not getting what was going on wrong, and searching it up online was not helping.


#8

It's not that I didn't have a plan at first. I did, I spent my time planning what I wanted to do. But it's just that once I started doing it, and it wasn't working right, then I couldn't see what was I doing wrong.

Anyways, I've modified the code again:

def is_prime(x):
    if x < 2:
        return False
    for n in range(2, x-1):
        if x % n == 0:
            return False
    return True
print is_prime

Is that alright with you, or are there any other problems with it?


#9

Point is that you need to be able to reason about and motivate what you write. By creating functions and other abstractions that make promises we can avoid having to think about what the code does all the way down to the silicon, but we need to be in full control at the abstraction level that we're currently dealing with. You need to know what every single character you print out is doing in your code. You're still going to make mistakes, but if you have a purpose for each and every thing in the code then you'll be spending far less time debugging it.

Your last line?


#10

I had left it in from when I was still getting "None" for 2, and I wanted to see what was causing that strange result, but I usually keep it just to see some result in the screen.


#11

What does it do though? (You're telling it to do something rather strange with that line)
Or do you just mean that you want it to print anything as a marker of that your code has stopped? I guess I won't argue with that. But if it's anything else


#12

It just prints whatever value the number that was evaluated came out to be, there's no need for it, it's more like, just for me to know that the function went through and actually did what I wanted it to do.


#13

But it doesn't do that, does it. For starters, which number? I imagine that prints something like <function is_prime at 0x7ff40dbd45f0>, because what value are you printing?


#14

Whatever number the codeacademy compiler, or I personally put into the function, I just wanted to know what did it come out to be, I guess it was more like, a personal curiosity to see whether the compiler at here was actually testing it to see if the function works for evaluating what was not a prime, or what was a prime, than anything, and, on my personal compiler, to know if the function was working or not properly.


#15

While just about all languages are both compiled and interpreted to some degree, python is generally thought of as interpreted - it uses a virtual machine that reads your instructions and carries them out (as opposed to the cpu reading your compiled code, carrying that out)

You're printing the function itself. is_prime refers to a function. What would make more sense is to call the function with an argument and print out the result of that.

What runs your code on codecademy is about the same as what's on your computer. The code is sent off to a server where it's executed, probably on linux, with python.org's interpreter. That interpreter is probably of an older version than what you use, but it's mostly the same. Codecademy has mucked about a bit with the environment as well in order to inspect the behaviour of your code.

After your code has executed, codecademy runs a short script that evaluates your code, in this case it'll run a loop 0 through 100 or so and check the results - that's something you could do as well for testing if you wanted. Essentially you would google for the first couple of primes, put them in a python list and see if your function identifies them correctly.


#16

Oh... Oh, well... I guess then it's basically pointless, that print there? Aw... Oh, well.


#17

Well, was it doing what you intended? That's for you to judge, right? And definitely something you should react to if it wasn't.

Testing could be something like this:

primes_below_100 = {
    2, 3, 5, 7, 11, 13, 17,
    19, 23, 29, 31, 37, 41,
    43, 47, 53, 59, 61, 67,
    71, 73, 79, 83, 89, 97}
for n in range(100):
    correct = n in primes_below_100
    result = is_prime(n)
    if correct != result:
        print ('is_prime({}) returned {} instead of {}'
               .format(n, result, correct))

I put the known primes in a data type called set, it's similar to dict, but only has keys, as opposed to key-value pairs. It's better than list for testing if a value is there as it has constant-time lookup, while a list would have to check each element

In python3, print is a function (still exists in python2 but is hidden by the statement), note the space after print before parenthesis, that's because it's not a function. In python3 I would have removed that space. Purely cosmetic, but they mean different things so the formatting would be different.


#18

Well, while I did test for prime, literally one by one, the first fifty numbers, and some more random numbers above 100, 1000 and beyond to see if the program was working fine, I actually added an extra function that would list all the factors of the number, just to have that extra confirmation.

This is what the code looked like (my compiler runs on Python 3.5, so some few things look different):

while True:
    x = input("What number do you want me to check? ")

    if x == "Quit":
        break
    else:
        x = int(x)
        def is_prime(x):
            if x < 2:
                return False
            for n in range(2, x-1):
                if x % n == 0:
                    return False
            return True
        def factors(x):
            for n in range(2, x+1):
                if x % n == 0:
                    print(n)
                else:
                    pass
            if is_prime(x) == True:
                print("Hooray! This IS a prime! It can only divide by itself!")
            else:
                print("This is NOT a prime. Those are its factors.")

        print(is_prime(x))
        print(factors(x))

And it's nice because it shows all the factors up to number itself, so even for primes, it shows the number itself as its own factor.


#19

interpreter!

Yeah, and how much of a horrible experience was it to enter those 50 numbers manually and checking the results? Automating it soon turns out to be less of an effort. It's as much code as the function itself, I know. But that's not uncommon. If you're going to test, put it in code, automate!

Another way of testing (other than writing the test in code) is to have files with input/output/correct and feed the input into the program, write the output to file, then compare that output to the correct result:

$ ./myprogram < input_data > output_data # feed input to program, write output to file
$ diff -s correct_answer output_data     # compare the files

That might look a bit alien, that's alright.


#20

Oh and...

That's just.. no.