|
From: Ken A. <kan...@bb...> - 2004-06-03 20:05:35
|
Every year or so, i need a queue. So, i look up Peter Norvig's clever implementation. Here's a JScheme version including synchronized procedures, and an example.
k
;;; Norvig's very clever queue, PAIP p 342.
;;; A queue is a (last . contents) pair.
;;; Unfortuantely, it is circular, and thus not printable in Scheme,
;;; so we wrap it in a thunk.
(define (queue-contents q) (cdr (q)))
(define (make-queue)
;; Build a new queue, with no elements.
(let ((q (cons '() '())))
(set-car! q q)
(lambda () q)))
(define (enqueue item queue)
;; Insert item at the end of the queue.
(let ((q (queue)))
(set-car! q (set-cdr! (car q) (cons item '()))))
queue)
(define (dequeue q)
;; Remove an item from the front of the queue.
(let ((q (q)))
(set-cdr! q (cdr (cdr q)))
(if (null? (cdr q)) (set-car! q q)))
q)
(define (front q) (car (queue-contents q)))
(define (empty-queue? q) (null? (queue-contents q)))
(define (queue-pop q)
(let ((it (front q)))
(dequeue q)
it))
;;; Synchronized versions.
(define (senqueue item q)
;; Synchronized version of (enqueue).
(synchronize q
(lambda (q)
(enqueue item q)
(.notify q))))
(define (squeue-pop q)
;; Synchronized version of (queue-pop)
(synchronize q
(lambda (q)
(let loop ()
(if (empty-queue? q)
(begin
(.wait q)
(loop))))
(queue-pop q))))
(define (Runner)
;; Example. Returns a procedure that takes a thunk. When the
;; procedure is invoked, the thunk is placed on a queue and a
;; separate Thread executes it. Multiple tasks can be enqueued, the
;; are executed sequentially.
(let* ((q (make-queue))
(thread (Thread. (lambda ()
(let loop ((it (squeue-pop q)))
(it)
(loop (squeue-pop q)))))))
(.start thread)
(lambda (action) (senqueue action q))))
|