Functional Problem Solving (CSC 151 2016S) : EBoards
Primary: [Front Door] [Schedule] - [Academic Honesty] [Disabilities] [Email] - [FAQ] [Teaching & Learning] [Grading] [Taking Notes] [Rubric]
Current: [Assignment] [EBoard] [Lab] [Outline] [Reading]
Sections: [Assignments] [EBoards] [Labs] [Outlines] [Readings] - [Examples] [Handouts]
Reference: [Setup] [Remote] [VM] [Errors] - [Functions A-Z] [Functions By Topic] - [Racket] [Scheme Report (R5RS)] [R6RS] [TSPL4]
Related Courses: [Curtsinger (2016S)] [Davis (2013F)] [Rebelsky (2015F)] [Weinman (2014F)]
Misc: [SamR] [Glimmer Labs] [CS@Grinnell] [Grinnell] - [Issue Tracker (Course)]
Thank you to the students who showed up so that we'll have notes that that other students can read over later.
Finding This Page
Overview
Topics
Types of questions
Reminder: Key tree operations
empty - empty tree (equiv to null)(empty? val) - determine if a value represents the empty tree(node val left right) - builds a new tree (or joins two trees
with a new root value)(leaf val) - a special case of node in which the subtrees are empty(contents tree) - gets the root value (similar to car)(left tree) - gets the left subtree (similar to cdr)(right tree) - gets the right subtree (similar to cdr)Trees are just vectors, right?
Yes. But we want to think about them in terms of the operations we can use, rather than the underlying representation.
What is a tree?
A tree is like a list, except two-dimensional rather than one dimensional.
Instead of one successor, each value can have two successors.
Useful for organizing some kinds of information.
Pairs are used more for lists.
With lists, we always need null on the end. Does something similar hold for trees (i.e., that there's an empty at the end)?
Yes. We assume that we'll always end a subtree with empty. A leaf has two empty subtrees.
Draw the tree for the following expression. You need not draw the empty nodes.
(node 1 (node 2 (leaf 3) empty) (node 4 empty (leaf 5)))
Write an expression that would generate the following tree
4
/ \
/ \
2 5
/ \ /
/ \ /
1 3 6
(node 4 (node 2 (leaf 1) (leaf 3)) (node 5 (leaf 5) empty))
We might use a tree to represent reporting relationships.
a. Sketch a tree that shows these reporting relationships. ("reports to" can be read as "falls immediately below in the tree")
b. Write an expression to generate that tree.
leafSuppose the leaf procedure were not defined. Define it.
(define leaf (lambda (val) _____))
(define leaf (lambda (val) (node val empty empty))
tree-smallestImplement a procedure, tree-smallest, that finds the smallest value
in a non-empty tree.
(define tree-smallest
(lambda (tree)
(cond
; Leaf!
[(and (empty? (left tree)) (empty? (right tree)))
_________________]
; No left subtree
[(empty? (left tree))
(________ (contents tree)
(tree-smallest (right tree)))]
; No right subtree
[(empty? (right tree))
(________ (contents tree)
(tree-smallest _________))]
; Both left and right subtrees
[else
])))
(define tree-smallest
(lambda (tree)
(cond
; Leaf!
[(and (empty? (left tree)) (empty? (right tree)))
(contents tree)]
; No left subtree
[(empty? (left tree))
(min (contents tree)
(tree-smallest (right tree)))]
; No right subtree
[(empty? (right tree))
(min (contents tree)
(tree-smallest (left tree)))]
; Both left and right subtrees
[else
(min (tree-smallest (left tree))
(tree-smallest (right tree))
(contents tree))])))
What does the following procedure do?
(define tree-something
(lambda (tree)
(if (empty? tree)
0
(+ 1 (max (tree-something (left tree))
(tree-something (right tree)))))))
Consider the following procedure.
(define list-something
(lambda (lst)
(if (null? lst)
empty
(node (car lst)
(list-something (cdr lst))
(list-something (cdr lst))))))
Sketch the output of (list-something (list 1 2 3)).
(list-something '(1 2 3))
Not null
1
/ \
/ \
(list-something '(2 3)) (list-something '(2 3))
We expand the left call.
1
/ \
/ \
2 (list-something '(2 3))
/ \
/ \
(list-something '(3)) (list-something '(3))
We expand the left call
1
/ \
/ \
2 (list-something '(2 3))
/ \
/ \
3 (list-something '(3))
/ \
/ \
(list-something '()) (list-something '())
And again
1
/ \
/ \
2 (list-something '(2 3))
/ \
/ \
3 (list-something '(3))
/ \
/ \
(list-something '())
And again 1 / \ / \ 2 (list-something '(2 3)) / \ / \ 3 3 / \ / \ / \ / \
What are the new things we need to know as we move from list recurstion to tree recursion?
For trees, we almost always do direct recursion. That is, recurse on the left subtree; recurse on the right subtree; grab the contents; combine.
You have to be comfortable with the "trust that the recursive call works" model.
What does tree-sum look like?
(define tree-sum
(lambda (tree)
; Base case
(if (empty? tree)
0
; Recursive case
(+ (contents tree)
(tree-sum (left tree))
(tree-sum (right tree))))))
That's really similar to the original list sum, isn't it?
(define list-sum
(lambda (lst)
; Base case
(if (null? lst)
0
; Recursive case
(+ (car lst)
(list-sum (cdr lst))))))
Is it always this simple?
No. Think about tree-smallest vs list-smallest
In list smallest, there are two cases: (a) One element list or (b) more than one element list.
(define list-smallest
(lambda (lst)
(if (null? (cdr lst))
(car lst)
(min (car lst) (list-smallest (cdr lst))))))
In tree smallest, there are four cases: (a) one element / leaf; (b) both subtrees exist; (c) no left subtree; (d) no right subtree
Each of these cases is similar, but there are more of them.