Iteration using while in Python (Part 2)
Example: counting specific characters in a string
How might we use iteration to count the number of times a specific character, like “a” or “2”, appears in an input string? Let's write a while loop using our recipe.
While loop recipe
- Express the task as iteration.
- Write a condition.
- Ensure initial values of variables checked in the condition.
- Ensure the condition can change in the body of the loop.
Check characters in the string in order of index. Like in the previous example, our loop allows us to check each possibility. Instead of trying all possible factors, here we check all possible characters in the string.
We only keep going when the index is less than the length of the string, giving the following code:
while pos < len(entry):
We start with the index at the beginning of the string, giving the following code:
pos = 0
Once we have processed a character in the string, we move to the next character in the string by incrementing pos
, giving the following code:
pos = pos + 1
Putting it all together
Putting these four steps all together, we get the following code:
def count_chars(entry, char):
"""Determines number of occurrences of char in entry.
Preconditions:
entry: nonempty string
char: string of length 1
Parameters:
entry: the string with character to count
char: the character to count
Returns: int, nonnegative
"""
In the docstring we are careful to ensure that the string is nonempty and that char is a string of length 1.
pos = 0
while pos < len(entry):
if entry[pos] == char:
count = count + 1
pos = pos + 1
return count
Following the steps of the recipe, we write a condition, ensure there are initial values of variables checked in the condition, and ensure that the condition can change in the body of the loop. To process a character, we add to the count
if it matches the input character. Finally, we return the value of count
.
def test_count_chars():
"""Tests correctness of count_chars
"""
assert count_chars("apple", "p") == 2
assert count_chars("apple", "l") == 1
assert count_chars("apple", "d") == 0
test_count_chars()
We create a function to test our function.
We run the program and observe the outputs:
Traceback (most recent call last): File "<string>", line 30, in <module> File "<string>", line 26, in test_count_chars File "<string>", line 19, in count_chars UnboundLocalError: local variable 'count' referenced before assignment
We discover that it fails. Why? Because count
also needed an initial value before being incremented in the loop.
We fix these errors by initializing count, inserting count = 0
before the while loop.
Now our function works properly.
Example: Pig Latin
As our last example, let's return again to Pig Latin:
def pig_latin(word):
"""Docstring here
"""
pos = first_vowel(word)
if pos == 0:
return(word[1:] + "w" + "ay")
else:
return(word[pos:] + word[0:pos] + "ay")
This time, we don't have to simplify the code. If we are able to find the position of the first vowel in a word, we can move all the leading consonants to the end before adding "ay".
Finding the first vowel
So how do we write the helper function that finds the first vowel?
def first_vowel(word):
"""Determines the first vowel in word or
length of word if none.
Preconditions:
word: string of letters only
Parameter:
word: a word to encode
Returns: int in range 0 to len(word)
"""
We return a position or the length of the string if there are no vowels. We specify that the string consists only of letters and indicate the range of possible values returned.
While loop recipe
- Express the task as iteration.
- Write a condition.
- Ensure initial values of variables checked in the condition.
- Ensure the condition can change in the body of the loop.
Going back to our while recipe, we use iteration to move from one position to the next in the string. Keep moving right while characters are consonants and return the next position.
We continue as long as we haven't reached the end of the word and we're still looking at a consonant, giving the following code:
while pos < len(word) and is_consonant(word[pos]):
Notice that we're using the helper function is-consonant
that we created earlier.
We give pos
the initial value 0, giving the following code:
pos = 0
We increment pos
in the loop, giving the following code:
pos = pos + 1
Writing the body of the function
def is_vowel(char):
"""Docstring here
"""
is_a = char == "a" or char == "A"
is_e = char == "e" or char == "E"
is_i = char == "i" or char == "I"
is_o = char == "o" or char == "O"
is_u = char == "u" or char == "U"
return is_a or is_e or is_i or is_o or is_u
def is_consonant(char):
"""Docstring here
"""
return len(char) == 1 and not is_vowel(char)
We enter our helper functions is-vowel
and is-consonant
and our main function pig_latin
, which will appear after we define first_vowel
.
For the sake of space, we omit all docstrings.
def first_vowel(word):
## Start at first position
pos = 0
## Loop over consonants
while pos < len(word) and \
is_consonant(word[pos]):
pos = pos + 1
## Return next position
return pos
We start with the header of the function first_vowel
and the steps as comments. Now we use the recipe.
Write a condition, ensure initial values, and ensure the condition can be changed.
The value returned will be the value of pos
when the condition is false, either because the character in that position is not a consonant or because the end of the input has been reached.
def pig_latin(word):
"""Docstring here
"""
pos = first_vowel(word)
if pos == 0:
return(word + "way")
else:
return(word[pos:] + word[0:pos] + "ay")
def test_first_vowel():
"""Tests correctness of first_vowel
"""
assert first_vowel("chrome") == 3
assert first_vowel("apple") == 0
assert first_vowel("sdk") == 3
assert first_vowel("") == 0
test_first_vowel()
We test our helper function.
def test_pig_latin():
"""Tests correctness of pig_latin
"""
assert pig_latin("apple") == "appleway"
assert pig_latin("chrome") == "omechray"
test_pig_latin()
Once the helper function works, we test pig-latin
.