Primitive Representation | 1 2 3 True False (..,..) [..,..] |
Data abstraction | make_rat() numer() denom() |
add_rat() mul_rat() print_rat() equal_rat() |
|
User program | exact_harmonic_number() |
Each layer only uses the layer above it.
Type | Examples |
---|---|
Integers | 0 -1 0xFF 0b1101 |
Booleans | True False |
Functions | def f(x)...
lambda x: ...
|
Strings | "pear" "I say, \"hello!\"" |
Tuples | (1, 10) ("Oh", "hi", 11) |
Ranges | range(11) range(1, 6) |
Lists | [] ["apples", "bananas"] [x**3 for x in range(2)]
|
A dict
is a mutable mapping of key-value pairs
states = {
"CA": "California",
"DE": "Delaware",
"NY": "New York",
"TX": "Texas",
"WY": "Wyoming"
}
Queries:
>>> len(states)
5
>>> "CA" in states
True
>>> "ZZ" in states
False
words = {
"mรกs": "more",
"otro": "other",
"agua": "water"
}
Select a value:
>>> words["otro"]
'other'
>>> first_word = "agua"
>>> words[first_word]
'water'
>>> words["pavo"]
KeyError: pavo
>>> words.get("pavo", "๐ค")
'๐ค'
Create an empty dict:
users = {}
Add values:
users["profpamela"] = "b3stp@ssEvErDontHackMe"
Change values:
users["profpamela"] += "itsLongerSoItsMoreSecure!!"
>>> users["profpamela"]
'b3stp@ssEvErDontHackMeitsLongerSoItsMoreSecure!!'
spiders = {
"smeringopus": {
"name": "Pale Daddy Long-leg",
"length": 7
},
"holocnemus pluchei": {
"name": "Marbled cellar spider",
"length": (5, 7)
}
}
insects = {"spiders": 8, "centipedes": 100, "bees": 6}
for name in insects:
print(insects[name])
...is the same as:
for name in list(insects):
print(insects[name])
What will be the order of items?
8 100 6
Keys are iterated over in the order they are first added.
Lists of lists | [ [1, 2], [3, 4] ] |
Lists of tuples | [ (1, 2), (3, 4) ] |
Tuples of tuples | ( (1, 2), (3, 4) ) |
Dicts of tuples | {"x": (1, 2), "y": (3, 4)} |
Dicts of lists | {"heights": [89, 97], "ages": [6, 8]} |
...what else?! Dicts of dicts, Lists of dicts, etc.
Consider a matrix (two-dimensional table) like this:
1 | 2 | 0 | 4 |
0 | 1 | 3 | -1 |
0 | 0 | 1 | 8 |
That matrix has three rows and four columns, with integer values in each location.
We want this constructor, selector, and mutator:
matrix(rows, cols) |
Returns a ROWS x COLS matrix with all values set to 0 |
value(matrix, row, col) |
Returns value of MATRIX at (ROW, COL) |
set_value(matrix, row, col, val) |
Sets value of MATRIX at (ROW, COL) to VAL |
How could we implement? Answer the poll!
A list of lists, row-major order:
[ [1,2,0,4], [0,1,3,-1], [0,0,1,8] ]
def matrix(rows, cols):
return [ [0 for col in range(cols)] for row in range(rows) ]
def value(matrix, row, col):
return matrix[row][col]
def set_value(matrix, row, col, val):
matrix[row][col] = val
m = matrix(3, 4)
set_value(m, 0, 0, 1)
set_value(m, 0, 1, 2)
set_value(m, 0, 3, 4)
A list of lists, column-major order:
[ [1,0,0], [2,1,0], [0,3,1], [4,-1,8] ]
def matrix(rows, cols):
return [ [0 for row in range(rows)] for col in range(cols) ]
def value(matrix, row, col):
return matrix[col][row]
def set_value(matrix, row, col, val):
matrix[col][row] = val
m = matrix(3, 4)
set_value(m, 0, 0, 1)
set_value(m, 0, 1, 2)
set_value(m, 0, 3, 4)
A tuple of lists, row-major order:
( [1,0,0], [2,1,0], [0,3,1], [4, -1,8] )
def matrix(rows, cols):
return tuple( [0 for col in range(cols)] for row in range(rows) )
def value(matrix, row, col):
return matrix[row][col]
def set_value(matrix, row, col, val):
matrix[row][col] = val
m = matrix(3, 4)
set_value(m, 0, 0, 1)
set_value(m, 0, 1, 2)
set_value(m, 0, 3, 4)
A list of tuples?
[ (1,2,0,4), (0,1,3,-1), (0,0,1,8) ]
def matrix(rows, cols):
return [ tuple(0 for col in range(cols))
for row in range(rows) ]
def value(matrix, row, col):
return matrix[row][col]
def set_value(matrix, row, col, val):
matrix[row][col] = val
m = matrix(3, 4)
set_value(m, 0, 0, 1) โ
set_value(m, 0, 1, 2) โ
set_value(m, 0, 3, 4) โ
A list of tuples?
[ (1,2,0,4), (0,1,3,-1), (0,0,1,8) ]
def matrix(rows, cols):
return [ tuple(0 for col in range(cols))
for row in range(rows) ]
def value(matrix, row, col):
return matrix[row][col]
def set_value(matrix, row, col, val):
matrix[row] = matrix[row][:col] + (val,) + matrix[row][col+1:]
m = matrix(3, 4)
set_value(m, 0, 0, 1)
set_value(m, 0, 1, 2)
set_value(m, 0, 3, 4)
Which implementation was your favorite?
Answer the poll!
When might you use a tuple?
When might you use a list?
When might you use a dict?
We want this constructor and selectors:
tree(label, children) |
Returns a tree with given LABEL at its root, whose children are CHILDREN |
label(tree) |
Returns the label of root node of TREE |
children(tree) |
Returns the children of TREE (each a tree). |
is_leaf(tree) |
Returns true if TREE is a leaf node. |
How could we implement? Answer the poll!
A list of label + list for each tree/subtree:
[20,[12,[9,[7],[2]],[3]],[8,[4],[4]]]
def tree(label, children=[]):
return [label] + children
def label(tree):
return tree[0]
def children(tree):
return tree[1:]
def is_leaf(tree):
return len(children(tree)) == 0
t = tree(20, [tree(12,
[tree(9,
[tree(7), tree(2)]),
tree(3)]),
tree(8,
[tree(4), tree(4)])])
A number-list tuple for each tree/subtree:
(20,[(12,[(9,[(7,[]),(2, [])]),(3, [])]),(8,[(4,[]),(4,[])])])
def tree(label, children=[]):
return (label, children)
def label(tree):
return tree[0]
def children(tree):
return tree[1]
t = tree(20, [tree(12,
[tree(9,
[tree(7), tree(2)]),
tree(3)]),
tree(8,
[tree(4), tree(4)])])
A dictionary for each tree/subtree:
{'l':20,'c':[{'l':12,'c':[{'l':9,'c':[{'l':7,'c': []},{'l':2,'c':[]}]},{'l':3,'c':[]}]},{'l':8,'c':[{'l':4,'c':[]},{'l':4,'c':[]}]}]}
def tree(label, children=[]):
return {"l": label, "c": children}
def label(tree):
return tree["l"]
def children(tree):
return tree["c"]
t = tree(20, [tree(12,
[tree(9,
[tree(7), tree(2)]),
tree(3)]),
tree(8,
[tree(4), tree(4)])])
A tree is a recursive structure.
Each tree has:
Recursive structure implies recursive algorithm!
def count_leaves(t):
"""Returns the number of leaf nodes in T."""
if is_leaf(t):
return 1
else:
children_leaves = 0
for c in children(t):
children_leaves += count_leaves(c)
return children_leaves
What's the base case? What's the recursive call?
The sum()
function sums up the items of an iterable.
>>> sum([1, 1, 1, 1])
4
That leads to this shorter function:
def count_leaves(t):
"""Returns the number of leaf nodes in T."""
if is_leaf(t):
return 1
else:
return sum([count_leaves(c) for c in children(t)])
A function that creates a tree from another tree is also often recursive.
def double(t):
"""Returns a tree identical to T, but with all labels doubled."""
if is_leaf(t):
return tree(label(t) * 2)
else:
return tree(label(t) * 2,
[double(c) for c in children(t)])
What's the base case? What's the recursive call?
Longer...
def double(t):
"""Returns a tree identical to T, but with all labels doubled."""
if is_leaf(t):
return tree(label(t) * 2)
else:
doubled_children = []
for c in children(t):
doubled_children.append(double(c))
return tree(label(t) * 2, doubled_children)
Shorter!
def double(t):
"""Returns the number of leaf nodes in T."""
return tree(label(t) * 2,
[double(c) for c in children(t)])
Try this on your own:
def list_of_leaves(t):
"""Return a list containing the leaf labels of T.
>>> leaves(t) # Using the t from the slides
[7, 2, 3, 4, 4]
"""
if ______:
return ______
else:
______
return ______
Hint: If you sum a list of lists, you get a list containing the elements of those lists. The sum function takes a second argument, the starting value of the sum.
Primitive Representation | 1 2 3 True False (..,..) [..,..] {...} |
Data abstraction | tree() children() label() |
is_leaf() |
|
User program | double(t) count_leaves(t) |
Each layer only uses the layer above it.
For natural languages...
Key: S = Sentence, NP = Noun phrase, D = Determiner, N = Noun, V = Verb, VP = Verb Phrase
For programming languages, too...
Key: E = expression