### How to Annotate an S-List

##### Putting Several Patterns Together

#### An `annotate`

Function for s-lists

Recall
the `annotate`

function
from Session 10. It takes a flat list of symbols, such as
`(ball-state michigan-state northern-iowa)`

,
and returns a list of symbol/position lists,
`((ball-state 1) (michigan-state 2) (northern-iowa 2))`

.
Solving this problem required a new idea, the interface procedure.

Then we learned about a new data type, the s-list, which allows symbols in the list to be nested inside of other lists. S-lists are useful to us because they resemble Racket programs, which means that learning how to process s-lists helps us learn how to process programs of this form.

Can we annotate an s-list? Sure. Instead of a symbol's position in any particular list, we would probably care more about its depth in the tree. For example:

> (annotate-slist '(ball-state michigan-state northern-iowa)) '((ball-state 0) (michigan-state 0) (northern-iowa 0)) > (annotate-slist '((a b) (((b g r) (f r)) c (d e)) b)) '(((a 1) (b 1)) ((((b 3) (g 3) (r 3)) ((f 3) (r 3))) (c 1) ((d 2) (e 2))) (b 0))

#### Writing `annotate-slist`

`annotate-slist`

now. This problem is an
application of three techniques we have studied:
structural recursion,
mutual recursion,
and
interface procedures.
When you are done, press the button to see a solution...

#### A Solution

As in the original annotate function, we know that we will need a counter to keep track of the nesting level, which we can use to annotate each symbol. We will have to pass that level on each recursive call. So let's start with an interface procedure and an outline of the helper that handles s-lists. The helper will follow the BNF description of an s-list:

(define annotate-slist (lambda (slst) (annotate-counted slst 0))) (define annotate-counted (lambda (slst level) (if (null? slst) ; handle empty list ; handle cons )))

The empty list case is easy to handle: the result is also the
empty list. The `cons`

case is straightforward, too:
annotate the `car`

, annotate the `cdr`

, and
return a new `cons`

of the results:

(define annotate-counted (lambda (slst level) (if (null? slst) '() (cons (... annotate) (`first`

...... annotate)))))`rest`

...

The `rest`

is an s-list, so a call to
`annotate-counted`

will do the trick.
The `first`

is a symbol expression, so let's create
a helper function and call it:

(define annotate-counted (lambda (slst level) (if (null? slst) '() (cons (annotate-sym-expr (first slst) level) (annotate-counted (rest slst) level))))) (define annotate-sym-expr (lambda (sym-expr level) (if (symbol? sym-expr) ; handle symbol ; handle slst)))

We're almost there. We annotate a symbol by returning a 2-list
containing the symbol and the current level. If
`sym-expr`

is an s-list, we can annotate it by calling
`annotate-counted`

! All we have to do is record the
fact that we are going one level deeper in the structure, by adding
1 to the level. Here goes:

(define annotate-sym-expr (lambda (sym-expr level) (if (symbol? sym-expr) (list sym-expr level) (annotate-counted sym-expr (+ level 1)))))

The result is three procedures that follow from the three design patterns you've used several times over the last few weeks: structural recursion, interface procedure, and mutual recursion. The problem is a challenge but, if we proceed systematically, the patterns guide us home.

Here is a Racket file containing the entire solution, for your programming pleasure.