5.22 Exercise 5.22
We are asked to reimplement append and append! in our assembly language.
(define (append x y) (if (null? x) y (cons (car x) (append (cdr x) y))))
start-machine (assign continue (label append-done)) recurse-test (test (op null?) (reg x)) (branch (label base-case)) (save continue) (assign continue (label after-recurse)) (save x) (assign x (op cdr) (reg x)) (goto (label recurse-test)) after-recurse (restore x) (assign rest (reg val)) (assign first (op car) (reg x)) (assign val (op cons) (reg first) (reg rest)) (restore continue) (goto (reg continue)) base-case (assign val (reg y)) (goto (reg continue)) append-done
(define (append! x y) (set-cdr! (last-pair x) y) x) (define (last-pair x) (if (null? (cdr x)) x (last-pair (cdr x))))
If we ignore the function separation between append! and last-pair, we can write a very compact procedure:
last-pair-init (assign x-iter (reg x)) last-pair-recurse (assign temp (op cdr) (reg x-iter)) (test (op null?) (reg temp)) (branch (label perform-append)) (assign x-iter (reg temp)) (goto (label last-pair-recurse)) perform-append (perform (op set-cdr!) (reg x-iter) (reg y))
However, we ought to go the extra mile and make last-pair a real function. By my implicit calling conventions, this means that it should return a value in the val register and should not require a special communication register like x-iter. This version is quite similar: The only substantive differences are that we save the value of x before starting to recurse, and that we assign the last x value we encounter to the val register before exiting.
last-pair-init (save x) last-pair-recurse (assign temp (op cdr) (reg x)) (test (op null?) (reg temp)) (branch (label after-last-pair-recurse)) (assign x (reg temp)) (goto (label last-pair-recurse)) after-last-pair-recurse (assign val (reg x)) (restore x) perform-append (perform (op set-cdr!) (reg val) (reg y))
Both of these machines work, but it should be noted that append! doesn’t support appending to an empty list, due to its use of set-cdr!.
> (set-register-contents! recursive-append-machine 'x '(1 2 3)) |
'done |
|
> (set-register-contents! recursive-append-machine 'y '(4 5 6)) |
'done |
|
> (start recursive-append-machine) |
'done |
|
> (get-register-contents recursive-append-machine 'val) |
(mcons 1 (mcons 2 (mcons 3 (mcons 4 (mcons 5 (mcons 6 '())))))) |
|
> (set-register-contents! iterative-append-machine 'x '(1 2 3)) |
'done |
|
> (set-register-contents! iterative-append-machine 'y '(4 5 6)) |
'done |
|
> (start iterative-append-machine) |
'done |
|
> (get-register-contents iterative-append-machine 'x) |
(mcons 1 (mcons 2 (mcons 3 (mcons 4 (mcons 5 (mcons 6 '())))))) |