diff --git a/collects/rackunit/private/gui/gvector.rkt b/collects/data/gvector.rkt similarity index 53% rename from collects/rackunit/private/gui/gvector.rkt rename to collects/data/gvector.rkt index c5eadfedb7..39ff73a7b6 100644 --- a/collects/rackunit/private/gui/gvector.rkt +++ b/collects/data/gvector.rkt @@ -1,17 +1,26 @@ #lang racket/base -(require racket/contract - racket/dict) +;; written by ryanc +(require (for-syntax racket/base + unstable/wrapc) + racket/contract/base + racket/dict + racket/vector) (define (make-gvector* #:capacity [capacity 10]) (make-gvector (make-vector capacity #f) 0)) -(define (check-index who index n) +(define (check-index who index n set-to-add?) (unless (exact-nonnegative-integer? index) (raise-type-error who "exact nonnegative integer" index)) (unless (< index n) - (if (zero? n) - (error who "index out of range for empty gvector: ~s" index) - (error who "index out of range [0,~s]: ~s" (sub1 n) index)))) + (error who "index out of range ~a~a: ~s" + (let ([max-index (if set-to-add? (- n 2) (- n 1))]) + (cond [(< max-index 0) "(empty)"] + [else (format "[0,~s]" max-index)])) + (if set-to-add? + (format " or ~s to add" (- n 1)) + "") + index))) (define ((bad-index-error who index)) (raise-mismatch-error who "index out of range" index)) @@ -33,7 +42,7 @@ (define (gvector-remove! gv index) (let ([n (gvector-n gv)] [v (gvector-vec gv)]) - (check-index 'gvector-remove! index n) + (check-index 'gvector-remove! index n #f) (set-gvector-n! gv (sub1 n)) (vector-copy! v index v (add1 index) n) (vector-set! v (sub1 n) #f))) @@ -51,27 +60,37 @@ (default) default))) +;; gvector-set! with index = |gv| is interpreted as gvector-add! (define (gvector-set! gv index item) - (check-index 'gvector-set! index (gvector-n gv)) - (vector-set! (gvector-vec gv) index item)) + (let ([n (gvector-n gv)]) + (check-index 'gvector-set! index (add1 n) #t) + (if (= index n) + (gvector-add! gv item) + (vector-set! (gvector-vec gv) index item)))) + +;; creates a snapshot vector +(define (gvector->vector gv) + (vector-copy (gvector-vec gv) 0 (gvector-n gv))) ;; Iteration methods +;; A gvector position is represented as an exact-nonnegative-integer. + (define (gvector-iterate-first gv) (and (positive? (gvector-n gv)) 0)) (define (gvector-iterate-next gv iter) - (check-index 'gvector-iterate-next iter (gvector-n gv)) + (check-index 'gvector-iterate-next iter (gvector-n gv) #f) (let ([n (gvector-n gv)]) (and (< (add1 iter) n) (add1 iter)))) (define (gvector-iterate-key gv iter) - (check-index 'gvector-iterate-key iter (gvector-n gv)) + (check-index 'gvector-iterate-key iter (gvector-n gv) #f) iter) (define (gvector-iterate-value gv iter) - (check-index 'gvector-iterate-value iter (gvector-n gv)) + (check-index 'gvector-iterate-value iter (gvector-n gv) #f) (gvector-ref gv iter)) (define (in-gvector gv) @@ -79,6 +98,28 @@ (raise-type-error 'in-gvector "gvector" gv)) (in-dict-values gv)) +(define-sequence-syntax in-gvector* + (lambda () #'in-vector) + (lambda (stx) + (syntax-case stx () + [[(var) (in-gv gv-expr)] + (with-syntax ([gv-expr-c (wrap-expr/c #'gvector? #'gv-expr #:macro #'in-gv)]) + (syntax/loc stx + [(var) + (:do-in ([(gv) gv-expr-c]) + (void) ;; outer-check; handled by contract + ([index 0] [vec (gvector-vec gv)] [n (gvector-n gv)]) ;; loop bindings + (< index n) ;; pos-guard + ([(var) (vector-ref vec index)]) ;; inner bindings + #t ;; pre-guard + #t ;; post-guard + ((add1 index) (gvector-vec gv) (gvector-n gv)))]))] + [[(var ...) (in-gv gv-expr)] + (with-syntax ([gv-expr-c (wrap-expr/c #'gvector? #'gv-expr #:macro #'in-gv)]) + (syntax/loc stx + [(var ...) (in-vector gv-expr-c)]))] + [_ #f]))) + (define-struct gvector (vec n) #:mutable #:property prop:dict @@ -95,10 +136,12 @@ #:property prop:sequence in-gvector) (provide/contract + [gvector? + (-> any/c any)] [rename make-gvector* make-gvector (->* () (#:capacity exact-positive-integer?) any)] [gvector-ref - (-> gvector? exact-nonnegative-integer? any)] + (->* (gvector? exact-nonnegative-integer?) (any/c) any)] [gvector-set! (-> gvector? exact-nonnegative-integer? any/c any)] [gvector-add! @@ -107,5 +150,7 @@ (-> gvector? exact-nonnegative-integer? any)] [gvector-count (-> gvector? any)] - [in-gvector - (-> gvector? sequence?)]) + [gvector->vector + (-> gvector? vector?)]) + +(provide (rename-out [in-gvector* in-gvector])) diff --git a/collects/data/scribblings/data.scrbl b/collects/data/scribblings/data.scrbl index baffcf6e7e..bd9132d8be 100644 --- a/collects/data/scribblings/data.scrbl +++ b/collects/data/scribblings/data.scrbl @@ -18,4 +18,4 @@ This manual documents data structure libraries available in the @include-section["queue.scrbl"] @include-section["skip-list.scrbl"] @include-section["interval-map.scrbl"] - +@include-section["gvector.scrbl"] diff --git a/collects/data/scribblings/gvector.scrbl b/collects/data/scribblings/gvector.scrbl new file mode 100644 index 0000000000..deab65fe8e --- /dev/null +++ b/collects/data/scribblings/gvector.scrbl @@ -0,0 +1,99 @@ +#lang scribble/manual +@(require scribble/eval + (for-label data/gvector + racket/contract + racket/dict + racket/base)) + +@title[#:tag "gvector"]{Growable vectors} + +@(define the-eval (make-base-eval)) +@(the-eval '(require data/gvector)) +@(the-eval '(require racket/dict)) + +@defmodule[data/gvector] + +@author[@author+email["Ryan Culpepper" "ryanc@racket-lang.org"]] + +A growable vector (gvector) is a mutable sequence whose length can +change over time. A gvector also acts as a dictionary (@racket[dict?] +from @racketmodname[racket/dict]), where the keys are zero-based +indexes and the values are the elements of the gvector. A gvector can +be extended by adding an element to the end, and it can be shrunk by +removing any element, although removal can take time linear in the +number of elements in the gvector. + +@defproc[(make-gvector [#:capacity capacity exact-positive-integer? 10]) + gvector?]{ + +Creates a new empty growable vector (gvector) with an initial capacity +of @racket[capacity]. +} + +@defproc[(gvector? [x any/c]) boolean?]{ + +Returns @racket[#t] if @racket[x] was created by +@racket[make-gvector], @racket[#f] otherwise. +} + +@defproc[(gvector-ref [gv gvector?] + [index exact-nonnegative-integer?] + [default any/c (error ....)]) + any/c]{ + +Returns the element at index @racket[index], if @racket[index] is less +than @racket[(gvector-count gv)]. Otherwise, @racket[default] is +invoked if it is a procedure, returned otherwise. +} + +@defproc[(gvector-add! [gv gvector?] + [value any/c]) + void?]{ + +Adds @racket[value] to the end of the gvector @racket[gv]. +} + +@defproc[(gvector-set! [gv gvector?] + [index (and/c exact-nonnegative-integer? + (vector [gv gvector?]) + vector?]{ + +Returns a vector of length @racket[(gvector-count gv)] containing the +elements of @racket[gv] in order. +} + +@defproc[(in-gvector [gv gvector?]) + sequence?]{ + +Returns a sequence whose elements are the elements of +@racket[gv]. Mutation of @racket[gv] while the sequence is running +changes the elements produced by the sequence. To obtain a sequence +from a snapshot of @racket[gv], use @racket[(in-vector +(gvector->vector gv))] instead. +} diff --git a/collects/rackunit/private/gui/model.rkt b/collects/rackunit/private/gui/model.rkt index 110efadbdb..74e25ccb48 100644 --- a/collects/rackunit/private/gui/model.rkt +++ b/collects/rackunit/private/gui/model.rkt @@ -2,7 +2,7 @@ (require racket/class unstable/class-iop racket/list - "gvector.rkt" + data/gvector "../base.rkt" "interfaces.rkt" "cache-box.rkt")