Code Review
Compare your solutions
#|
Упражнение 3.21
Бен Битобор решает протестировать вышеописанную реализацию. Он вводит процедуры в интерпретаторе Лиспа
и тестирует их:
(define q1 (make-queue))
(insert-queue! q1 'a)
((a) a)
(insert-queue! q1 'b)
((a b) b)
(delete-queue! q1)
((b) b)
(delete-queue! q1)
(() b)
«Ничего не работает! — жалуется он. — Ответ интерпретатора показывает, что последний элемент попадает
в очередь два раза. А когда я оба элемента уничтожаю, второе b по-прежнему там сидит, так что очередь
не становится пустой, хотя должна бы». Ева Лу Атор говорит, что Бен просто не понимает, что происходит.
«Дело не в том, что элементы два раза оказываются в очереди, — объясняет она. — Дело в том, что
стандартная лисповская печаталка не знает, как устроено представление очереди. Если ты хочешь, чтобы
очередь правильно печаталась, придется написать специальную процедуру распечатки очередей». Объясните,
что имеет в виду Ева Лу. В частности, объясните, почему в примерах Бена на печать выдается именно такой
результат. Определите процедуру print-queue, которая берет на входе очередь и выводит на печать
последовательность ее элементов.
|#
(#%require rackunit)
(define (make-queue) (mcons '() '()))
(define (front-ptr queue) (mcar queue))
(define (rear-ptr queue) (mcdr queue))
(define (set-front-ptr! queue item)
(set-mcar! queue item))
(define (set-rear-ptr! queue item)
(set-mcdr! queue item))
(define (empty-queue? queue)
(null? (front-ptr queue)))
(define (front-queue queue)
(if (empty-queue? queue)
(error "FRONT called with an empty queue" queue)
(mcar (front-ptr queue))))
(define (insert-queue! queue item)
(let ((new-pair (mcons item '())))
(cond ((empty-queue? queue)
(set-front-ptr! queue new-pair)
(set-rear-ptr! queue new-pair)
queue)
(else
(set-mcdr! (rear-ptr queue) new-pair)
(set-rear-ptr! queue new-pair)
queue))))
(define (delete-queue! queue)
(cond ((empty-queue? queue)
(error "DELETE! called with an empty queue" queue))
(else
(set-front-ptr! queue (mcdr (front-ptr queue)))
queue)))
#|
Так происходит, потому что Scheme воспринимает очередь как пару из двух элементов и, соответственно,
выводит эти элементы целиком. Если это указатель на голову списка, то и выводится список.
Удаление элементов из очереди не убирает ссылку на последний элемент, поэтому в нашей реализации у
пустой очереди может быть непустая ссылка на последний элемент, что и наблюдается в примере выше.
Печаталка должна выводить пустой список, если указатель на голову списка пуст.
|#
(define (print-queue queue op)
(define back (rear-ptr queue))
(define (bypass-queue front)
(if (eq? front back)
(mcons (mcar front) '())
(mcons (mcar front) (bypass-queue (mcdr front)))))
(display
(if (empty-queue? queue)
'()
(bypass-queue (front-ptr queue)))
op))
(define q1 (make-queue))
(define op1 (open-output-string))
(print-queue q1 op1)
(define display-result (get-output-string op1))
(check-equal? display-result "()")
(close-output-port op1)
(define op2 (open-output-string))
(print-queue (insert-queue! q1 'a) op2)
(define display-result-2 (get-output-string op2))
(check-equal? display-result-2 "{a}")
(close-output-port op2)
(define op3 (open-output-string))
(print-queue (insert-queue! q1 'b) op3)
(define display-result-3 (get-output-string op3))
(check-equal? display-result-3 "{a b}")
(close-output-port op3)
(define op4 (open-output-string))
(print-queue (delete-queue! q1) op4)
(define display-result-4 (get-output-string op4))
(check-equal? display-result-4 "{b}")
(close-output-port op4)
(define op5 (open-output-string))
(print-queue (delete-queue! q1) op5)
(define display-result-5 (get-output-string op5))
(check-equal? display-result-5 "()")
(close-output-port op5)