Com S 227  Introduction to Computer Programming
THE RECURSIVE MOMENT (HANDOUT)
Copyright (c) 1993 Gary T. Leavens. All rights reserved.
Compare the following with section 4.2 in Springer and Friedman's book.
Note: there are blanks below. These are for students to fill in.
REMOVE2ND
Many students have a misconception about what such a problem involves,
in part due to its name. Despite the name, remove2nd, this procedure,
like any other in this part of the course, cannot change any list.
So the following picture of what remove2nd does, crossing out the second
cat, removing it from the list, is *wrong*.
\/
my  cat  loves  cXt  food ] NOT!
/\
You cannot change a list in Scheme.
Instead, what you can do is to produce an entirely new list, that bears
a certain relationship to the argument list. I do find it helpful to
draw pictures of this, but the picture has to show both the input (ls) and
the output (answer1):

ls > my  cat  loves  cat  food ]



 (remove2nd 'cat ls)

v

answer1 > my  cat  loves  food ]

picture 1
Note that answer1 is a new list, that resembles ls in a certain way.
Now here comes the essential idea for recursion, I draw another picture
for ``the rest of the journey''; that is, for the recursion:

(cdr ls) > cat  loves  cat  food ]





v

answer2 > cat  loves  food ]

picture 2
How did we get from (cdr ls) to answer2 in picture 2? Well, we could
go through the same process again, but that doesn't get us anywhere.
Instead, we get to use remove2nd as a helping procedure, and assume
that it works! This is recursion. So picture 2 is accomplished by
(remove2nd 'cat (cdr ls))
I am looking to find this when thinking about recursion, so this
shouldn't be surprising.
Incidentally, you can think of answer2 in picture 2 as either
as the result of (remove2nd 'cat (cdr ls)) or as (cdr answer1).
Now how to make answer2 (the result of the rest of the journey)
into answer1? That is, how do we get answer1 from answer2?
So we can write:
(cons (car ls) (remove2nd 'cat (cdr ls)))
for answer1.
That is one case of remove2nd. Note that the symbol we were to remove
was *not* in (car ls). If we look at the problem, we can see that we
need to do something else. So let's consider the case where (car ls)
is equal to what we want to remove.

ls > cat  loves  cat  food  for  a  cat ]



 (remove2nd 'cat ls)

v

answer1 > cat  loves  food  for  a  cat ]

picture 3
Again we consider the rest of the journey. We draw the picture for
the cdr of this ls (in picture 3):

(cdr ls) > loves  cat  food  for  a  cat ]





v
answer2 > ??????
But should we write for answer2? There are at least two candidates:
1. (remove2nd 'cat (cdr ls))
2. (cdr answer1)
In some problems you may need to think about other candidates for answer2,
but these are good ones to try. Indeed alternative 1 should be thought
of as the default and only abandoned when it doesn't work. So let's try it.
The value of alternative 1 would be the list
(loves cat food for a)
but it would be hard to get answer1 from
that (although there might be a way). Since that's not simple, we try
the second alternative. This makes the following picture.

(cdr ls) > loves  cat  food  for  a  cat ]





v

answer2 > loves  food  for  a  cat ]

picture 4
How do we get from (cdr ls) to answer2 in this case?
We can't use remove2nd; it doesn't do the right thing.
What will do it? It may help to look at similar examples.
(For lack of space, I'll not do that here.)
Now how do we get answer1 from answer2?
So we write:
(cons (car ls)
(remove1st 'cat (cdr ls)))
This exhausts the cases, except the base case (for the empty list).
But that's easy. So we can now write our procedure.
We have to remember to change the 'cat in our examples to a variable:
(DEFINE remove2nd
(LAMBDA (item ls)
(COND
[(null? ls) '()]
[(not (eq? item (car ls))) ; picture 1
(cons (car ls) (remove2nd item (cdr ls)))]
[ELSE ; picture 3
(cons (car ls) (remove1st item (cdr ls)))])))
or
(DEFINE remove2nd
(LAMBDA (item ls)
(COND
[(null? ls) '()]
[(eq? item (car ls)) ; picture 3
(cons (car ls) (remove1st item (cdr ls)))]
[ELSE ; picture 1
(cons (car ls) (remove2nd item (cdr ls)))])))
LASTADJOUT (REMOVELAST)
There are many ways to think about this problem. One way is to see
that you can reverse the list, take the first occurrence of the adjective
out of the reversal, and then reverse that. It's possible to find this
by thinking of symmetry considerations, but that seems a bit magical.
Instead I'll show you how to invent another solution,
in the same way we worked problem 16.
Let's picture an example.

ls > the  nice  dog  saw  the  nice  nice  puppy ]



 (lastadjout 'nice ls)

v

answer1 > the  nice  dog  saw  the  nice  puppy ]

picture 5
The rest of the journey is pictured as follows.

(cdr ls) > nice  dog  saw  the  nice  nice  puppy ]



 (lastadjout 'nice (cdr ls))

v

answer2 > nice  dog  saw  the  nice  puppy ]

picture 6
Here answer2 is the recursive call on the cdr and also (cdr answer1).
How do we get to answer1 from answer2?
So we can write for this case:
Now let's consider the case where (car ls) is the symbol we are looking for.
There are two cases. Here is the first case:

ls > nice  dog  saw  the  nice  nice  puppy ]



 (lastadjout 'nice ls)

v

answer1 > nice  dog  saw  the  nice  puppy ]

picture 7
What is the picture for the rest of the journey? (for (cdr ls))
How do you get from (cdr ls) to answer2?
How do you get answer1 from answer2?
So we write for this case:
Here is the second case where nice is in (car ls).

ls > nice  puppy ]



 (lastadjout 'nice ls)

v

answer1 > puppy ]

picture 9
What is the picture for the rest of the journey? (for (cdr ls))
How do you get from (cdr ls) to answer2?
How do you get answer1 from answer2?
So we write for this case:
Now put the whole thing together into a program, remembering that
you still have to treat the case where ls is null, and change the variables.
The problem will be to distinguish the cases given in pictures 7 and 9.
How can we distinguish pictures 7 and 9?
That is, what is the difference between the following?

ls > nice  dog  saw  the  nice  nice  puppy ]


ls > nice  puppy ]

In both cases, (car ls) is the same, so that doesn't help.
But in the first case (picture 7), the nice that is (car ls) is not
the last occurrence of nice in the list. Why not?
Because there are other occurrences in (cdr ls).
We want a procedure that returns a boolean (#t or #f) to answer this
question for us. What is the exact problem it should solve?
At this point there are two options, we could write a procedure
to take the lists (nice ...) and tell if there is another nice in the cdr,
or we could write a procedure that takes the cdr and tells whether there
is an occurrence of nice in the cdr. Let's try the first one.
It's useful to set this up as a problem to be solved explicitly.
(occursincdr 'nice '(nice dog saw the nice nice puppy)) ==> #t
(occursincdr 'nice '(nice puppy)) ==> #f
(occursincdr 'nice '(nice nice puppy)) ==> #t
(occursincdr 'nice '(x)) ==> #f
So we can write:

ls > nice  dog  saw  the  nice  nice  puppy ]



 (occursincdr 'nice ls)

v
answer1 > #t
Let's look at the recursion (what happens to (cdr ls)).
We'll just consider using the recursion to produce answer2

(cdr ls) > dog  saw  the  nice  nice  puppy ]



 (occursincdr 'nice (cdr ls))

v
answer2 > #t
How do we get #t from #t? We do nothing.
That works in this case, but what if our example was:

ls > nice  nice  puppy ]



 (occursincdr 'nice ls)

v
answer1 > #t
We're just considering using the recursion to produce answer2, so we have:

(cdr ls) > nice  puppy ]



 (occursincdr 'nice (cdr ls))

v
answer2 > #f
So we can't use (occursincdr 'nice (cdr ls)) because we want to get #t,
and this gives #f. We could try using not to get answer1 from answer2,
but then that wouldn't work for the case above. So we're stuck.
What to do when you're stuck like this?
You could bludgeon ahead, and figure out that you can have occursincdr
first call cdr and then call member?
(define occursincdr
(lambda (item ls)
(member? item (cdr ls))))
But you might not think of that. What else can you do? I'd try again.
That is, I'd think about other ways to solve what we need to solve,
which is *not* how to write occursincdr, but ultimately to write
lastadjout.
So we need to find a procedure that will tell us the difference between
the following:

ls > nice  dog  saw  the  nice  nice  puppy ]


ls > nice  puppy ]

Think about what makes the nice in the first of these not the last
and what makes the nice in the second the last. It's what's in the
cdr of ls in each case. So we want to test something about the rest of
these lists. You might write yourself out an example for this subproblem:
(member? 'nice '(dog saw the nice nice puppy)) ==> #t
(member? 'nice '(puppy)) ==> #f
(member? 'nice '()) ==> #t
Now you can either solve this or remember that it is in the book.