A Recursive remove
Function
Recap: The remove-first
Function
In Session 9, we wrote
a function named remove-first
that takes two arguments, a symbol s
and a list of
symbols los
. It returns a list just like
los
minus only the first occurrence of
s
.
(define remove-first (lambda (s los) (if (null? los) '() (if (eq? (first los) s) (rest los) (cons (first los) (remove-first s (rest los)))))))
It works:
> (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)
For a little practice, let's try a variation of this problem.
The remove
Function
What if we wanted to remove all occurrences of a symbol from a list of symbols?
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
it onto the solution to the recursive step. - 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)))))))
Does it work?
> (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.