4.18 Dictionaries
dictionary(字典)是一种将键映射到值的数据类型的实例。以下数据类型都是字典:
vectors (using only exact integers as keys);
lists of pairs 作为 association list(关联列表),使用 equal? 比较键,键必须互不相同;以及
实现了 gen:dict generic interface 的 structures。
当 pair 的列表用作 association list 但键不互不相同时(因此它不是真正的 association list),dict-ref 和 dict-remove 等操作作用于键的第一个实例,而 dict-map 和 dict-keys 等操作则为键的每个实例产生一个元素。
| (require racket/dict) | package: base |
4.18.1 Dictionary Predicates and Contracts
注意,dict? 对 pair 不是常数时间的测试,因为检查 v 是否是 association list 可能需要遍历整个列表。
> (dict? #hash((a . "apple"))) #t
> (dict? '#("apple" "banana")) #t
> (dict? '("apple" "banana")) #f
> (dict? '((a . "apple") (b . "banana"))) #t
procedure
(dict-implements? d sym ...) → boolean?
d : dict? sym : symbol?
> (dict-implements? (hash 'a "apple") 'dict-set!) #f
> (dict-implements? (make-hash '((a . "apple") (b . "banana"))) 'dict-set!) #t
> (dict-implements? (make-hash '((b . "banana") (a . "apple"))) 'dict-remove!) #t
> (dict-implements? (vector "apple" "banana") 'dict-set!) #t
> (dict-implements? (vector 'a 'b) 'dict-remove!) #f
> (dict-implements? (vector 'a "apple") 'dict-set! 'dict-remove!) #f
procedure
(dict-implements/c sym ...) → flat-contract?
sym : symbol?
> (struct deformed-dict () #:methods gen:dict [])
> (define/contract good-dict (dict-implements/c) (deformed-dict))
> (define/contract bad-dict (dict-implements/c 'dict-ref) (deformed-dict)) bad-dict: broke its own contract
promised: (dict-implements/c dict-ref)
produced: #<deformed-dict>
in: (dict-implements/c dict-ref)
contract from: (definition bad-dict)
blaming: (definition bad-dict)
(assuming the contract is correct)
at: eval:14:0
procedure
(dict-mutable? d) → boolean?
d : dict?
等价于 (dict-implements? d 'dict-set!)。
> (dict-mutable? #hash((a . "apple"))) #f
> (dict-mutable? (make-hash)) #t
> (dict-mutable? '#("apple" "banana")) #f
> (dict-mutable? (vector "apple" "banana")) #t
> (dict-mutable? '((a . "apple") (b . "banana"))) #f
procedure
d : dict?
等价于 (or (dict-implements? d 'dict-remove!) (dict-implements? d 'dict-remove))。
> (dict-can-remove-keys? #hash((a . "apple"))) #t
> (dict-can-remove-keys? '#("apple" "banana")) #f
> (dict-can-remove-keys? '((a . "apple") (b . "banana"))) #t
procedure
d : dict?
等价于 (dict-implements? d 'dict-set)。
> (dict-can-functional-set? #hash((a . "apple"))) #t
> (dict-can-functional-set? (make-hash)) #f
> (dict-can-functional-set? '#("apple" "banana")) #f
> (dict-can-functional-set? '((a . "apple") (b . "banana"))) #t
4.18.2 Generic Dictionary Interface
syntax
> (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)))]) ; etc. other methods > (define d1 (alist '((1 . a) (2 . b)))) > (dict? d1) #t
> (dict-ref d1 1) 'a
> (dict-remove d1 1) #<alist>
value
dict-set!, or #f if unsupported
dict-set, or #f if unsupported
dict-remove!, or #f if unsupported
dict-remove, or #f if unsupported
4.18.2.1 Primitive Dictionary Methods
这些 gen:dict 方法没有回退实现;仅支持直接实现它们的字典类型。
procedure
dict : dict? key : any/c
failure-result : failure-result/c = (lambda () (raise (make-exn:fail ....)))
如果 failure-result 是一个过程,则通过尾调用以无参数方式调用它来产生结果。
否则,failure-result 作为结果返回。
> (dict-ref #hash((a . "apple") (b . "beer")) 'a) "apple"
> (dict-ref #hash((a . "apple") (b . "beer")) 'c) hash-ref: no value found for key
key: 'c
> (dict-ref #hash((a . "apple") (b . "beer")) 'c #f) #f
> (dict-ref '((a . "apple") (b . "banana")) 'b) "banana"
> (dict-ref #("apple" "banana") 1) "banana"
> (dict-ref #("apple" "banana") 3 #f) #f
> (dict-ref #("apple" "banana") -3 #f) dict-ref: contract violation
expected: natural?
given: -3
in: the k argument of
(->i
((d dict?) (k (d) (dict-key-contract d)))
((default any/c))
any)
contract from: <collects>/racket/dict.rkt
blaming: top-level
(assuming the contract is correct)
at: <collects>/racket/dict.rkt:182:2
> (define h (make-hash)) > (dict-set! h 'a "apple") > h '#hash((a . "apple"))
> (define v (vector #f #f #f)) > (dict-set! v 0 "apple") > v '#("apple" #f #f)
procedure
(dict-set dict key v) → (and/c dict? immutable?)
dict : (and/c dict? immutable?) key : any/c v : any/c
> (dict-set #hash() 'a "apple") '#hash((a . "apple"))
> (dict-set #hash((a . "apple") (b . "beer")) 'b "banana") '#hash((a . "apple") (b . "banana"))
> (dict-set '() 'a "apple") '((a . "apple"))
> (dict-set '((a . "apple") (b . "beer")) 'b "banana") '((a . "apple") (b . "banana"))
procedure
(dict-remove! dict key) → void?
dict : (and/c dict? (not/c immutable?)) key : any/c
> (define h (make-hash)) > (dict-set! h 'a "apple") > h '#hash((a . "apple"))
> (dict-remove! h 'a) > h '#hash()
procedure
(dict-remove dict key) → (and/c dict? immutable?)
dict : (and/c dict? immutable?) key : any/c
> (define h #hash()) > (define h (dict-set h 'a "apple")) > h '#hash((a . "apple"))
> (dict-remove h 'a) '#hash()
> h '#hash((a . "apple"))
> (dict-remove h 'z) '#hash((a . "apple"))
> (dict-remove '((a . "apple") (b . "banana")) 'a) '((b . "banana"))
procedure
(dict-iterate-first dict) → any/c
dict : dict?
> (dict-iterate-first #hash((a . "apple") (b . "banana"))) 0
> (dict-iterate-first #hash()) #f
> (dict-iterate-first #("apple" "banana")) 0
> (dict-iterate-first '((a . "apple") (b . "banana"))) #<assoc-iter>
procedure
(dict-iterate-next dict pos) → any/c
dict : dict? pos : any/c
> (define h #hash((a . "apple") (b . "banana"))) > (define i (dict-iterate-first h)) > i 0
> (dict-iterate-next h i) 1
> (dict-iterate-next h (dict-iterate-next h i)) #f
procedure
(dict-iterate-key dict pos) → any
dict : dict? pos : any/c
> (define h '((a . "apple") (b . "banana"))) > (define i (dict-iterate-first h)) > (dict-iterate-key h i) 'a
> (dict-iterate-key h (dict-iterate-next h i)) 'b
procedure
(dict-iterate-value dict pos) → any
dict : dict? pos : any/c
> (define h '((a . "apple") (b . "banana"))) > (define i (dict-iterate-first h)) > (dict-iterate-value h i) "apple"
> (dict-iterate-value h (dict-iterate-next h i)) "banana"
4.18.2.2 Derived Dictionary Methods
这些 gen:dict 方法基于其他方法有回退实现;即使未直接实现它们的字典类型也可能支持这些方法。
procedure
(dict-has-key? dict key) → boolean?
dict : dict? key : any/c
任何实现了 dict-ref 的 dict 都支持此方法。
> (dict-has-key? #hash((a . "apple") (b . "beer")) 'a) #t
> (dict-has-key? #hash((a . "apple") (b . "beer")) 'c) #f
> (dict-has-key? '((a . "apple") (b . "banana")) 'b) #t
> (dict-has-key? #("apple" "banana") 1) #t
> (dict-has-key? #("apple" "banana") 3) #f
> (dict-has-key? #("apple" "banana") -3) #f
procedure
(dict-set*! dict key v ... ...) → void?
dict : (and/c dict? (not/c immutable?)) key : any/c v : any/c
任何实现了 dict-set! 的 dict 都支持此方法。
> (define h (make-hash)) > (dict-set*! h 'a "apple" 'b "banana") > h '#hash((a . "apple") (b . "banana"))
> (define v1 (vector #f #f #f)) > (dict-set*! v1 0 "apple" 1 "banana") > v1 '#("apple" "banana" #f)
> (define v2 (vector #f #f #f)) > (dict-set*! v2 0 "apple" 0 "banana") > v2 '#("banana" #f #f)
procedure
(dict-set* dict key v ... ...) → (and/c dict? immutable?)
dict : (and/c dict? immutable?) key : any/c v : any/c
任何实现了 dict-set 的 dict 都支持此方法。
> (dict-set* #hash() 'a "apple" 'b "beer") '#hash((a . "apple") (b . "beer"))
> (dict-set* #hash((a . "apple") (b . "beer")) 'b "banana" 'a "anchor") '#hash((a . "anchor") (b . "banana"))
> (dict-set* '() 'a "apple" 'b "beer") '((a . "apple") (b . "beer"))
> (dict-set* '((a . "apple") (b . "beer")) 'b "banana" 'a "anchor") '((a . "anchor") (b . "banana"))
> (dict-set* '((a . "apple") (b . "beer")) 'b "banana" 'b "ballistic") '((a . "apple") (b . "ballistic"))
任何实现了 dict-ref 和 dict-set! 的 dict 都支持此方法。
> (dict-ref! (make-hasheq '((a . "apple") (b . "beer"))) 'a #f) "apple"
> (dict-ref! (make-hasheq '((a . "apple") (b . "beer"))) 'c 'cabbage) 'cabbage
> (define h (make-hasheq '((a . "apple") (b . "beer")))) > (dict-ref h 'c) hash-ref: no value found for key
key: 'c
> (dict-ref! h 'c (λ () 'cabbage)) 'cabbage
> (dict-ref h 'c) 'cabbage
procedure
(dict-update! dict key updater [ failure-result]) → void? dict : (and/c dict? (not/c immutable?)) key : any/c updater : (any/c . -> . any/c)
failure-result : failure-result/c = (lambda () (raise (make-exn:fail ....)))
任何实现了 dict-ref 和 dict-set! 的 dict 都支持此方法。
> (define h (make-hash)) > (dict-update! h 'a add1) hash-update!: no value found for key: 'a
> (dict-update! h 'a add1 0) > h '#hash((a . 1))
> (define v (vector #f #f #f)) > (dict-update! v 0 not) > v '#(#t #f #f)
procedure
(dict-update dict key updater [failure-result])
→ (and/c dict? immutable?) dict : dict? key : any/c updater : (any/c . -> . any/c)
failure-result : failure-result/c = (lambda () (raise (make-exn:fail ....)))
任何实现了 dict-ref 和 dict-set 的 dict 都支持此方法。
> (dict-update #hash() 'a add1) hash-update: no value found for key: 'a
> (dict-update #hash() 'a add1 0) '#hash((a . 1))
> (dict-update #hash((a . "apple") (b . "beer")) 'b string-length) '#hash((a . "apple") (b . 4))
任何实现了 dict-iterate-first、dict-iterate-next、dict-iterate-key 和 dict-iterate-value 的 dict 都支持此方法。
procedure
(dict-map/copy dict proc) → dict?
dict : dict? proc : (any/c any/c . -> . (values any/c any/c))
任何实现了 dict-iterate-first、dict-iterate-next、dict-iterate-key 和 dict-iterate-value,以及 dict-set 和 dict-clear,或 dict-set!、dict-copy 和 dict-clear! 的 dict 都支持此方法。
> (dict-map/copy #hash((a . "apple") (b . "banana")) (lambda (k v) (values k (string-upcase v)))) '#hash((a . "APPLE") (b . "BANANA"))
Added in version 8.5.0.2 of package base.
任何实现了 dict-iterate-first、dict-iterate-next、dict-iterate-key 和 dict-iterate-value 的 dict 都支持此方法。
> (dict-for-each #hash((a . "apple") (b . "banana")) (lambda (k v) (printf "~a = ~s\n" k v)))
b = "banana"
a = "apple"
procedure
(dict-empty? dict) → boolean?
dict : dict?
任何实现了 dict-iterate-first 的 dict 都支持此方法。
> (dict-empty? #hash((a . "apple") (b . "banana"))) #f
> (dict-empty? (vector)) #t
procedure
(dict-count dict) → exact-nonnegative-integer?
dict : dict?
任何实现了 dict-iterate-first 和 dict-iterate-next 的 dict 都支持此方法。
> (dict-count #hash((a . "apple") (b . "banana"))) 2
> (dict-count #("apple" "banana")) 2
任何实现了 dict-clear、dict-set!、dict-iterate-first、dict-iterate-next、dict-iterate-key 和 dict-iterate-value 的 dict 都支持此方法。
> (define original (vector "apple" "banana")) > (define copy (dict-copy original)) > original '#("apple" "banana")
> copy '#("apple" "banana")
> (dict-set! copy 1 "carrot") > original '#("apple" "banana")
> copy '#("apple" "carrot")
procedure
(dict-clear dict) → dict?
dict : dict?
任何支持 dict-remove、dict-iterate-first、dict-iterate-next 和 dict-iterate-key 的 dict 都支持此方法。
> (dict-clear #hash((a . "apple") ("banana" . b))) '#hash()
> (dict-clear '((1 . two) (three . "four"))) '()
procedure
(dict-clear! dict) → void?
dict : dict?
任何支持 dict-remove!、dict-iterate-first 和 dict-iterate-key 的 dict 都支持此方法。
> (define table (make-hash)) > (dict-set! table 'a "apple") > (dict-set! table "banana" 'b) > table '#hash((a . "apple") ("banana" . b))
> (dict-clear! table) > table '#hash()
任何实现了 dict-iterate-first、dict-iterate-next 和 dict-iterate-key 的 dict 都支持此方法。
procedure
(dict-values dict) → list?
dict : dict?
任何实现了 dict-iterate-first、dict-iterate-next 和 dict-iterate-value 的 dict 都支持此方法。
> (define h #hash((a . "apple") (b . "banana"))) > (dict-values h) '("banana" "apple")
procedure
(dict->list dict) → list?
dict : dict?
任何实现了 dict-iterate-first、dict-iterate-next、dict-iterate-key 和 dict-iterate-value 的 dict 都支持此方法。
> (define h #hash((a . "apple") (b . "banana"))) > (dict->list h) '((b . "banana") (a . "apple"))
4.18.3 Dictionary Sequences
任何实现了 dict-iterate-first、dict-iterate-next、dict-iterate-key 和 dict-iterate-value 的 dict 都支持此方法。
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([(k v) (in-dict h)]) (format "~a = ~s" k v)) '("b = \"banana\"" "a = \"apple\"")
procedure
(in-dict-keys dict) → sequence?
dict : dict?
任何实现了 dict-iterate-first、dict-iterate-next 和 dict-iterate-key 的 dict 都支持此方法。
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([k (in-dict-keys h)]) k) '(b a)
procedure
(in-dict-values dict) → sequence?
dict : dict?
任何实现了 dict-iterate-first、dict-iterate-next 和 dict-iterate-value 的 dict 都支持此方法。
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([v (in-dict-values h)]) v) '("banana" "apple")
procedure
(in-dict-pairs dict) → sequence?
dict : dict?
任何实现了 dict-iterate-first、dict-iterate-next、dict-iterate-key 和 dict-iterate-value 的 dict 都支持此方法。
> (define h #hash((a . "apple") (b . "banana")))
> (for/list ([p (in-dict-pairs h)]) p) '((b . "banana") (a . "apple"))
4.18.4 Contracted Dictionaries
(list dict-vector (vector type-key-contract type-value-contract type-iter-contract instance-key-contract instance-value-contract instance-iter-contract))
第一个向量必须是包含 10 个过程的向量,匹配 gen:dict generic interface(此外,它必须是不可变向量)。第二个向量必须包含六个元素;前三个分别是字典类型的键、值和位置的 contract。后三个是 #f 或用于从字典实例中提取 contract 的过程。
procedure
(dict-key-contract d) → contract?
d : dict?
procedure
(dict-value-contract d) → contract?
d : dict?
procedure
(dict-iter-contract d) → contract?
d : dict?
4.18.5 Custom Hash Tables
syntax
(define-custom-hash-types name optional-predicate comparison-expr optional-hash-functions)
optional-predicate =
| #:key? predicate-expr optional-hash-functions =
| hash1-expr | hash1-expr hash2-expr
定义七个名称:
name? 识别新类型的实例,
immutable-name? 识别新类型的不可变实例,
mutable-name? 识别新类型的键强引用可变实例,
weak-name? 识别新类型的键弱引用可变实例,
make-immutable-name 构造新类型的不可变实例,
make-mutable-name 构造新类型的键强引用可变实例,以及
make-weak-name 构造新类型的键弱引用可变实例。
所有构造函数都接受一个字典作为可选参数,提供初始键/值对。
> (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) #t
> (dict? mut) #t
> (string-hash? imm) #t
> (string-hash? mut) #t
> (immutable-string-hash? imm) #t
> (immutable-string-hash? mut) #f
> (dict-ref imm "apple") 'a
> (dict-ref mut "banana") 'b
> (dict-set! mut "banana" 'berry) > (dict-ref mut "banana") 'berry
> (equal? imm mut) #f
> (equal? (dict-remove (dict-remove imm "apple") "banana") (make-immutable-string-hash)) #t
procedure
(make-custom-hash-types eql? [ hash1 hash2 #:key? key? #:name name #:for who]) →
(any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (any/c . -> . boolean?) (->* [] [dict?] dict?) (->* [] [dict?] dict?) (->* [] [dict?] dict?)
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? : (any/c . -> . boolean?) = (const #true) name : symbol? = 'custom-hash who : symbol? = 'make-custom-hash-types
比较函数 eql? 可以接受 2 或 3 个参数。如果接受 2 个参数,则给定两个键来比较它们。如果接受 3 个参数且不接受 2 个参数,则还给定一个递归比较函数,用于在比较键的子部分时处理数据循环。
hash 函数 hash1 和 hash2 可以接受 1 或 2 个参数。如果任一 hash 函数接受 1 个参数,则将其应用于键来计算对应的 hash 值。如果任一 hash 函数接受 2 个参数且不接受 1 个参数,则还给定一个递归 hash 函数,用于在计算键的子部分的 hash 值时处理数据循环。
谓词 key? 必须接受 1 个参数,用于识别新字典类型的有效键。
产生七个值:
识别新字典类型所有实例的谓词,
识别不可变实例的谓词,
识别可变实例的谓词,
识别弱引用实例的谓词,
不可变实例的构造函数,
可变实例的构造函数,以及
弱引用实例的构造函数。
参见 define-custom-hash-types 获取示例。
make-custom-hash 和 make-weak-custom-hash 函数创建不支持函数式更新的可变字典,而 make-immutable-custom-hash 创建支持函数式更新的不可变字典。make-weak-custom-hash 创建的字典弱引用其键,类似 make-weak-hash 的结果。
当具有相同的可变性和键引用强度、关联的过程是 equal? 的、且当键和值使用 equal? 比较时键值映射相同时,make-custom-hash 等创建的字典是 equal? 的。
4.18.6 Passing Keyword Arguments in Dictionaries
procedure
(keyword-apply/dict proc kw-dict pos-arg ... pos-args #:<kw> kw-arg ...) → any proc : procedure? kw-dict : dict? pos-arg : any/c pos-args : (listof any/c) kw-arg : any/c
kw-dict 中的所有键必须是关键字。kw-dict 中的关键字不必排序。但是,kw-dict 中的关键字与直接提供的 #:<kw> 关键字不得重叠。给定的 proc 必须接受 kw-dict 中的所有关键字加上 #:<kw>。
> (define (sundae #:ice-cream [ice-cream '("vanilla")] #:toppings [toppings '("brownie-bits")] #:sprinkles [sprinkles "chocolate"] #:syrup [syrup "caramel"]) (format "A sundae with ~a ice cream, ~a, ~a sprinkles, and ~a syrup." (string-join ice-cream #:before-last " and ") (string-join toppings #:before-last " and ") sprinkles syrup)) > (keyword-apply/dict sundae '((#:ice-cream "chocolate")) '()) "A sundae with chocolate ice cream, brownie-bits, chocolate sprinkles, and caramel syrup."
> (keyword-apply/dict sundae (hash '#:toppings '("cookie-dough") '#:sprinkles "rainbow" '#:syrup "chocolate") '()) "A sundae with vanilla ice cream, cookie-dough, rainbow sprinkles, and chocolate syrup."
> (keyword-apply/dict sundae #:sprinkles "rainbow" (hash '#:toppings '("cookie-dough") '#:syrup "chocolate") '()) "A sundae with vanilla ice cream, cookie-dough, rainbow sprinkles, and chocolate syrup."
Added in version 7.9 of package base.