Deep Lists can be thought of as Trees.

Using An Accumulator to Make Computation More Efficient.

Association lists provide finite mappings.

Given a deeply-nested list such as:

(a ((b c)) (d (e (f))) g)

it is often useful to visualise it as a tree written out as follows:

(a ((b c)) (d (e (f))) g) | ---------------------------------------- | | | | a ((b c)) (d (e (f))) g | | | ---------- | | | (b c) d (e (f)) | | ------ -------- | | | | b c e (f) | f

In order to explore this whole tree, that is to visit every sublist of a
list, we need to use *deep-recursion*. A function is deeply
recursive if it is called recursively on the `car` of a list as well
as on its `cdr`.

```
(define (occurs_atom? x expr)
(cond
((atom? expr) (eqv? x expr))
(else (or
(occurs_atom? x (car expr))
(occurs_atom? x (cdr expr))
))
)
)
```

```
(example '(occurs_atom? 'a '(b (c d ((a))))) #t)
```

Instead of merely reporting on the existence of an atomic quantity in a
list-structure, we may want to report on how many times it occurs. The
above function is quite easily modified to do this.
```
(define (count_occurrences x expr)
(cond
((atom? expr) (if (eqv? x expr) 1 0))
(else (+
(count_occurrences x (car expr))
(count_occurrences x (cdr expr))
))
)
)
```

```
(example '(count_occurrences 'a '((b (c)) (a) (b ((a))))) 2)
```

Let us use trace to see how count_occurrences works.

```
(trace count_occurrences)
```

If we now call

```
(count_occurrences 'a '(b (a) (b (c a))))
```

we obtain the following print-out *italics* were inserted
by the author.

(count_occurrences a (b (a) (b (c a))) )top level call|(count_occurrences a b )recursive call on car of list|count_occurrences = 0 adoes not occur inb |(count_occurrences a ((a) (b (c a))) )now docdrof original list| (count_occurrences a (a) )and do itscar | |(count_occurrences a a )and thecarof that| |count_occurrences = 1in which a occurs once.| |(count_occurrences a () )and do the cdr of'(a) | |count_occurrences = 0in which a does not occur| count_occurrences = 1so a occurs once in'(a) | (count_occurrences a ((b (c a))) ) | |(count_occurrences a (b (c a)) ) | | (count_occurrences a b ) | | count_occurrences = 0 | | (count_occurrences a ((c a)) ) | | |(count_occurrences a (c a) ) | | | (count_occurrences a c ) | | | count_occurrences = 0 | | | (count_occurrences a (a) ) | | | |(count_occurrences a a ) | | | |count_occurrences = 1 | | | |(count_occurrences a () ) | | | |count_occurrences = 0 | | | count_occurrences = 1 | | |count_occurrences = 1 | | |(count_occurrences a () ) | | |count_occurrences = 0 | | count_occurrences = 1 | |count_occurrences = 1 | |(count_occurrences a () ) | |count_occurrences = 0 | count_occurrences = 1 |count_occurrences = 2 count_occurrences = 2

```
(define (subst x y expr)
(if
(atom? expr)
(if (eq? x expr) y expr)
(cons (subst x y (car expr))
(subst x y (cdr expr))
)
)
)
```

Consider the function `flatten` which takes a tree and makes a
(flat) list of all the "tips" of the tree.

```
(define (flatten tree)
(cond
((null? tree) '())
((atom? (car tree)) (cons (car tree) (flatten (cdr tree))))
(else
(append (flatten (car tree))
(flatten (cdr tree)))))
)
```

```
(example '(flatten '((b (c)) (a) (b ((a))))) '(b c a b a))
```

Notice that `flatten`
has complexity * O(n^2)* - it takes *O(n)* operations
to do an append, and this is done *O(n)* times.

We can make a more efficient tree-flattening function using an
*accumulator*. In the following definition, the accumulator
`ans` can be regarded as a kind of basket. We crawl all over the
tree, "picking cherries", that is the things we want to find in the tree,
and putting them in the basket.

```
(define (flatten2 tree ans)
(cond
((null? tree) ans)
((atom? tree) (cons tree ans))
(else
(flatten2 (car tree) (flatten2 (cdr tree) ans)))
)
)
```

```
(define (flatten tree) (flatten2 tree '()))
```

```
(example '(flatten '((a b (c)) (d) (e ((f))))) '(a b c d e f))
```

```
(example '(subst 'a 3 '(+ (* a 7) b)) '(+ (* 3 7) b))
```

```
(define (reverse l)
(if (null? l)
'()
(append (reverse (cdr l)) (list (car l)))
)
)
```

```
(example '(reverse '(1 2 3)) '(3 2 1))
```

But we can make a more efficient version using accumulation thus:
```
(define (reverse l)
(reverse_1 l '())
)
```

```
(define (reverse_1 l acc)
(if (null? l)
acc
(reverse_1 (cdr l) (cons (car l) acc))
)
)
```

```
(reverse '(2 3 4))
(4 3 2)
```

Often in computing we want to set up a *finite mapping*
that relates one finite
set to another. For example we may want to represent the fact that a variable
a has the value 1, b has the value 2 and c has the value 3. We can represent
such a mapping using an *association list* or *alist*:

```
(define e '((a 1) (b 2) (c 3)))
```

In order to access and update such alists, Scheme provides 3 functions assoc, assq and assv. They all take two arguments. The first of these is a "key" and the second is an alist. The key is compared successively with the first element of each sub-list of the alist. The first sub-list whose first member "matches" the key is returned as the result of the function. If no match is found, false is returned.

```
(example '(assoc 'b e) '(b 2))
```

The difference between assoc, assq and assv lies in the equality function used to test for matching. assoc uses the equal function, and provides a capability that is generally useful, but may be rather slow. assq and assv use eq? eqv? respectively.

```
(example '(assq 'a e) '(a 1))
(example '(assq 'b e) '(b 2))
(example '(assq 'd e) #f)
(example '(assq '(a) '(((a) 4) ((b) 5) ((c) 7) )) #f)
(example '(assoc (list 'a) '(((a)) ((b)) ((c)))) '((a)))
(example '(assq 5 '((2 3) (5 7) (11 13))) '(5 7)) ; ** see note below
(example '(assv 5 '((2 3) (5 7) (11 13))) '(5 7))
```

** Note - the Scheme standard does not specify what the result of using eq? to compare numbers is, so this example may produce different results with different implementations of Scheme.

We could define `assoc` as follows:

```
(define (assoc obj alist)
(cond
((null? alist) #f)
((equal? obj (caar alist)) (car alist))
(else (assoc obj (cdr alist)))
))
```

Typically we will use `assoc`
to locate the sublist in which a value resides,
and then take the appropriate action to find the value. For example, an
interpreter for a programming language might say something like:

(if (symbol? expr) (lookup expr environment) expr)

where the `lookup` function is defined as:

```
(define (lookup var env) ;;; find the value of a variable
(let ((pair (assoc var env))) ;;; find where its value is held
(if pair ;;; does an entry exist for the variable?
(cadr pair) ;;; extract its value
(error "unbound variable" var)
) ) )
```