Iteration using for in Python (Part 3)

Revisiting determining if a number is prime

We are now ready to look at more complex examples using for loops.

def is_prime(num):
    """Determines if num is prime.

       Preconditions:
       num: int > 1
    
       Parameters:
       num: number being checked
 
       Returns: Boolean (True if prime)
    """

Let's rewrite the function that determines if a number is prime, this time using break.


    for factor in range(2, num-1):

We'll keep the same range for the for loop.


        fails = is_multiple(num, factor)

This time, we'll use a variable fails to store the value of is_multiple.


        if fails:
            break

If fails is ever set to True, then we break out of the loop.

What value should we return?

    return not fails

If fails is True, then the input is not prime. That suggests that we should return not fails.

What should the initial value of fails be?


    fails = False

We want to return True if the result of is_multiple is always False. Since the value we return is not fails, we return True when fails is False. This is how we initialize the value.


Putting all of this code together in the correct order gives the following:


def is_prime(num):
    """Determines if num is prime.

       Preconditions:
       num: int > 1
    
       Parameters:
       num: number being checked
 
       Returns: Boolean (True if prime)
    """
    fails = False
    for factor in range(2, num-1):
        fails = is_multiple(num, factor)
        if fails:
            break
    return not fails

Example: search in a sorted list of numbers

To show another example using early exit from a loop, suppose we are searching for an item in a sorted list of numbers.

def search(sequence, target):
    for item in sequence:
        if item == target:
            return item

As usual, we iterate through all the elements, and if we find the item we want, we return the item.


        if item > target:
            break

Since the items are in sorted order, we know that if we see an element larger than target, then there is no point in looking further: all other elements will be larger than our target, so it is not there.


    return "Not found"

We can then return "Not found".


def test_search():
    """Tests correctness of search
    """
    thelist = [10, 20, 30, 40, 50]
    assert search(thelist, 30) == 30
    assert search(thelist, 45) == "Not found"
    assert search([], 35) == "Not found"

test_search()

We make sure that our tests cover cases in which an item is found, one in which it is not found, and one in which the list is empty.


An alternate version of search

Suppose we wanted instead to return the position of the item being found.

def search(sequence, target):
    for counter in range(len(sequence)):

In this case, we iterate over positions by using range. Remember that the values go from zero to the length minus one, since the stop value in the range is not included.


        if sequence[counter] == target:
            return counter

We then use the counter in the index, returning the counter if the item is found.


        if sequence[counter] > target:
            break

Again, we break early if the current item is larger than the one we want to find.


    return "Not found"

Finally, we return "Not found" if we complete the loop, either by going through all the iterations or by breaking out early.


def test_search():
    """Tests correctness of search
    """
    thelist = [10, 20, 30, 40, 50]
    assert search(thelist, 30) == 2
    assert search(thelist, 45) == "Not found"
    assert search([], 35) == "Not found"

test_search()

We have similar cases covered in our tests.


Example: palindromes

How might we use a for loop to determine if a string is a palindrome?

Definition

A palindrome is a string that is the same as its reversal.

R

0

A

1

D

2

A

3

R

4

A palindrome is a string, like radar, that reads the same whether from front to back or back to front.

An indexed sequence of boxes containing characters of the string "RADAR". R is at index 0 and index 4.

We can check a string by first seeing if the first and last characters are the same.


An indexed sequence of boxes containing characters of the string "RADAR". A is at index 1 and index 3.

Then we see if the second and second-to-last are the same, and so on.


An indexed sequence of boxes containing characters of the string "RADAR". D is at index 2 and is the middle character.

The “so on” is a way of describing iteration.

Writing the function

We'll start by figuring out the starting and ending values for the counter. Since we are reading the string from the front and the back, we can stop at the middle.

def is_pal(entry):
    length = len(entry)
    mid = length // 2
    for counter in range(mid):

So we'll create variable for the middle position, which will be the ending value for our iterations.


        if entry[counter] != entry[- (counter + 1)]:
            return False

We want to keep checking until we find a mismatch, and as soon as we have a mismatch, we can exit the loop with the value False.


    return True

If there are no mismatches, we can return True.


def test_is_pal():
    """Tests correctness of is_pal
    """
    assert is_pal("") == True
    assert is_pal("a") == True
    assert is_pal("otto") == True  
    assert is_pal("bob") == True
    assert is_pal("apple") == False
    assert is_pal("area") == False

test_is_pal()

We now test the function and see that it works.