Upload
berniece-merritt
View
214
Download
1
Embed Size (px)
Citation preview
cs1321cs1321Fall, AY2002
11/01/01 (that’s 3516)
Lecture 20
Agenda
1. Tail Recursion
2. Graphs
Tail or Accumulator-Style Functions
Outline• Prerequisites
– Recursion
– Structures
– Graphs
• Objectives
– Need for Accumulation
– Accumulator Style Functions
– Transforming Recursive Functions
• Reference
– HTDP Chapters 31, 32
Background• Pure (augmenting or “head”) recursion follows
the functional paradigm – The recursion you have seen so far– Does not permit memory– Forgets useful intermediate results– Unnecessarily repeats large computations
• We now consider ways to improve the efficiency of recursion– Passing extra parameters to remember useful
details– Basically collecting partial results as we go!!
• Frequently referred to as “Tail Recursion”
A Different View
OK so we noted the cost of recursion can be large, if it’s not designed well.
(define (factorial n) (if (zero? n) 1 (* n (factorial (- n 1)))))
Let’s trace this for 4!Let’s trace this for 4!
fact (4) 4 *
fact (3)
4 *
3 *
fact (2)
4 *
3 *
2 *
fact (1)
4 *
3 *
2 *
1 *
fact (0)
4 *
3 *
2 *
1 *
1
Example(define (factorial n) (if (zero? n) 1 (* n (factorial (- n 1)))))
Let’s trace this for 4!Let’s trace this for 4!
fact (4) 4 *
fact (3)
4 *
3 *
fact (2)
4 *
3 *
2 *
fact (1)
4 *
3 *
2 *
1 *
fact (0)
4 *
3 *
2 *
1 *
1
ExampleEach recursive call creates a new activation frame.
This implied that recursive solutions could potentially take large amounts of memory.
Augmentative RecursionAnalysis: The problem was that in order to find what’s returned from one function call, we had to make a recursive call.
Substitution Model of Evaluation
(factorial 3)(* 3 (factorial 2))(* 3 (* 2 (factorial 1)))(* 3 (* 2 (* 1 (factorial 0))))(* 3 (* 2 (* 1 1)))(* 3 (* 2 1))(* 3 2)6
Each argument is ‘evaluated’ and substituted.
We’ll see later how to halt this evaluation, whenthis becomes useful.
Substitution Model of Evaluation
(factorial 3)(* 3 (factorial 2))(* 3 (* 2 (factorial 1)))(* 3 (* 2 (* 1 (factorial 0))))(* 3 (* 2 (* 1 1)))(* 3 (* 2 1))(* 3 2)6
Each argument is ‘evaluated’ and substituted.
We’ll see later how to halt this evaluation, whenthis becomes useful.
This required us to save frames for each recursive call.
Tail RecursionIf we can “solve” the return result from each level, the computer will not have to save each activation frame.
This approach is know as tail recursion--it’s a technique that allows the computer to optimize recursion so that it takes less memory.
Instead of postponingpostponing multiplication:
3 * fact(2)
… we can instead add extra parametersadd extra parameters so that calculations can be done entirely inside the frame.
Requires recursive call
Tail RecursionWe start with the usual function, but call a helper function:
(define (fact n)(fact-helper 1 n))
(define (fact-helper product n)(cond ((zero? n) product)
(else(fact-helper (* product n)
(- n 1)))))
This is similar to our previous solution, but look at the calculations--each term can be know inside the frame.
Tail Recursion
(define (fact n)(fact-helper 1 n))
(define (fact-helper product n) (cond [ (zero? n) product ] [ else (fact-helper (* product n) (- n 1))]))
We now have all the information needed to calculate the result inside the frame, without saving each recurse.
(* n (factorial (- n 1)) (fact-helper (* product n)(- n 1))
Augmentative/HeadAugmentative/Head Tail RecursiveTail Recursive
All-in-oneAll-in-oneRequires recursive callRequires recursive call
Tail RecursionWe can see the memory use of this approach by tracing
(fact 3)
(fact-helper 1 3)(fact-helper 3 2)(fact-helper 6 1)(fact-helper 6 0)
==>6
Each frame completed, andis not necessary to save.
Tail Recursion
(fact 3)
(fact-helper 1 3)(fact-helper 3 2)(fact-helper 6 1)(fact-helper 6 0)
==>6 (fact 3)
(fact-helper 1 3)
(fact-helper 3 2)
(fact-helper 6 1)
(fact-helper 6 0)
Potentially, the activation stack could grow just as we saw with augmentative recursion. But the Scheme interpreter (and many compilers) can optimize tail recursive expressions to constant memory use. (In fact, Scheme interpreters are required to optimize tail recursion.)
Tail RecursionSo tail recursion is nothing very different from what you’ve seen before, except that:
It’s written with an awareness of memory use
Each calculation is independent of subsequent recursive calls
Extra parameters (usually) are used to eliminate dependence on calculations made in subsequent recursive calls.In plain English, they hold partial results!!
33
22
11
HintsCan’t write it tail recursively? Just try this:
Write the function recursively.
Identify which calculations cannot be madeentirely within a single frame
Add sufficient extra parameters to eliminate the need to save information in a stack
11
22
33
ExampleRecently, we considered a function that would multiply numbers by adding repeatedly.
Reworked, we get:
(define mult a b)(mult-iterative 0 a b))
(define (mult-iterative result num1 num2)(if (= num2 0)
result(mult-iterative (+ result num1)
num1(- num2 1))))
Example(define mult a b)
(mult-iterative 0 a b))
(define (mult-iterative result num1 num2)(if (= num2 0)
result(mult-iterative (+ result num1)
num1(- num2 1))))
(mult 3 4)(mult-it 0 3 4)(mult-it 3 3 3)(mult-it 6 3 2)(mult-it 9 3 1)(mult-it 12 3 0)
A trace confirms the reduceduse of memory. None of the ‘mult-it’ calls need to be kept on the activation stack; they do not depend on each other.
Notes On Terminology
The following questions are presented to suggest further reading:
What’s a recursive process? What’s a recursive procedure? What’s the difference?
See http://mitpress.mit.edu/sicp/full-text/book-Z-H-11.html
Footnote
• To solve problems using tail recursion, we normally develop at least two different functions.
• In the integration example, these were (integrate lst) and (integrate-2 lst accum)
• Leaving these “lying around” separate is a little tacky.
• We use the idea of (local …) functions to clean this up.
Recall (local …)
• The syntax for a function using (local …) is as follows:(define (use-local …)
(local ((<definition-1>)
(<definition-2>)
…
(<definition-n>))
<expression> ))
• Where the definition list is encapsulated, and when the function is invoked by evaluating
(use-local … ) it actually evaluates the body <expression> which then uses the encapsulated definitions.
For example:
(define (integrate lst) (local ( (define (integrate-2 lst accum) (cond [(empty? lst) empty] [else (cons (+ (first lst) accum) (integrate-2 (rest lst) (+ accum (first lst))))]))) (integrate-2 lst 0)))
(define (integrate-2 lst accum) (cond [(empty? lst) empty] [else (cons (+ (first lst) accum) (integrate-2 (rest lst) (+ accum (first lst))))]))(define (integrate lst) (integrate-2 lst 0)) Unchanged (integrate-2 …)
“absorbed” into its wrapper, (integrate …), and called by it
Generalized template
• Deciding that a function will benefit from an accumulator requires experience borne of much practice.
• When you decide this is probably the case, (local … ) provides an excellent template.(define (<old-fn> <params>) (local ( (define (<aux-fn> <params> <accum>) (cond [(<end?> <params>) … ] [else … <params> … <accum> … (<aux-fn> <params> … <accum> …))]))) (<aux-fn> <params> <initial-acc>)))
Dissecting this Template
(define (<old-fn> <params>) (local ( (define (<aux-fn> <params> <accum>) (cond [(<end?> <params>) … ] [else … <params> … <accum> … (<aux-fn> <params> … <accum> …))]))) (<aux-fn> <params> <initial-acc>)))
What you really want What you need to do the job efficiently
Call the new function with initial accumulator value
Use parameters and accumulator as required
Update accumulator value in the recursive call
Commentary on the Design Process
1. First, draw a picture of the problem
2. Write a normal recursive solution or focus on what your partial result at each stage will be
3. If you notice opportunities to avoid loss of useful information,a) Start with the (local …) version of the template
b) Visualize how the accumulator computation will work
c) Figure out how to initialize the accumulation
d) Figure out how this affects the termination condition
e) Figure out how to update the accumulator for the recursive call
ExampleWrite a function to add all numbers from 0 to N.
Write this using augmentative recursion and tail recursion.
tail.scm
Questions?
Summary
• You should now know…
– Need for Accumulation
– Accumulator Style Functions
– Transforming Recursive Functions
Intro to Graphs
Outline
• Prerequisites
– List manipulation
• Objectives
– Basic Graph Terminology
– Depth-First Search
• Reference
– HTDP Section 28.1
Graphs
• Have nothing to do with plotting data (i.e. a graph of the function y = sin(x).
• Are widely used to solve a large number of totally unrelated problems in CS, Engineering, Math, Business, etc.
• Are studied in Graph Theory• So what do we mean by a graph?
Graph Terminology
What is a graph?
We can think of a graph as a combination of two things: a setset of points (verticesvertices) possibly in a plane and a set of line segments (edgesedges).
Sometimes, a graph is formally defined as:
G = (V, E)
where ‘G’, ‘V’ and ‘E’ represent Graph, Vertices (or points) and Edges (the line segments).
Graph TerminologyA graph may be directeddirected, meaning that the line segments join points only in one direction.
A B
C
DE
F
Can’t travel from E to D directly, only from D to Eon this edge.
Graph TerminologyA graph also might be undirectedundirected, meaning that line segments join points from either direction.
A B
C
DE
F
An undirected or bidirectional graph
Graph TerminologyA graph also might be undirected, meaning that line segments join points from either direction.
A B
C
DE
F
When you see edges with no arrows, you may presume the graph is undirected, or bidirectional
Graph TerminologyWhen an edge connects two vertices or nodes, we say that they are ‘adjacent’adjacent’.
A B
C
DE
F
E is adjacent to F, A, B, and D, but not C. Sure, you can still get to C from E, but not directly.
Graph TerminologyUnlike trees, which are special types of graphs, general graphs may have cyclescycles, meaning that children can be the parents of their ‘ancestor nodes’.
A B
C
DE
F
Put another way, it’s possible to walk in circles through a graph, if you don’t keep track of where you’ve been.
A
B
C
E
Graph TerminologyA collection of individual points (or edges) in a graph may represent a path.
A B
C
DE
F
A path is just a way of getting from a start node to another node.
Graph TerminologyIn the wacky world of graphs, a node may be connected even to itself.
A B
C
DE
F
It seems odd, but since a graph is made of edges and vertices, an edge just might connect a node to itself.
Note
Graph TerminologyWe can also assign ‘weights’ or values to edges.
A B
C
DE
F
In such a case, we have a weighted graphweighted graph. The weights can represent cost, distance, opportunity costs--anything.
5
7
12
25
12
8
9
3
Graph TerminologyIn the beginning, we will mainly concern ourselves with unweighted graphs.
A B
C
DE
F
We’ll just be interested in establishing if there’s a connection or relationship between nodes. We can later add weights to provide a richer graph data set.
Quick QuizSo far, we’ve worked with trees. Are trees really just graphs? Consider the definition of a graph...
Graph TerminologyGraph Terminology
What is a graph?
We can think of a graph as a combination of two things:a set of points in a plane and a set of line segments.
Sometimes, a graph is formally defined as:
G = (V, E)
where ‘G’, ‘V’ and ‘E’ represent Graph, Vertices (orpoints) and Edges (the line segments).
Graph TerminologyGraph Terminology
What is a graph?
We can think of a graph as a combination of two things:a set of points in a plane and a set of line segments.
Sometimes, a graph is formally defined as:
G = (V, E)
where ‘G’, ‘V’ and ‘E’ represent Graph, Vertices (orpoints) and Edges (the line segments).
Do trees have vertices and edges?
Yup.
Are they graphs?
Of course.
Graph Examples
Map of Distilleries in Scotland.
Is this a graph? (What is a graph?)
Does this provide useful information?
What’s missing?
(Nothing; We’re only missing NSF Funding for research on this graph.)
• Graphs can also track the flows and movements of individuals in society.
• E.g., Krempel’s map of Duisburg zoo visitors. The width of the lines indicates the number of visitors taking an edge.
Graph/Network Uses
(Note: the Autobahn divides the zoo in half. Can you see evidence of this in the graph?)
Summary Graphs (cont’d)
Graphs are also useful for modeling hierarchy networks.
A good example is the internet, with various ‘tiers’ of providers that link portions of the net together.
A graph can be used to model the network relationship between computers.
More Examples
Visit: www.theyrule.net
Representing Graphs
A B
C
DE
F
Let’s take a simple graph, and try to draw a Scheme-like list that represents its data.
Representing Graphs
A B
C
DE
F
(define graph'((A (B E F)) (B (A C D E)) (C (B D)) (D (B C E)) (E (A B D F)) (F (A E))))
A
B E
D E F
F
C A A E
Caution: Think ofthis is a flowchartand not a graph.
Uses for GraphsWhat practical use is such a data structure?
(define tech-graph'((Skiles (S-C Library DMS)) (S-C (Skiles CoC Rich Library )) (CoC (S-C Rich )) (Rich (S-C CoC Library )) (Library (Skiles S-C Rich DMS)) (DMS (Skiles Library ))))
A B
C
DE
F
Skiles
DMSmith
Library Rich
StudentCenter
CoCWe could use it to modelmap relationships, find efficient paths, etc.
More on this later...
Searching GraphsWhat good is a graph if you can’t search it?
Our tree traversals proved useful, so can we ‘walk’ a graph using similar techniques?
Problem:Problem: What if a node has more than one child?
Our previous algorithms just considered three items: node, left and right.
Problem:Problem: What if we cycle? A B
C
DE
FLet’s learn more about searching first.
Searching GraphsWe need to keep these problems in mind...
Searching GraphsSearching GraphsWhat good is a graph if you can’t search it?
Our tree traversals proved useful, so can we ‘walk’ agraph using similar techniques?
Problem:Problem: What if a node has more than one child?
Our previous algorithms just considered threeitems: node, left and right.
Problem:Problem: What if we cycle? A B
C
DE
FLet’s learn more about searching first.
Searching GraphsSearching GraphsWhat good is a graph if you can’t search it?
Our tree traversals proved useful, so can we ‘walk’ agraph using similar techniques?
Problem:Problem: What if a node has more than one child?
Our previous algorithms just considered threeitems: node, left and right.
Problem:Problem: What if we cycle? A B
C
DE
FLet’s learn more about searching first.
For now, however, let’s consider searches that are simple and don’t have this problem.
Hierarchical search
We can also arrange knowledge into hierarchies. Perhaps the most familiar is the zoological hierarchy of kingdoms, phylum, genus, etc.
Lepidoptera
Moth Butterfly
MourningCloak
Monarch
Hierarchical search
Hierarchies allow us to make statements about items in the collection. For example, we can say that a monarch “is a” butterfly, and a butterfly “is a” lepidoptera.
Lepidoptera
Moth Butterfly
MourningCloak
Monarch
Hierarchical search
Since most people are unfamiliar with insects families, we’ll instead switch to a more familiar hierarchy: the Flintstones.
Chip Roxy
PebblesBamm-Bamm
Wilma Fred
Barney Betty
Hierarchical search
We’ll also give attributes to this hierarchy:
Chip Roxy
PebblesBamm-Bamm
Wilma Fred Barney Betty
Has dad
Has dadHas dad
Has mom
Has mom
Has mom
Hierarchical searchThis allows us to do important research, like asking “who is Chip’s mother?” Or “who is Chip’s grandfather on his mother’s side?”
Chip Roxy
PebblesBamm-Bamm
Wilma Fred BarneyBetty
Has dad
Has dad
Has dadHas mom
Has mom
Has mom
Depth First SearchThe simplest form of search in a hierarchical or network structure is called "depth-first search". We can write an algorithm for a depth-first search on a binary tree:
DFS (depth first search):
1. Look at the root 2. If it's what you're looking for, then return success 3. If the root has no descendants, then return failure 4. Call df-search on the subtree whose root is the leftmost descendant and return success if that search is successful 5. Call df-search on the subtree whose root is the rightmost descendant and return success if that search is successful
Depth First SearchIf this seems familiar, it’s because DFS is a variation of one tree search we saw recently:
preorder
1.visit the root 2.call preorder on the left subtree 3.call preorder on the right subtree
Comparison
preorder
1.visit the root 2.call preorder on the left subtree 3.call preorder on the right subtree
DFS (depth first search):
1. Look at the root 2. If it's what you're looking for, then return success 3. If the root has no descendants, then return failure 4. Call df-search on the subtree whose root is the leftmost descendant and return success if that search is successful 5. Call df-search on the subtree whose root is the rightmost descendant and return success if that search is successful
Comparison
preorder
1.visit the root 2.call preorder on the left subtree 3.call preorder on the right subtree
DFS (depth first search):
1. Look at the root 2. If it's what you're looking for, then return success 3. If the root has no descendants, then return failure 4. Call df-search on the subtree whose root is the leftmost descendant and return success if that search is successful 5. Call df-search on the subtree whose root is the rightmost descendant and return success if that search is successful
Comparison
preorder
1.visit the root 2.call preorder on the left subtree 3.call preorder on the right subtree
DFS (depth first search):
1. Look at the root 2. If it's what you're looking for, then return success 3. If the root has no descendants, then return failure 4. Call df-search on the subtree whose root is the leftmost descendant and return success if that search is successful 5. Call df-search on the subtree whose root is the rightmost descendant and return success if that search is successful
What’s different?What’s different?
DFS - Preorder Differences
The big differences between the preorder algorithm and the depth-first search algorithm are these:
depth-first search stops before searching the whole tree, if it finds what it's looking for; preorder traversal always examines the entire tree
with depth-first search, searching the right subtree occurs only if the search of the left subtree failed to find what was being looked for; with preorder traversal, the right subtree is always explored (this is sort of a corollary to the first difference listed just above)
11
22
Implementing Depth-First Search
Let’s first note how we might represent the trees in Scheme:
(define-struct node ( name mother father ) )
(define harriet (make-node 'harriet false false))(define ozzie (make-node 'ozzie false false))(define betty (make-node 'betty false false))(define barney (make-node 'barney false false))(define fred (make-node 'fred false false))(define wilma (make-node 'wilma false false)) … Chip Roxy
PebblesBamm-Bamm
Wilma Fred Barney Betty
Implementing Depth-First Search
(define (dfs target here) (cond [(not here) false] [(symbol=? target (node-name here)) true] [else (or (dfs target (node-father here)) (dfs targer (node-mother here)))]))
We start with a function called dfs:
Improving DFSNow, just telling us true or false is not entirely useful. Weactually want to use a hierarchical search to produce a list that shows the relationship between items.
We’ll use tail recursion . . .
Improving DFS(define (dfs-list who here) (local ( (define (dfs-aux who here answer) (cond [(not here) empty] [(symbol=? who (node-name here)) (cons (node-name here) answer)] [else (local ((define mother-list (dfs-aux who (node-mother here) (cons (node-name here) answer)))) (if (empty? mother-list) (dfs-aux who (node-father here) (cons (node-name here) answer)) mother-list))]))) (dfs-aux who here empty)))
dfs.scm
Questions?
Summary
• You Should Now Know
– Basic Graph Terminology
– Depth-First Search