123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- ;;; This is a purely functional FIFO (first in first out) queue data structure
- ;;; in GNU Guile. This queue implementation makes use of a front stack and back
- ;;; stack, which are exchanged, when the front stack is empty and one tries to
- ;;; access the head of the queue.
- ;;; Adapted from https://programmingpraxis.com/2013/11/08/two-stacks-make-a-queue/
- ;;; Changes I made to adapt the code to GNU Guile:
- ;;; - translation to GNU Guile (Scheme)
- ;;; - binding renamings for better readability,
- ;;; - imports of required modules
- ;;; - bug fixes (endless loop in case of dequeue-ing from empty queue)
- ;;; - some error handling
- ;;; - comments (there were none)
- ;;; - docstrings (there were none)
- (library (queue)
- (export empty-queue
- enqueue
- dequeue
- queue-empty?)
- (import
- (except (rnrs base) let-values map error)
- (only (guile)
- lambda* λ)
- (ice-9 match)
- ;; receive form
- (srfi srfi-8))
- ;; A stack is implemented using a list.
- (define empty-stack '())
- (define (push stack x)
- "Push an element onto the stack by cons-ing it to the list,
- which is used as stack."
- (cons x stack))
- (define (pop stack)
- "Pop the top element of the stack, returning both, the top
- element and the updated stack."
- (values (car stack) (cdr stack)))
- ;; Checking whether a stack is empty is simply checking
- ;; whether a list is empty.
- (define empty-stack? null?)
- (define (transfer src dst)
- "Transfer all element of one stack to the other stack, reversing their order,
- as we can only pop elements, which are on top of a stack and
- only push elements onto elements of a stack."
- (cond [(empty-stack? src) dst]
- [else
- (receive (x xs) (pop src)
- ;; Transfer the rest of the elements. Recur.
- (transfer xs (push dst x)))]))
- ;; Creating an empty queue means creating an empty front
- ;; stack and empty back stack.
- (define empty-queue (cons empty-stack empty-stack))
- (define (enqueue queue elem)
- "Enqueue an element x into the given queue."
- ;; First, via match, separate the front stack and back
- ;; stack of the queue, so that we can push the new
- ;; element onto one of the stacks.
- (match queue
- ;; A queue is the pair of back stack and front
- ;; stack. Push the element onto the back stack and
- ;; make a new queue.
- [(back-stack . front-stack)
- (cons (push back-stack elem)
- front-stack)]
- [_
- (error "enqueue got something else than a queue:" queue)]))
- (define (dequeue queue)
- "Dequeue the head of the queue."
- ;; First, via match, separate the front stack and back
- ;; stack of the queue.
- (cond
- [(queue-empty? queue)
- (error "cannot dequeue from empty queue")]
- [else
- (match queue
- [(back-stack . front-stack)
- (cond
- ;; If the front stack is empty, transfer all
- ;; elements from the back stack to the front stack
- ;; and then dequeue.
- [(empty-stack? front-stack)
- (dequeue
- (cons empty-stack
- (transfer back-stack front-stack)))]
- ;; Otherwise pop an element from the front stack
- ;; and return both, the element and the updated
- ;; queue.
- [else
- (receive (elem updated-front-stack) (pop front-stack)
- (values elem (cons back-stack updated-front-stack)))])]
- [_
- (error "dequeue got something else than a queue:" queue)])]))
- (define (queue-empty? queue)
- "Check whether a queue is empty, by checking whether both
- stacks are empty."
- (and (null? (car queue))
- (null? (cdr queue)))))
|