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.
A palindrome is a string, like radar, that reads the same whether from front to back or back to front.
We can check a string by first seeing if the first and last characters are the same.
Then we see if the second and second-to-last are the same, and so on.
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.