Session 15
Recursive Programming Review
Where We Are
For the last few weeks, we have been learning techniques for writing recursive programs based on inductively-defined data. These include one base technique and three ideas to use when the basic technique needs a little help:
- Our basic technique is structural recursion, which asks us to mimic the structure of the data we are processing in our function.
- We use an interface procedure when our structurally-recursive function requires an extra argument in order to do its job.
- We use mutual recursion when our inductive data definition includes two or more data structures defined in terms of one another.
- We sometimes use program derivation to fold the combine the two or more functions produced by mutual recursion into a single function that is more efficient and nearly as easy to read.
We then began using structural recursion to write recursive
functions to answer questions about programs in
a little language
consisting only of variable references, one-argument functions,
and applications (function calls).
(occurs-bound? var exp)
was the first of several functions that process expressions in the
little language.
At the same time, we began using syntax procedures defined for the little language, which allowed us to focus on the meaning of our data rather than their implementation in Racket. Syntax procedures give our little language what we have come to expect from Racket's built-in data types: accessors, type predicates, and constructors.
Today, let's review many of these ideas as you begin to prepare for the quiz. We'll go over problems from Homework 6 and do a few practice problems from a previous Quiz 2.
Review of Homework 6
-
Problem 1,
tails
- "Think carefully about what the function should return if it receives the empty list as an argument. This is also the base case for the recursion."
-
Problem 2,
n-list?
-
This function is a type predicate for n-lists. To implement
similar functions for flat lists, we could use
map
andapply
. (In this case, we would useandmap
because we can't applyand
booleans). Mutual recursion makes this function almost as easy to implement over the nested lists. -
Problem 3,
tree-min
-
Trees cannot be empty: A tree is either a number or a list
of size three, containing a number and two trees. This
function does not need a
null?
case, and it does not need to recur on thecdr
of a list. -
Problem 4,
declared-vars
-
To solve this problem, you first need to understand that only
lambda
expressions declare variables. With that knowledge, the BNF description guides us toward a solution. -
Problem 5,
prefix->postfix
- The solution is straightforward. Only apps call functions, so our function needs to change only app expressions — and any expressions that contain apps. Follow the data structure... and use the syntax procs.
Two general notes:
- Some of you are finding ways to solve mutually inductive problems without following the BNF description. (Some are not.) But most who do this pay a price: spending too much time and writing too much code, some of which is duplicated. The idea behind following the pattern is to not reinvent the wheel for every new problem. Software engineers strive to build things in a predictable way and a predictable amount of time. "Going fast" is not a goal, but not going unnecessarily slow is.
- Using syntax procedures is simply a matter of doing for our own data types what we do for Racket's built-in types. The idea of hiding the implementation of a data type and thinking in terms of its abstractions is not new to this course. One of the most important ideas from your Data Structures course is that we can — and should — for the same. Think of the push, pop, and top operations for a stack.
Practice Problems
Many students have been asking for more ways to practice writing recursive functions, or to see examples of Quiz 2 problems. Today, let's try to satisfy both kinds of request by working these practice problems drawn from previous Quiz 2s:
(tree-count test? tree)
(annotate prefix-exp)
(slist-contains? s slst)
(contains-varref? v exp)
These functions all refer to data types you have worked with multiple times before. If you follow the structure of the data closely, each is relatively short. The problems on your quiz will have both of these features, too.
Note: we skipped (annotate prefix-exp)
in class.
The solution is the code file.
Other Ideas to Review
What other kinds of questions might appear on the quiz? We have studied a number of ideas and techniques that you will want to understand:
- tail recursion (in Session 11): what is it, and what does it mean?
- free and bound variables (in Sessions 12 and 13): how are variables declared, and what does it man to say that a variable reference is bound or free?
You will also want to be able to discuss the ideas and techniques linked in Where We Are above, and why we use them.
Wrap Up
-
Reading
- Review the notes for Sessions 8-15.
- Today's notes include two mini-study guides:
-
Homework
- Homework 6 was due yesterday.
-
Quiz 2
- Quiz 2 is next session. We will also get a sneak preview of the next unit of the course, on syntactic abstraction.