In Python, using a class:
class Tree:
def __init__(self, label, branches=[]):
self.label = label
self.branches = list(branches)
def is_leaf(self):
return not self.branches
In Scheme, using procedures to build a data abstraction:
(define (tree label branches)
(cons label branches))
(define (label t) (car t))
(define (branches t) (cdr t))
(define (is-leaf t) (null? (branches t)))
A tree is a recursive structure, where each branch may itself be a tree.
[5, [6, 7], 8, [[9], 10]]
(+ 5 (- 6 7) 8 (* (- 9) 10))
(S
(NP (JJ Short) (NNS cuts))
(VP (VBP make)
(NP (JJ long) (NNS delays)))
(. .))
<ul>
<li>Midterm <strong>1</strong></li>
<li>Midterm <strong>2</strong></li>
</ul>
Tree processing often involves recursive calls on subtrees.
Implement bigs
, which takes a Tree
instance t
containing integer labels. It returns the number of nodes in t
whose labels are larger than all labels of their ancestor nodes.
def bigs(t):
"""Return the number of nodes in t that are larger than all their ancestors.
>>> a = Tree(1, [Tree(4, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(2)])])])
>>> bigs(a)
4
"""
def bigs(t):
"""Return the number of nodes in t that are larger than all their ancestors.
>>> a = Tree(1, [Tree(4, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(2)])])])
>>> bigs(a)
4
"""
Typical tree processing structure?
if t.is_leaf():
return ___
else:
return ___([___ for b in t.branches])
❌ That won't work, since we need to know about ancestors.
def bigs(t):
"""Return the number of nodes in t that are larger than all their ancestors.
>>> a = Tree(1, [Tree(4, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(2)])])])
>>> bigs(a)
4
"""
Some code that increments the total count
1 + _____
Some way of tracking ancestor labels or max of ancestors seen so far.
if node.label > max(ancestors):
if node.label > max_ancestor:
def bigs(t):
"""Return the number of nodes in t that are larger than all their ancestors.
>>> a = Tree(1, [Tree(4, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(2)])])])
>>> bigs(a)
4
"""
# a is the current subtree, x is the largest ancestor
def f(a, x):
if ______________________: # Track the largest ancestor
return 1 + __________ # Increment total
else:
return ______________
return ______________________
def bigs(t):
"""Return the number of nodes in t that are larger than all their ancestors.
>>> a = Tree(1, [Tree(4, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(2)])])])
>>> bigs(a)
4
"""
def f(a, x):
if a.label > x:
return 1 + sum([f(b, a.label) for b in a.branches])
else:
return sum([f(b, x) for b in a.branches])
return f(t, t.label - 1)
def bigs(t):
"""Return the number of nodes in t that are larger than all their ancestors.
>>> a = Tree(1, [Tree(4, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(2)])])])
>>> bigs(a)
4
"""
def f(a, x):
if a.label > x:
return 1 + sum([f(b, a.label) for b in a.branches])
else:
return sum([f(b, x) for b in a.branches])
return f(t, t.label - 1)
Initialize some data structure to an empty/zero value, and populate it as you go.
Implement smalls
, which takes a Tree
instance t
containing integer labels. It returns the non-leaf nodes in t
whose labels are smaller than any labels of their descendant nodes.
def smalls(t):
"""Return the non-leaf nodes in t that are smaller than all their descendants.
>>> a = Tree(1, [Tree(2, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(6)])])])
>>> sorted([t.label for t in smalls(a)])
[0, 2]
"""
def smalls(t):
"""Return the non-leaf nodes in t that are smaller than all their descendants.
>>> a = Tree(1, [Tree(2, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(6)])])])
>>> sorted([t.label for t in smalls(a)])
[0, 2]
"""
Something which finds the smallest value in a subtree
min(___)
Something which compares smallest to current
t.label < smallest
Something which adds a subtree to a list
__.append(t)
def smalls(t):
"""Return the non-leaf nodes in t that are smaller than all their descendants.
>>> a = Tree(1, [Tree(2, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(6)])])])
>>> sorted([t.label for t in smalls(a)])
[0, 2]
"""
result = [] # The result list
def process(t): # t is a Tree
if t.is_leaf():
return ______________________
else:
smallest = __________________ # Finds smallest
if _________________________: # Compares smallest
_________________________ # Appends subtree to list
return min(smallest, t.label)
process(t)
return result
def smalls(t):
"""Return the non-leaf nodes in t that are smaller than all their descendants.
>>> a = Tree(1, [Tree(2, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(6)])])])
>>> sorted([t.label for t in smalls(a)])
[0, 2]
"""
result = []
def process(t):
if t.is_leaf():
return t.label
else:
smallest = min([process(b) for b in t.branches])
if t.label < smallest:
result.append(t)
return min(smallest, t.label)
process(t)
return result
def smalls(t):
"""Return the non-leaf nodes in t that are smaller than all their descendants.
>>> a = Tree(1, [Tree(2, [Tree(4), Tree(5)]), Tree(3, [Tree(0, [Tree(6)])])])
>>> sorted([t.label for t in smalls(a)])
[0, 2]
"""
result = []
def process(t):
if t.is_leaf():
return t.label
else:
smallest = min([process(b) for b in t.branches])
if t.label < smallest:
result.append(t)
return min(smallest, t.label)
process(t)
return result
Which strings are matched by each regular expression?
Expressions: | abc | cab | bac | baba | ababca | aabcc | abbaba |
---|---|---|---|---|---|---|---|
[abc]*
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
a*b*c*
| ✅ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
ab|[bc]*
| ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
(a[bc]+)+a?
| ✅ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ |
(ab|ba)+(ab|[bc])?
| ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ |
What expressions are passed to scheme_eval
when evaluating the following expressions?
(define x (+ 1 2))
(define (f y) (+ x y))
(f (if (> 3 2) 4 5))