Lab 9: Part 4. Map and Filter
A higher order function (HOF) is a function that either:
- A) take a function as a parameter, or
- B) returns a function
In Python, the built-in functions map
and filter
are higher order functions because they each take a function as a parameter.
Function signatures for map
and filter
taken from docs.python.org:
map(function, iterable, ...)
filter(function, iterable)
Task 1. map
This semester we've seen how to perform map operations (doing the same thing to each item in the list) by using loops or list comprehensions.
Today, we'll explore this same kind of technique but we'll use Python's map
function.
Complete the following tasks in lab08/mapFilter.py
, where you'll find some predefined lists you can use for testing purposes.
Task 1a. yell
Define a function yell
that accepts a string and returns the string with the last letter repeated 5 times.
yell('cat') # => 'catttttt'
When yell
is used with map
, it should produce results like this:
map(yell, animals) # => ['catttttt', 'dogggggg', 'zebraaaaaa', 'pigggggg', 'bunnyyyyyy']
map(yell, superheroes) # => ['elektraaaaaa', 'firestarrrrrr', 'elastigirllllll', 'wonder womannnnnn', 'catwomannnnnn', 'batgirllllll']
Task 1b. flip
Define a function flip
that accepts a string and returns that string reversed.
flip('cat') # => 'tac'
Hint: Recall the slicing syntax used to reverse a String: 'hello'[::-1] => 'olleh'
When flip
is used with map
, it should produce results like this:
map(flip, animals) # => ['tac', 'god', 'arbez', 'gip', 'ynnub']
map(flip, superheroes) # => ['artkele', 'ratserif', 'lrigitsale', 'namow rednow', 'namowtac', 'lrigtab']
Task 1c. double
Most of our examples today will focus on Strings, but map
also works with numbers.
Define a function named double
that multiplies a given number x 2.
double(2) # => 4
When double
is used with map
, it should produce results like this:
map(double, [1, 2, 3, 4, 5]) # => [2, 4, 6, 8, 10]
Task 2: Anonymous functions
The following is a typical Python function, defined using a style we've seen all semester:
def pluralize(x):
return str(x) + 's'
In this example, def
is used to define a function named pluralize
.
Python provides a more succinct way of defining functions using something called lambda notation. The above function written in lambda notation would look like this:
lambda x: str(x) + 's'
Observations:
- The function is not named— this is why functions defined using lambda notation are referred to as anonymous functions.
- The entire function is written on one line.
- The body of the function,
str(x) + 's'
, is implicitly returned
(Refer to Sorting and Lambda lecture notes, Slide #10 for more lambda notation information)
Anonymous functions are useful when working with higher order functions, where you often need a small helper function that you don't intend on using elsewhere in your code.
For example, instead of writing a definition of the function flip
as you did above...
def flip(s):
return s[::-1] # reverses a string
...you could use an anonymous function like this:
map(lambda s: s[::-1], superheroes)
Task 2a. Anonymous “yell”
Using lambda notation, complete the following code to produce the expected output:
map(???, animals) # => ['catttttt', 'dogggggg', 'zebraaaaaa', 'pigggggg', 'bunnyyyyyy']
Task 2b. Anonymous “flip”
Using lambda notation, complete the following code to produce the expected output:
map(???, animals) # => ['tac', 'god', 'arbez', 'gip', 'ynnub']
Task 3: Filter
Python's built-in filter
function can be used to filter a sequence based on some provided criteria.
For example, we could filter the animals
list to only include animals that contain the letter a
.
Task 3a. containsA
To do this, start by defining a helper function called containsA
that returns a Boolean value as to whether a given String contains the letter a
.
containsA('cat') # => True
containsA('dog') # => False
When containsA
is used with filter
, it should produce results like this:
filter(containsA, animals) # => ['cat', 'zebra']
filter(containsA, fruit) # => ['apple', 'banana', 'strawberry', 'pomegranate']
Task 3b. mediumLength
Next, define a helper function called mediumLength
that returns a Boolean value as to whether a given String is at least 4 characters long, but not longer than 7 characters long. In other words, the length of the string must be between 4 and 6, inclusive.
mediumLength('cat') # => False
mediumLength('zebra') # => True
mediumLength('pillow') # => True
mediumLength('encyclopedia') # => False
When mediumLength
is used with filter
, it should produce results like this:
filter(mediumLength, animals) # => ['zebra', 'bunny']
filter(mediumLength, fruit) # => ['apple', 'banana', 'cherry']
Task 4: Filter with anonymous functions
Using lambda notation, complete the tasks to produce the expected output.
Task 4a. Anonymous “containsA”
# Find words that contain the letter a
filter(???, fruit) # => ['apple', 'banana', 'strawberry', 'pomegranate']
Task 4b. Anonymous “mediumLength”
# Find words that are between 4 and 6 characters long
filter(???, animals) # => ['zebra', 'bunny']
Task 4c. Anonymous “mediumLengthWithA”
# Find words that contain 'a' and are between 4 and 6 characters long
filter(???, animals) # => ['zebra']
Task 5. Consecutive letters in a string
Task 5a. consecLetters
To start, we want to define a function called consecLetters
which accepts a String and returns True
if it contains two consecutive letters that are the same; False
otherwise.
consecLetters('bunny')
# => True, b/c there are two consecutive letters: 'nn'
consecLetters('cat')
# => False, no consecutive letters
consecLetters('Jessica')
# => True, b/c there are two consecutive letters: 'ss'
We'll break this function down into three steps, so copy the following scaffold into your lab08/mapFilter.py
file:
def consecLetters(theString):
#### Step 1 ####
# Make pairs of all the letters
# [...Your code here...]
#### Step 2 ####
# Using `map`, create a list of True/False values for each pair, regarding whether it's a match (True) or not (False)
# [...Your code here...]
#### Step 3 ####
# Using `any`, return True if at least one of the pairs was a match (i.e. True)
# [...Your code here...]
Read through the comments to see the “big picture” of how consecLetters
will work.
The following hints walk you through completing each step.
Step 1 Hint
Use zip
to create pairs of neighboring letters in a word (which will be useful when searching for consecutive leters).
Example usage of zip:
letterPairs = zip('bunny', 'bunny'[1:]) # => [('b', 'u'), ('u', 'n'), ('n', 'n'), ('n', 'y')]
Step 2 Hint
If we think about what consecLetters
is trying to accomplish, only one of the tuples in the list created by zip
needs to contain a match in order for consecLetters
to return True.
For example, bunny
would qualify because at least one of the pairs has consecutive letters (('n', 'n')
):
# [('b', 'u'), ('u', 'n'), ('n', 'n'), ('n', 'y')]
To programmatically identify a matching pair, we could use for loop to look at each tuple, but since our focus is on higher-order-functions today, we'll use map
to create a list of True/False values as to whether each pair is a match or not.
The following map
function can be completed to accomplish this step. (It's up to you if you want to define a helper function or use lambda
notation to create an anonymous function.)
map(???, letterPairs) # => [False, False, True, False]
Step 3 Hint
At this point, the results of the previous step should give you a list of Boolean values, indicating which letter pairs are matching (True) or not (False).
We only need one of those pairs to be matching in order to meet the requirements for “consecutive letters”, so we can call upon Python's built-in function any
which returns True
if at least one item in the list is True
; Otherwise it returns False
.
Example uses of any
:
any([True, False, False]) # => True
any([False, False, False, False, False]) # => False
any([False, False, False, True, False, True]) # => True
Test consecLetters
Once you've completed the three steps in consecLetters
, you can test your work:
consecLetters('bunny') # => True
consecLetters('cat') # => False
consecLetters('book') # => True
Task 5b.
With consecLetters
working, we can now use it to filter a list of words down to only words that contain consecutive letters.
list(filter(consecLetters, animals)) # => ['bunny']
list(filter(consecLetters, fruit)) # => ['apple', 'cherry', 'strawberry']
You can import a list of names of all CS111 students like this:
from names import names
And then you can test as follows:
list(filter(consecLetters, names))
should return this list:
['Leen', 'Chantelle', 'Kerry', 'Jesslyn', 'Isabella', 'Hannah', 'Isabelle',
'Jisoo', 'Vanessa','Terri', 'Ann', 'Tiffany', 'Stella', 'Tyanna', 'Annie',
'Michelle', 'Michaella', 'Colleen', 'Pallavi', 'Jessica', 'Rebecca',
'Katianna', 'Jessica', 'Michaella', 'Leanne', 'Anna', 'Jessica']