EECS 110: Lec 17: Review for the Final Exam
Aleksandar Kuzmanovic
Northwestern University
http://cs.northwestern.edu/~akuzma/classes/EECS110-s09/
General Info
Wednesday, June 3, 10-11:30am, Annenberg Hall G15
To be done individually
Closed book
One 8.5” by 11” sheet of paper permitted
Please do not discuss the exam with others until everyone has taken it.
There are six questions. Each question is worth 20 points.
Hence, you can acquire 120 points in total.
Those who get > 90 points will get an A.
Those who get >80 points and <90 points will get a B, etc.
2
Final Exam
6 questions:1. Misc
2. Loops and recursion
3. Mutation
4. 2D in Python
5. Dictionaries
6. Classes and Objects3
What does this code do?
print 'It keeps on'
while True: print 'going and'
print 'Phew! I\'m done!'
Q2 (and Q1): Loops (while and for) and Recursion
Extreme Looping
Anatomy of a while loop:
print 'It keeps on'
while True: print 'going and'
print 'Phew! I\'m done!'
“while” loop
the loop keeps on running as long as this test is True
This won't print until the while loop finishes - in this case, never!
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
??
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
??
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
3
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
310
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
3105
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
310516
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
3105168
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
31051684
What do these two loops print?
n = 0
for c in 'forty-two':
if c not in 'aeiou':
n += 1
print n
7
n = 3while n > 1: print n if n%2 == 0: n = n/2 else: n = 3*n + 1
310516842
fore!
for x in range(8):
print 'x is', x
print 'Phew!'
x is assigned each value from this
sequence
the BODY or BLOCK of the for loop runs with that x
Code AFTER the loop will not run until the loop is finished.
1
2
3
4
LOOP back to step 1 for EACH value in the list
Two kinds of for loops
Element-based Loops
L = [ 42, -10, 4 ]
x
L = [ 42, -10, 4 ]
i0 1 2
Index-based Loops
sum = 0
for x in L: sum += x
sum = 0
for i in range(len(L)): sum += L[i]
L[i]
Q2: Recursion vs. Loops
Solve a problem using:
(a) recursion
(b) loops
Example 1a: fac(N) with recursion
Let recursion do the work for you.
def fac(N):
if N <= 1: return 1 else: return N * fac(N-1)
You handle the base case – the easiest possible case to
think of!
Recursion does almost all of the rest of the problem!
Exploit self-similarity
Produce short, elegant code
Less work !
Example 1b: fac with for (v1)
def fact( n ):
answer = 1
for x in range(n):
answer = answer * x
return answer
def fact( n ):
answer = 1
for x in range(1,n+1):
answer = answer * x
return answer
Example 1b: fac with for (v2)
def sum(L):
""" input: a list of numbers, L
output: L's sum
"""
if len(L) == 0:
return 0.0
else:
return L[0] + sum(L[1:])
Base Caseif the input
has no elements, its sum is zero
Recursive Case
if L does have an element, add
that element's value to the sum of the REST of the
list…
This input to the recursive call must be "smaller"
somehow…
Example 2a: sum(L) with recursion
Example 2b: sum(L) with for
def sum( L ):
""" returns the sum of L's elements """
sum = 0
for x in L:
sum = sum + x
return sum
Finding the sum of a list:
Accumulator!
Example 3: isPrime(n) and addPrimes(L)
Problem:
(a)Use a loop to write a Python function isPrime(n), which takes in an integer n and returns True if n is prime and False if n is composite. You may assume that n will be strictly greater than 1.
(b)Use recursion (no loops!) to write a Python function addPrimes(L), which takes in a list L of integers (all integers will be at least 2) and it returns the sum of only the prime numbers in the list L. The function should use the above isPrime(n) function.
Example 3a: isPrime(n)
def isPrime(n): if n < 2: return False for i in range(2, n/2+1): if n % i == 0: return False return True
Example 3b: addPrimes(L)
def addPrimes(L): if len(L) == 0: return 0 else: if isPrime(L[0]): return L[0] + addPrimes(L[1:]) else: return addPrimes(L[1:])
Changeable types:
dictionary
Unchangeable types:
list
tuple
string
int
float
bool
Mutable vs. Immutable data
Question 3: Mutation
Functions and (immutable) Variables
def fact(a): result = 1 while a > 0: result *= a a -= 1 return result
>>> x = 5>>> y = fact(x)>>> x??
Functions and (immutable) Variables
def fact(a): result = 1 while a > 0: result *= a a -= 1 return result
>>> x = 5>>> y = fact(x)>>> x5
Functions and (immutable) Variables
def swap(a, b): temp = a a = b b = temp
>>> x = 5>>> y = 10>>> swap(x, y)>>> print x, y??
x
y
a
b
temp
Functions and (immutable) Variables
def swap(a, b): temp = a a = b b = temp
>>> x = 5>>> y = 10>>> swap(x, y)>>> print x, y5, 10
x
y
a
b
temp
Functions and Mutable Types
def swap(L, i1, i2): temp = L[i1] L[i1] = L[i2] L[i2] = temp
>>> MyL = [2, 3, 4, 1]>>> swap(myL, 0, 3) >>> print myL??
RAM
MyL
L
i1
i2
42
43
44
45
Functions and Mutable Types
def swap(L, i1, i2): temp = L[i1] L[i1] = L[i2] L[i2] = temp
>>> MyL = [2, 3, 4, 1]>>> swap(myL, 0, 3) >>> print myL[1,3,4,2]
RAM
MyL
L
i1
i2
42
43
44
45
The conclusion
You can change the contents of lists in functions that take those lists as input.
Those changes will be visible everywhere.
(actually, lists or any mutable objects)
(immutable objects are safe, however)
Example 1
def zeroOdd1( L ): for i in range(len(L)): if L[i] % 2 == 1: L[i] = 0
def zeroOdd2( L ): for i in L: if i % 2 == 1: i = 0
>>> L = [1, 2, 3, 4, 5]>>> zeroOdd1(L)>>> L??>>> L = [1, 2, 3, 4, 5]>>> zeroOdd2(L)>>> L
Example 1
def zeroOdd1( L ): for i in range(len(L)): if L[i] % 2 == 1: L[i] = 0
def zeroOdd2( L ): for i in L: if i % 2 == 1: i = 0
>>> L = [1, 2, 3, 4, 5]>>> zeroOdd1(L)>>> L[0,2,0,4,0]>>> L = [1, 2, 3, 4, 5]>>> zeroOdd2(L)>>> L??
Example 1
def zeroOdd1( L ): for i in range(len(L)): if L[i] % 2 == 1: L[i] = 0
def zeroOdd2( L ): for i in L: if i % 2 == 1: i = 0
>>> L = [1, 2, 3, 4, 5]>>> zeroOdd1(L)>>> L[0,2,0,4,0]>>> L = [1, 2, 3, 4, 5]>>> zeroOdd2(L)>>> L[1,2,3,4,5]
Example 2
What are the values of A, B, C and D at the indicated points?def mystery1(L1, N, C): for i in L1: if i == N: C += 1 return C def mystery2(C): for i in range(len(C)): C[i] *= 2
>>> A = [22, 10, 30]>>> B = 22>>> C = 0>>> D = mystery1(A, B, C) >>> mystery2(A)
12
1)
2)
Example 2
What are the values of A, B, C and D at the indicated points?def mystery1(L1, N, C): for i in L1: if i == N: C += 1 return C def mystery2(C): for i in range(len(C)): C[i] *= 2
>>> A = [22, 10, 30]>>> B = 22>>> C = 0>>> D = mystery1(A, B, C) >>> mystery2(A)>>> A
1
1)
2)
A = [22, 10, 30]B = 22C = 0D = 1
2
Example 2
What are the values of A, B, C and D at the indicated points?def mystery1(L1, N, C): for i in L1: if i == N: C += 1 return C def mystery2(C): for i in range(len(C)): C[i] *= 2
>>> A = [22, 10, 30]>>> B = 22>>> C = 0>>> D = mystery1(A, B, C) >>> mystery2(A)>>> A
1
2
1)
2)
A = [22, 10, 30]B = 22C = 0D = 1
A = [44, 20, 60]
Example 3: avoiding mutation
Problem:
Write function x(L), …., The list L should NOT mutate, i.e., it should stay the same.
Example 3: avoiding mutation
def x(L): # some function
…
L2=copyList(L)
… # manipulate L2, not L
def copyList(L): L2 = [] for x in L: L2 += [x] return L2
Handling rectangular arrays …
listA
list
list
list
A[0]
A[1]
A[2]
How many rows does A have, in general ?
How many columns does A have, in general ?
A[2][3]
A[0][0]
Question 4: 2D in Python
Rectangular arrays
Handling rectangular arrays …
listA
list
list
list
A[0]
A[1]
A[2]
How many rows does A have, in general ?
How many columns does A have, in general ?
A[2][3]
A[0][0]
len(A)
Rectangular arrays
Handling rectangular arrays …
listA
list
list
list
A[0]
A[1]
A[2]
How many rows does A have, in general ?
How many columns does A have, in general ?
A[2][3]
A[0][0]
len(A)
len(A[0])
Which one works?
How could we create a rectangular array (of default data, 0),given its height and width ?
or
A = [ [0]*width ]*height
A = [ [0]*height ]*width
Which one works?
How could we create a rectangular array (of default data, 0), given its height and width ?
A = [ [0]*width ]*height
A = [ [0]*height ]*width
What's really going on?
A = [ [0]*width ]*height
inner = [0]*widthA = [inner]*height
Creating a 2d array
def create2dArray( width, height ): """ does just that """
A = [] # start with nothing
for row in range( height ):
for col in range( width ):
Creating a 2d array
def create2dArray( width, height ): """ does just that """
A = [] # start with nothing
for row in range( height ): A = A + [[]] for col in range( width ): A[row] = A[row] + [0] return A
Example 1: What 2d loops would create a checkerboard image?
def checkerboard():
# loop over each pixel for col in range(width):
for row in range(height):
Example 1: What 2d loops would create a checkerboard image?
def checkerboard():
# loop over each pixel for col in range(width):
for row in range(height): if (col/20 + row/20)%2 == 0:
image.plotPoint( col, row )
Example 2: Write a function that returns the number of all-zero rows
Example 2: Write a function that returns the number of all-zero rows
def num_zero_rows(L): width = len(L[0]) height = len(L) n = 0 for row in range(height): k = 0 for col in range(width): if L[row][col] != 0: k = 1 if k == 0: n += 1 return n
In Python a dictionary is a set of key - value pairs.
It's a list where the index can be any immutable-type key.
>>> d = {}
>>> d[1988] = 'dragon'
>>> d[1989] = 'snake'
>>> d
{1988: 'dragon', 1989: 'snake'}
>>> d[1988]
'dragon'
>>> d[1987]
key error
Question 5: Dictionaries
Lists vs. Dictionaries
In Python a dictionary is a set of key - value pairs.
It's a list where the index can be any immutable-type key.
>>> d = {}
>>> d[1988] = 'dragon'
>>> d[1989] = 'snake'
>>> d
{1988: 'dragon', 1989: 'snake'}
>>> d[1988]
'dragon'
>>> d[1987]
key error
creates an empty dictionary, d
1988 is the key'dragon' is the value
1989 is the key'snake' is the value
Anyone seen this before?
Retrieve data as with lists…
or almost !
More on dictionaries
Dictionaries have lots of built-in methods:
>>> d = {1988: 'dragon', 1989: 'snake'}
>>> d.keys()
[ 1989, 1988 ]
>>> d.has_key( 1988 )
True
>>> d.has_key( 1969 )
False
>>> d.pop( 1988 )
'dragon'
delete a key (and its value)
check if a key is present
get all keys
A family dictionary?
A family dictionary…
T = {'abe' :['homer','herb'], 'jackie':['marge','patty','selma'], 'homer' :['hugo','bart','lisa','maggie'], 'marge' :['hugo','bart','lisa','maggie']}
keys can be any immutable type
values can be any type at all…
T['abe']
How to get 'selma' from T?
A family dictionary…
T = {'abe' :['homer','herb'], 'jackie':['marge','patty','selma'], 'homer' :['hugo','bart','lisa','maggie'], 'marge' :['hugo','bart','lisa','maggie']}
keys can be any immutable type
values can be any type at all…
T['abe']
How to get 'selma' from T?
['homer','herb']
A family dictionary…
T = {'abe' :['homer','herb'], 'jackie':['marge','patty','selma'], 'homer' :['hugo','bart','lisa','maggie'], 'marge' :['hugo','bart','lisa','maggie']}
keys can be any immutable type
values can be any type at all…
T['abe']
How to get 'selma' from T?
['homer','herb']
T['jackie'][2]
(T['jackie'] is a list)
Example 1: favChild
def favChild( person, Tree ): """ person is a name (a string) Tree is a dictionary of children returns person's favorite child """
if Tree.has_key( person ): Kids = Tree[person] Kids.sort() return Kids[0] else: return 'no children'
Who is favored ?
Side effects ?
Example 2: addChild
def addChild( person, Tree, jr ):""" adds person's new child to Tree ""“
For example, >>> addChild( 'lisa', T, 'abejr' )
def addChild( person, Tree, jr ):""" adds person's new child to Tree ""“if Tree.has_key(person):
kids = Tree[person]kids += [jr]
For example, >>> addChild( 'lisa', T, 'abejr' )
Example 2: addChild
EXAMPLE 3: Based on favChild, write favGChild to return the first grandchild
alphabetically - or return 'no one' if there are none.
def favChild( person, Tree ): if Tree.has_key( person ): Kids = Tree[person] Kids.sort() return Kids[0] else: return 'no children'
EXAMPLE 3: Based on favChild, write favGChild to return the first grandchild
alphabetically - or return 'no one' if there are none.
def favGChild( person, Tree ): gChildren = [] if Tree.has_key( person ): for child in Tree[person]: if Tree.has_key( child ): gChildren += Tree[child] if gChildren == []: return 'no one' else: gChildren.sort() return gChildren[0]
def favChild( person, Tree ): if Tree.has_key( person ):
Kids = Tree[person] Kids.sort()
return Kids[0] else:
return 'no children'
An object-oriented programming language allows you to build your own customized types of variables.
(1) A class is a type of variable.
(2) An object is one such variable.
There will typically be MANY objects of
a single class.
Question 6: Classes and Objects
Objects
An object is a data structure (like a list), except
(1) Its data elements have names chosen by
the programmer.
(2) Data elements are chosen & organized
by the programmer
(3) An object can have behaviors built-in
by the programmer.
Objects
An object is a data structure (like a list), except
(1) Its data elements have names chosen by
the programmer.
(2) Data elements are chosen & organized
by the programmer
(3) An object can have behaviors built-in
by the programmer.usually called "methods" instead of functions
class Date: """ a blueprint (class) for objects that represent calendar days """ def __init__( self, mo, dy, yr ): """ the Date constructor """ self.month = mo self.day = dy self.year = yr
def __repr__( self ): """ used for printing Dates """ s = "%02d/%02d/%04d" % (self.month, self.day, self.year) return s
The Date class
The Date Class
this is an object of type Date
>>> d = Date(1,1,2008)
>>> d
1/1/2008
this is a CONSTRUCTOR …
the representation of a particular object of type Date
class Date: def __init__( self, mo, dy, yr ): def __repr__(self): def isLeapYear(self):
def copy(self): """ returns a DIFFERENT object w/SAME date! """
def equals(self, d2): """ returns True if they represent the same date; False otherwise """
More Date
class Date: def __init__( self, mo, dy, yr ): def __repr__(self): def isLeapYear(self):
def copy(self): """ returns a DIFFERENT object w/SAME date! ""“
return Date(self.month, self.day, self.year)
def equals(self, d2): """ returns True if they represent the same date; False otherwise """
More Date
class Date: def __init__( self, mo, dy, yr ): def __repr__(self): def isLeapYear(self):
def copy(self): """ returns a DIFFERENT object w/SAME date! ""“
return Date(self.month, self.day, self.year)
def equals(self, d2): """ returns True if they represent the same date; False otherwise ""“
return self.month == d2.month and self.day == d2.day and self.year == d2.year
More Date
Good luck with the Exam!