2.68 Exercise 2.68
encode-symbol requires us to visit all of the nodes in the tree, and must return a list of bits representing the path to the node containing the symbol we are looking for if it is present in the tree. The exercise asks us to throw an error if the symbol is not found. We will handle this (and only this) in an outer procedure, while doing the traversal and message returning inside an inner procedure, because it is rather elegant to return nil if the symbol is not found.
First, the inner procedure, named encode-symbol-1:
(define (encode-symbol-1 symbol tree bits) (if (leaf? tree) (if (eq? symbol (symbol-leaf tree)) bits '()) (append (encode-symbol-1 symbol (left-branch tree) (append bits '(0))) (encode-symbol-1 symbol (right-branch tree) (append bits '(1))))))
When encode-symbol-1 is visiting a current node (the root of tree), bits contains the encoded path to this node. Therefore, if the current node is a leaf and its symbol is the one we’re looking for, we can return bits. If the current node is a leaf and the symbol is not the one we’re looking for, there’s no more work to be done in the tree and we return nil, for reasons that will become clearer soon.
If the current node is not a leaf, we need to search both the left and right subtrees for the symbol. We can call encode-symbol-1 with the left and right branches of the tree and with 0 and 1 appended to bits, respectively. This encodes the path we have taken through the tree into the list of bits.
However, despite doing two recursive searches for the symbol, we only want to return one list of bits. The easiest way to do this is to append the results together, and to make sure that we return nil if we don’t find the symbol. This will make the final result be the same as the bit encoding of the symbol in the tree.
To return an error when the symbol isn’t found, we can write a simple wrapper procedure as our actual encode-symbol:
(define (encode-symbol symbol tree) (let ((result (encode-symbol-1 symbol tree '()))) (if (null? result) (error "Couldn't find symbol in tree") result)))
To verify that this works, we can add to our equal? procedure from Exercise 2.54 to also check equality of numbers, and then use if to verify that the sample encoded message is the same as a message we encode ourselves using our previously decoded message:
(define (equal? a b) (cond ((and (null? a) (null? b)) #t) ((and (symbol? a) (symbol? b)) (eq? a b)) ((and (number? a) (number? b)) (= a b)) ((and (list? a) (list? b)) (if (or (null? a) (null? b)) #f (and (equal? (car a) (car b)) (equal? (cdr a) (cdr b))))) (else #f)))
> (define (encode message tree) (if (null? message) nil (append (encode-symbol (car message) tree) (encode (cdr message) tree)))) > (equal? sample-message (encode (decode sample-message sample-tree) sample-tree)) #t