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
consit 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
consed 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).
And 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.