Today we'll talk about looping (iteration) over sequences such as str
and list
.
Assume we are given different words, such as "Williams", "Boston", "Eephs", "Berkshires", and so on, and want to count the number of vowels in each word, or to count how many times a particular letter occurs in the word.
How do we do that? We can utilize the predicate, isVowel
defined below.
isVowel
¶def isVowel(char):
"""Predicate that returns true only when a letter is a vowel."""
return char.lower() in 'aeiou'
We can access each character (or element) in a string word
by using indices. These are integers from 0
up to one less than the length of word
.
Length of a sequence. Python has an in-built len()
function that computes the length of a sequence such as a string or a list.
word = "Boston"
word[0] # character at 0th index?
len(word) # returns length of a word
word[1] # character at 1st index?
word[3]
Question: Will the following expression work?
word[6]
Question: What about this one, will this work?
word[-1]
Question: Can you access the character "s" by using a negative index? Write it below to test:
word[-4]
Sequence indexing: Refer to the lecture slides to see an explanation of the indexing scheme for sequences such as lists and strings.
How can we count the vowels in a word? We can use isVowel
and the indices to test each character and keep track of vowels through a counter variable.
But, how do we write the conditionals to test for each character?
if
statements¶Can you predict the result?
word = 'Boston'
counter = 0
if isVowel(word[0]):
counter += 1
if isVowel(word[1]):
counter += 1
if isVowel(word[2]):
counter += 1
if isVowel(word[3]):
counter += 1
if isVowel(word[4]):
counter += 1
if isVowel(word[5]):
counter += 1
print(counter)
Can you predict the result?
word = 'Boston'
counter = 0
if isVowel(word[0]):
counter += 1
elif isVowel(word[1]):
counter += 1
elif isVowel(word[2]):
counter += 1
elif isVowel(word[3]):
counter += 1
elif isVowel(word[4]):
counter += 1
elif isVowel(word[5]):
counter += 1
print(counter)
Summary. Be careful to know when to use a bunch of if statements verses if-elif-else blocks.
We always strive to write code that is generic, what will happen when we run it with a new string?
word = 'Anna'
counter = 0
if isVowel(word[0]):
counter += 1
if isVowel(word[1]):
counter += 1
if isVowel(word[2]):
counter += 1
if isVowel(word[3]):
counter += 1
if isVowel(word[4]):
counter += 1
if isVowel(word[5]):
counter += 1
print(counter)
Summary. The above logic of using conditionals to manually check every letter does not generalize to arbitrary strings. We need a way to to ''run through'' all the letters of a string (of some length n
) and be able to do the same check for every character of the string.
for
loops¶We can solve the problem of vowel-counting using a for
loop. A loop is an execution mechanism to repeat a series of steps for a certain number of times.
In the following example, the for block repeats itself length(word) number of times. char
is the loop variable which first takes the value of word[0]
, then it takes the value of word[1]
, and so on, on the final iteration it takes the value of word[len(word)-1]
(the last character of the string) after which the loop is completed and the control flow exits the for block.
def countAllVowels(word):
'''Returns number of vowels in the word'''
count = 0 # initialize the counter
for char in word: # iterate over the word one character at a time
if isVowel(char):
count += 1
return count
countAllVowels('Williams')
countAllVowels('Eephs')
# countAllVowels() # insert a word with a lot of vowels?
Notice how our for loop approach works for words of any length! The for loop automatically finished after we run out of characters of word
, even though we have not computed the length of word manually. This is the beauty of Python---it lets us iterate directly over sequences.
Tracing the loop executation. You can trace the execution of the loop, and how the variables within its body are changing using print statements that display their content during each iteration.
def traceCountAllVowels(word):
'''Traces the execution of countAllVowels function'''
count = 0 # initialize the counter
for char in word: # iterate over the word one character at a time
print('char, count: ('+ char + ' , ' + str(count) +')')
if isVowel(char):
print('Incrementing counter')
count += 1
return count
traceCountAllVowels('Williams')
traceCountAllVowels('Queue')
Summary. As you can see, the loop variable char
takes the value of every character in the string one by one until the last character. Inside the loop, we check if char
is a vowel and if so we increment the counter.
countChar
¶Let us do an exercise. Define a function countChar
that takes two arguments -- a character and a word -- and returns the number of times that character appears in the word.
# your function definition for countChar
def countChar1(char, word):
'''Counts the number of times a character appears in a word'''
count = 0
for letter in word:
if char == letter:
count += 1
return count
countChar1('h', 'character')
countChar1('a', 'Alabama') # there are 4 a's in Alabama but our function says 3, how do we fix this?
# your function definition for countChar
def countChar2(char, word):
'''Counts the number of times a character appears in a word'''
count = 0
for letter in word:
if char.lower() == letter.lower():
count += 1
return count
countChar2('a', 'Alabama')
countChar2('E', 'Eephs')
Summary. When comparing two characters or strings, make sure that they are in the same form (in this case both are lowercase or uppercase letters.
for
loops with lists¶Let us use for
loops with another sequence, lists. We can loop over lists the same way we loop over strings. The loop variable iteratively takes on the values of each item in the list, starting with the first item, then second, and finally the last item of the list.
Similar to strings, we can index lists as well. See examples:
phrase = ["A", "lovely", "Fall", "day"] # define a new list
phrase[0] # first item of a sequence is indexed at 0
phrase[1] # second item in list phrase
phrase[-1] #predict what this does! Review how indexing in
# sequences works in the lecture slides
# for loop over a list
for word in phrase:
print(word)
wordStartEnd
¶You can define a function that iterates over a given list, the same way we did with strings, and keeps track of some property, perhaps, this time for fun, let's count the number of words in the given list that start and end with the same letter.
def wordStartEnd(wordList):
'''Takes a list of words and counts the
number of words in it that start and end
with the same letter'''
count = 0 #initilize counter
for word in wordList:
if len(word): #why do we need this?
if word[0].lower() == word[-1].lower(): # will this work?
# debugging print here perhaps
# print(word)
count += 1
return count
wordStartEnd(['Exude', 'Eerie', 'Soup', 'knack', 'snack'])
wordStartEnd(['Scared', 'Sure', 'Sars', 'Viral', 'viv', 'stills'])
Python provides an easy way to iterate over common numerical sequences through the range
function.
In Python 3, we don't have an easy way to display the numbers stored in a range object. If we want to examine the contents of a range object, we may pass the object into the function list() which returns a list of the numbers in the range object.
range(0,10)
type(range(0, 10))
list(range(0, 10))
Notice. The range(firNum, secNum)
represents all numbers from firNum
through secNum - 1.
If the firNum
is 0, we can omit. For example:
list(range(10))
Looping over ranges. Range functions provides us with an iterable sequence, which we can loop over, just like we did with strings and list.
What are some reasons we might want to loop over a range of numbers?
for i in range(1, 11): # simple for loop that prints numbers 1-11
print(i)
for i in range(5): # for loops to print patterns
print('$' * i)
for j in range(5):
print('*' * j)
while
loops¶for
loops iterate over a pre-determined sequence and stop at the end of the sequence.
while
loops are useful when we don't know in advance when to stop. A while loop will keep iterating until the condition in the parenthesis is satisfied and will halt if the condition fails to hold.
while
loop that depends on user input¶prompt = 'Please enter your name (type quit to exit): '
name = input(prompt)
while (name.lower() != 'quit'):
print('Hi,', name)
name = input(prompt)
print('Goodbye')
while
loop that depends on argument value¶def printHalves(n):
while n > 0:
print(n)
n = n//2
printHalves(100)
printHalves(22)
We sometimes might by accident write an infinite loop, one that never ends (it happens to the best of us!). In these cases, use Ctrl+C (keyboard interrupt) to break out of the loop.
def printHalves2(n):
"""Attempts to print positive successive halves of n.
"""
while n > 0:
print(n)
n = n//2
NOTE: In the Notebook itself, it might not be possible sometimes to break the loop, even with Kernel -> Interrupt. In this case, close the tab and open it again.
printHalves2(22) # not executed here because it leads to an large output
Suppose we want to test a function we have written. There are several ways to do so. You can test using interactively python by importing the function from checking to see if it returns the correct output when called on a bunch of different values.
Testing using doctests. Python's doctest module allows you to embed your test cases and expected output directly into a functions docstring. To use the doctest module we must import it. To make sure the test cases are run when the program is run as a script from the terminal, we need to call doctest.testmod(). To make sure that the tests are not run in an interactive shell or when the functions from the module are imported, we should place the command within a guarded if __name__ == "__main__":
block. See slides for more explanation.
isVowel
using doctests¶The doctest
module searches for pieces of text that look like interactive Python sessions, and then executes those sessions to verify that they work exactly as shown. There are several common ways to use doctest:
def isVowel(char):
"""Predicate that returns true only when a letter is a vowel.
>>> isVowel('d')
False
>>> isVowel('e')
True
"""
return char.lower() in 'aeiou'
import doctest
doctest.testmod(verbose = True)
# Task: try this out as a script and
# run from the terminal us try this out