#lang scribble/doc @(require "mz.rkt" (for-label racket/generic)) @(define dict-eval (make-base-eval)) @examples[#:hidden #:eval dict-eval (require racket/dict racket/generic racket/contract)] @title[#:tag "dicts"]{Dictionaries} A @deftech{dictionary} is an instance of a datatype that maps keys to values. The following datatypes are all dictionaries: @itemize[ @item{@techlink{hash tables};} @item{@techlink{vectors} (using only exact integers as keys);} @item{@techlink{lists} of @techlink{pairs} (an @deftech{association list} using @racket[equal?] to compare keys); and} @item{@techlink{structures} whose types implement the @racket[gen:dict] @tech{generic interface}.} ] @note-lib[racket/dict] @section{Dictionary Predicates and Contracts} @defproc[(dict? [v any/c]) boolean?]{ Returns @racket[#t] if @racket[v] is a @tech{dictionary}, @racket[#f] otherwise. Beware that @racket[dict?] is not a constant-time test on pairs, since checking that @racket[v] is an @tech{association list} may require traversing the list. @examples[ #:eval dict-eval (dict? #hash((a . "apple"))) (dict? '#("apple" "banana")) (dict? '("apple" "banana")) (dict? '((a . "apple") (b . "banana"))) ]} @defproc[(dict-implements? [d dict?] [sym symbol?] ...) boolean?]{ Returns @racket[#t] if @racket[d] implements all of the methods from @racket[gen:dict] named by the @racket[sym]s; returns @racket[#f] otherwise. Fallback implementations do not affect the result; @racket[d] may support the given methods via fallback implementations yet produce @racket[#f]. @examples[ #:eval dict-eval (dict-implements? (hash 'a "apple") 'dict-set!) (dict-implements? (make-hash '((a . "apple") (b . "banana"))) 'dict-set!) (dict-implements? (make-hash '((b . "banana") (a . "apple"))) 'dict-remove!) (dict-implements? (vector "apple" "banana") 'dict-set!) (dict-implements? (vector 'a 'b) 'dict-remove!) (dict-implements? (vector 'a "apple") 'dict-set! 'dict-remove!) ] } @defproc[(dict-implements/c [sym symbol?] ...) flat-contract?]{ Recognizes dictionaries that support all of the methods from @racket[gen:dict] named by the @racket[sym]s. Note that the generated contract is not similar to @racket[hash/c], but closer to @racket[dict-implements?]. @examples[ #:eval dict-eval (struct deformed-dict () #:methods gen:dict []) (define/contract good-dict (dict-implements/c) (deformed-dict)) (eval:error (define/contract bad-dict (dict-implements/c 'dict-ref) (deformed-dict)))] } @defproc[(dict-mutable? [d dict?]) boolean?]{ Returns @racket[#t] if @racket[d] is mutable via @racket[dict-set!], @racket[#f] otherwise. Equivalent to @racket[(dict-implements? d 'dict-set!)]. @examples[ #:eval dict-eval (dict-mutable? #hash((a . "apple"))) (dict-mutable? (make-hash)) (dict-mutable? '#("apple" "banana")) (dict-mutable? (vector "apple" "banana")) (dict-mutable? '((a . "apple") (b . "banana"))) ]} @defproc[(dict-can-remove-keys? [d dict?]) boolean?]{ Returns @racket[#t] if @racket[d] supports removing mappings via @racket[dict-remove!] and/or @racket[dict-remove], @racket[#f] otherwise. Equivalent to @racket[(or (dict-implements? d 'dict-remove!) (dict-implements? d 'dict-remove))]. @examples[ #:eval dict-eval (dict-can-remove-keys? #hash((a . "apple"))) (dict-can-remove-keys? '#("apple" "banana")) (dict-can-remove-keys? '((a . "apple") (b . "banana"))) ]} @defproc[(dict-can-functional-set? [d dict?]) boolean?]{ Returns @racket[#t] if @racket[d] supports functional update via @racket[dict-set], @racket[#f] otherwise. Equivalent to @racket[(dict-implements? d 'dict-set)]. @examples[ #:eval dict-eval (dict-can-functional-set? #hash((a . "apple"))) (dict-can-functional-set? (make-hash)) (dict-can-functional-set? '#("apple" "banana")) (dict-can-functional-set? '((a . "apple") (b . "banana"))) ]} @section{Generic Dictionary Interface} @defidform[gen:dict]{ A @tech{generic interface} (see @secref["struct-generics"]) that supplies dictionary method implementations for a structure type via the @racket[#:methods] option of @racket[struct] definitions. This interface can be used to implement any of the methods documented as @secref["primitive-dict-methods"] and @secref["derived-dict-methods"]. @examples[#:eval dict-eval (struct alist (v) #:methods gen:dict [(define (dict-ref dict key [default (lambda () (error "key not found" key))]) (cond [(assoc key (alist-v dict)) => cdr] [else (if (procedure? default) (default) default)])) (define (dict-set dict key val) (alist (cons (cons key val) (alist-v dict)))) (define (dict-remove dict key) (define al (alist-v dict)) (alist (remove* (filter (λ (p) (equal? (car p) key)) al) al))) (define (dict-count dict) (length (remove-duplicates (alist-v dict) #:key car)))]) (code:comment "etc. other methods") (define d1 (alist '((1 . a) (2 . b)))) (dict? d1) (dict-ref d1 1) (dict-remove d1 1) ] } @defthing[prop:dict struct-type-property?]{ A deprecated structure type property used to define custom extensions to the dictionary API. Use @racket[gen:dict] instead. Accepts a vector of 10 method implementations: @itemize[ @item{@racket[dict-ref]} @item{@racket[dict-set!], or @racket[#f] if unsupported} @item{@racket[dict-set], or @racket[#f] if unsupported} @item{@racket[dict-remove!], or @racket[#f] if unsupported} @item{@racket[dict-remove], or @racket[#f] if unsupported} @item{@racket[dict-count]} @item{@racket[dict-iterate-first]} @item{@racket[dict-iterate-next]} @item{@racket[dict-iterate-key]} @item{@racket[dict-iterate-value]} ] } @subsection[#:tag "primitive-dict-methods"]{Primitive Dictionary Methods} These methods of @racket[gen:dict] have no fallback implementations; they are only supported for dictionary types that directly implement them. @defproc[(dict-ref [dict dict?] [key any/c] [failure-result (failure-result/c any/c) (lambda () (raise (make-exn:fail ....)))]) any]{ Returns the value for @racket[key] in @racket[dict]. If no value is found for @racket[key], then @racket[failure-result] determines the result: @itemize[ @item{If @racket[failure-result] is a procedure, it is called (through a tail call) with no arguments to produce the result.} @item{Otherwise, @racket[failure-result] is returned as the result.} ] @examples[ #:eval dict-eval (dict-ref #hash((a . "apple") (b . "beer")) 'a) (eval:error (dict-ref #hash((a . "apple") (b . "beer")) 'c)) (dict-ref #hash((a . "apple") (b . "beer")) 'c #f) (dict-ref '((a . "apple") (b . "banana")) 'b) (dict-ref #("apple" "banana") 1) (dict-ref #("apple" "banana") 3 #f) (eval:error (dict-ref #("apple" "banana") -3 #f)) ]} @defproc[(dict-set! [dict (and/c dict? (not/c immutable?))] [key any/c] [v any/c]) void?]{ Maps @racket[key] to @racket[v] in @racket[dict], overwriting any existing mapping for @racket[key]. The update can fail with a @racket[exn:fail:contract] exception if @racket[dict] is not mutable or if @racket[key] is not an allowed key for the dictionary (e.g., not an exact integer in the appropriate range when @racket[dict] is a @tech{vector}). @examples[ #:eval dict-eval (define h (make-hash)) (dict-set! h 'a "apple") h (define v (vector #f #f #f)) (dict-set! v 0 "apple") v ]} @defproc[(dict-set [dict (and/c dict? immutable?)] [key any/c] [v any/c]) (and/c dict? immutable?)]{ Functionally extends @racket[dict] by mapping @racket[key] to @racket[v], overwriting any existing mapping for @racket[key], and returning an extended dictionary. The update can fail with a @racket[exn:fail:contract] exception if @racket[dict] does not support functional extension or if @racket[key] is not an allowed key for the dictionary. @examples[ #:eval dict-eval (dict-set #hash() 'a "apple") (dict-set #hash((a . "apple") (b . "beer")) 'b "banana") (dict-set '() 'a "apple") (dict-set '((a . "apple") (b . "beer")) 'b "banana") ]} @defproc[(dict-remove! [dict (and/c dict? (not/c immutable?))] [key any/c]) void?]{ Removes any existing mapping for @racket[key] in @racket[dict]. The update can fail if @racket[dict] is not mutable or does not support removing keys (as is the case for @tech{vectors}, for example). @examples[ #:eval dict-eval (define h (make-hash)) (dict-set! h 'a "apple") h (dict-remove! h 'a) h]} @defproc[(dict-remove [dict (and/c dict? immutable?)] [key any/c]) (and/c dict? immutable?)]{ Functionally removes any existing mapping for @racket[key] in @racket[dict], returning the fresh dictionary. The update can fail if @racket[dict] does not support functional update or does not support removing keys. @examples[ #:eval dict-eval (define h #hash()) (define h (dict-set h 'a "apple")) h (dict-remove h 'a) h (dict-remove h 'z) (dict-remove '((a . "apple") (b . "banana")) 'a) ]} @defproc[(dict-iterate-first [dict dict?]) any/c]{ Returns @racket[#f] if @racket[dict] contains no elements, otherwise it returns a non-@racket[#f] value that is an index to the first element in the dict table; ``first'' refers to an unspecified ordering of the dictionary elements. For a mutable @racket[dict], this index is guaranteed to refer to the first item only as long as no mappings are added to or removed from @racket[dict]. @examples[ #:eval dict-eval (dict-iterate-first #hash((a . "apple") (b . "banana"))) (dict-iterate-first #hash()) (dict-iterate-first #("apple" "banana")) (dict-iterate-first '((a . "apple") (b . "banana"))) ]} @defproc[(dict-iterate-next [dict dict?] [pos any/c]) any/c]{ Returns either a non-@racket[#f] that is an index to the element in @racket[dict] after the element indexed by @racket[pos] or @racket[#f] if @racket[pos] refers to the last element in @racket[dict]. If @racket[pos] is not a valid index, then the @exnraise[exn:fail:contract]. For a mutable @racket[dict], the result index is guaranteed to refer to its item only as long as no items are added to or removed from @racket[dict]. The @racket[dict-iterate-next] operation should take constant time. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (define i (dict-iterate-first h)) i (dict-iterate-next h i) (dict-iterate-next h (dict-iterate-next h i)) ]} @defproc[(dict-iterate-key [dict dict?] [pos any/c]) any]{ Returns the key for the element in @racket[dict] at index @racket[pos]. If @racket[pos] is not a valid index for @racket[dict], the @exnraise[exn:fail:contract]. The @racket[dict-iterate-key] operation should take constant time. @examples[ #:eval dict-eval (define h '((a . "apple") (b . "banana"))) (define i (dict-iterate-first h)) (dict-iterate-key h i) (dict-iterate-key h (dict-iterate-next h i)) ]} @defproc[(dict-iterate-value [dict dict?] [pos any/c]) any]{ Returns the value for the element in @racket[dict] at index @racket[pos]. If @racket[pos] is not a valid index for @racket[dict], the @exnraise[exn:fail:contract]. The @racket[dict-iterate-key] operation should take constant time. @examples[ #:eval dict-eval (define h '((a . "apple") (b . "banana"))) (define i (dict-iterate-first h)) (dict-iterate-value h i) (dict-iterate-value h (dict-iterate-next h i)) ]} @subsection[#:tag "derived-dict-methods"]{Derived Dictionary Methods} These methods of @racket[gen:dict] have fallback implementations in terms of the other methods; they may be supported even by dictionary types that do not directly implement them. @defproc[(dict-has-key? [dict dict?] [key any/c]) boolean?]{ Returns @racket[#t] if @racket[dict] contains a value for the given @racket[key], @racket[#f] otherwise. Supported for any @racket[dict] that implements @racket[dict-ref]. @examples[ #:eval dict-eval (dict-has-key? #hash((a . "apple") (b . "beer")) 'a) (dict-has-key? #hash((a . "apple") (b . "beer")) 'c) (dict-has-key? '((a . "apple") (b . "banana")) 'b) (dict-has-key? #("apple" "banana") 1) (dict-has-key? #("apple" "banana") 3) (dict-has-key? #("apple" "banana") -3) ]} @defproc[(dict-set*! [dict (and/c dict? (not/c immutable?))] [key any/c] [v any/c] ... ...) void?]{ Maps each @racket[key] to each @racket[v] in @racket[dict], overwriting any existing mapping for each @racket[key]. The update can fail with a @racket[exn:fail:contract] exception if @racket[dict] is not mutable or if any @racket[key] is not an allowed key for the dictionary (e.g., not an exact integer in the appropriate range when @racket[dict] is a @tech{vector}). The update takes place from the left, so later mappings overwrite earlier mappings. Supported for any @racket[dict] that implements @racket[dict-set!]. @examples[ #:eval dict-eval (define h (make-hash)) (dict-set*! h 'a "apple" 'b "banana") h (define v1 (vector #f #f #f)) (dict-set*! v1 0 "apple" 1 "banana") v1 (define v2 (vector #f #f #f)) (dict-set*! v2 0 "apple" 0 "banana") v2 ]} @defproc[(dict-set* [dict (and/c dict? immutable?)] [key any/c] [v any/c] ... ...) (and/c dict? immutable?)]{ Functionally extends @racket[dict] by mapping each @racket[key] to each @racket[v], overwriting any existing mapping for each @racket[key], and returning an extended dictionary. The update can fail with a @racket[exn:fail:contract] exception if @racket[dict] does not support functional extension or if any @racket[key] is not an allowed key for the dictionary. The update takes place from the left, so later mappings overwrite earlier mappings. Supported for any @racket[dict] that implements @racket[dict-set]. @examples[ #:eval dict-eval (dict-set* #hash() 'a "apple" 'b "beer") (dict-set* #hash((a . "apple") (b . "beer")) 'b "banana" 'a "anchor") (dict-set* '() 'a "apple" 'b "beer") (dict-set* '((a . "apple") (b . "beer")) 'b "banana" 'a "anchor") (dict-set* '((a . "apple") (b . "beer")) 'b "banana" 'b "ballistic") ]} @defproc[(dict-ref! [dict dict?] [key any/c] [to-set any/c]) any]{ Returns the value for @racket[key] in @racket[dict]. If no value is found for @racket[key], then @racket[to-set] determines the result as in @racket[dict-ref] (i.e., it is either a thunk that computes a value or a plain value), and this result is stored in @racket[dict] for the @racket[key]. (Note that if @racket[to-set] is a thunk, it is not invoked in tail position.) Supported for any @racket[dict] that implements @racket[dict-ref] and @racket[dict-set!]. @examples[ #:eval dict-eval (dict-ref! (make-hasheq '((a . "apple") (b . "beer"))) 'a #f) (dict-ref! (make-hasheq '((a . "apple") (b . "beer"))) 'c 'cabbage) (define h (make-hasheq '((a . "apple") (b . "beer")))) (eval:error (dict-ref h 'c)) (dict-ref! h 'c (λ () 'cabbage)) (dict-ref h 'c) ]} @defproc[(dict-update! [dict (and/c dict? (not/c immutable?))] [key any/c] [updater (any/c . -> . any/c)] [failure-result (failure-result/c any/c) (lambda () (raise (make-exn:fail ....)))]) void?]{ Composes @racket[dict-ref] and @racket[dict-set!] to update an existing mapping in @racket[dict], where the optional @racket[failure-result] argument is used as in @racket[dict-ref] when no mapping exists for @racket[key] already. Supported for any @racket[dict] that implements @racket[dict-ref] and @racket[dict-set!]. @examples[ #:eval dict-eval (define h (make-hash)) (eval:error (dict-update! h 'a add1)) (dict-update! h 'a add1 0) h (define v (vector #f #f #f)) (dict-update! v 0 not) v ]} @defproc[(dict-update [dict dict?] [key any/c] [updater (any/c . -> . any/c)] [failure-result (failure-result/c any/c) (lambda () (raise (make-exn:fail ....)))]) (and/c dict? immutable?)]{ Composes @racket[dict-ref] and @racket[dict-set] to functionally update an existing mapping in @racket[dict], where the optional @racket[failure-result] argument is used as in @racket[dict-ref] when no mapping exists for @racket[key] already. Supported for any @racket[dict] that implements @racket[dict-ref] and @racket[dict-set]. @examples[ #:eval dict-eval (eval:error (dict-update #hash() 'a add1)) (dict-update #hash() 'a add1 0) (dict-update #hash((a . "apple") (b . "beer")) 'b string-length) ]} @defproc[(dict-map [dict dict?] [proc (any/c any/c . -> . any/c)]) (listof any/c)]{ Applies the procedure @racket[proc] to each element in @racket[dict] in an unspecified order, accumulating the results into a list. The procedure @racket[proc] is called each time with a key and its value. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], @racket[dict-iterate-key], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (dict-map #hash((a . "apple") (b . "banana")) vector) ]} @defproc[(dict-for-each [dict dict?] [proc (any/c any/c . -> . any)]) void?]{ Applies @racket[proc] to each element in @racket[dict] (for the side-effects of @racket[proc]) in an unspecified order. The procedure @racket[proc] is called each time with a key and its value. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], @racket[dict-iterate-key], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (dict-for-each #hash((a . "apple") (b . "banana")) (lambda (k v) (printf "~a = ~s\n" k v))) ]} @defproc[(dict-empty? [dict dict?]) boolean?]{ Reports whether @racket[dict] is empty. Supported for any @racket[dict] that implements @racket[dict-iterate-first]. @examples[ #:eval dict-eval (dict-empty? #hash((a . "apple") (b . "banana"))) (dict-empty? (vector)) ] } @defproc[(dict-count [dict dict?]) exact-nonnegative-integer?]{ Returns the number of keys mapped by @racket[dict], usually in constant time. Supported for any @racket[dict] that implements @racket[dict-iterate-first] and @racket[dict-iterate-next]. @examples[ #:eval dict-eval (dict-count #hash((a . "apple") (b . "banana"))) (dict-count #("apple" "banana")) ]} @defproc[(dict-copy [dict dict?]) dict?]{ Produces a new, mutable dictionary of the same type as @racket[dict] and with the same key/value associations. Supported for any @racket[dict] that implements @racket[dict-clear], @racket[dict-set!], @racket[dict-iterate-first], @racket[dict-iterate-next], @racket[dict-iterate-key], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (define original (vector "apple" "banana")) (define copy (dict-copy original)) original copy (dict-set! copy 1 "carrot") original copy ] } @defproc[(dict-clear [dict dict?]) dict?]{ Produces an empty dictionary of the same type as @racket[dict]. If @racket[dict] is mutable, the result must be a new dictionary. Supported for any @racket[dict] that supports @racket[dict-remove], @racket[dict-iterate-first], @racket[dict-iterate-next], and @racket[dict-iterate-key]. @examples[ #:eval dict-eval (dict-clear #hash((a . "apple") ("banana" . b))) (dict-clear '((1 . two) (three . "four"))) ] } @defproc[(dict-clear! [dict dict?]) void?]{ Removes all of the key/value associations in @racket[dict]. Supported for any @racket[dict] that supports @racket[dict-remove!], @racket[dict-iterate-first], and @racket[dict-iterate-key]. @examples[ #:eval dict-eval (define table (make-hash)) (dict-set! table 'a "apple") (dict-set! table "banana" 'b) table (dict-clear! table) table ] } @defproc[(dict-keys [dict dict?]) list?]{ Returns a list of the keys from @racket[dict] in an unspecified order. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], and @racket[dict-iterate-key]. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (dict-keys h) ]} @defproc[(dict-values [dict dict?]) list?]{ Returns a list of the values from @racket[dict] in an unspecified order. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (dict-values h) ]} @defproc[(dict->list [dict dict?]) list?]{ Returns a list of the associations from @racket[dict] in an unspecified order. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], @racket[dict-iterate-key], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (dict->list h) ]} @section{Dictionary Sequences} @defproc[(in-dict [dict dict?]) sequence?]{ Returns a @tech{sequence} whose each element is two values: a key and corresponding value from @racket[dict]. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], @racket[dict-iterate-key], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (for/list ([(k v) (in-dict h)]) (format "~a = ~s" k v)) ]} @defproc[(in-dict-keys [dict dict?]) sequence?]{ Returns a sequence whose elements are the keys of @racket[dict]. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], and @racket[dict-iterate-key]. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (for/list ([k (in-dict-keys h)]) k) ]} @defproc[(in-dict-values [dict dict?]) sequence?]{ Returns a sequence whose elements are the values of @racket[dict]. Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (for/list ([v (in-dict-values h)]) v) ]} @defproc[(in-dict-pairs [dict dict?]) sequence?]{ Returns a sequence whose elements are pairs, each containing a key and its value from @racket[dict] (as opposed to using @racket[in-dict], which gets the key and value as separate values for each element). Supported for any @racket[dict] that implements @racket[dict-iterate-first], @racket[dict-iterate-next], @racket[dict-iterate-key], and @racket[dict-iterate-value]. @examples[ #:eval dict-eval (define h #hash((a . "apple") (b . "banana"))) (for/list ([p (in-dict-pairs h)]) p) ]} @section{Contracted Dictionaries} @defthing[prop:dict/contract struct-type-property?]{ A structure type property for defining dictionaries with contracts. The value associated with @racket[prop:dict/contract] must be a list of two immutable vectors: @racketblock[ (list _dict-vector (vector _type-key-contract _type-value-contract _type-iter-contract _instance-key-contract _instance-value-contract _instance-iter-contract)) ] The first vector must be a vector of 10 procedures which match the @racket[gen:dict] @tech{generic interface} (in addition, it must be an immutable vector). The second vector must contain six elements; each of the first three is a contract for the dictionary type's keys, values, and positions, respectively. Each of the second three is either @racket[#f] or a procedure used to extract the contract from a dictionary instance. } @deftogether[[ @defproc[(dict-key-contract [d dict?]) contract?] @defproc[(dict-value-contract [d dict?]) contract?] @defproc[(dict-iter-contract [d dict?]) contract?]]]{ Returns the contract that @racket[d] imposes on its keys, values, or iterators, respectively, if @racket[d] implements the @racket[prop:dict/contract] interface. } @section{Custom Hash Tables} @defform[(define-custom-hash-types name optional-predicate comparison-expr optional-hash-functions) #:grammar ([optional-predicate (code:line) (code:line #:key? predicate-expr)] [optional-hash-functions (code:line) (code:line hash1-expr) (code:line hash1-expr hash2-expr)])]{ Creates a new dictionary type based on the given comparison @racket[comparison-expr], hash functions @racket[hash1-expr] and @racket[hash2-expr], and key predicate @racket[predicate-expr]; the interfaces for these functions are the same as in @racket[make-custom-hash-types]. The new dictionary type has three variants: immutable, mutable with strongly-held keys, and mutable with weakly-held keys. Defines seven names: @itemize[ @item{@racket[name]@racketidfont{?} recognizes instances of the new type,} @item{@racketidfont{immutable-}@racket[name]@racketidfont{?} recognizes immutable instances of the new type,} @item{@racketidfont{mutable-}@racket[name]@racketidfont{?} recognizes mutable instances of the new type with strongly-held keys,} @item{@racketidfont{weak-}@racket[name]@racketidfont{?} recognizes mutable instances of the new type with weakly-held keys,} @item{@racketidfont{make-immutable-}@racket[name] constructs immutable instances of the new type,} @item{@racketidfont{make-mutable-}@racket[name] constructs mutable instances of the new type with strongly-held keys, and} @item{@racketidfont{make-weak-}@racket[name] constructs mutable instances of the new type with weakly-held keys.} ] The constructors all accept a dictionary as an optional argument, providing initial key/value pairs. @examples[ #:eval dict-eval (define-custom-hash-types string-hash #:key? string? string=? string-length) (define imm (make-immutable-string-hash '(("apple" . a) ("banana" . b)))) (define mut (make-mutable-string-hash '(("apple" . a) ("banana" . b)))) (dict? imm) (dict? mut) (string-hash? imm) (string-hash? mut) (immutable-string-hash? imm) (immutable-string-hash? mut) (dict-ref imm "apple") (dict-ref mut "banana") (dict-set! mut "banana" 'berry) (dict-ref mut "banana") (equal? imm mut) (equal? (dict-remove (dict-remove imm "apple") "banana") (make-immutable-string-hash)) ] } @defproc[(make-custom-hash-types [eql? (or/c (any/c any/c . -> . any/c) (any/c any/c (any/c any/c . -> . any/c) . -> . any/c))] [hash1 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [hash2 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [#:key? key? (any/c . -> . boolean?) (const #true)] [#:name name symbol? 'custom-hash] [#:for who symbol? 'make-custom-hash-types]) (values (any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (->* [] [dict?] dict?) (->* [] [dict?] dict?) (->* [] [dict?] dict?))]{ Creates a new dictionary type based on the given comparison function @racket[eql?], hash functions @racket[hash1] and @racket[hash2], and predicate @racket[key?]. The new dictionary type has variants that are immutable, mutable with strongly-held keys, and mutable with weakly-held keys. The given @racket[name] is used when printing instances of the new dictionary type, and the symbol @racket[who] is used for reporting errors. The comparison function @racket[eql?] may accept 2 or 3 arguments. If it accepts 2 arguments, it given two keys to compare them. If it accepts 3 arguments and does not accept 2 arguments, it is also given a recursive comparison function that handles data cycles when comparing sub-parts of the keys. The hash functions @racket[hash1] and @racket[hash2] may accept 1 or 2 arguments. If either hash function accepts 1 argument, it is applied to a key to compute the corresponding hash value. If either hash function accepts 2 arguments and does not accept 1 argument, it is also given a recursive hash function that handles data cycles when computing hash values of sub-parts of the keys. The predicate @racket[key?] must accept 1 argument and is used to recognize valid keys for the new dictionary type. Produces seven values: @itemize[ @item{a predicate recognizing all instances of the new dictionary type,} @item{a predicate recognizing immutable instances,} @item{a predicate recognizing mutable instances,} @item{a predicate recognizing weak instances,} @item{a constructor for immutable instances,} @item{a constructor for mutable instances, and} @item{a constructor for weak instances.} ] See @racket[define-custom-hash-types] for an example. } @deftogether[( @defproc[(make-custom-hash [eql? (or/c (any/c any/c . -> . any/c) (any/c any/c (any/c any/c . -> . any/c) . -> . any/c))] [hash1 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [hash2 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [#:key? key? (any/c . -> . boolean?) (const #true)]) dict?] @defproc[(make-weak-custom-hash [eql? (or/c (any/c any/c . -> . any/c) (any/c any/c (any/c any/c . -> . any/c) . -> . any/c))] [hash1 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [hash2 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [#:key? key? (any/c . -> . boolean?) (const #true)]) dict?] @defproc[(make-immutable-custom-hash [eql? (or/c (any/c any/c . -> . any/c) (any/c any/c (any/c any/c . -> . any/c) . -> . any/c))] [hash1 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [hash2 (or/c (any/c . -> . exact-integer?) (any/c (any/c . -> . exact-integer?) . -> . exact-integer?)) (const 1)] [#:key? key? (any/c . -> . boolean?) (const #true)]) dict?] )]{ Constructs an instance of a new dictionary type based on the given comparison function @racket[eql?], hash functions @racket[hash1] and @racket[hash2], and key predicate @racket[key?]. These procedures are deprecated; use @racket[define-custom-hash-types] instead. } @close-eval[dict-eval]