3.14 Exercise 3.14
Consider the procedure below:
(define (mystery x) (define (loop x y) (if (null? x) y (let ((temp (cdr x))) (set-cdr! x y) (loop temp x)))) (loop x '()))
In general, this procedure will reverse a list (while trashing the list being reversed – more on this later). Consider this brief trace:
Suppose x is the list v, defined as (list ’a ’b ’c ’d). We start by evaluating (loop x ’()). Since x is not nil, we then set aside (cdr x), which is ’(b c d), and set the cdr of x to ’(). The list v is now ’(a). Then we call loop again, as (loop temp x).
Since temp, the new x, is not null, we set aside its cdr and set the cdr to be the list ’(a). The list is now ’(b a). We then call (loop temp x) again.
We continue looping until temp is empty, which will happen after every element of the list has been prepended to the front of the list we are now calling x. This list is then returned. However, the original list is still set to ’(a) – that is, what x was set to in the first loop. x still has the value from the first loop because subsequent calls to loop shadowed it, resulting in the modification of other lists. The only lists we are left with are this shortened version of x and the fully-reversed original list.
It would possibly be more useful to define a procedure that points x to the reversed list. In that way, we could reverse a list in place, with the list taking on the name of its reversed self. However, doing so is fraught with peril. We can’t simply make mystery return (set! x (loop x ’())) because this will only change what the name x points to, not the actual list passed in as a parameter. And using set-car! and set-cdr! to change this list is also dangerous. Suppose we do something like
(let ((reversed (loop x '()))) (set-car! x (car reversed)) (set-cdr! x (cdr reversed)))
This looks like it will set x to be the list made up of the components of the reversed list. However, it does not do this. First we set the first element of x to be the first element of the reversed list. However, this also changes the value at the end of the reversed list, making it ’(c b d)! And then setting the rest of x to be the rest of the reversed list, where the last element of the reversed list is actually x, will create a cycle.
TODO: Can we actually solve this?