From e2e63684de77f0b3525d14a7062230f388c9c917 Mon Sep 17 00:00:00 2001 From: Ryan Culpepper Date: Fri, 21 Oct 2011 02:32:33 -0600 Subject: [PATCH] data/order: added support for transparent structs, added tests --- collects/data/order.rkt | 79 +++++++++++++++++++----- collects/data/scribblings/order.scrbl | 86 ++++++++++++++++++++------- collects/tests/data/order.rkt | 66 ++++++++++++++++++++ 3 files changed, 196 insertions(+), 35 deletions(-) create mode 100644 collects/tests/data/order.rkt diff --git a/collects/data/order.rkt b/collects/data/order.rkt index 46087d7059..58115b65e7 100644 --- a/collects/data/order.rkt +++ b/collects/data/order.rkt @@ -2,7 +2,8 @@ (require racket/dict racket/contract/base racket/string - unstable/prop-contract) + unstable/prop-contract + ffi/unsafe/atomic) (define ordering/c (or/c '= '< '>)) @@ -154,7 +155,8 @@ Other: < pair < vector < box - < prefab + < prefab-struct + < fully-transparent-struct ;; FIXME: What else to add? regexps (4 kinds?), syntax, ... @@ -171,16 +173,16 @@ Other: (define (gen-cmp x y natural?) (define-syntax-rule (recur x* y*) (gen-cmp x* y* natural?)) - #| - (cond ... + (cond [(eq? x y) '=] + #| [(T? x) ...] - ;; at this point, Type(x) > T + ;; at this point, Type(x) > T [(T? y) ;; Type(x) > T = Type(y), so: - '>]) - Assumes arguments are legal. - |# - (cond [(real? x) + '>] + Assumes arguments are legal. + |# + [(real? x) (if (real? y) (cond [natural? (cmp* < = x y)] @@ -252,7 +254,7 @@ Other: [(pair? y) '>] [(vector? x) (if (vector? y) - (vector] [(box? x) @@ -263,16 +265,29 @@ Other: [(prefab-struct-key x) (if (prefab-struct-key y) (lexico (recur (prefab-struct-key x) (prefab-struct-key y)) - (vectorvector x) (struct->vector y) 1 natural?)) + ;; FIXME: use struct-ref to avoid allocation? + (vector-cmp (struct->vector x) (struct->vector y) 1 natural?)) '<)] [(prefab-struct-key y) '>] + [(fully-transparent-struct-type x) + => (lambda (xtype) + (cond [(fully-transparent-struct-type y) + => (lambda (ytype) + ;; could also do another lexico with object-name first + (lexico (object-cmp xtype ytype) + ;; FIXME: use struct-ref to avoid allocation? + (vector-cmp (struct->vector x) (struct->vector y) + 1 natural?)))] + [else '<]))] + [(fully-transparent-struct-type y) + '>] [else (raise-type-error (if natural? 'natural-cmp 'datum-cmp) (string-join '("number" "string" "bytes" "keyword" "symbol" "boolean" "character" "null" "pair" "vector" "box" - "or prefab struct") + "prefab struct" "or fully-transparent struct") ", ") 0 x y)])) @@ -287,18 +302,54 @@ Other: ((>) '>))) (define (symbolstring x) (symbol->string y))) -(define (vector)] [(< i (vector-length y)) '<] [else '=])) +;; fully-transparent-struct-type : any -> struct-type or #f +(define (fully-transparent-struct-type x) + (parameterize ((current-inspector weak-inspector)) + (let-values ([(x-type x-skipped?) (struct-info x)]) + (and (not x-skipped?) x-type)))) + +;; weak inspector controls no struct types; +;; so if it can inspect, must be transparent +(define weak-inspector (make-inspector)) + +;; Impose an arbitrary (but consistent) ordering on eq?-compared +;; objects. Use eq? and eq-hash-code for common fast path. Fall back +;; to table when comparing struct-types *same eq-hash-code* but *not +;; eq?*. That should be rare. +(define object-order-table (make-weak-hasheq)) +(define object-order-next 0) +(define (object-cmp x y) + (cond [(eq? x y) '=] + [else + (lexico + (cmp* < = (eq-hash-code x) (eq-hash-code y)) + (call-as-atomic + (lambda () + (let ([xi (hash-ref object-order-table x #f)] + [yi (hash-ref object-order-table y #f)]) + (cond [(and xi yi) + ;; x not eq? y, so xi != yi + (if (< xi yi) '< '>)] + [xi '<] + [yi '>] + [else ;; neither one is in table; we only need to add one + (hash-set! object-order-table x object-order-next) + (set! object-order-next (add1 object-order-next)) + '<])))))])) + (define datum-order (order* 'datum-order any/c datum-cmp)) diff --git a/collects/data/scribblings/order.scrbl b/collects/data/scribblings/order.scrbl index 363bc00d11..f33daf945d 100644 --- a/collects/data/scribblings/order.scrbl +++ b/collects/data/scribblings/order.scrbl @@ -65,8 +65,10 @@ implementing the @tech{ordered dictionary} interface (via } @deftogether[[ -@defproc[(dict-iterate-least [dict ordered-dict?]) any/c] -@defproc[(dict-iterate-greatest [dict ordered-dict?]) any/c]]]{ +@defproc[(dict-iterate-least [dict ordered-dict?]) + (or/c (dict-iter-contract dict) #f)] +@defproc[(dict-iterate-greatest [dict ordered-dict?]) + (or/c (dict-iter-contract dict) #f)]]]{ Returns the position of the least (greatest) key in the ordered dictionary @racket[dict]. If @racket[dict] is empty, @racket[#f] is @@ -74,10 +76,14 @@ returned. } @deftogether[[ -@defproc[(dict-iterate-least/>? [dict ordered-dict?] [key any/c]) any/c] -@defproc[(dict-iterate-least/>=? [dict ordered-dict?] [key any/c]) any/c] -@defproc[(dict-iterate-greatest/? [dict ordered-dict?] [key any/c]) + (or/c (dict-iter-contract dict) #f)] +@defproc[(dict-iterate-least/>=? [dict ordered-dict?] [key any/c]) + (or/c (dict-iter-contract dict) #f)] +@defproc[(dict-iterate-greatest/ 8.0 5.0) +(t < 'apple 'candy) +(t < '(a #:b c) '(a #:c d c)) +(t > '(5 . 4) '(3 2 1)) +(t < '(a b . c) '(a b . z)) +(t > "apricot" "apple") +(t > '#(1 2 3) '#(1 2)) +(t < '#(1 2 3) '#(1 3)) +(t > (box 'car) (box 'candy)) +(t < '#s(point a 1) '#s(point b 0)) +(t < '#s(A 1 2) '#s(Z 3 4 5)) +(t < (fish 'alewife) (fish 'sockeye)) + +(define-syntax-rule (tc x y) + (test-case (format "~s" '(tc x y)) + (let ([xy (datum-order x y)] + [xy2 (datum-order x y)] + [yx (datum-order y x)] + [xy3 (datum-order x y)]) + ;; check consistency across multiple runs + (check-equal? xy xy2) + (check-equal? xy xy3) + ;; check oppositeness + (check member (list xy yx) '((< >) (> <)))))) + +(tc 1 2.0) +(tc 3+5i 3+2i) +(tc 'apple "zucchini") +(tc '(a b) '(a b . c)) +(tc 0 'zero) + +(tc (fish 'alewife) (fowl 'dodo)) + +(tc (fish 'alewife) + (let () + (struct fish (x)) + (fish 'alewife)))