racket/collects/data/queue.rkt
Robby Findler 98633665b3 extend data/queue library
- add enqueue-front!

- add queue-filter!

- use the predicates instead of the /c contracts

- make queue-length take constant time

- add some random tests

- note the running times of all of the operations in the docs

- make queues be sequences directly (and use make-do-sequence
  to implement in-queue instead of building a list)

- added non-empty-queue? (note extra hypen as compared
  to the past; this seems better since the function
  wasn't exported before and we already have other
  functions named "non-empty-<something>" but not
  others namedn "nonempty-<something>")
2012-11-21 11:10:02 -06:00

132 lines
4.1 KiB
Racket

#lang racket/base
(require racket/contract/base
(for-syntax racket/base
unstable/wrapc))
;; A Queue contains a linked list with mutable cdrs, holding two pointers
;; to the head and the tail -- where items are pulled from the head and
;; pushed on the tail. It is not thread safe: mutating a queue from
;; different threads can break it.
(struct queue (head tail length) #:mutable
#:property prop:sequence (λ (q) (in-queue q)))
;; (Note: uses #f for `head' to mark an empty queue, but in those cases
;; the tail will be set to #f too, to avoid holding on to values that
;; should be collected.)
(struct link (value [tail #:mutable]))
(define (make-queue) (queue #f #f 0))
(define (queue-empty? q) (not (queue-head q)))
(define (non-empty-queue? v) (and (queue? v) (queue-head v) #t))
(define (enqueue! q v)
(unless (queue? q) (raise-type-error enqueue! "queue" 0 q))
(let ([new (link v #f)])
(if (queue-head q)
(set-link-tail! (queue-tail q) new)
(set-queue-head! q new))
(set-queue-tail! q new)
(set-queue-length! q (+ (queue-length q) 1))))
(define (enqueue-front! q v)
(unless (queue? q) (raise-type-error enqueue! "enqueue-front!" 0 q))
(define fr (queue-head q))
(cond
[fr
(set-queue-head! q (link v fr))]
[else
(define k (link v #f))
(set-queue-head! q k)
(set-queue-tail! q k)])
(set-queue-length! q (+ (queue-length q) 1)))
(define (dequeue! q)
(unless (queue? q) (raise-type-error dequeue! "queue" 0 q))
(let ([old (queue-head q)])
(unless old (raise-type-error 'dequeue! "non-empty queue" 0 q))
(cond
[(eq? old (queue-tail q))
(set-queue-tail! q #f)
(set-queue-head! q #f)]
[else
(set-queue-head! q (link-tail old))])
(set-queue-length! q (- (queue-length q) 1))
(link-value old)))
(define (queue->list q) (for/list ([e (in-queue q)]) e))
;; queue->vector could be implemented as (list->vector (queue->list q))
;; but this is somewhat slow. a direct translation between queue's and
;; vector's should be fast so the ideal situation is not to use a list
;; as an intermediate data structure.
;; maybe add the elements to a gvector and use gvector->vector?
(define (queue-filter! q pred?)
(unless (queue-empty? q)
(let loop ([prev #f]
[curr (queue-head q)]
[i 0])
(cond
[(not curr)
(set-queue-tail! q prev)
(set-queue-length! q i)]
[else
(define passed? (pred? (link-value curr)))
(cond
[passed?
(loop curr (link-tail curr) (+ i 1))]
[else
(define tl (link-tail curr))
(if prev
(set-link-tail! prev tl)
(set-queue-head! q tl))
(loop prev tl i)])]))))
(define (in-queue q)
(make-do-sequence
(λ ()
(values
link-value
link-tail
(queue-head q)
link?
#f #f))))
(define-sequence-syntax in-queue*
(lambda () #'in-queue)
(lambda (stx)
(syntax-case stx ()
([(var) (in-queue* queue-expression)]
(with-syntax ([queue-expression/c (wrap-expr/c #'queue? #'queue-expression
#:macro #'in-queue*)])
#'[(var)
(:do-in ([(queue) queue-expression/c])
(void) ;; handled by contract
([link (queue-head queue)])
link
([(var) (link-value link)])
#t
#t
((link-tail link)))]))
([(var ...) (in-queue* queue-expression)]
#f))))
;; --- contracts ---
(define queue/c queue?)
(define nonempty-queue/c non-empty-queue?)
(provide/contract
[queue/c flat-contract?]
[nonempty-queue/c flat-contract?]
[queue? (-> any/c boolean?)]
[non-empty-queue? (-> any/c boolean?)]
[make-queue (-> queue?)]
[queue-empty? (-> queue? boolean?)]
[queue-length (-> queue? exact-nonnegative-integer?)]
[queue->list (-> queue? (listof any/c))]
[queue-filter! (-> queue? (-> any/c any/c) void?)])
(provide enqueue! enqueue-front!
dequeue! (rename-out [in-queue* in-queue]))