## How to Annotate an S-List

### An annotate Function for S-lists

Recall the function annotate from Session 9. 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 allowed 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))
```

Try to write annotate-slist now. This problem is a straight application of three techniques we have studied: structural recursion, mutual recursion, and interface procedures. Then scroll down to see how I solved the problem.

. Spoilers below.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

Let's start with an interface procedure and a data-driven outline of the helper that handles s-lists:

```    (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 almost as easy: 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 call a helper:

```    (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. Annotating a symbol is easy: we return a 2-list of the symbol and the count. Annotating an s-list isn't much harder, because we just wrote annotate-counted! All we have to do is record the fact that we are going one level deeper in the structure. 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, again, if we proceed systematically, the patterns guide us home.

Eugene Wallingford ..... wallingf@cs.uni.edu ..... February 22, 2018