Programming Languages and Paradigms

Suppose that I have a list of course enrollment numbers and need to know if any of the sections is too big for our classrooms.

Write a function ** (any-bigger-than? n lon)**,
where

> (any-bigger-than? 1 '(1 2 3)) #t > (any-bigger-than? 40 '(26 37 41 25 12)) #t > (any-bigger-than? 40 '(26 37 14 25 12)) #f

How would you approach this task in a loopy language? You
might compare `n` to each element in `lon`. As
soon as you find a larger list item you can return true. How
do you know there are *no* larger members in `lon`?
Only by examining every item in `lon` and never finding
one.

for i in lon: if i > n: return true return false

This is the same thinking you need to write a recursive function in Racket. If your curiosity gets the best of you, peek ahead to a solution...

Don't feel bad if this problem seems like a big challenge at this point. Most things are difficult when we lack the knowledge we need to solve them. Sometimes, we have the knowledge but don't have a clear plan for which knowledge to use when, or why.

Over the next few sessions, we will learn some techniques that help us think about problems and recursive solutions in a new way. These techniques will be quite useful when we move on to processing languages.

the Philippine Islands [note] |

**Recursion** is a technique for writing programs. Even
when we think we know a word, checking out a dictionary
definition can help us to understand it better. You can check
out the definition of
recursion
at
Merriam-Webster Online.
Here is a similar definition, from another version of
Merriam-Webster's dictionary:

recursion n. (1616)

- the act of returning
- the determination of a succession of elements (such as numbers or functions) by operation on one or more preceding elements according to a rule or formula involving a finite number of steps
- a computer programming technique involving the use of ... a function or algorithm that calls itself ...

To *recurse* is to **return** to the same place. A
function can do that.

In computer science, a **recursive program** is one that:

- immediately returns an answer for one or more simple problems, and
- computes answers for more complex problems in terms of the answers to simpler problems, for which it calls itself.

As a result of the second part of this definition, we can see that a recursive program is defined, in part, in terms of itself. In practice, we create a function that calls itself.

We sometimes use recursive relationships to understand mathematical properties. For example, we can examine a sequence of values containing a number raised to a power and see a pattern:

power(x, 0) = 1 power(x, 1) = 1 * x = x * power(x, 0) power(x, 2) = 1 * x * x = x * power(x, 1) power(x, 3) = 1 * x * x * x = x * power(x, 2) ...

We can turn this into something more usable by turning the power itself into a variable and using an inductive definition to make the pattern explicit:

power(x, 0) = 1 power(x, n) = x * power(x, n-1)

In fact, there are programming languages -- such as Prolog and Haskell -- in which you write the recursive equations just like that!

In Racket, we would write:

(define power ; behaves like the built-in function expt (lambda (x n) (if (zero? n) 1 (* x (power n (sub1 n))))))

The fundamental idea behind recursion is this: If a problem can
be defined in terms of a similar, yet *simpler* problem,
recursion may be a useful tool for expressing a solution.

*"O, thou hast damnable iteration and
are indeed able to corrupt a saint."
-- Falstaff*,
in Shakespeare's

Most programmers learn to write loops first and then see recursion as a more difficult way to do things they can already do. But I think you'll find iteration and recursion are really pretty similar. More importantly, you think about the same things no matter which one you are using.

Consider the task described in the song "99 Bottles of Beer on the Wall".

- If n = 0, there is nothing left to do but sing the last verse.
- If n > 0, there is a bottle of beer to take down and pass around, plus n-1 more bottles of beer on the wall.

How might we solve this task with a loop?

n = 99; while (n > 0) { take_one_down(); pass_it_around(); n--; } sing_last_verse();

Notice that you get to solve "n-1 bottles" with the *same*
code that solved "n bottles". That's exactly what we do when we
write a recursive solution:

bottles_of_beer(99)where

bottles_of_beer(0) = sing_last_verse() bottles_of_beer(n) = take_one_down() pass_it_around() bottles_of_beer(n-1)

In both cases, you have to answer the same questions:

- How do you know when to stop?
- What do you do when you stop?
- How do you know to keep going?
- What do you do with each item?
- What gets smaller on each pass?

Iteration and recursion really are the same kind of process.
You may even have noticed that
the dictionary definition
of "recursion" we saw earlier says as much. The third entry
of that definition ends with the phrase **compare
iteration**!

Be not afraid. If you can write a loop, then you, too, can write recursive functions.

(See this footnote for the source of this example and for a little unexpected goodness.)

More formally, we will say that every recursive program consists of:

- one or more
*base*cases that terminate computation in a pre-defined answer, and - one or more
*recursive*cases that compute solutions in terms of simpler problems. The "limit" of these smaller problems is one of the base cases.

Each recursive case consists of three steps:

- Split the data into smaller pieces. For example:
- break a list into parts, using
`first`and`rest` - break a positive integer
`n`into parts, such as 1 and`n - 1`

How you split the data into sub-problems depends on**the type of the argument**. We can access the parts of a list with`first`or`rest`, or the parts of a pair with`car`or`cdr`. The structure of the data type matters (more soon!), so the data type matters. - break a list into parts, using
- Solve the pieces.
- compute the value of the function for the
`first`, and compute the value of the function for the`rest`. - compute the value of the function for 1 and, compute
the value of the function for
`n - 1`.

The "big" sub-problem, the`rest`of the list or the number`n-1`, is often topologically similar to the original problem. We can often take advantage of this by solving it with a recursive call. A recursive call is, in effect, a way of assuming that one of the pieces is already solved. - compute the value of the function for the
- Combine the solutions for the parts into a single solution
for the original argument. For example:
- assemble a list from its parts, using
`cons` - assemble an integer from its parts, using
`+` - assemble a boolean from its parts, using
`and`

How you combine the sub-solutions depends on**the type of value you are returning**. A list can be re-assembled with`cons`, though sometimes we need to use other list-producing functions such as`list`or`append`. A number can be assembled using`+`,`max`or some other binary operator. A boolean can be assembled using`and`,`or`, or some other boolean function. - assemble a list from its parts, using

This is usually where the descriptions of recursion end in our
textbooks. "Okay," you might say, "great. But how do I do
**that**?" The goal of the next few weeks is to help you
feel
last session's TL;DR
in your bones: Recursion doesn't have to be scary. Sometimes,
*it's all about the data*.

In our last session, we saw that we can use inductive definitions to specify data types. An inductive definition is one that:

- lists one or more specific members of the type, and
- describes how to construct more complex members from simpler ones.

Inductive specifications have essentially the same structure as recursive programs. For this reason, inductive data specs -- especially ones formalized in a BNF description -- can serve as a powerful guide for writing recursive programs that operate on the data.

In fact, this guidance is so useful that I offer you a
*Little Schemer*-style commandment based on it:

When defining a program to process
an inductively-defined data type,
the structure of the program should follow the structure of the data. |

To see how this works, let's create a function that operates on
a list of numbers, ** list-length**, which returns
the length of the list. You may recall the definition for the
data type called

<list-of-numbers> ::= () | (<number> . <list-of-numbers>)

This BNF definition can serve as a pattern for defining any
program that operates on lists of numbers. A function that
operates on a `<list-of-numbers>` will receive one
of two things as an argument:

- the empty list,
`()`,**or** - a pair whose
`car`is a`<number>`and whose`cdr`is a`<list-of-numbers>`.

According to the data definition, **these are the only
possibilities**! There are no other cases to worry about.

The definition of a `<list-of-numbers>` consists
of a choice. A function that operates on a
`<list-of-numbers>` will have to make the same
choice: is the argument an empty list or a pair? For lists, we
use `null?` to make this choice. This boolean condition
serves as the selector in an `if` or `cond`
expression that defines actions to take for each arm.

We can start writing ** (list-length lon)** with
the pattern for a function that we have used many times before:

(define list-length (lambda (lon) ... ))

Following the rule above, our program's structure should mimic the structure of the BNF specification for the data type. A list of numbers is either an empty list or a pair. So, we start with the code for a choice:

(define list-length (lambda (lon) (if (null? lon) ;; then handle an empty list ;; else handle a pair )))

Now we can write code to handle the two cases in either order. Often, the base case has a simple answer, so we usually write this case first. How should our function act when the list is empty? The length of the empty list is 0, so:

(define list-length (lambda (lon) (if (null? lon) 0 ;; else handle a pair )))

Now, we handle the second part of the specification. What if
`lon` is not empty? The BNF for this element states that
such a list of numbers consists of a number followed by a list of
numbers. This tells us that we can decompose our problem into two
subproblems:

- the length of the first part of the pair, and
- the length of the second part of the pair.

What is the length of `first`? What is the length of
the `rest`? How do we combine these answers?

The `first` of the list is a number, not a list. It
contributes one item to the length of the overall list.

The `rest` of the list is the rest of the list. (Ha!)
It, too, is a `<list-of-numbers>` -- the same
data type as the argument to `list-length`. How can
we find its length? Call `list-length`!

How do we put those numbers together to get the length of the whole list? We add them together.

So, the pair has a length of 1, for the number in the cons cell
we are processing, plus the result of
`(list-length (rest lon))`:

(define list-length (lambda (lon) (if (null? lon) 0 (+ 1 (list-length (rest lon))) ))) ; can use add1 > (list-length '()) 0 > (list-length '(42)) 1 > (list-length '(1 10 100 1000 10000 100000 2 4 6 8)) 10

And our definition is complete!

Another way to think about the recursive case is this: Split
the list into its `first` and its `rest`, which
is also a `<list-of-numbers>`. *Suppose that
we already know the answer for the rest.* How
can we solve the

As we take successive `rest`s of the list, we will
eventually encounter the empty list, which is our base case.
But we don't have to think about that now. We received either
an empty list or a pair.

Notice: We do *not* add an explicit guard to our code
so that we don't try to take the `rest` of a non-list.
Our code *cannot* make this error! The function takes
the `rest` of its argument only after it knows the
argument is not the empty list. But then the only alternative
is a pair, which always has a `cdr` that is a list.

We did assume that the original argument received by
`list-length` is, in fact, a `<list-of-numbers>`.
The specification for the function states as much. This
*precondition* makes it the responsibility of the caller
of the function to provide a suitable argument. If the caller
doesn't, then our function is not responsible for the error.
The same is true in a statically-typed language, though in that
case we usually have the compiler to catch the error for us.

Optional Digression: How can we implementlist-lengthwithout recursion, using only higher-order functions? Check out this code file to see a solution, as well as a discussion oflist-member?andany-bigger-than?.

We can use the same technique to implement
`any-bigger-than?`, from
our warm-up exercise.
`any-bigger-than?` returns true if `n` is smaller
than any member of `lon`, and false otherwise.

We now know to pattern our solution on the BNF definition of
`<list-of-numbers>`. So:

(define any-bigger-than? (lambda (n lon) (if (null? lon) ;; then: handle an empty list ;; else: handle a pair )))

There are no numbers in an empty list, so we know that `n`
cannot be smaller than any number there. In the base case, we
return false.

(define any-bigger-than? (lambda (n lon) (if (null? lon) #f ;; else handle pair )))

In the recursive case, `n` is smaller than a member of
`lon` **either** if it is *smaller than the
first*

Racket provides us with built-in functions for expressing both
the comparison, `<`, and the disjunction, `or`,
so:

(define any-bigger-than? (lambda (n lon) (if (null? lon) #f (or (< n (first lon)) (any-bigger-than? n (rest lon))))))

If you wrote a complete solution to the exercise, it may have
different from this: you may have used another `if` in
the recursive case. The version here is more faithful to the
BNF for our data type specification and to how we define the
answer, so most functional programmers prefer it. However,
both solutions compute the same value. The most important
thing is that you develop a habit for writing recursive
functions by thinking in this way.

?Quick Exercise: Can we we eliminate the remainingifexpression, too

When you are first writing functions of this type, you may well feel uncomfortable trusting that your solution works in the recursive case, because that means relying on the function that you are writing. The only way to overcome this discomfort is to do thorough testing of the function -- and to get lots of experience writing recursive functions!

In order for us to gain strength as recursive programmers,
let's practice on some less intuitive problems. I borrow
these examples from other textbooks, most notably Section 1.2.2
of the original
Essentials of Programming Languages.
I used *EOPL* for this course in the now distant past.

These problems are important for two reasons. First, we will
use the functions we write later in the course and in future
homework assignments. But if that were the only reason they
were important, we would need to understand only **what**
they do, but not how they do it.

The second reason that they are important, though, is that
they illustrate several common **patterns in recursive programs**
and how to implement them. So it will be worth our effort to
study in detail **how** they do what they do.

The rest of our examples today operate on values of the
`<list-of-symbols>` data type. As its name
suggests, `<list-of-symbols>` is quite similar to
`<list-of-numbers>`. We can specify this data
type inductively as:

<list-of-symbols> ::= () | (<symbol> . <list-of-symbols>)

`remove-first` takes two arguments, a symbol `s`
and a list of symbols `los`. It returns a list just
like `los` minus the first occurrence of `s`.
For example:

> (remove-first 'b '(a b c)) (a c)

Note that `remove-first` does *not* modify the
original `los`. In functional programming, our
functions almost never modify their arguments; instead, they
compute a new value for us.

We start with the familiar pattern for handling list recursion.

(define remove-first (lambda (s los) (if (null? los) ; then handle an empty list ; else handle a pair )))

In the base case, `los` is empty, so the result of
removing the first occurrence of `s` is still empty! So,
return `()`.

(define remove-first (lambda (s los) (if (null? los) '() ; else handle a pair )))

What if `los` is not empty? Then we need to remove the
first occurrence of `s` from a pair, if there is one.
There are two cases. Either the first element in `los`
is the symbol we want to remove, or it is not.

(define remove-first (lambda (s los) (if (null? los) '() (if (eq? (first los) s) ; then remove s from the car of los ; else remove s from the cdr of los ) )))

If the `s` is the first element in `los`, what
is the answer returned by `remove-first`? The rest
of the list:

(define remove-first (lambda (s los) (if (null? los) '() (if (eq? (first los) s) (rest los) ; else remove s from the cdr of los ) )))

Now comes the tough case... If the first element of
`los` is *not* the symbol we want to remove, then
we need to remove the first occurrence of that symbol from the
rest of the list. What is the answer to be returned by
`remove-first` in this case? We need a list whose
`first` is the `first` of `los` and whose
`rest` is the list we get by removing `s` from
the *rest* of `los`:

... Show examples of removingbfrom(a b c d)and(e d c b)and(c d e)...

... Draw pictures of lists that show the result ismaking a listfrom a head element and a tail list ...cons!

We reassemble a list from a `first` and a `rest`
using `cons`. Into which list do we `cons` the
`first` item of `los`? The result of removing
the first occurrence of `s` from the `rst` of
`los` -- which `remove-first` can compute for us!

(define remove-first (lambda (s los) (if (null? los) '() (if (eq? (first los) s) (rest los) (cons (first los) (remove-first s (rest los)))))))

And we are done! Let's test our function:

> (remove-first 'a '(a b c)) (b c) > (remove-first 'b '(a b c)) (a c) > (remove-first 'd '(a b c)) (a b c) > (remove-first 'a '()) () > (remove-first 'a '(a a a a a a a a a a)) ; count 'em up! (a a a a a a a a a)

Quick Exercise:: Suppose that, instead of(cons (car los) (remove-first s (cdr los)))as the 'else' clause of the secondif, we had just(remove-first s (cdr los))What function wouldremove-firstthen compute?

Our understanding of the list-of-symbols data structure -- and
especially of its BNF description -- guided us well in writing
this function. We still have to think, of course. The task
presented a couple of challenges. But **the structure helps
know what to think about**.

The function `remove` behaves like `remove-first`,
but it removes all occurrences of the symbol, not just the first.
The structure of `remove-first` and `remove` are so
similar that we can focus on how to modify `remove-first`
to convert it into `remove`.

In terms of our code, how does the new function differ from
`remove-first`?

- In the base case, our answer is still the empty list.
- If the first item in the list does
*not*match the item to remove, then we still need to`cons`into our recursive solution. - If the first item in the list
*does*match the item to remove, then we need to do something different.

So:

(define remove (lambda (s los) (if (null? los) ; on an empty list, the '() ; answer is still empty (if (eq? (first los) s) ;; WHAT DO WE DO HERE? (cons (first los) ; we still have to preserve (remove s (rest los))) ; non-s symbols in los ))))

In `remove-first`, as soon as we find `s` we return
the rest of the `los`, into which are `cons`ed any
non-`s` symbols that preceded `s` in `los`.
But in `remove`, we need to be sure to remove not just the
first `s` (by returning the `rest` of `los`)
but **all** the `s`'s, including any that may be
lurking in `(rest los)`. So:

(define remove (lambda (s los) (if (null? los) '() (if (eq? (first los) s) (remove s (rest los)) ;; *** HERE IS THE CHANGE! *** (cons (first los) (remove s (rest los))))))) > (remove 'a '(a b c)) (b c) > (remove 'a '(a a a a a a a a a a)) ()

Notice the relationship between the structure of the data and the
the structure of our code. The structure of the data did not
change from `remove-first` to `remove`, so neither
did the structure of the function. *A small change in spec
resulted in a small change in code*.

`remove-first` and `remove` demonstrate the basic
technique for writing recursive programs based on inductive data
specifications. This is a pattern you will find in many programs,
both functional and object-oriented. We call this pattern
**structural recursion**.

Structural recursion is the basis for nearly every function we
write. Occasionally, we will encounter bumps along the way to
a solution. Rather than pitching structural recursion and
flailing at our code without guidance, we will look for ways
to get over, or around, the bump. Over the next few sessions,
we will learn several techniques that we can use when we
encounter difficulties using structural recursion. The first
of these is the **interface procedure**.

Unless you are omniscient, writing a recursive function will
occasionally require "fixing" the function along the way instead
of writing it straight through from beginning to end. Consider
the function `annotate`, which takes as its only argument
a `<list-of-symbols>`. For example, if we pass to
`annotate`

(jerry george elaine kramer)

it returns a list with each symbol *annotate*d by its
position in the list:

((jerry 1) (george 2) (elaine 3) (kramer 4))

We can use structural recursion to build the framework of our answer:

(define annotate (lambda (los) (if (null? los) ; then handle an empty list ; else handle a pair )))

The base case of the data spec is the empty list. In this case, an empty list can be returned, since there are no items to annotate:

(define annotate (lambda (los) (if (null? los) '() ; else handle a pair )))

The inductive case is a symbol followed by a list of symbols.
We can combine the annotated symbol with the rest of the list
annotated using `cons`. The result is:

(define annotate (lambda (los) (if (null? los) '()(cons)))<something computed from (first los)>(annotate (rest los)))

When we write a function that computes a list of answers, one for each item in the original list, we will often use a piece of code that looks just like this. It will constitute a common mechanism for "putting our answer back together".

How can we annotate a symbol? By creating a list consisting of the symbol and its position of the symbol in the list:

(define annotate (lambda (los) (if (null? los) '() (cons (list (first los) position) (annotate (rest los))))))

Oops! We've run into a slight problem. We need the position of the symbol in the list, but we haven't supplied it anywhere. We could pass the current position down to each recursive call:

(define annotate (lambda (losposition) (if (null? los) '() (cons (list (first los) position) (annotate (rest los) (add1 position))))))

This does the work we need, but we have two related problems:

- What is the initial value of
`position`, the one used on the*first*call to`annotate`? The caller will have to tell`annotate`to start at position 1! -
`annotate`is defined to take one argument, but we have produced a function that requires two. Often, we don't want to change the spec of a program, even when we have the power to do so, because other parts of our code may rely on the specified interface.

In the case of`annotate`, changing the interface requires that*all*calls pass two arguments. Yet we always start annotating with position 1, and now we will have to repeat the 1 in every "first call" call to`annotate`.

Finally, we will no longer be able to`map`this function over a list of lists, because it takes two arguments.`map`requires a one-argument function. By taking`map`and similar higher-order functions out of our toolbox, we give up much of the power and productivity in the functional style.

These reasons should persuade us to look for a different solution. Programmers face this problem all of the time and have developed a common "patch". First, rename this version of the solution as a helper function:

(define annotate-with-position(lambda (los position) (if (null? los) '() (cons (list (first los) position) (annotate-with-position (rest los) (add1 position))))))

Second, implement `annotate` as a function that calls
the renamed function:

(define annotate ;; now write annotate ... (lambda (los) ;; ... to jump-start the helper (annotate-with-position los 1)))

We call the new `annotate` an **interface procedure**.
It serves as an interface to the function that does the real
work.

Creating an interface procedure is a common practice in many
kinds of programming, including functional programming. It
allows us to write our code *naturally* -- in the way
that follows our understanding of the problem -- even when the
task becomes complicated, without disturbing the tranquility
of the world in which the function resides.

The interface function pattern illustrates a valuable wisdom:
When you encounter a difficulty implementing structural
recursion, *don't give up on the technique*. We are
following the structure of our data for many good reasons.
Instead of giving up, solve the new difficulty. The problem
we encountered while implementing `annotate` is so
common that other programmers have developed a standard solution.
This wisdom generalizes beyond structural recursion to any
well-justified technique, including most every design pattern
we use.

Take a close look at this image. The big island is Luzon, one of the Philippine Islands. In the middle of Luzon is Lake Taal. Inside Lake Taal is Vulcano Island. Notice Crater Lake, the dot of water in the middle of Vulcano Island. Crater Lake holds a unique distinction. It is the largest lake on an island in a lake on an island in the world.

How's that for recursion?

This is a bit more complicated than the simplest recursion. We have a lake on an island in a lake on an island. Two different kinds of objects are recursing back and forth between one another. As we learn next session, this is a special kind of recursion, worthy of its own programming technique!

You can see this image along with a few other fun lake/island combinations at The Island and Lake Combination.

I adopted my "99 Bottles of Beer on the Wall" example from this message on an Erlang mailing list. (Erlang is a cool programming language: not quite functional, not quite imperative. It handles concurrency as a primitive!)

That message continues with a bit of news that surprises many
programmers who grew up with iteration before learning
recursion: **Iteration is a special case of recursion -- with
limits**.

Take a look at our examples above. In the loop, we always
handle the one bottle first, then the remaining n-1 bottles.
But in the recursive function, we could make the recursive
call *first* to handle the n-1 bottles first, and then
handle the one bottle last. We could even make the call
between "take one down" and "pass it around", and interleave
the activities! Try that with a loop.

In both iteration and recursion, a program handles an arbitrary number of steps with a single piece of code. This code either branches to the top of a loop (an implicit jump) or makes a function call (an explicit jump) -- and re-enters itself, so that one piece of code handles any number of things. In both cases, you have to make sure that some measure of the problem size (the length of a list, the number of things left to work on, the size of a tree, etc.) is strictly smaller every time you enter it.

But the function call is more powerful. As the creator of
Scheme wrote back in the 1970s, ** lambda is the
ultimate goto**.

- Reading -- Read Chapters 1-3 of
*The Little Schemer*.

Read today's notes. Pay special attention to the section called "Interface Procedures", an idea we will return to next time.

Make sure to study today's examples of recursion carefully. Then, begin to use the techniques learn as you work on... - Homework 4, which is available now and due a week from today.