FUNCTIONS AS
OBJECTS,
DICTIONARIES
6.00.1X LECTURE 1
FUNCTIONS AS OBJECTS
functions are first class objects:
◦ have types
◦ can be elements of data structures like lists
◦ can appear in expressions
◦ as part of an assignment statement
◦ as an argument to a function!!
particularly useful to use functions as arguments
when coupled with lists
◦ aka higher order programming using functions inside of lists to do things on lists
Functions can be used in same places we would use numbers or strings or lists.
Functions can be elements of a data structure and can appear inside expressions. Same thing as strings.
EXAMPLE
def applyToEach(L, f):
"""assumes L is a list, f a function
mutates L by replacing each element,
e, of L by f(e)"""
for i in range(len(L)):
L[i] = f(L[i])
For each index that goes into the list, get out the element of hte list, apply f to it, and then put that back int that spot inside of the
list. You are mutating the list as you do it.
EXAMPLE
def applyToEach(L, f):
for i in range(len(L)):
L[i] = f(L[i])
L = [1, -2, 3.4]
applyToEach(L, abs)
applyToEach(L, int)
applyToEach(L, fact)
applyToEach(L, fib)
EXAMPLE
def applyToEach(L, f):
for i in range(len(L)):
L[i] = f(L[i])
L = [1, -2, 3.4]
applyToEach(L, abs)
[1, 2, 3.4]
applyToEach(L, int)
applyToEach(L, fact)
applyToEach(L, fib)
EXAMPLE
def applyToEach(L, f):
for i in range(len(L)):
L[i] = f(L[i])
L = [1, -2, 3.4]
applyToEach(L, abs)
[1, 2, 3.4]
applyToEach(L, int)
[1, 2, 3]
applyToEach(L, fact)
applyToEach(L, fib)
EXAMPLE
def applyToEach(L, f):
for i in range(len(L)):
L[i] = f(L[i])
L = [1, -2, 3.4]
applyToEach(L, abs)
[1, 2, 3.4]
applyToEach(L, int)
[1, 2, 3]
applyToEach(L, fact)
[1, 2, 6]
applyToEach(L, fib)
Now you can think of doing something to each element of the list.
Apply to each is literally saying, given a data structure, give me
back the data structure where i've done something to each
element of the list.
EXAMPLE
def applyToEach(L, f):
for i in range(len(L)):
L[i] = f(L[i])
L = [1, -2, 3.4]
applyToEach(L, abs)
[1, 2, 3.4]
applyToEach(L, int)
[1, 2, 3]
applyToEach(L, fact) applying factorial to each element of the list
[1, 2, 6]
applyToEach(L, fib)
[1, 2, 13]
LISTS OF FUNCTIONS
def applyFuns(L, x): assumes L is a list of functions.
for f in L: For element in eac
print(f(x))
applyFuns([abs, int, fact, fib], 4)
4 abs of 4
4 int of 4
24 factorial of 4
5 fibonacci of 4
map takes a function that has only one argument, and a list of appropriate arguments.
and it creates a list where it has applied that function to each element in turn.
GENERALIZATION OF HOPS (higher order procedure)
Python provides a general purpose HOP, map
simple form – a unary function and a collection of suitable arguments
◦ map(abs, [1, -2, 3, -4])
produces an ‘iterable’, so need to walk down it
for elt in map(abs, [1, -2, 3, -4]):
print(elt)
[1, 2, 3, 4] previously, mutating a list. Map is different.
map gives you back a structure that that is going to act like a list, but it's something you have to iterate to get it out.
general form – an n-ary function and n collections of arguments
◦ L1 = [1, 28, 36] with two lists, can map a function down those pairs of lists and generates for us
◦ L2 = [2, 57, 9] something that has the result of doing that processing.
for elt in map(min, L1, L2):
print(elt)
[1, 28, 9]
6.00.1X LECTURE 11
STRINGS, TUPLES, RANGES,
LISTS all 4 are very similar to one another and can do similar things
Common operations
◦ seq[i] ith element of sequence
◦ len(seq) length of sequence
◦ seq1 + seq2 concatenation of sequences (not range)
◦ n*seq sequence that repeats seq n times (not range)
◦ seq[start:end] slice of sequence
◦ e in seq True if e contained in sequence
◦ e not in seq True if e contained in sequence
◦ for e in seq iterates over elements of sequence
6.00.1X LECTURE 12
String - concatenation of characters
Tuple - concatenation of any type of object
Note: only the List is mutable. Range - concatenation of a sequence (i.e. integers)
List - a sequence of any kind of object
PROPERTIES
Type Type of Examples of literals Mutable
elements
str characters ‘ ‘, ‘a’, No
‘abc’
tuple any type (), (3,), No
(‘abc’, 4)
range integers range(10), No
range(1,10,2)
list any type [], [3], Yes
[‘abc’, 4]
6.00.1X LECTURE 13
6.00.1X LECTURE 14
DICTIONARIES
So far, we've introduced Lists and Tuples, Mutables vs. Immutables.
6.00.1X LECTURE 15
HOW TO STORE
STUDENT INFO
so far, can store using separate lists for every info
names = ['Ana', 'John', 'Denise', 'Katy'] ex. storing student data in a
database
grade = ['B', 'A+', 'A', 'A']
course = [2.00, 6.0001, 20.002, 9.01]
a separate list for each item
each list must have the same length so you can associate pieces together
info stored across lists at same index, each index
refers to info for a different person
Issue is that you need to be able to associate all of those pieces together. Each index stores information for a different person.
6.00.1X LECTURE 16
HOW TO UPDATE/RETRIEVE
STUDENT INFO
def get_grade(student, name_list, grade_list, course_list):
i = name_list.index(student) Find the index of that student
grade = grade_list[i] Then, find that students index in the grade list
course = course_list[i]
return (course, grade)
messy if have a lot of different info to keep track of
must maintain many lists and pass them as arguments
must always index using integers this way is very inconvenient
must remember to change multiple lists
6.00.1X LECTURE 17
A BETTER AND CLEANER WAY –
A DICTIONARY
nice to index item of interest directly (not always int)
nice to use one data structure, no separate lists
A list A dictionary
0 Elem 1 Key 1 Val 1 In a dictionary,
rather than saying,
give me the zeroth
1 Elem 2 Key 2 Val 2 element, you will say
"give me the element
associate with this
2 Elem 3 Key 3 Val 3 key"
3 Elem 4 Key 4 Val 4 So, we will call the
indices key to use
them as labels that
… … … … tell you where to
find the element
inside of the
dictionary
6.00.1X LECTURE 18
A PYTHON DICTIONARY
store pairs of data 'Ana'
Key 1 'B'1
Val
• key Key = the thing to look up 'Denise'
Key 2 'A'2
Val
• value
'John'
Key 3 'A+'
Val 3
'Katy'
… 'A'
…
my_dict = {} Note, now we use curly brackets to represent a dictionary
grades = {'Ana':'B', 'John':'A+', 'Denise':'A', 'Katy':'A'}
key1 val1 key2 val2 key3 val3 key4 val4
Each one is an association pairing of key or value
6.00.1X LECTURE 19
DICTIONARY LOOKUP
similar to indexing into a list 'Ana' 'B'
looks up the key 'Denise' 'A'
returns the value associated 'John' 'A+'
with the key 'Katy' 'A'
if key isn’t found, get an error
grades = {'Ana':'B', 'John':'A+', 'Denise':'A', 'Katy':'A'}
grades['John'] evaluates to 'A+'
grades['Sylvan'] gives a KeyError
6.00.1X LECTURE 20
'Ana' 'B'
DICTIONARY 'Denise' 'A'
OPERATIONS 'John' 'A+'
'Katy' 'A'
'Sylvan' 'A'
grades = {'Ana':'B', 'John':'A+', 'Denise':'A', 'Katy':'A'}
add an entry
grades['Sylvan'] = 'A'
test if key in dictionary
'John' in grades returns True
'Daniel' in grades returns False
delete entry
del(grades['Ana'])
6.00.1X LECTURE 21
'Ana' 'B'
DICTIONARY 'Denise' 'A'
OPERATIONS 'John' 'A+'
'Katy' 'A'
grades = {'Ana':'B', 'John':'A+', 'Denise':'A', 'Katy':'A'}
get an iterable that acts like a tuple of all keys
grades.keys() returns ['Denise','Katy','John','Ana']
get an iterable that acts like a tuple of all values
grades.values() returns ['A', 'A', 'A+', 'B']
Note: cannot guarantee there is a particular order. It depends how you put it in there.
6.00.1X LECTURE 22
The values that go into dictionary can be any type.
DICTIONARY KEYS and VALUES
values
• any type (immutable and mutable)
• can be duplicates
• dictionary values can be lists, even other dictionaries!
keys
• must be unique
• immutable type (int, float, string, tuple,bool)
• actually need an object that is hashable, but think of as immutable as all
immutable types are hashable
• careful with float type as a key if float has accuracy issue, you may not be able to find
what you need
no order to keys or values!
d = {4:{1:0}, (1,3):"twelve", 'const':[3.14,2.7,8.44]}
6.00.1X LECTURE 23
list vs dict
ordered sequence of matches “keys” to
elements “values”
look up elements by an look up one item by
integer index another item
indices have an order no order is guaranteed
index is an integer key can be any
- They are ordered so therefore you can get an index immutable type
lookup out of it
- just a collection of entries and you match keys to values
6.00.1X LECTURE 24
6.00.1X LECTURE 25
EXAMPLE: 3 FUNCTIONS TO
ANALYZE SONG LYRICS
1) create a frequency dictionary mapping str:int
2) find word that occurs the most and how many times
• use a list, in case there is more than one word
• return a tuple (list,int) for (words_list, highest_freq)
3) find the words that occur at least X times
• let user choose “at least X times”, so allow as parameterS
• return a list of tuples, each tuple is a (list, int)
containing the list of words ordered by their frequency
• IDEA: From song dictionary, find most frequent word. Delete
most common word. Repeat. It works because you are
mutating the song dictionary.
6.00.1X LECTURE 26
CREATING A DICTIONARY lyrics is a string of
words separated by
spaces
def lyrics_to_frequencies(lyrics):
myDict = {}
for word in lyrics:
if word in myDict:
myDict[word] += 1
else:
myDict[word] = 1
if word not in dictionary, set value corresponding to that word to 1
return myDict
6.00.1X LECTURE 27
USING THE DICTIONARY
def most_common_words(freqs):
values = freqs.values()
best = max(values) in 2 lines, give me the
values in the dictionary.
words = [] we could also write a long
iterable, but it's a lot of
for k in freqs: code
if freqs[k] == best: i.e. is it one of the most common words
words.append(k) add it to the end of the list
return (words, best)
6.00.1X LECTURE 28
LEVERAGING DICTIONARY
PROPERTIES
def words_often(freqs, minTimes):
result = []
done = False
while not done:
temp = most_common_words(freqs)
if temp[1] >= minTimes:
result.append(temp)
for w in temp[0]:
given i've done the analysis, find the
del(freqs[w]) most common word.
else:
If it occurs more than minimum what
done = True i've set, then collect them, and then
return result remove them from the dictionary. So we
are mutating the dictionary.
Keep doing it until when you pull out
the most common word that doesn't
print(words_often(beatles, 5)) exceed threshold, then set that value to
true.
6.00.1X LECTURE 29
6.00.1X LECTURE 30
FIBONACCI RECURSIVE CODE
def fib(n):
if n == 1:
return 1
elif n == 2:
return 2
else:
return fib(n-1) + fib(n-2)
two base cases
calls itself twice
this code is inefficient
6.00.1X LECTURE 31
INEFFICIENT FIBONACCI
fib(n) = fib(n-1) + fib(n-2)
fib(5)
fib(4) fib(3)
you didn't really need to do
fib(3) fib(3). The calculation was
fib(2) fib(2) fib(1) already done in fib(4).
it will slow down when
trying to do large Fibonacci
fib(2) fib(1) numbers
recalculating the same values many times! This is how dictionaries help
could keep track of already calculated values
6.00.1X LECTURE 32
FIBONACCI WITH A
DICTIONARY
def fib_efficient(n, d): calculate the nth fibonacci but give it a dictionary
if n in d: if i've already done the work, just look it up and return it
return d[n]
else:
ans = fib_efficient(n-1, d) + fib_efficient(n-2, d)
d[n] = ans
return ans
d = {1:1, 2:2}
print(fib_efficient(6, d))
you can use the dictionary to hold on to values you've already computed and you don't need to recompute
do a lookup first in case already calculated the value
modify dictionary as progress through function calls
6.00.1X LECTURE 33
6.00.1X LECTURE 34
Global Variables allow you to move variables out of scope.
But it will result in side effects.
GLOBAL VARIABLES
can be dangerous to use can really lead to side effects in a bad way
◦ breaks the scoping of variables by function call
◦ allows for side effects of changing variable values in ways
that affect other computation
but can be convenient when want to keep track of
information inside a function
example – measuring how often fib and
fib_efficient are called
6.00.1X LECTURE 35
Before, anything inside function only accessible inside the call of the function itself.
global --> allows accessibility outside the scope of the function as well
TRACKING EFFICIENCY
def fib(n): def fibef(n, d):
global numFibCalls global numFibCalls
numFibCalls += 1 numFibCalls += 1
if n == 1: if n in d:
return 1 return d[n]
elif n == 2: else:
ans = fibef(n-1,d)+fibef(n-2,d)
return 2
d[n] = ans
else:
return ans
return fib(n-1)+fib(n-2)
6.00.1X LECTURE 36
Comparing efficiency of Fib vs Fib_efficient
TRACKING EFFICIENCY
numFibCalls = 0 initialize the global variable outside
print(fib(12))
print('function calls', numFibCalls)
287 calls with fib
numFibCalls = 0
d = {1:1, 2:2}
print(fib_efficient(12, d))
print('function calls', numFibCalls)
only 21 calls with fib_efficient
As a result: Dictionaries can be very handy and can help more efficiently capture information
6.00.1X LECTURE 37