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.