Sometimes we want a data structure that provides access to its elements on ``first-in, first-out'' basis, rather than the ``last-in, first-out'' constraint that a stack imposes. (For example, it might be prudent to treat that pile of unpaid bills a little differently, adding new elements at the bottom of the pile rather than the top, Paying off the most recent bill first, as in a stack, can make one's other, older creditors a little testy.)
Such a structure is called a queue. Like a line of people waiting for some service, a queue acquires new elements at one end (the rear of the queue) and releases old elements at the other (the front). Here is the abstract data type definition for queues, with the conventional names for the operations:
Create a new, empty queue object.
Determine whether the queue is empty; return true if it is and false if it is not.
Add a new element at the rear of a queue.
Remove an element from the front of the queue and return it. (This operation cannot be performed if the queue is empty.)
Return the element at the front of the queue (without removing it from the queue). (Again, this operation cannot be performed if the queue is empty.)
The implementation of queues in Scheme is somewhat trickier than the
implementation of stacks. Again, we'll keep the elements of the queue in a
list. However, it turns out that the enqueue operation can be
slightly faster if we represent an empty queue by a list containing one
element, a ``dummy header,'' and store the actual queue elements after this
header, oldest first. The dummy header is not inserted by enqueue
and cannot be removed by the dequeue. It is not there to provide a
value, but just to keep the list from becoming null, so that one can always
set-cdr! procedure to it without first testing to
see whether it is null. The fact that the underlying list never becomes
completely null is an invariant of this implementation of queues.
The other novel feature of this implementation is that we'll actually be
accessing the list through two different fields,
front field always contains the entire
list structure, beginning with the dummy header;
is the list of the actual elements of the queue, and
front) is the first element of the queue (when it is not empty).
rear field, on the other hand, is always a one-element
list; it contains the last element of the queue, except when the queue is
empty, in which case the
rear field contains the dummy header.
The following box-and-pointer diagram shows a queue into which the symbols
c have been enqueued, in
Here is the constructor for queue objects:
(define queue-class (lambda () (let* ((front (list 'dummy-header)) (rear front)) (lambda (message . arguments) (case message ((empty?) (null? (cdr front))) ((enqueue!) (if (null? arguments) (error 'queue-class "method ENQUEUE!: An argument is required") (begin ; Attach a new cons cell behind the current rear ; element. (set-cdr! rear (list (car arguments))) ; Advance REAR so that it is once more a list ; containing only the last element. (set! rear (cdr rear))))) ((dequeue!) (if (null? (cdr front)) (error 'queue-class "method DEQUEUE!: The queue is empty") ; Recover the first element of the queue (not including ; the dummy header). (let ((removed (cadr front))) ; Splice out the element to be dequeued. (set-cdr! front (cddr front)) ; If you just spliced out the last element of the ; queue, reset REAR so that it holds the dummy ; header. (if (null? (cdr front)) (set! rear front)) removed))) ((front) (if (null? (cdr front)) (error 'queue "method FRONT: The queue is empty") (cadr front))) (else (error 'queue-class "unrecognized message")))))))
Add to the queue an additional method, activated by the
Write a procedure
print-symbols that creates an empty queue,
then processes a list to put each symbol on the list or on any sublist into
the queue, and finally uses the
(print-symbols '(((to) be) (or (not (to ((be)))))))should print
to be or not to be
Rewrite the previous
print-symbols procedure to use a stack
instead of a queue to store the symbols. How does the output differ from
that obtained in the previous problem?
This document is available on the World Wide Web as
created April 28, 1997
last revised January 11, 2000
Henry Walker (email@example.com) and John David Stone (firstname.lastname@example.org)