Functional Programming
Contents
Functional Programming#
Table of Contents
Introduction#
The code we have written so far in this class has either been procedural or object oriented. Today we’re going to learn about another programming paradigm: functional programming.
Functional programming is an approach to programming that focuses computations via modular, stateless, deterministic, goal oriented functions.
Procedural code is comprised of statements describing the exact steps to solve a particular problem and usually involve making modifications to global or external values along the way. In functional programming on the other hand, you rely on tools built into the language to decide how to go about solving the problem, which you provide with functions that describe the desired result. These tend to be more expression, and ideally return the processed data without changing the external state.
Functional programming is a concise and a powerful tool for data processing. The results are often less error prone and (once you get the hang of it) more readable than the procedural equivalent.
In this lesson you’ll learn the functional programming features provided by Python.
Part 1: Mapping#
Generating a new collection by applying the same transformation to every item in a container is called mapping.
Part 1.1: Procedural#
The procedural way to map an iterable is via the old standby, the for
loop.
In this example we append to a list converted
which contains all elements of
the date
list with str()
applied.
Appending to converted
is an example of what is meant when we say that the
shared state has been changed.
date = ["Wednesday", "Nov", 4, 2021]
converted = []
for elm in date:
converted.append(str(elm))
print(converted)
['Wednesday', 'Nov', '4', '2021']
Part 1.2: map()
#
Python provides a built in map()
function which does the same thing in a
single function call.
Hint
map()
returns a iterator object which is displayed as something like
<map at 0x10f174b80>
. Convert it to a list
to see the contents.
map()
takes two arguments: the function to apply then the iterator.
date = ["Wednesday", "Nov", 4, 2021]
converted = map(str, date)
list(converted)
['Wednesday', 'Nov', '4', '2021']
days = [
'Monday',
'Tuesday',
'Wednesday',
'Thursday',
'Friday',
'Saturday',
'Sunday'
]
list(map(lambda x: x[:3], days))
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
…a user defined function…
from fractions import Fraction
def to_dec(val):
num = Fraction(str(val))
return round(num.numerator / num.denominator, 2)
measurements = ["1/4", "2/5", "5/64"]
list(map(to_dec, measurements))
[0.25, 0.4, 0.08]
…or a type.
from pathlib import Path
files = [
"README.md",
"pyproject.toml",
"bin/build",
]
list(map(Path, files))
[PosixPath('README.md'), PosixPath('pyproject.toml'), PosixPath('bin/build')]
You can even use type methods.
words = [
'clarify',
'whereby',
'above',
'strong',
'able',
]
list(map(str.capitalize, words))
['Clarify', 'Whereby', 'Above', 'Strong', 'Able']
Part 1.2: Exercise#
(Map)
Write a function normalize()
that takes one argument text
and returns a
lowercased version with leading and trailing whitespace removed and spaces
replace with underscores.
Map the following list of files
list using both a for
loop and map()
.
files = [
"Oxford English Dictionary.txt",
"ColorFAQ.txt",
"custom.css",
"weather.json",
]
Solution to Exercise 101 (Map)
1def normalize(text):
2 return text.strip().lower().replace(" ", "_")
3
4files = [
5 "Oxford English Dictionary.txt",
6 "ColorFAQ.txt",
7 "custom.css",
8 "weather.json",
9]
10
11transformed = []
12
13for x in files:
14 transformed.append(normalize(x))
15
16transformed = list(map(normalize, files))
Part 1.3: Multiple Args#
For a transformation callable that takes multiple arguments, you can provide
multiple iterables to map()
.
One value from each iterable will be passed as an argument each iteration.
from operator import add
list(map(add, "abc", "123"))
['a1', 'b2', 'c3']
Part 1.3: Exercise#
(Mapping Multiple Args)
Using map()
apply the function operator.mul
to multiply each of the
following prices
keys by their exchange rate value.
rates = {
"USD": 1,
"EUR": .86,
"CAD": 1.24,
"GBP": 0.73,
"MXN": 20.83,
}
prices = {
141: rates["EUR"],
45: rates["USD"],
47: rates["GBP"],
155: rates["CAD"]
}
Solution to Exercise 102 (Mapping Multiple Args)
1from operator import mul
2
3rates = {
4 "USD": 1,
5 "EUR": .86,
6 "CAD": 1.24,
7 "GBP": 0.73,
8 "MXN": 20.83,
9}
10
11prices = {
12 141: rates["EUR"],
13 45: rates["USD"],
14 47: rates["GBP"],
15 155: rates["CAD"]
16}
17
18list(map(mul, prices.keys(), prices.values()))
Part 2: Filter#
Generating a new collection containing only items that match a certain condition is called filtering.
Part 2.1: Procedural#
Here is how you would filter the old fashioned way, via a for
loop.
numbers = [13, 88, 80, 58, 23, 33, 31, 28]
even = []
for num in numbers:
if num % 2 == 0:
even.append(num)
even
[88, 80, 58, 28]
Part 2.2: filter()
#
Python provides a built in filter()
function.
filter()
takes two arguments: a boolean function that serves as the filtering
condition followed by the iterable to filter.
def is_even(num):
return num % 2 == 0
numbers = [13, 88, 80, 58, 23, 33, 31, 28]
list(filter(is_even, numbers))
[88, 80, 58, 28]
As with map()
you can use any kind of callable as the filtering function.
animals = ["ox", "Tiger", "rabbit", "Dragon"]
list(filter(str.istitle, animals))
['Tiger', 'Dragon']
Keep in mind, the result is evaluated in a boolean context, meaning that falsy values will be filtered out.
numbers = [55, 38, 168, 71, 123, 31, 118, 15]
list(filter(lambda x: x//100, numbers))
[168, 123, 118]
Part 2.3: Truthy filtering#
Often you just want to filter a list for non-empty or truthy values.
If you pass None
as the first argument to filter()
, it will return only
truthy values. In this example, all blank lines will be filtered out.
poem = """
"The sun was shining on the sea,
Shining with all his might:
He did his very best to make
The billows smooth and bright —
And this was odd, because it was
The middle of the night.
"""
list(filter(None, poem.splitlines()))
['"The sun was shining on the sea,',
' Shining with all his might:',
'He did his very best to make',
' The billows smooth and bright —',
'And this was odd, because it was',
' The middle of the night.']
Part 3: Reducing#
Aggregation operations, or those that apply the same operation, cumulatively, to each element in a collection to arrive at a single value is called reducing.
Part 3.1: Procedural#
Here’s an example of how you would reduce via a for
loop.
In this example, we add each number in the numbers
list to the previous total
to arrive at the sum of all numbers in the sequence.
numbers = [9, 7, 5, 1, 1]
total = 0
for num in numbers:
total = total + num
print(total)
23
Part 3.2: reduce()
#
Python provides the reduce()
function from the functools
module.
It takes two arguments: the function to apply followed by the iterable to apply it to.
In this example, we use the operator.add
function to calculate the sum of all
numbers
.
from functools import reduce
from operator import add
numbers = [9, 7, 5, 1, 1]
total = reduce(add, numbers)
print(total)
23
Any function used in reduce()
must take two arguments:
result – the accumulated value of all previous operations
current – the current sequence element
It must return a single value which will be the result value for the next
iteration, or returned by reduce()
after the last element.
def func(res, cur):
"""Signature for `reduce()` functions."""
# calculate value here
value = ...
# returned value will be the
# next iterations res
return value
In this example we re-implement the add()
function, using the parameter name
running_total
for the result and number
for the current element.
def add(running_total, number):
value = running_total + number
return value
total = reduce(add, numbers)
print(total)
23
Part 3.2: Exercise#
(Reduce)
Make a list assigned to the variable letters
with all the letters in the
word of the day.
Concatonate all letters using both a for
loop and the functools.reduce()
function.
Solution to Exercise 103 (Reduce)
1from operator import add
2from functools import reduce
3
4letters = ['g', 'r', 'i', 's', 'l', 'y']
5
6word = ""
7
8for letter in letters:
9 word += letter
10
11print(word)
12
13word = reduce(add, letters)
14
15print(word)
Part 3.3: Initial value#
reduce()
determines the initial value of res
by calling the type of the
first element of the iterable. For example, if your iterable is [1, 2, 3]
,
the initial value of res
will be int()
or 0
.
If you need a different initial value, you can pass it as the optional third argument.
In this example we are converting a tuple of integers into strings then concatenating them.
reduce()
would choose 0
as the initial value, but we can override that by
passing an empty string (""
) as the third argument.
numbers = [1, 2, 3]
reduce(
lambda res, cur: res + str(cur),
numbers,
""
)
'123'
Here is a more complicated example that removes all punctuation characters from a string by iterating over a string containing the punctuation characters, and using the string to strip as the initial value.
import string
from functools import reduce
filename = "hello_world.py"
reduce(
lambda text, char: text.replace(char, ""),
string.punctuation,
filename
)
'helloworldpy'
Part 3.3: Exercise#
(Initializer)
Use reduce()
to count the number of vowels in a string.
Solution to Exercise 104 (Initializer)
1def addifvowel(total, char):
2 if char.lower() in "aeiou":
3 return total + 1
4 else:
5 return total
6
7
8text = "Einstein"
9reduce(addifvowel, text, 0)
Challenges#
This section contains a series of more challenging exercises using map()
,
filter()
or range()
.
Fibonacci sequence#
(Fibonacci)
Use reduce()
to generate a list containing the first 10
numbers of the
fibonacci sequence in which each successive number is the result of adding
the preceeding two.
Example output
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Solution to Exercise 105 (Fibonacci)
reduce(lambda seq, _: seq + [sum(seq[-2:])], range(10), [0, 1])
RGB to Hex Color Code#
(RGB to Hex)
Use reduce()
to convert a iterable of three RGB color values from 0-255
to
a six character string containing the equivalant hex color code.
(More info.)
Note: The solution is not a mathmatical equation.
Need a hint?
Use string formatting to convert each number to its hex equivalant.
Example
Given
RGB = (34, 34, 171)
Output
'2222AB'
Solution to Exercise 106 (RGB to Hex)
1yellow = (255, 255, 0)
2
3reduce(lambda hex, val: hex + f"{val:02x}", yellow, "")
Hex to RGB Color Values#
(Hex to RGB)
Use reduce()
to convert a six character hex color code to list of the
equivalant RGB color values from 0-255
. (More info.)
Note: The solution is not a mathmatical equation.
Need a hint?
Use the range() function and a slice to exract each two digit hex number from the six digit string.
You can convert a hex number to a base
10
integer (the normal kind) using theint()
constructor by including the optional second argument.
Example
Given
hex_code = "00ffff"
Output
(0, 255, 255)
Solution to Exercise 107 (Hex to RGB)
1def to_rgb(hex_code, i):
2 code = hex_code[i*2:i*2+2]
3 return int(code, base=16)
4
5reduce(lambda rgb, i: rgb + [to_rgb("ffff00", i)], range(3), [])
Get values for multiple keys#
(Multiple Dictionary Keys)
Given a dictionary mapping letters
to their unicode codepoint, use reduce()
go return a list containing the values for an iterable of keys
.
Example
Given
letters = {
'a': 97,
'b': 98,
'c': 99,
'd': 100,
'e': 101,
}
keys = ['c', 'a', 't']
Output
[99, 97, None]
Solution to Exercise 108 (Multiple Dictionary Keys)
1letters = {
2 'a': 97,
3 'b': 98,
4 'c': 99,
5 'd': 100,
6 'e': 101,
7}
8
9keys = ['c', 'a', 't']
10
11reduce(lambda values, key: values + [alpha.get(key)], "aeiou", [])
Reference#
Glossary#
Functional Programming#
- callable#
Any object that can be called such as a function, method, or type.
- map#
A functional programming function that applies a function to every element of an iterable and returns a collection of results.
See also: map.
- filter#
A functional programming function that applies a predicate function to every element of an iterable and returns a collection that contains the elements for which the predicate evaluated to
True
.See also: filter.
- reduce#
A functional programming function that applies a function to an every element of an iterable and returns a single cumulative value.
See also: reduce.
- predicate#
A function that returns a boolean value of some condition.
See also: predicate.
- aggregation#
Grouping together multiple values to calculate a single summary value.
- procedural programming#
Code comprised of a list of instructions to tell the computer what to do step by step. Code is organized into functions (proceedures).
See also: procedural programming.
- object oriented programming#
An approach to programing focused on encapsulating data and behavior into objects. Code is organized into classes, objects and methods.
See also: object oriented programming.
- functional programming#
An approach to programming that focuses computations via modular, isolated, deterministic, goal oriented functions.
See also: functional programming.
- deterministic#
When an algorithm, given particular input, will always produce the same output regardless of any external factors. In contrast to nondeterministic.
See also: deterministic.
- nondeterministic#
When an algorithm may exhibit different behavior for the same input. In contrast to deterministic.
See also: nondeterministic.
- modular#
A software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules, such that each contains everything necessary to execute only one aspect of the desired functionality.
See also: modular.
- imperative#
- imperative programming#
An approach to programming that involves describing the exact steps the program should take via a series of statements that make updates to the shared state. In contrast to declarative programming.
See also: imperative programming.
- declarative#
- declarative programming#
An approach to programming that involves describing what the program should do via expressions and tools built into the language rather than describing how to do it via a series of statements. In contrast to imperative programming.
See also: declarative programming.
- state#
The information retained throughout a programs runtime–for example the data stored in variables or loaded in from a file.
See also: state.
- statefulness#
- stateful#
A term describing software, a software component (like a function), or a protocol that references information from previous operations and/or makes changes to the information available for future operations. In contrast to statelessness.
- statelessness#
- stateless#
A term describing software, a software component (like a function), or a protocol that does not reference information from previous operations nor have any side effects such as saving state for future operations. Instead each operation starts from scratch based only on the input and the only thing effected is the returned output. In contrast to statefulness.
See Also#
See also
Summary#
map()
is used to transform elementsfilter()
is used to select elementsreduce()
is used for aggregation