Lists and Loops
Contents
Lists and Loops¶
A New Sequence: Lists¶
Recall that sequences are an abstract type in Python that represent ordered collections of elements.
In the last lecture we focused on strings. Today we will discuss lists.
Unlike strings, which are a homogenous sequence of characters, lists can be a collection of heterogenous objects.
# Examples of various lists:
wordList = ['What', 'a', 'beautiful', 'day']
numList = [1, 5, 8, 9, 15, 27]
charList = ['a', 'e', 'i', 'o', 'u']
mixedList = [3.145, 'hello', 13, True] # lists can be heterogeous
type(numList)
list
Sequence Operators and Functions¶
We saw several operators and functions that apply to sequences when we discussed strings. They apply to lists as well.
Indexing elements of lists using
[]
Using
len
function to find lengthSlicing lists using
[:]
Testing membership using the
in
andnot in
operatorsConcatenation using
+
Recall that the in
operator tests membership and returns True
if and only if an element is in a sequence. On the other hand, the not in
operator returns True
if and only if a given element is not in the sequence.
Note that it is preferable and more readable to say if el not in seq
compared to the (logically equivalent) if not el in seq
.
wordList = ['What', 'a', 'beautiful', 'day']
wordList[3]
'day'
wordList[-1]
'day'
len(wordList)
4
nameList = ["Anna", "Beth", "Chris", "Daxi", "Emory", "Fatima"]
nameList[2:4]
['Chris', 'Daxi']
wordList[::-1] # what will this do?
['day', 'beautiful', 'a', 'What']
nameList = ["Aamir", "Beth", "Chris", "Daxi", "Emory"]
"Anna" in nameList # test membership
False
"Jeannie" in nameList
False
"Jeannie" not in nameList # not in returns true if el not in seq
True
"a" not in "Chris" # also works on strings
True
aList = ['the', 'quick', 'brown', 'fox']
bList = ['jumped', 'over', 'the', 'dogs']
aList + bList # concatenate lists
['the', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'dogs']
aList # aList is unchanged
['the', 'quick', 'brown', 'fox']
bList = bList + ['back'] # add 'back' to bList
bList # since we reassign result to bList, bList has changed
['jumped', 'over', 'the', 'dogs', 'back']
Looping over Lists¶
We can use a for
loop to iterate over the elements of a list just as we did with strings.
numList = [0, 2, 4, 6, 8, 10]
for num in numList:
print(num)
0
2
4
6
8
10
List Accumulations¶
It is often the case that we use loops to iterate over a sequence to accumulate certain items from it. For example, we may want to collect all the words from a wordList that begin with a vowel (or have some other interesting property). How would we approach this problem?
First we need to be able to iterate over all words in the original list.
Then, for each word we must check if it has the property we are looking for.
If it does, we may want to count it, or remember it by storing it somewhere.
If we want to store it, we can use a list as the accumulation variable. If we were interested in returning a string (e.g., in
vowelSeq
), we would collect the characters in a string accumulation variable.
Such processes where we are accumulating something while iterating over sequences call for the use of an appropriate accumulation variable. It is important to initialize these accumulation variables before the loop.
Exercise: countItem
function¶
Write a function countItem
that takes as input a sequence seq
(can be a string or a list), and an element el
and returns the number of times el
appears in the sequence seq
. Here your accumulation variable should be an integer.
def countItem(seq, el):
"""Takes list of sequence seq, and returns
the number of times el appears"""
pass
def countItem(seq, el):
"""Takes list of sequence seq, and returns
the number of times el appears"""
# initialize accumulation variable
count = 0
for item in seq:
# check to see if the next item in seq is el
if item == el:
count += 1 # increment count
return count # return count after for loop ends
countItem([3, 2, 2, 3, 2, 1, 4], 2)
3
countItem("onomatopoeia", "o")
4
countItem("Alabama", "a")
3
countItem(["apple", "banana", "orange"], "kiwi")
0
Exercise: wordStartEnd
¶
Write a function that iterates over a given list of words wordList
and returns a (new) list containing all the words in wordList
that start and end with the same letter (ignoring case). Your accumulation variable should be a list.
Expected behavior:
>>> wordStartEnd(['Anna', 'banana', 'salad', 'Rigor', 'tacit', 'hope'])
['Anna', 'Rigor', 'tacit']
>>> wordStartEnd(['New York', 'Tokyo', 'Paris'])
[]
>>> wordStartEnd(['*Hello*', '', 'nope'])
['*Hello*']
def wordStartEnd(wordList):
'''Takes a list of words and returns the list of words in it
that start and end with the same letter'''
pass
def wordStartEnd(wordList):
'''Takes a list of words and returns the list of words in it
that start and end with the same letter'''
# initialize accumulation variable (of type list)
result = []
for word in wordList: # iterate over list
#check for empty strings before indexing
if len(word) != 0:
if word[0].lower() == word[-1].lower():
result += [word] # concatenate to resulting list
return result # notice the indentation of return
wordStartEnd(['Anna', 'banana', 'salad', 'Rigor', 'tacit', 'hope'])
['Anna', 'Rigor', 'tacit']
wordStartEnd(['Scared', 'Sure', 'Sars', 'Viral', 'viv', 'stills'])
['Sars', 'viv', 'stills']
wordStartEnd(['New York', 'Tokyo', 'Paris'])
[]
wordStartEnd(['*Hello*', '', 'nope'])
['*Hello*']
wordStartEnd(['Exude', 'Eerie', 'Soup', 'knack', 'snack'])
['Exude', 'Eerie', 'knack']
Exercise: palindromes
¶
Write a function that iterates over a given list of strings sList
, returns a (new) list containing all the strings in sList
that are palindromes (read the same backward and forward).
Expected behavior:
>>> palindromes(['Anna', 'banana', 'kayak', 'rigor', 'tacit', 'hope'])
['Anna', 'kayak']
>>> palindromes(['1313', '1110111', '0101'])
['1110111']
>>> palindromes(['Level', 'Stick', 'Gag'])
['Level', 'Gag']
def palindromes(sList):
'''Takes a list of words and counts the
number of words in it that start and end
with the same letter'''
# initialize an accumlation
results = []
# iterate over each item in seq
for item in sList:
# check if it's a palindrome
# lower-case!
lowerWd = str(item).lower()
# check that it has at least two characters!
if len(lowerWd) > 1:
# check forward is same as the reverse [::-1]
if lowerWd == lowerWd[::-1]:
# add to accumulation variable
results = results + [item]
# return what we accumulated
return results
palindromes(["Anna", "1110111", "computer","kayak"])
palindromes([1110111, 78899])
palindromes(["", 'a'])
[]
def palindromes(sList):
'''Takes a list of words and counts the
number of words in it that start and end
with the same letter'''
# initialize accumulation variable (of type list)
result = []
for word in sList: # iterate over list
wLower = word.lower()
if wLower[::-1] == wLower:
result += [word] # concatenate to resulting list
return result
palindromes(['Anna', 'banana', 'kayak', 'rigor', 'tacit', 'hope'])
['Anna', 'kayak']
palindromes(['1313', '1110111', '0101'])
['1110111']
palindromes(['Level', 'Stick', 'Gag'])
['Level', 'Gag']
Nested Loops¶
We can put for loops
inside other for loops: this is called nested loops
.
Let us see some examples.
# What does this do?
def mysteryPrint(word1, word2):
"""Prints something"""
for char1 in word1:
for char2 in word2:
print(char1, char2)
mysteryPrint('123', 'abc')
1 a
1 b
1 c
2 a
2 b
2 c
3 a
3 b
3 c
# What does this print?
for letter in ['b','d','r','s']:
for suffix in ['ad', 'ib', 'ump']:
print(letter + suffix)
bad
bib
bump
dad
dib
dump
rad
rib
rump
sad
sib
sump
An Aside: Testing Functions¶
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 insert a 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.
You’ll get to practice using doctests in Lab 3.
Doctests module¶
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.
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
Trying:
isVowel('d')
Expecting:
False
ok
Trying:
isVowel('e')
Expecting:
True
ok
5 items had no tests:
__main__
__main__.countItem
__main__.mysteryPrint
__main__.palindromes
__main__.wordStartEnd
1 items passed all tests:
2 tests in __main__.isVowel
2 tests in 6 items.
2 passed and 0 failed.
Test passed.
TestResults(failed=0, attempted=2)