Session 9 Extension

A Recursive remove Function

CS 3540
Programming Languages and Paradigms

Recap: The remove-first Function

We just 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)

A quick exercise: before moving forward: Suppose that, instead of

                (cons (first los)
                      (remove-first s (rest los)))
as the 'else' clause of the second if, we had just
                (remove-first s (rest los))
What function would remove-first then compute?

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?


    (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). 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.

Eugene Wallingford ..... ..... February 14, 2023