;; chapter 5: A Queue ;; --- queue ------------------------------------------------------------------- ;; Note: this queue doesn't implement the capacity restriction ;; of McKim and Mitchell's queue but this is really minor and easy to add. (module queue mzscheme (require (lib "contract.ss") (lib "etc.ss")) ;; a contract utility (define (all-but-last l) (reverse (cdr (reverse l)))) (define (eq/c x) (lambda (y) (eq? x y))) ;; implementation (define-struct queue (list p? eq)) (define (initialize p? eq) (make-queue '() p? eq)) (define items queue-list) (define (put q x) (make-queue (append (queue-list q) (list x)) (queue-p? q) (queue-eq q))) (define (count s) (length (queue-list s))) (define (is-empty? s) (null? (queue-list s))) (define not-empty? (compose not is-empty?)) (define (remove s) (make-queue (cdr (queue-list s)) (queue-p? s) (queue-eq s))) (define (head s) (car (queue-list s))) ;; interface (provide/contract ;; predicate [queue? (-> any/c boolean?)] ;; primitive queries ;; Imagine providing this 'query' for the interface of the module ;; only. Then in Scheme, there is no reason to have count or is-empty? ;; around (other than providing it to clients). After all items is ;; exactly as cheap as count. [items (->d queue? (compose listof queue-p?))] ;; derived queries [count (->r ([q queue?]) ;; We could express this second part of the post ;; condition even if count were a module "attribute" ;; in the language of Eiffel; indeed it would use the ;; exact same syntax (minus the arrow and domain). (and/c natural-number/c (=/c (length (items q)))))] [is-empty? (->r ([q queue?]) (and/c boolean? (eq/c (null? (items q)))))] [head (->r ([q (and/c queue? (compose not is-empty?))]) (and/c (queue-p? q) (eq/c (car (items q)))))] ;; creation [initialize (-> contract? (contract? contract? . -> . boolean?) (and/c queue? (compose null? items)))] ;; commands [put (->r ([oldq queue?][i (queue-p? oldq)]) (and/c queue? (lambda (q) (define old-items (items oldq)) (equal? (items q) (append old-items (list i))))))] [remove (->r ([oldq (and/c queue? (compose not is-empty?))]) (and/c queue? (lambda (q) (equal? (cdr (items oldq)) (items q)))))]) ;; end of interface ) ;; --- tests ------------------------------------------------------------------- (module test mzscheme (require (planet "test.ss" ("schematics" "schemeunit.plt" 1 2)) (planet "text-ui.ss" ("schematics" "schemeunit.plt" 1 2))) (require (lib "contract.ss")) (require queue) (define s (put (put (initialize (flat-contract integer?) =) 2) 1)) (test/text-ui (make-test-suite "queue" (make-test-case "empty" (assert-true (is-empty? (initialize (flat-contract integer?) =)))) (make-test-case "put" (assert-true (queue? s))) (make-test-case "count" (assert = (count s) 2)) (make-test-case "put exn" #;(assert-exn exn:fail:contract? (push (initialize (flat-contract integer?)) 'a)) (assert-true (with-handlers ([exn:fail:contract? (lambda _ #t)]) (put (initialize (flat-contract integer?)) 'a) #f))) (make-test-case "remove" (assert-true (queue? (remove s)))) (make-test-case "head" (assert = (head s) 2)))) ) (require test)