dictionaries
svn: r9383
This commit is contained in:
parent
ed478254e2
commit
a0f65ba33e
|
@ -946,6 +946,8 @@ operations that expect textual ports.
|
|||
(hashtable-clear! #f "r6rs-lib-Z-H-14.html" "node_idx_1204")
|
||||
(equal-hash #f "r6rs-lib-Z-H-14.html" "node_idx_1218")]
|
||||
|
||||
A hashtable is a dictionary in the sense of @schememodname[scheme/dict].
|
||||
|
||||
@r6rs-module[rnrs/enums-6 (rnrs enums (6))
|
||||
"r6rs-lib-Z-H-15.html" "node_idx_1226" "Enumerations"
|
||||
(make-enumeration #f "r6rs-lib-Z-H-15.html" "node_idx_1236")
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#lang scheme/base
|
||||
|
||||
(require scheme/dict)
|
||||
|
||||
(provide make-eq-hashtable
|
||||
make-eqv-hashtable
|
||||
(rename-out [r6rs:make-hashtable make-hashtable])
|
||||
|
@ -27,7 +29,23 @@
|
|||
unwrap
|
||||
mutable?
|
||||
equivalence-function
|
||||
hash-function))
|
||||
hash-function)
|
||||
#:property prop:dict
|
||||
(vector (case-lambda
|
||||
[(ht key)
|
||||
(hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key))]
|
||||
[(ht key default)
|
||||
(hash-ref (hashtable-ht ht) ((hashtable-wrap ht) key) default)])
|
||||
(lambda (ht key val) (hashtable-set! ht key val))
|
||||
#f
|
||||
(lambda (ht key) (hashtable-delete! ht key))
|
||||
#f
|
||||
(lambda (ht) (hashtable-size ht))
|
||||
(lambda (ht) (hash-iterate-first (hashtable-ht ht)))
|
||||
(lambda (ht pos) (hash-iterate-next (hashtable-ht ht) pos))
|
||||
(lambda (ht pos) ((hashtable-unwrap ht)
|
||||
(hash-iterate-key (hashtable-ht ht) pos)))
|
||||
(lambda (ht pos) (hash-iterate-value (hashtable-ht ht) pos))))
|
||||
|
||||
(define-struct eqv-box (val)
|
||||
#:property prop:equal+hash (list
|
||||
|
|
517
collects/scheme/dict.ss
Normal file
517
collects/scheme/dict.ss
Normal file
|
@ -0,0 +1,517 @@
|
|||
#lang scheme/base
|
||||
|
||||
(require (for-syntax scheme/base))
|
||||
|
||||
(provide prop:dict
|
||||
dict?
|
||||
|
||||
dict-mutable?
|
||||
dict-can-remove-keys?
|
||||
dict-can-functional-set?
|
||||
|
||||
dict-ref
|
||||
dict-set!
|
||||
dict-set
|
||||
dict-remove!
|
||||
dict-remove
|
||||
dict-count
|
||||
dict-iterate-first
|
||||
dict-iterate-next
|
||||
dict-iterate-key
|
||||
dict-iterate-value
|
||||
|
||||
dict-map
|
||||
dict-for-each
|
||||
|
||||
in-dict
|
||||
in-dict-keys
|
||||
in-dict-values
|
||||
in-dict-pairs
|
||||
|
||||
(rename-out [create-custom-hash make-custom-hash]
|
||||
[create-immutable-custom-hash make-immutable-custom-hash])
|
||||
make-weak-custom-hash)
|
||||
|
||||
(define-values (prop:dict dict-struct? dict-struct-ref)
|
||||
(make-struct-type-property 'dict
|
||||
(lambda (v info)
|
||||
(unless (and
|
||||
(vector? v)
|
||||
(= 10 (vector-length v))
|
||||
(let-values ([(ref set! set remove! remove count
|
||||
iterate-first iterate-next
|
||||
iterate-key iterate-value)
|
||||
(vector->values v)])
|
||||
(and (procedure? ref)
|
||||
(and (procedure-arity-includes? ref 2)
|
||||
(procedure-arity-includes? ref 3))
|
||||
(or (not set!)
|
||||
(and (procedure? set!)
|
||||
(procedure-arity-includes? set! 3)))
|
||||
(or (not set)
|
||||
(and (procedure? set)
|
||||
(procedure-arity-includes? set 3)))
|
||||
(or (not remove!)
|
||||
(and (procedure? remove!)
|
||||
(procedure-arity-includes? remove! 2)))
|
||||
(or (not remove)
|
||||
(and (procedure? remove)
|
||||
(procedure-arity-includes? remove 2)))
|
||||
(procedure? count)
|
||||
(procedure-arity-includes? count 1)
|
||||
(procedure? iterate-first)
|
||||
(procedure-arity-includes? iterate-first 1)
|
||||
(procedure? iterate-next)
|
||||
(procedure-arity-includes? iterate-next 2)
|
||||
(procedure? iterate-key)
|
||||
(procedure-arity-includes? iterate-key 2)
|
||||
(procedure? iterate-value)
|
||||
(procedure-arity-includes? iterate-value 2))))
|
||||
(raise-type-error 'prop:dict-guard
|
||||
"vector of dict methods"
|
||||
v))
|
||||
v)))
|
||||
|
||||
(define (get-dict-ref v)
|
||||
(vector-ref v 0))
|
||||
(define (get-dict-set! v)
|
||||
(vector-ref v 1))
|
||||
(define (get-dict-set v)
|
||||
(vector-ref v 2))
|
||||
(define (get-dict-remove! v)
|
||||
(vector-ref v 3))
|
||||
(define (get-dict-remove v)
|
||||
(vector-ref v 4))
|
||||
(define (get-dict-count v)
|
||||
(vector-ref v 5))
|
||||
(define (get-dict-iterate-first v)
|
||||
(vector-ref v 6))
|
||||
(define (get-dict-iterate-next v)
|
||||
(vector-ref v 7))
|
||||
(define (get-dict-iterate-key v)
|
||||
(vector-ref v 8))
|
||||
(define (get-dict-iterate-value v)
|
||||
(vector-ref v 9))
|
||||
|
||||
(define (assoc? v)
|
||||
(and (list? v) (andmap pair? v)))
|
||||
|
||||
(define (dict? v)
|
||||
(or (hash? v)
|
||||
(vector? v)
|
||||
(assoc? v)
|
||||
(dict-struct? v)))
|
||||
|
||||
(define (dict-mutable? d)
|
||||
(if (dict? d)
|
||||
(or (and (or (hash? d)
|
||||
(vector? d))
|
||||
(not (immutable? d)))
|
||||
(and (dict-struct? d)
|
||||
(get-dict-set! (dict-struct-ref d))
|
||||
#t))
|
||||
(raise-type-error 'dict-mutable? "dict" d)))
|
||||
|
||||
(define (dict-can-remove-keys? d)
|
||||
(if (dict? d)
|
||||
(or (hash? d)
|
||||
(assoc? d)
|
||||
(and (dict-struct? d)
|
||||
(or (get-dict-remove! (dict-struct-ref d))
|
||||
(get-dict-remove (dict-struct-ref d)))
|
||||
#t))
|
||||
(raise-type-error 'dict-can-remove-keys? "dict" d)))
|
||||
|
||||
(define (dict-can-functional-set? d)
|
||||
(if (dict? d)
|
||||
(or (and (hash? d) (immutable? d))
|
||||
(assoc? d)
|
||||
(and (dict-struct? d)
|
||||
(get-dict-set (dict-struct-ref d))
|
||||
#t))
|
||||
(raise-type-error 'dict-can-functional-set? "dict" d)))
|
||||
|
||||
(define dict-ref
|
||||
(case-lambda
|
||||
[(d key)
|
||||
(cond
|
||||
[(hash? d) (hash-ref d key)]
|
||||
[(vector? d) (vector-ref d key)]
|
||||
[(assoc? d)
|
||||
(let ([a (assoc key d)])
|
||||
(if a
|
||||
(cdr a)
|
||||
(raise-mismatch-error 'dict-ref
|
||||
(format "no value for key: ~e in: "
|
||||
key)
|
||||
d)))]
|
||||
[(dict-struct? d)
|
||||
((get-dict-ref (dict-struct-ref d)) d key)]
|
||||
[else
|
||||
(raise-type-error 'dict-ref 'dict 0 d key)])]
|
||||
[(d key default)
|
||||
(cond
|
||||
[(hash? d) (hash-ref d key default)]
|
||||
[(vector? d) (if (and (exact-nonnegative-integer? key)
|
||||
(key . < . (vector-length d)))
|
||||
(vector-ref d key)
|
||||
(if (procedure? default)
|
||||
(default)
|
||||
default))]
|
||||
[(assoc? d)
|
||||
(let ([a (assoc key d)])
|
||||
(if a
|
||||
(cdr a)
|
||||
(if (procedure? default)
|
||||
(default)
|
||||
default)))]
|
||||
[(dict-struct? d)
|
||||
((get-dict-ref (dict-struct-ref d)) d key default)]
|
||||
[else
|
||||
(raise-type-error 'dict-ref 'dict 0 d key default)])]))
|
||||
|
||||
(define (dict-set! d key val)
|
||||
(cond
|
||||
[(hash? d) (hash-set! d key val)]
|
||||
[(vector? d) (vector-set! d key val)]
|
||||
[(assoc? d)
|
||||
(raise-type-error 'dict-set! "mutable dict" 0 d key val)]
|
||||
[(dict-struct? d)
|
||||
(let ([s! (get-dict-set! (dict-struct-ref d))])
|
||||
(if s!
|
||||
(s! d key val)
|
||||
(raise-type-error 'dict-set! "mutable dict" 0 d key val)))]
|
||||
[else
|
||||
(raise-type-error 'dict-set! "dict" 0 d key val)]))
|
||||
|
||||
(define (dict-set d key val)
|
||||
(cond
|
||||
[(hash? d) (hash-set d key val)]
|
||||
[(vector? d)
|
||||
(raise-type-error 'dict-set "functional-update dict" 0 d key val)]
|
||||
[(assoc? d)
|
||||
(let loop ([xd d])
|
||||
(cond
|
||||
[(null? xd) (list (cons key val))]
|
||||
[else
|
||||
(let ([a (car xd)])
|
||||
(if (equal? (car a) key)
|
||||
(cons (cons key val) (cdr xd))
|
||||
(cons a (loop (cdr xd)))))]))]
|
||||
[(dict-struct? d)
|
||||
(let ([s (get-dict-set (dict-struct-ref d))])
|
||||
(if s
|
||||
(s d key val)
|
||||
(raise-type-error 'dict-set "functional-update dict" 0 d key val)))]
|
||||
[else
|
||||
(raise-type-error 'dict-set "dict" 0 d key val)]))
|
||||
|
||||
(define (dict-remove! d key)
|
||||
(cond
|
||||
[(hash? d) (hash-remove! d key)]
|
||||
[(vector? d)
|
||||
(raise-type-error 'dict-remove! "dict with removeable keys" 0 d key)]
|
||||
[(assoc? d)
|
||||
(raise-type-error 'dict-remove! "mutable dict" 0 d key)]
|
||||
[(dict-struct? d)
|
||||
(let ([r! (get-dict-remove! (dict-struct-ref d))])
|
||||
(if r!
|
||||
(r! d key)
|
||||
(raise-type-error 'dict-remove! "mutable dict with removable keys" 0 d key)))]
|
||||
[else
|
||||
(raise-type-error 'dict-remove! "dict" 0 d key)]))
|
||||
|
||||
(define (dict-remove d key)
|
||||
(cond
|
||||
[(hash? d) (hash-remove d key)]
|
||||
[(vector? d)
|
||||
(raise-type-error 'dict-remove "dict with removeable keys" 0 d key)]
|
||||
[(assoc? d)
|
||||
(let loop ([xd d])
|
||||
(cond
|
||||
[(null? xd) null]
|
||||
[else
|
||||
(let ([a (car xd)])
|
||||
(if (equal? (car a) key)
|
||||
(cdr xd)
|
||||
(cons a (loop (cdr xd)))))]))]
|
||||
[(dict-struct? d)
|
||||
(let ([s (get-dict-remove (dict-struct-ref d))])
|
||||
(if s
|
||||
(s d key)
|
||||
(raise-type-error 'dict-remove "dict with functionally removeable keys" 0 d key)))]
|
||||
[else
|
||||
(raise-type-error 'dict-remove "dict" 0 d key)]))
|
||||
|
||||
(define (dict-count d)
|
||||
(cond
|
||||
[(hash? d) (hash-count d)]
|
||||
[(vector? d) (vector-length d)]
|
||||
[(assoc? d) (length d)]
|
||||
[(dict-struct? d) ((get-dict-count (dict-struct-ref d)) d)]
|
||||
[else
|
||||
(raise-type-error 'dict-count "dict" d)]))
|
||||
|
||||
(define-struct assoc-iter (head pos))
|
||||
|
||||
(define (dict-iterate-first d)
|
||||
(cond
|
||||
[(hash? d) (hash-iterate-first d)]
|
||||
[(vector? d) (if (zero? (vector-length d))
|
||||
#f
|
||||
0)]
|
||||
[(assoc? d) (if (null? d) #f (make-assoc-iter d d))]
|
||||
[(dict-struct? d) ((get-dict-iterate-first (dict-struct-ref d)) d)]
|
||||
[else
|
||||
(raise-type-error 'dict-iterate-first "dict" d)]))
|
||||
|
||||
(define (dict-iterate-next d i)
|
||||
(cond
|
||||
[(hash? d) (hash-iterate-next d i)]
|
||||
[(vector? d) (let ([len (vector-length d)])
|
||||
(cond
|
||||
[(and (exact-nonnegative-integer? i)
|
||||
(i . < . len))
|
||||
(let ([i (add1 i)])
|
||||
(if (= i len)
|
||||
#f
|
||||
i))]
|
||||
[else
|
||||
(raise-mismatch-error
|
||||
'dict-iterate-next
|
||||
"invalid iteration position for vector: "
|
||||
i)]))]
|
||||
[(and (assoc-iter? i)
|
||||
(eq? d (assoc-iter-head i)))
|
||||
(let ([pos (cdr (assoc-iter-pos i))])
|
||||
(if (null? pos)
|
||||
#f
|
||||
(make-assoc-iter d pos)))]
|
||||
[(dict-struct? d) ((get-dict-iterate-next (dict-struct-ref d)) d i)]
|
||||
[(assoc? d)
|
||||
(raise-mismatch-error
|
||||
'dict-iterate-next
|
||||
"invalid iteration position for association list: "
|
||||
i)]
|
||||
[else
|
||||
(raise-type-error 'dict-iterate-next "dict" d)]))
|
||||
|
||||
(define (dict-iterate-key d i)
|
||||
(cond
|
||||
[(hash? d) (hash-iterate-key d i)]
|
||||
[(vector? d) i]
|
||||
[(and (assoc-iter? i) (eq? d (assoc-iter-head i))) (caar (assoc-iter-pos i))]
|
||||
[(dict-struct? d) ((get-dict-iterate-key (dict-struct-ref d)) d i)]
|
||||
[(assoc? d)
|
||||
(raise-mismatch-error
|
||||
'dict-iterate-key
|
||||
"invalid iteration position for association list: "
|
||||
i)]
|
||||
[else
|
||||
(raise-type-error 'dict-iterate-key "dict" d)]))
|
||||
|
||||
(define (dict-iterate-value d i)
|
||||
(cond
|
||||
[(hash? d) (hash-iterate-value d i)]
|
||||
[(vector? d) (vector-ref d i)]
|
||||
[(and (assoc-iter? i) (eq? d (assoc-iter-head i))) (cdar (assoc-iter-pos i))]
|
||||
[(dict-struct? d) ((get-dict-iterate-value (dict-struct-ref d)) d i)]
|
||||
[(assoc? d)
|
||||
(raise-mismatch-error
|
||||
'dict-iterate-value
|
||||
"invalid iteration position for association list: "
|
||||
i)]
|
||||
[else
|
||||
(raise-type-error 'dict-iterate-value "dict" d)]))
|
||||
|
||||
(define-sequence-syntax :in-dict
|
||||
(lambda () #'in-dict)
|
||||
(lambda (stx)
|
||||
(syntax-case stx ()
|
||||
[((key-id val-id) (_ dict-expr))
|
||||
#'[(key-id val-id)
|
||||
(:do-in ([(d) dict-expr])
|
||||
(void)
|
||||
([i (dict-iterate-first d)])
|
||||
i
|
||||
([key-id (dict-iterate-key d i)]
|
||||
[val-id (dict-iterate-value d i)])
|
||||
#t
|
||||
#t
|
||||
((dict-iterate-next d i)))]]
|
||||
[_ #f])))
|
||||
|
||||
(define (in-dict d)
|
||||
(make-dict-sequence
|
||||
d
|
||||
(lambda (i)
|
||||
(values (dict-iterate-key d i)
|
||||
(dict-iterate-value d i)))
|
||||
(lambda (k v) #t)
|
||||
(lambda (i k v) #t)))
|
||||
|
||||
(define (in-dict-keys d)
|
||||
(make-dict-sequence
|
||||
d
|
||||
(lambda (i) (dict-iterate-key d i))
|
||||
(lambda (k) #t)
|
||||
(lambda (i k) #t)))
|
||||
|
||||
(define (in-dict-values d)
|
||||
(make-dict-sequence
|
||||
d
|
||||
(lambda (i) (dict-iterate-value d i))
|
||||
(lambda (v) #t)
|
||||
(lambda (i v) #t)))
|
||||
|
||||
(define (in-dict-pairs d)
|
||||
(make-dict-sequence
|
||||
d
|
||||
(lambda (i)
|
||||
(cons (dict-iterate-key d i)
|
||||
(dict-iterate-value d i)))
|
||||
(lambda (p) #t)
|
||||
(lambda (i p) #t)))
|
||||
|
||||
(define (make-dict-sequence d get val-true val+pos-true)
|
||||
(make-do-sequence
|
||||
(lambda ()
|
||||
(values get
|
||||
(lambda (i) (dict-iterate-next d i))
|
||||
(dict-iterate-first d)
|
||||
(lambda (i) i)
|
||||
val-true
|
||||
val+pos-true))))
|
||||
|
||||
(define (dict-map d f)
|
||||
(for/list ([(k v) (in-dict d)])
|
||||
(f k v)))
|
||||
|
||||
(define (dict-for-each d f)
|
||||
(for ([(k v) (in-dict d)])
|
||||
(f k v)))
|
||||
|
||||
;; ----------------------------------------
|
||||
|
||||
(define-struct hash-box (key))
|
||||
|
||||
(define custom-hash-ref
|
||||
(case-lambda
|
||||
[(d k) (hash-ref (custom-hash-table d)
|
||||
((custom-hash-make-box d) k)
|
||||
(lambda ()
|
||||
(raise-mismatch-error
|
||||
'dict-ref
|
||||
"no value found for key: "
|
||||
k)))]
|
||||
[(d k fail) (hash-ref (custom-hash-table d)
|
||||
((custom-hash-make-box d) k)
|
||||
fail)]))
|
||||
|
||||
(define (custom-hash-set! d k v)
|
||||
(hash-set! (custom-hash-table d)
|
||||
((custom-hash-make-box d) k)
|
||||
v))
|
||||
|
||||
(define (custom-hash-set d k v)
|
||||
(let ([table (hash-set (custom-hash-table d)
|
||||
((custom-hash-make-box d) k)
|
||||
v)])
|
||||
(make-immutable-custom-hash table
|
||||
(custom-hash-make-box d))))
|
||||
|
||||
(define (custom-hash-remove! d k)
|
||||
(hash-remove! (custom-hash-table d)
|
||||
((custom-hash-make-box d) k)))
|
||||
|
||||
(define (custom-hash-remove d k)
|
||||
(let ([table (hash-remove (custom-hash-table d)
|
||||
((custom-hash-make-box d) k))])
|
||||
(make-immutable-custom-hash table
|
||||
(custom-hash-make-box d))))
|
||||
|
||||
(define (custom-hash-count d)
|
||||
(hash-count (custom-hash-table d)))
|
||||
|
||||
(define (custom-hash-iterate-first d)
|
||||
(hash-iterate-first (custom-hash-table d)))
|
||||
|
||||
(define (custom-hash-iterate-next d i)
|
||||
(hash-iterate-next (custom-hash-table d) i))
|
||||
|
||||
(define (custom-hash-iterate-key d i)
|
||||
(hash-box-key (hash-iterate-key (custom-hash-table d) i)))
|
||||
|
||||
(define (custom-hash-iterate-value d i)
|
||||
(hash-iterate-value (custom-hash-table d) i))
|
||||
|
||||
(define-struct custom-hash (table make-box)
|
||||
#:property prop:dict
|
||||
(vector custom-hash-ref
|
||||
custom-hash-set!
|
||||
#f
|
||||
custom-hash-remove!
|
||||
#f
|
||||
custom-hash-count
|
||||
custom-hash-iterate-first
|
||||
custom-hash-iterate-next
|
||||
custom-hash-iterate-key
|
||||
custom-hash-iterate-value)
|
||||
#:property prop:equal+hash
|
||||
(list (lambda (a b recur)
|
||||
(and (recur (custom-hash-make-box a)
|
||||
(custom-hash-make-box b))
|
||||
(recur (custom-hash-table a)
|
||||
(custom-hash-table b))))
|
||||
(lambda (a recur) (recur (custom-hash-table a)))
|
||||
(lambda (a recur) (recur (custom-hash-table a)))))
|
||||
|
||||
(define-struct (immutable-custom-hash custom-hash) ()
|
||||
#:property prop:dict
|
||||
(vector custom-hash-ref
|
||||
#f
|
||||
custom-hash-set
|
||||
#f
|
||||
custom-hash-remove
|
||||
custom-hash-count
|
||||
custom-hash-iterate-first
|
||||
custom-hash-iterate-next
|
||||
custom-hash-iterate-key
|
||||
custom-hash-iterate-value))
|
||||
|
||||
(define-values (create-custom-hash
|
||||
create-immutable-custom-hash
|
||||
make-weak-custom-hash)
|
||||
(let ([mk
|
||||
(lambda (hash hash2 =? who make-custom-hash table)
|
||||
(unless (and (procedure? =?)
|
||||
(procedure-arity-includes? =? 2))
|
||||
(raise-type-error who "procedure (arity 2)" =?))
|
||||
(unless (and (procedure? hash)
|
||||
(procedure-arity-includes? hash 1))
|
||||
(raise-type-error who "procedure (arity 1)" hash))
|
||||
(unless (and (procedure? hash2)
|
||||
(procedure-arity-includes? hash2 1))
|
||||
(raise-type-error who "procedure (arity 1)" hash2))
|
||||
(let ()
|
||||
(define-struct (box hash-box) ()
|
||||
#:property prop:equal+hash (list
|
||||
(lambda (a b recur)
|
||||
(=? (hash-box-key a) (hash-box-key b)))
|
||||
(lambda (v recur)
|
||||
(hash (hash-box-key v)))
|
||||
(lambda (v recur)
|
||||
(hash2 (hash-box-key v)))))
|
||||
(make-custom-hash table make-box)))])
|
||||
(let ([make-custom-hash
|
||||
(lambda (=? hash [hash2 (lambda (v) 10001)])
|
||||
(mk hash hash2 =? 'make-custom-hash make-custom-hash (make-hash)))]
|
||||
[make-immutable-custom-hash
|
||||
(lambda (=? hash [hash2 (lambda (v) 10001)])
|
||||
(mk hash hash2 =? 'make-immutable-custom-hash make-immutable-custom-hash #hash()))]
|
||||
[make-weak-custom-hash
|
||||
(lambda (=? hash [hash2 (lambda (v) 10001)])
|
||||
(mk hash hash2 =? 'make-immutable-custom-hash make-immutable-custom-hash (make-weak-hash)))])
|
||||
(values make-custom-hash
|
||||
make-immutable-custom-hash
|
||||
make-weak-custom-hash))))
|
|
@ -2,6 +2,7 @@
|
|||
(require scheme/contract
|
||||
scheme/class
|
||||
scheme/unit
|
||||
scheme/dict
|
||||
scheme/include
|
||||
scheme/pretty
|
||||
scheme/math
|
||||
|
@ -24,6 +25,7 @@
|
|||
(provide (all-from-out scheme/contract
|
||||
scheme/class
|
||||
scheme/unit
|
||||
scheme/dict
|
||||
scheme/include
|
||||
scheme/pretty
|
||||
scheme/math
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
(define-sequence-syntax in-par
|
||||
(lambda () (raise-syntax-error 'in-par "bad"))
|
||||
(lambda (orig-stx stx)
|
||||
(lambda (stx)
|
||||
(syntax-case stx ()
|
||||
[((id) (_ lst-exprs))
|
||||
#'[(id)
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
0
|
||||
proc1 proc2))
|
||||
(unless (and (procedure? proc2)
|
||||
(procedure-arity-includes? proc2 2))
|
||||
(procedure-arity-includes? proc2 1))
|
||||
(raise-type-error 'define-sequence-syntax
|
||||
"procedure (arity 1)"
|
||||
1
|
||||
|
@ -165,7 +165,7 @@
|
|||
(let ([xformer (sequence-transformer-ref m 1)]
|
||||
[introducer (make-syntax-introducer)]
|
||||
[certifier (sequence-transformer-ref m 2)])
|
||||
(let ([xformed (xformer orig-stx (introducer (syntax-local-introduce clause)))])
|
||||
(let ([xformed (xformer (introducer (syntax-local-introduce clause)))])
|
||||
(if xformed
|
||||
(expand-clause orig-stx (certify-clause (syntax-local-introduce (introducer xformed))
|
||||
certifier
|
||||
|
@ -863,7 +863,7 @@
|
|||
|
||||
(define-sequence-syntax *in-range
|
||||
(lambda () #'in-range)
|
||||
(lambda (orig-stx stx)
|
||||
(lambda (stx)
|
||||
(let loop ([stx stx])
|
||||
(syntax-case stx ()
|
||||
[[(id) (_ a b step)] #`[(id)
|
||||
|
@ -898,7 +898,7 @@
|
|||
|
||||
(define-sequence-syntax *in-naturals
|
||||
(lambda () #'in-naturals)
|
||||
(lambda (orig-stx stx)
|
||||
(lambda (stx)
|
||||
(let loop ([stx stx])
|
||||
(syntax-case stx ()
|
||||
[[(id) (_ start)]
|
||||
|
@ -929,7 +929,7 @@
|
|||
|
||||
(define-sequence-syntax *in-list
|
||||
(lambda () #'in-list)
|
||||
(lambda (orig-stx stx)
|
||||
(lambda (stx)
|
||||
(syntax-case stx ()
|
||||
[((id) (_ lst-expr))
|
||||
#'[(id)
|
||||
|
@ -956,7 +956,7 @@
|
|||
vector-length-id
|
||||
in-vector-id
|
||||
vector-ref-id)
|
||||
(lambda (orig-stx stx)
|
||||
(lambda (stx)
|
||||
(with-syntax ([vector? vector?-id]
|
||||
[in-vector in-vector-id]
|
||||
[vector-length vector-length-id]
|
||||
|
@ -1009,7 +1009,7 @@
|
|||
|
||||
(define-sequence-syntax *in-indexed
|
||||
(lambda () #'in-indexed)
|
||||
(lambda (orig-stx stx)
|
||||
(lambda (stx)
|
||||
(syntax-case stx ()
|
||||
[((id1 id2) (_ gen-expr))
|
||||
#'[(id1 id2) (in-parallel gen-expr (*in-naturals))]]))))
|
||||
|
|
|
@ -195,6 +195,18 @@
|
|||
(hash-set! ht v v2)
|
||||
(set-box! v2 (copy-value (unbox v) ht))
|
||||
v2)]
|
||||
[(hash? v) (let ([ph (make-placeholder #f)])
|
||||
(hash-set! ht v ph)
|
||||
(let ([a (hash-map v (lambda (k v)
|
||||
(cons (copy-value k ht)
|
||||
(copy-value v ht))))])
|
||||
(placeholder-set!
|
||||
ph
|
||||
((if (hash-eq? v)
|
||||
make-hasheq-placeholder
|
||||
make-hash-placeholder)
|
||||
a)))
|
||||
ph)]
|
||||
[else v]))
|
||||
|
||||
(define (strip-comments stx)
|
||||
|
|
137
collects/scribblings/reference/booleans.scrbl
Normal file
137
collects/scribblings/reference/booleans.scrbl
Normal file
|
@ -0,0 +1,137 @@
|
|||
#lang scribble/doc
|
||||
@(require "mz.ss")
|
||||
|
||||
@title[#:tag "booleans"]{Booleans and Equality}
|
||||
|
||||
True and false are represented by the values @scheme[#t] and
|
||||
@scheme[#f], respectively, though operations that depend a boolean
|
||||
value typically treat anything other than @scheme[#f] as true.
|
||||
|
||||
See also: @scheme[and], @scheme[or], @scheme[andmap], @scheme[ormap].
|
||||
|
||||
|
||||
@defproc[(boolean? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is @scheme[#t] or @scheme[#f],
|
||||
@scheme[#f] otherwise.}
|
||||
|
||||
|
||||
@defproc[(not [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is @scheme[#f], @scheme[#f] otherwise.
|
||||
}
|
||||
|
||||
|
||||
@defproc[(equal? [v1 any/c] [v2 any/c]) boolean?]{
|
||||
|
||||
Two values are @scheme[equal?] if and only if they are @scheme[eqv?],
|
||||
unless otherwise specified for a particular datatype.
|
||||
|
||||
Datatypes with further specification of @scheme[equal?] include
|
||||
strings, byte strings, numbers, pairs, mutable pairs, vectors, hash
|
||||
tables, and inspectable structures. In the last five cases, equality
|
||||
is recursively defined; if both @scheme[v1] and @scheme[v2] contain
|
||||
reference cycles, they are equal when the infinite unfoldings of the
|
||||
values would be equal. See also @scheme[prop:equal+hash].}
|
||||
|
||||
|
||||
@defproc[(eqv? [v1 any/c] [v2 any/c]) boolean?]{
|
||||
|
||||
Two values are @scheme[eqv?] if and only if they are @scheme[eq?],
|
||||
unless otherwise specified for a particular datatype.
|
||||
|
||||
The number and character datatypes are the only ones for which
|
||||
@scheme[eqv?] differs from @scheme[eq?].}
|
||||
|
||||
|
||||
@defproc[(eq? [v1 any/c] [v2 any/c]) boolean?]{
|
||||
|
||||
Return @scheme[#t] if @scheme[v1] and @scheme[v2] refer to the same
|
||||
object, @scheme[#f] otherwise. See also @secref["model-eq"].}
|
||||
|
||||
|
||||
@defproc[(immutable? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is an immutable @tech{string},
|
||||
@tech{byte string}, @tech{vector}, @tech{hash table}, or box,
|
||||
@scheme[#f] otherwise.}
|
||||
|
||||
@defthing[prop:equal+hash struct-type-property?]{
|
||||
|
||||
A @tech{structure type property} (see @secref["structprops"]) that
|
||||
supplies an equality predicate and hashing functions for a structure
|
||||
type. The property value must be a list of three procedures:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{@scheme[_equal-proc : (any/c any/c (any/c any/c . ->
|
||||
. boolean?) . -> . any/c)] --- tests whether the first two
|
||||
arguments are equal, where both values are instances of the
|
||||
structure type to which the property is associated (or a
|
||||
subtype of the structure type).
|
||||
|
||||
The third argument is an @scheme[equal?] predicate to use for
|
||||
recursive equality checks; use the given predicate instead of
|
||||
@scheme[equal?] to ensure that data cycles are handled
|
||||
properly.
|
||||
|
||||
The @scheme[_equal-proc] is called for a pair of structures
|
||||
only when they are not @scheme[eq?], and only when they both
|
||||
have a @scheme[prop:equal+hash] value inherited from the same
|
||||
structure type. With this strategy, the order in which
|
||||
@scheme[equal?] receives two structures does not matter. It
|
||||
also means that, by default, a structure sub-type inherits the
|
||||
equality predicate of its parent, if any.}
|
||||
|
||||
@item{@scheme[_hash-proc : (any/c (any/c . -> . exact-integer?) . ->
|
||||
. exact-integer?)] --- computes a hash code for the given
|
||||
structure, like @scheme[equal-hash-code]. The first argument is
|
||||
an instance of the structure type (or one of its subtypes) to
|
||||
which the property is associated.
|
||||
|
||||
The second argument is a @scheme[equal-hash-code]-like
|
||||
procedure to use for recursive hash-code computation; use the
|
||||
given procedure instead of @scheme[equal-hash-code] to ensure
|
||||
that data cycles are handled properly.}
|
||||
|
||||
@item{@scheme[_hash2-proc : (any/c (any/c . -> . exact-integer?) . ->
|
||||
. exact-integer?)] --- computes a secondary hash code for the
|
||||
given structure. This procedure is like @scheme[_hash-proc],
|
||||
but analogous to @scheme[equal-secondary-hash-code].}
|
||||
|
||||
}
|
||||
|
||||
Take care to ensure that @scheme[_hash-proc] and @scheme[_hash2-proc]
|
||||
are consistent with @scheme[_equal-proc]. Specifically,
|
||||
@scheme[_hash-proc] and @scheme[_hash2-proc] should produce the same
|
||||
value for any two structures for which @scheme[_equal-proc] produces a
|
||||
true value.
|
||||
|
||||
When a structure type has no @scheme[prop:equal+hash] property, then
|
||||
transparent structures (i.e., structures with an @tech{inspector} that
|
||||
is controlled by the current @tech{inspector}) are @scheme[equal?]
|
||||
when they are instances of the same structure type (not counting
|
||||
sub-types), and when they have @scheme[equal?] field values. For
|
||||
transparent structures, @scheme[equal-hash-code] and
|
||||
@scheme[equal-secondary-hash-code] derive hash code using the field
|
||||
values. For opaque structure types, @scheme[equal?] is the same as
|
||||
@scheme[eq?], and @scheme[equal-hash-code] and
|
||||
@scheme[equal-secondary-hash-code] results are based only on
|
||||
@scheme[eq-hash-code].}
|
||||
|
||||
@section{Boolean Synonyms}
|
||||
|
||||
@note-lib[scheme/bool]
|
||||
|
||||
@defthing[true boolean?]{A synonym for @scheme[#t].}
|
||||
|
||||
@defthing[false boolean?]{A synonym for @scheme[#f].}
|
||||
|
||||
@defproc[(symbol=? [a symbol?] [b symbol?]) boolean?]{
|
||||
|
||||
Returns @scheme[(equal? a b)].}
|
||||
|
||||
@defproc[(boolean=? [a boolean?] [b boolean?]) boolean?]{
|
||||
|
||||
Returns @scheme[(equal? a b)].}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
@local-table-of-contents[]
|
||||
|
||||
A @pidefterm{byte string} is a fixed-length arary of bytes. A
|
||||
A @deftech{byte string} is a fixed-length arary of bytes. A
|
||||
@pidefterm{byte} is an exact integer between @scheme[0] and
|
||||
@scheme[255] inclusive.
|
||||
|
||||
|
|
|
@ -1,147 +1,15 @@
|
|||
#lang scribble/doc
|
||||
@(require "mz.ss")
|
||||
|
||||
@title[#:style 'toc #:tag "data"]{Primitive Datatypes}
|
||||
@title[#:style 'toc #:tag "data"]{Datatypes}
|
||||
|
||||
Each of the built-in datatypes comes with a set of procedures for
|
||||
manipulating members of the datatype.
|
||||
Each pre-defined datatype comes with a set of procedures for
|
||||
manipulating instances of the datatype.
|
||||
|
||||
@local-table-of-contents[#:style 'immediate-only]
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
|
||||
@section[#:tag "booleans"]{Booleans}
|
||||
|
||||
True and false are represented by the values @scheme[#t] and
|
||||
@scheme[#f], respectively, though operations that depend a boolean
|
||||
value typically treat anything other than @scheme[#f] as true.
|
||||
|
||||
See also: @scheme[and], @scheme[or], @scheme[andmap], @scheme[ormap].
|
||||
|
||||
|
||||
@defproc[(boolean? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is @scheme[#t] or @scheme[#f],
|
||||
@scheme[#f] otherwise.}
|
||||
|
||||
|
||||
@defproc[(not [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is @scheme[#f], @scheme[#f] otherwise.
|
||||
}
|
||||
|
||||
|
||||
@defproc[(equal? [v1 any/c] [v2 any/c]) boolean?]{
|
||||
|
||||
Two values are @scheme[equal?] if and only if they are @scheme[eqv?],
|
||||
unless otherwise specified for a particular datatype.
|
||||
|
||||
Datatypes with further specification of @scheme[equal?] include
|
||||
strings, byte strings, numbers, pairs, mutable pairs, vectors, hash
|
||||
tables, and inspectable structures. In the last five cases, equality
|
||||
is recursively defined; if both @scheme[v1] and @scheme[v2] contain
|
||||
reference cycles, they are equal when the infinite unfoldings of the
|
||||
values would be equal. See also @scheme[prop:equal+hash].}
|
||||
|
||||
|
||||
@defproc[(eqv? [v1 any/c] [v2 any/c]) boolean?]{
|
||||
|
||||
Two values are @scheme[eqv?] if and only if they are @scheme[eq?],
|
||||
unless otherwise specified for a particular datatype.
|
||||
|
||||
The number and character datatypes are the only ones for which
|
||||
@scheme[eqv?] differs from @scheme[eq?].}
|
||||
|
||||
|
||||
@defproc[(eq? [v1 any/c] [v2 any/c]) boolean?]{
|
||||
|
||||
Return @scheme[#t] if @scheme[v1] and @scheme[v2] refer to the same
|
||||
object, @scheme[#f] otherwise. See also @secref["model-eq"].}
|
||||
|
||||
|
||||
@defproc[(immutable? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is an immutable string, byte string,
|
||||
vector, or box, @scheme[#f] otherwise.}
|
||||
|
||||
@defthing[prop:equal+hash struct-type-property?]{
|
||||
|
||||
A @tech{structure type property} (see @secref["structprops"]) that
|
||||
supplies an equality predicate and hashing functions for a structure
|
||||
type. The property value must be a list of three procedures:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{@scheme[_equal-proc : (any/c any/c (any/c any/c . ->
|
||||
. boolean?) . -> . any/c)] --- tests whether the first two
|
||||
arguments are equal, where both values are instances of the
|
||||
structure type to which the property is associated (or a
|
||||
subtype of the structure type).
|
||||
|
||||
The third argument is an @scheme[equal?] predicate to use for
|
||||
recursive equality checks; use the given predicate instead of
|
||||
@scheme[equal?] to ensure that data cycles are handled
|
||||
properly.
|
||||
|
||||
The @scheme[_equal-proc] is called for a pair of structures
|
||||
only when they are not @scheme[eq?], and only when they both
|
||||
have a @scheme[prop:equal+hash] value inherited from the same
|
||||
structure type. With this strategy, the order in which
|
||||
@scheme[equal?] receives two structures does not matter. It
|
||||
also means that, by default, a structure sub-type inherits the
|
||||
equality predicate of its parent, if any.}
|
||||
|
||||
@item{@scheme[_hash-proc : (any/c (any/c . -> . exact-integer?) . ->
|
||||
. exact-integer?)] --- computes a hash code for the given
|
||||
structure, like @scheme[equal-hash-code]. The first argument is
|
||||
an instance of the structure type (or one of its subtypes) to
|
||||
which the property is associated.
|
||||
|
||||
The second argument is a @scheme[equal-hash-code]-like
|
||||
procedure to use for recursive hash-code computation; use the
|
||||
given procedure instead of @scheme[equal-hash-code] to ensure
|
||||
that data cycles are handled properly.}
|
||||
|
||||
@item{@scheme[_hash2-proc : (any/c (any/c . -> . exact-integer?) . ->
|
||||
. exact-integer?)] --- computes a secondary hash code for the
|
||||
given structure. This procedure is like @scheme[_hash-proc],
|
||||
but analogous to @scheme[equal-secondary-hash-code].}
|
||||
|
||||
}
|
||||
|
||||
Take care to ensure that @scheme[_hash-proc] and @scheme[_hash2-proc]
|
||||
are consistent with @scheme[_equal-proc]. Specifically,
|
||||
@scheme[_hash-proc] and @scheme[_hash2-proc] should produce the same
|
||||
value for any two structures for which @scheme[_equal-proc] produces a
|
||||
true value.
|
||||
|
||||
When a structure type has no @scheme[prop:equal+hash] property, then
|
||||
transparent structures (i.e., structures with an @tech{inspector} that
|
||||
is controlled by the current @tech{inspector}) are @scheme[equal?]
|
||||
when they are instances of the same structure type (not counting
|
||||
sub-types), and when they have @scheme[equal?] field values. For
|
||||
transparent structures, @scheme[equal-hash-code] and
|
||||
@scheme[equal-secondary-hash-code] derive hash code using the field
|
||||
values. For opaque structure types, @scheme[equal?] is the same as
|
||||
@scheme[eq?], and @scheme[equal-hash-code] and
|
||||
@scheme[equal-secondary-hash-code] results are based only on
|
||||
@scheme[eq-hash-code].}
|
||||
|
||||
@subsection{Boolean Synonyms}
|
||||
|
||||
@note-lib[scheme/bool]
|
||||
|
||||
@defthing[true boolean?]{A synonym for @scheme[#t].}
|
||||
|
||||
@defthing[false boolean?]{A synonym for @scheme[#f].}
|
||||
|
||||
@defproc[(symbol=? [a symbol?] [b symbol?]) boolean?]{
|
||||
|
||||
Returns @scheme[(equal? a b)].}
|
||||
|
||||
@defproc[(boolean=? [a boolean?] [b boolean?]) boolean?]{
|
||||
|
||||
Returns @scheme[(equal? a b)].}
|
||||
@include-section["booleans.scrbl"]
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@include-section["numbers.scrbl"]
|
||||
|
@ -163,10 +31,10 @@ Returns @scheme[(equal? a b)].}
|
|||
@section-index["symbols" "generating"]
|
||||
@section-index["symbols" "unique"]
|
||||
|
||||
A symbol is like an immutable string, but symbols are normally
|
||||
@deftech{interned}, so that two symbols with the same character
|
||||
content are normally @scheme[eq?]. All symbols produced by the default
|
||||
reader (see @secref["parse-symbol"]) are interned.
|
||||
A @deftech{symbol} is like an immutable string, but symbols are
|
||||
normally @deftech{interned}, so that two symbols with the same
|
||||
character content are normally @scheme[eq?]. All symbols produced by
|
||||
the default reader (see @secref["parse-symbol"]) are interned.
|
||||
|
||||
The two procedures @scheme[string->uninterned-symbol] and
|
||||
@scheme[gensym] generate @deftech{uninterned} symbols, i.e., symbols
|
||||
|
@ -222,8 +90,8 @@ ephemeron key (see @secref["ephemerons"]).
|
|||
|
||||
@guideintro["keywords"]{keywords}
|
||||
|
||||
A keyword is like an @tech{interned} symbol, but its printed form
|
||||
starts with @litchar{#:}, and a keyword cannot be used as an
|
||||
A @deftech{keyword} is like an @tech{interned} symbol, but its printed
|
||||
form starts with @litchar{#:}, and a keyword cannot be used as an
|
||||
identifier. Furthermore, a keyword by itself is not a valid
|
||||
expression, though a keyword can be @scheme[quote]d to form an
|
||||
expression that produces the symbol.
|
||||
|
@ -259,148 +127,8 @@ for each pair of keywords is the same as using
|
|||
@; ----------------------------------------------------------------------
|
||||
@include-section["mpairs.scrbl"]
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "vectors"]{Vectors}
|
||||
|
||||
A vector is a fixed-length array with constant-time access and update
|
||||
of the vector slots, which are numbered from @scheme[0] to one less
|
||||
than the number of slots in the vector.
|
||||
|
||||
Two vectors are @scheme[equal?] if they have the same length, and if
|
||||
the values in corresponding slots of the the vectors are
|
||||
@scheme[equal?].
|
||||
|
||||
A vector can be @defterm{mutable} or @defterm{immutable}. When an
|
||||
immutable vector is provided to a procedure like @scheme[vector-set!],
|
||||
the @exnraise[exn:fail:contract]. Vectors generated by the default
|
||||
reader (see @secref["parse-string"]) are immutable.
|
||||
|
||||
A vector can be used as a single-valued sequence (see
|
||||
@secref["sequences"]). The elements of the vector serve as elements
|
||||
of the sequence. See also @scheme[in-vector].
|
||||
|
||||
@defproc[(vector? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is a vector, @scheme[#f] otherwise.}
|
||||
|
||||
|
||||
@defproc[(make-vector [size nonnegative-exact-integer?]
|
||||
[v any/c 0]) vector?]{
|
||||
|
||||
Returns a mutable vector with @scheme[size] slots, where all slots are
|
||||
initialized to contain @scheme[v].}
|
||||
|
||||
|
||||
@defproc[(vector [v any/c] ...) vector?]{
|
||||
|
||||
Returns a mutable vector with as many slots as provided @scheme[v]s,
|
||||
where the slots are initialized to contain the given @scheme[v]s in
|
||||
order.}
|
||||
|
||||
|
||||
@defproc[(vector-immutable [v any/c] ...) (and/c vector?
|
||||
immutable?)]{
|
||||
|
||||
Returns an immutable vector with as many slots as provided
|
||||
@scheme[v]s, where the slots are contain the given @scheme[v]s in
|
||||
order.}
|
||||
|
||||
|
||||
|
||||
@defproc[(vector-length [vec vector?]) nonnegative-exact-integer?]{
|
||||
|
||||
Returns the length of @scheme[vec] (i.e., the number of slots in the
|
||||
vector).}
|
||||
|
||||
@defproc[(vector-ref [vec vector?][pos nonnegative-exact-integer?]) any/c]{
|
||||
|
||||
Returns the element in slot @scheme[pos] of @scheme[vec]. The first
|
||||
slot is position @scheme[0], and the last slot is one less than
|
||||
@scheme[(vector-length vec)].}
|
||||
|
||||
@defproc[(vector-set! [vec (and/c vector? (not/c immutable?))]
|
||||
[pos nonnegative-exact-integer?]
|
||||
[v any/c])
|
||||
void?]{
|
||||
|
||||
Updates the slot @scheme[pos] of @scheme[vec] to contain @scheme[v].}
|
||||
|
||||
|
||||
@defproc[(vector->list [vec vector?])
|
||||
list?]{
|
||||
|
||||
Returns a list with the same length and elements as @scheme[vec].}
|
||||
|
||||
|
||||
@defproc[(list->vector [lst list?])
|
||||
vector?]{
|
||||
|
||||
Returns a mutable vector with the same length and elements as
|
||||
@scheme[lst].}
|
||||
|
||||
|
||||
@defproc[(vector->immutable-vector [vec vector?])
|
||||
(and/c vector? immutable?)]{
|
||||
|
||||
Returns an immutable vector with the same length and elements as @scheme[vec].
|
||||
If @scheme[vec] is itself immutable, then it is returned as the result.}
|
||||
|
||||
|
||||
@defproc[(vector-fill! [vec (and/c vector? (not/c immutable?))]
|
||||
[v any/c])
|
||||
void?]{
|
||||
|
||||
Changes all slots of @scheme[vec] to contain @scheme[v].}
|
||||
|
||||
|
||||
@defproc[(vector-copy! [dest (and/c vector? (not/c immutable?))]
|
||||
[dest-start exact-nonnegative-integer?]
|
||||
[src vector?]
|
||||
[src-start exact-nonnegative-integer? 0]
|
||||
[src-end exact-nonnegative-integer? (vector-length src)])
|
||||
void?]{
|
||||
|
||||
Changes the elements of @scheme[dest] starting at position
|
||||
@scheme[dest-start] to match the elements in @scheme[src] from
|
||||
@scheme[src-start] (inclusive) to @scheme[src-end] (exclusive). The
|
||||
vectors @scheme[dest] and @scheme[src] can be the same vector, and in
|
||||
that case the destination region can overlap with the source region;
|
||||
the destination elements after the copy match the source elements
|
||||
from before the copy. If any of @scheme[dest-start],
|
||||
@scheme[src-start], or @scheme[src-end] are out of range (taking into
|
||||
account the sizes of the vectors and the source and destination
|
||||
regions), the @exnraise[exn:fail:contract].
|
||||
|
||||
@examples[(define v (vector 'A 'p 'p 'l 'e))
|
||||
(vector-copy! v 4 #(y))
|
||||
(vector-copy! v 0 v 3 4)
|
||||
v]}
|
||||
|
||||
|
||||
@defproc[(vector->values [vec vector?]
|
||||
[start-pos nonnegative-exact-integer? 0]
|
||||
[end-pos nonnegative-exact-integer? (vector-length vec)])
|
||||
any]{
|
||||
|
||||
Returns @math{@scheme[end-pos] - @scheme[start-pos]} values, which are
|
||||
the elements of @scheme[vec] from @scheme[start-pos] (inclusive) to
|
||||
@scheme[end-pos] (exclusive). If @scheme[start-pos] or
|
||||
@scheme[end-pos] are greater than @scheme[(vector-length vec)], or if
|
||||
@scheme[end-pos] is less than @scheme[start-pos], the
|
||||
@exnraise[exn:fail:contract].}
|
||||
|
||||
@defproc[(build-vector [n exact-nonnegative-integer?]
|
||||
[proc (exact-nonnegative-integer? . -> . any.c)])
|
||||
vector?]{
|
||||
|
||||
Creates a vector of @scheme[n] elements by applying @scheme[proc] to
|
||||
the integers from @scheme[0] to @scheme[(sub1 n)] in order. If
|
||||
@scheme[_vec] is the resulting vector, then @scheme[(vector-ref _vec
|
||||
_i)] is the value produced by @scheme[(proc _i)].
|
||||
|
||||
@examples[
|
||||
(build-vector 5 add1)
|
||||
]}
|
||||
@; ----------------------------------------------------------------------
|
||||
@include-section["vectors.scrbl"]
|
||||
|
||||
@; ------------------------------------------------------------
|
||||
@section[#:tag "boxes"]{Boxes}
|
||||
|
@ -431,279 +159,15 @@ Returns the content of @scheme[box]. For any @scheme[v],
|
|||
|
||||
Sets the content of @scheme[box] to @scheme[v].}
|
||||
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@section[#:tag "hashtables"]{Hash Tables}
|
||||
|
||||
A @deftech{hash table} (or simply @deftech{hash}) maps each of its
|
||||
keys to a single value. For a given hash table, keys are equivalent
|
||||
via @scheme[equal?] or @scheme[eq?], and keys are retained either
|
||||
strongly or weakly (see @secref["weakbox"]). A hash table is also
|
||||
either mutable or immutable; immutable tables support constant-time
|
||||
functional update.
|
||||
|
||||
A hash table can be used as a two-valued @tech{sequence} (see
|
||||
@secref["sequences"]). The keys and values of the hash table serve as
|
||||
elements of the sequence (i.e., each element is a key and its
|
||||
associated value). If a mapping is added to or removed from the hash
|
||||
table during iteration, then an iteration step may fail with
|
||||
@scheme[exn:fail:contract], or the iteration may skip or duplicate
|
||||
keys and values. See also @scheme[in-hash], @scheme[in-hash-keys],
|
||||
@scheme[in-hash-values], and @scheme[in-hash-pairs].
|
||||
|
||||
Two hash tables are @scheme[equal?] if they use the same
|
||||
key-comparison procedure (@scheme[equal?] or @scheme[eq?]), if they
|
||||
both hold keys strongly or weakly, and if they have the same
|
||||
mutability.
|
||||
|
||||
@bold{Caveats concerning concurrent modification:} A mutable hash
|
||||
table can be manipulated with @scheme[hash-ref], @scheme[hash-set!],
|
||||
and @scheme[hash-remove!] concurrently by multiple threads, and the
|
||||
operations are protected by a table-specific semaphore as needed. Two
|
||||
caveats apply, however:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{If a thread is terminated while applying @scheme[hash-ref],
|
||||
@scheme[hash-set!], or @scheme[hash-remove!] to a hash table that
|
||||
uses @scheme[equal?] key comparisons, all current and future
|
||||
operations on the hash table block indefinitely.}
|
||||
|
||||
@item{The @scheme[hash-map] and @scheme[hash-for-each] procedures do
|
||||
not use the table's semaphore. Consequently, if a hash table is
|
||||
extended with new keys by another thread while a map or for-each
|
||||
traversal is in process, arbitrary key--value pairs can be dropped
|
||||
or duplicated in the traversal. Similarly, if a map or for-each
|
||||
procedure itself extends the table, arbitrary key--value pairs can
|
||||
be dropped or duplicated. However, key mappings can be deleted or
|
||||
remapped by any thread with no adverse affects (i.e., the change
|
||||
does not affect a traversal if the key has been seen already,
|
||||
otherwise the traversal skips a deleted key or uses the remapped
|
||||
key's new value).}
|
||||
|
||||
}
|
||||
|
||||
@bold{Caveat concerning mutable keys:} If a key into an
|
||||
@scheme[equal?]-based hash table is mutated (e.g., a key string is
|
||||
modified with @scheme[string-set!]), then the hash table's behavior
|
||||
for insertion and lookup operations becomes unpredictable.
|
||||
|
||||
|
||||
@defproc[(hash? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is a @tech{hash table}, @scheme[#f]
|
||||
otherwise.}
|
||||
|
||||
@defproc[(hash-eq? [hash hash?]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[hash] compares keys with @scheme[eq?],
|
||||
@scheme[#f] if it compares with @scheme[equal?].}
|
||||
|
||||
|
||||
@defproc[(hash-weak? [hash hash?]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[hash] retains its keys weakly,
|
||||
@scheme[#f] if it retains keys strongly.}
|
||||
|
||||
|
||||
@defproc[(make-hash) hash?]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys strongly and that
|
||||
uses @scheme[equal?] to compare keys.}
|
||||
|
||||
|
||||
@defproc[(make-hasheq) (and/c hash? hash-eq?)]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys strongly and that
|
||||
uses @scheme[eq?] to compare keys.}
|
||||
|
||||
|
||||
@defproc[(make-weak-hash) (and/c hash? hash-weak?)]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys weakly and that
|
||||
uses @scheme[equal?] to compare keys.}
|
||||
|
||||
|
||||
@defproc[(make-weak-hasheq) (and/c hash? hash-eq? hash-weak?)]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys weakly and that
|
||||
uses @scheme[eq?] to compare keys.}
|
||||
|
||||
|
||||
@defproc[(make-immutable-hash [assocs (listof pair?)])
|
||||
(and/c hash? immutable?)]{
|
||||
|
||||
Creates an immutable hash table that compares keys with
|
||||
@scheme[equal?]. In each element of @scheme[assocs], the @scheme[car]
|
||||
of each pair is a key, and the @scheme[cdr] is the corresponding
|
||||
value. The mappings are added to the table in the order that they
|
||||
appear in @scheme[assocs], so later mappings can hide earlier
|
||||
mappings.}
|
||||
|
||||
@defproc[(make-immutable-hasheq [assocs (listof pair?)])
|
||||
(and/c hash? hash-eq? immutable?)]{
|
||||
|
||||
Like @scheme[make-immutable-hash], but the resulting hash table
|
||||
compares keys with @scheme[eq?].}
|
||||
|
||||
|
||||
@defproc[(hash-set! [hash (and/c hash? (not/c immutable?))]
|
||||
[key any/c]
|
||||
[v any/c]) void?]{
|
||||
|
||||
Maps @scheme[key] to @scheme[v] in @scheme[hash], overwriting
|
||||
any existing mapping for @scheme[key].}
|
||||
|
||||
|
||||
@defproc[(hash-set [hash (and/c hash? immutable?)]
|
||||
[key any/c]
|
||||
[v any/c])
|
||||
(and/c hash? immutable?)]{
|
||||
|
||||
Functionally extends @scheme[hash] by mapping @scheme[key] to
|
||||
@scheme[v], overwriting any existing mapping for @scheme[key], and
|
||||
returning an extended hash table.}
|
||||
|
||||
|
||||
@defproc[(hash-ref [hash hash?]
|
||||
[key any/c]
|
||||
[failure-result any/c (lambda () (raise (make-exn:fail ....)))])
|
||||
any]{
|
||||
|
||||
Returns the value for @scheme[key] in @scheme[hash]. If no value
|
||||
is found for @scheme[key], then @scheme[failure-result] determines the
|
||||
result:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{If @scheme[failure-result] is a procedure, it is called
|
||||
(through a tail call) with no arguments to produce the result.}
|
||||
|
||||
@item{Otherwise, @scheme[failure-result] is returned as the result.}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
@defproc[(hash-remove! [hash (and/c hash? (not/c immutable?))]
|
||||
[key any/c])
|
||||
void?]{
|
||||
|
||||
Removes any existing mapping for @scheme[key] in @scheme[hash].}
|
||||
|
||||
|
||||
@defproc[(hash-remove [hash (and/c hash? immutable?)]
|
||||
[key any/c])
|
||||
(and/c hash? immutable?)]{
|
||||
|
||||
Functionally removes any existing mapping for @scheme[key] in
|
||||
@scheme[hash], returning the updated hash table.}
|
||||
|
||||
|
||||
@defproc[(hash-map [hash hash?]
|
||||
[proc (any/c any/c . -> . any/c)])
|
||||
(listof any/c)]{
|
||||
|
||||
Applies the procedure @scheme[proc] to each element in
|
||||
@scheme[hash] in an unspecified order, accumulating the results
|
||||
into a list. The procedure @scheme[proc] is called each time with a
|
||||
key and its value. See the caveat above about concurrent
|
||||
modification.}
|
||||
|
||||
|
||||
@defproc[(hash-for-each [hash hash?]
|
||||
[proc (any/c any/c . -> . any)])
|
||||
void?]{
|
||||
|
||||
Applies @scheme[proc] to each element in @scheme[hash] (for the
|
||||
side-effects of @scheme[proc]) in an unspecified order. The procedure
|
||||
@scheme[proc] is called each time with a key and its value. See the
|
||||
caveat above about concurrent modification.}
|
||||
|
||||
|
||||
@defproc[(hash-count [hash hash?])
|
||||
nonnegative-exact-integer?]{
|
||||
|
||||
Returns the number of keys mapped by @scheme[hash]. If
|
||||
@scheme[hash] is not created with @scheme['weak], then the
|
||||
result is computed in constant time and atomically. If
|
||||
@scheme[hash] is created with @scheme['weak], see the caveat
|
||||
above about concurrent modification.}
|
||||
|
||||
|
||||
@defproc[(hash-iterate-first [hash hash?])
|
||||
(or/c false/c nonnegative-exact-integer?)]{
|
||||
|
||||
Returns @scheme[#f] if @scheme[hash] contains no elements, otherwise
|
||||
it returns an integer that is a index to the first element in the hash
|
||||
table; ``first'' refers to an unspecified ordering of the table
|
||||
elements, and the index values are not necessarily consecutive
|
||||
integers. For a mutable @scheme[hash], this index is guaranteed to
|
||||
refer to the first item only as long as no items are added to or
|
||||
removed from @scheme[hash].}
|
||||
|
||||
@defproc[(hash-iterate-next [hash hash?]
|
||||
[prev nonnegative-exact-integer?])
|
||||
(or/c false/c nonnegative-exact-integer?)]{
|
||||
|
||||
Returns either an integer that is an index to the element in
|
||||
@scheme[hash] after the element indexed by @scheme[pos] (which is not
|
||||
necessarily one more than @scheme[pos]) or @scheme[#f] if @scheme[pos]
|
||||
refers to the last element in @scheme[hash]. If @scheme[pos] is not a
|
||||
valid index, then the @exnraise[exn:fail:contract]. For a mutable
|
||||
@scheme[hash], the result index is guaranteed to refer to its item
|
||||
only as long as no items are added to or removed from @scheme[hash].}
|
||||
|
||||
|
||||
@defproc[(hash-iterate-key [hash hash?]
|
||||
[pos nonnegative-exact-integer?])
|
||||
any]{
|
||||
|
||||
Returns the key for the element in @scheme[hash] at index
|
||||
@scheme[pos]. If @scheme[pos] is not a valid index for
|
||||
@scheme[hash], the @exnraise[exn:fail:contract].}
|
||||
|
||||
|
||||
@defproc[(hash-iterate-value [hash hash?]
|
||||
[pos nonnegative-exact-integer?])
|
||||
any]{
|
||||
|
||||
Returns the value for the element in @scheme[hash] at index
|
||||
@scheme[pos]. If @scheme[pos] is not a valid index for
|
||||
@scheme[hash], the @exnraise[exn:fail:contract].}
|
||||
|
||||
|
||||
@defproc[(hash-copy [hash hash?])
|
||||
(and/c hash? (not/c immutable?))]{
|
||||
|
||||
Returns a mutable hash table with the same mappings, same
|
||||
key-comparison mode, and same key-holding strength as @scheme[hash].}
|
||||
|
||||
|
||||
@defproc[(eq-hash-code [v any/c]) exact-integer?]{
|
||||
|
||||
Returns an exact integer; for any two @scheme[eq?] values, the
|
||||
returned integer is the same. Furthermore, for the result integer
|
||||
@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)]
|
||||
implies @scheme[(eq? k j)].}
|
||||
|
||||
|
||||
@defproc[(equal-hash-code [v any/c]) exact-integer?]{
|
||||
|
||||
Returns an exact integer; for any two @scheme[equal?] values, the
|
||||
returned integer is the same. Furthermore, for the result integer
|
||||
@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)]
|
||||
implies @scheme[(eq? k j)]. A has code is computed even when
|
||||
@scheme[v] contains a cycle through pairs, vectors, boxes, and/or
|
||||
inspectable structure fields. See also @scheme[prop:equal+hash].}
|
||||
|
||||
@defproc[(equal-secondary-hash-code [v any/c]) exact-integer?]{
|
||||
|
||||
Like @scheme[equal-hash-code], but computes a secondary value suitable
|
||||
for use in double hashing.}
|
||||
@include-section["hashes.scrbl"]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@include-section["sequences.scrbl"]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@include-section["dicts.scrbl"]
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
@include-section["procedures.scrbl"]
|
||||
|
||||
|
|
468
collects/scribblings/reference/dicts.scrbl
Normal file
468
collects/scribblings/reference/dicts.scrbl
Normal file
|
@ -0,0 +1,468 @@
|
|||
#lang scribble/doc
|
||||
@(require "mz.ss"
|
||||
scribble/eval)
|
||||
|
||||
@(define dict-eval (make-base-eval))
|
||||
@interaction-eval[#:eval dict-eval (require scheme/dict)]
|
||||
|
||||
@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 @scheme[equal?] to compare keys); and}
|
||||
|
||||
@item{@techlink{structures} whose types have the @scheme[prop:dict]
|
||||
property.}
|
||||
|
||||
]
|
||||
|
||||
@note-lib[scheme/dict]
|
||||
|
||||
@defproc[(dict? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is a @tech{dictionary}, @scheme[#f]
|
||||
otherwise.
|
||||
|
||||
Beware that @scheme[dict?] is not a constant-time test on pairs, since
|
||||
checking that @scheme[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-mutable? [d dict?]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[d] is mutable via @scheme[dict-set!]
|
||||
and maybe @scheme[dict-remove!], @scheme[#f] otherwise.
|
||||
|
||||
@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 @scheme[#t] if @scheme[d] supports removing mappings via
|
||||
@scheme[dict-remove!] and/or @scheme[dict-remove], @scheme[#f]
|
||||
otherwise.
|
||||
|
||||
@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 @scheme[#t] if @scheme[d] supports functional update via
|
||||
@scheme[dict-set] and maybe @scheme[dict-remove], @scheme[#f]
|
||||
otherwise.
|
||||
|
||||
@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")))
|
||||
]}
|
||||
|
||||
|
||||
@defproc[(dict-set! [dict (and/c dict? (not/c immutable?))]
|
||||
[key any/c]
|
||||
[v any/c]) void?]{
|
||||
|
||||
Maps @scheme[key] to @scheme[v] in @scheme[dict], overwriting any
|
||||
existing mapping for @scheme[key]. The update can fail with a
|
||||
@scheme[exn:fail:contract] exception if @scheme[dict] is not mutable
|
||||
or if @scheme[key] is not an allowed key for the dictionary (e.g., not
|
||||
an exact integer in the appropriate range when @scheme[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 @scheme[dict] by mapping @scheme[key] to
|
||||
@scheme[v], overwriting any existing mapping for @scheme[key], and
|
||||
returning an extended dictionary. The update can fail with a
|
||||
@scheme[exn:fail:contract] exception if @scheme[dict] does not support
|
||||
functional extension or if @scheme[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-ref [dict dict?]
|
||||
[key any/c]
|
||||
[failure-result any/c (lambda () (raise (make-exn:fail ....)))])
|
||||
any]{
|
||||
|
||||
Returns the value for @scheme[key] in @scheme[dict]. If no value
|
||||
is found for @scheme[key], then @scheme[failure-result] determines the
|
||||
result:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{If @scheme[failure-result] is a procedure, it is called
|
||||
(through a tail call) with no arguments to produce the result.}
|
||||
|
||||
@item{Otherwise, @scheme[failure-result] is returned as the result.}
|
||||
|
||||
}
|
||||
|
||||
@examples[
|
||||
#:eval dict-eval
|
||||
(dict-ref #hash((a . "apple") (b . "beer")) 'a)
|
||||
(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)
|
||||
(dict-ref #("apple" "banana") -3 #f)
|
||||
]}
|
||||
|
||||
|
||||
|
||||
@defproc[(dict-remove! [dict (and/c dict? (not/c immutable?))]
|
||||
[key any/c])
|
||||
void?]{
|
||||
|
||||
Removes any existing mapping for @scheme[key] in @scheme[dict]. The
|
||||
update can fail if @scheme[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 @scheme[key] in
|
||||
@scheme[dict], returning the updated dictionary. The update can fail
|
||||
if @scheme[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-map [dict dict?]
|
||||
[proc (any/c any/c . -> . any/c)])
|
||||
(listof any/c)]{
|
||||
|
||||
Applies the procedure @scheme[proc] to each element in
|
||||
@scheme[dict] in an unspecified order, accumulating the results
|
||||
into a list. The procedure @scheme[proc] is called each time with a
|
||||
key and its 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 @scheme[proc] to each element in @scheme[dict] (for the
|
||||
side-effects of @scheme[proc]) in an unspecified order. The procedure
|
||||
@scheme[proc] is called each time with a key and its value.
|
||||
|
||||
@examples[
|
||||
#:eval dict-eval
|
||||
(dict-for-each #hash((a . "apple") (b . "banana"))
|
||||
(lambda (k v)
|
||||
(printf "~a = ~s\n" k v)))
|
||||
]}
|
||||
|
||||
|
||||
@defproc[(dict-count [dict dict?])
|
||||
nonnegative-exact-integer?]{
|
||||
|
||||
Returns the number of keys mapped by @scheme[dict], usually in
|
||||
constant time.
|
||||
|
||||
@examples[
|
||||
#:eval dict-eval
|
||||
(dict-count #hash((a . "apple") (b . "banana")))
|
||||
(dict-count #("apple" "banana"))
|
||||
]}
|
||||
|
||||
|
||||
@defproc[(dict-iterate-first [dict dict?]) any/c]{
|
||||
|
||||
Returns @scheme[#f] if @scheme[dict] contains no elements, otherwise
|
||||
it returns a non-@scheme[#f] value that is a index to the first
|
||||
element in the dict table; ``first'' refers to an unspecified ordering
|
||||
of the dictionary elements. For a mutable @scheme[dict], this index is
|
||||
guaranteed to refer to the first item only as long as no mappings are
|
||||
added to or removed from @scheme[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-@scheme[#f] that is an index to the element in
|
||||
@scheme[dict] after the element indexed by @scheme[pos] or @scheme[#f]
|
||||
if @scheme[pos] refers to the last element in @scheme[dict]. If
|
||||
@scheme[pos] is not a valid index, then the
|
||||
@exnraise[exn:fail:contract]. For a mutable @scheme[dict], the result
|
||||
index is guaranteed to refer to its item only as long as no items are
|
||||
added to or removed from @scheme[dict]. The @scheme[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 @scheme[dict] at index
|
||||
@scheme[pos]. If @scheme[pos] is not a valid index for @scheme[dict],
|
||||
the @exnraise[exn:fail:contract]. The @scheme[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 @scheme[dict] at index
|
||||
@scheme[pos]. If @scheme[pos] is not a valid index for @scheme[dict],
|
||||
the @exnraise[exn:fail:contract]. The @scheme[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))
|
||||
]}
|
||||
|
||||
|
||||
@defproc[(in-dict [dict dict?]) sequence?]{ Returns a @tech{sequence}
|
||||
whose each element is two values: a key and corresponding value from
|
||||
@scheme[dict].
|
||||
|
||||
@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 @scheme[dict].
|
||||
|
||||
@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 @scheme[dict].
|
||||
|
||||
@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
|
||||
@scheme[dict] (as opposed to using @scheme[in-dict], which gets the
|
||||
key and value as separate values for each element).
|
||||
|
||||
@examples[
|
||||
#:eval dict-eval
|
||||
(define h #hash((a . "apple") (b . "banana")))
|
||||
(for/list ([p (in-dict-pairs h)])
|
||||
p)
|
||||
]}
|
||||
|
||||
|
||||
|
||||
@defthing[prop:dict struct-type-property?]{
|
||||
|
||||
A @tech{structure type property} (see @secref["structprops"]) that
|
||||
supplies dictionary-operation implementations for a structure
|
||||
type. The property value must be a vector of ten procedures (some
|
||||
optional) that are applied only to instances of the structure type
|
||||
that has the property:
|
||||
|
||||
@itemize[
|
||||
|
||||
@item{@scheme[_ref] : a procedure like @scheme[dict-ref] that accepts
|
||||
either two or three arguments}
|
||||
|
||||
@item{@scheme[_set!] : a procedure like @scheme[dict-set!] that accepts
|
||||
three arguments, or @scheme[#f] if mutation is not supported}
|
||||
|
||||
@item{@scheme[_set] : a procedure like @scheme[dict-set] that accepts
|
||||
three arguments and returns an updated dictionary, or
|
||||
@scheme[#f] if functional update is not supported}
|
||||
|
||||
@item{@scheme[_remove!] : a procedure like @scheme[dict-remove!] that
|
||||
accepts two arguments, or @scheme[#f] if mutation is not
|
||||
supported or if key removal is not supported}
|
||||
|
||||
@item{@scheme[_remove] : a procedure like @scheme[dict-remove] that
|
||||
accepts two arguments and returns an updated dictionary, or
|
||||
@scheme[#f] if functional update or key removal is not
|
||||
supported}
|
||||
|
||||
@item{@scheme[_count] : a procedure like @scheme[dict-count] that accepts
|
||||
one argument}
|
||||
|
||||
@item{@scheme[_iterate-first] : a procedure like
|
||||
@scheme[dict-iterate-first] that accepts one argument}
|
||||
|
||||
@item{@scheme[_iterate-next] : a procedure like
|
||||
@scheme[dict-iterate-next] that accepts two arguments; the
|
||||
procedure is responsible for checking that the second argument
|
||||
is a valid position for the first argument}
|
||||
|
||||
@item{@scheme[_iterate-key] : a procedure like
|
||||
@scheme[dict-iterate-key] that accepts two arguments; the
|
||||
procedure is responsible for checking that the second argument
|
||||
is a valid position for the first argument}
|
||||
|
||||
@item{@scheme[_iterate-value] : a procedure like
|
||||
@scheme[dict-iterate-value] that accepts two arguments; the
|
||||
procedure is responsible for checking that the second argument
|
||||
is a valid position for the first argument}
|
||||
|
||||
]}
|
||||
|
||||
@deftogether[(
|
||||
@defproc[(make-custom-hash [eql? (any/c any/c . -> . any/c)]
|
||||
[hash-proc (any/c . -> . exact-integer?)]
|
||||
[hash2-proc (any/c . -> . exact-integer?)])
|
||||
dict?]
|
||||
@defproc[(make-immutable-custom-hash [eql? (any/c any/c . -> . any/c)]
|
||||
[hash-proc (any/c . -> . exact-integer?)]
|
||||
[hash2-proc (any/c . -> . exact-integer?)])
|
||||
dict?]
|
||||
@defproc[(make-weak-custom-hash [eql? (any/c any/c . -> . any/c)]
|
||||
[hash-proc (any/c . -> . exact-integer?)]
|
||||
[hash2-proc (any/c . -> . exact-integer?)])
|
||||
dict?]
|
||||
)]{
|
||||
|
||||
Creates a dictionary that is implemented in terms of a hash table
|
||||
where keys are compared with @scheme[eql?] and hashed with
|
||||
@scheme[hash-proc] and @scheme[hash2-proc]. See
|
||||
@scheme[prop:equal+hash] for information on suitable equality and
|
||||
hashing functions.
|
||||
|
||||
The @scheme[make-custom-hash] and @scheme[make-weak-custom-hash]
|
||||
functions create a mutable dictionary that does not support functional
|
||||
update, while @scheme[make-immutable-custom-hash] creates an immutable
|
||||
dictionary that supports functional update. The dictionary created by
|
||||
@scheme[make-weak-custom-hash] retains its keys weakly, like the result
|
||||
of @scheme[make-weak-hash].
|
||||
|
||||
Dictionaries created by @scheme[make-custom-hash] and company are
|
||||
@scheme[equal?] when they have the same mutability and key strength,
|
||||
the associated procedures are @scheme[equal?], and the key--value
|
||||
mappings are the same when keys and values are compared with
|
||||
@scheme[equal?].
|
||||
|
||||
@examples[
|
||||
#:eval dict-eval
|
||||
(define h (make-custom-hash (lambda (a b)
|
||||
(string=? (format "~a" a)
|
||||
(format "~a" b)))
|
||||
(lambda (a)
|
||||
(equal-hash-code
|
||||
(format "~a" a)))))
|
||||
(dict-set! h 1 'one)
|
||||
(dict-ref h "1")
|
||||
]}
|
772
collects/scribblings/reference/eval-model.scrbl
Normal file
772
collects/scribblings/reference/eval-model.scrbl
Normal file
|
@ -0,0 +1,772 @@
|
|||
#lang scribble/doc
|
||||
@(require scribble/struct
|
||||
(for-syntax scheme/base)
|
||||
"mz.ss"
|
||||
"prog-steps.ss")
|
||||
|
||||
@(define reduces (make-element #f (list 'rarr)))
|
||||
@(define rspace (make-element "ghost" (list 'rarr)))
|
||||
|
||||
@(define *redex (lambda (c)
|
||||
(make-element "highlighted" (list c))))
|
||||
@(define-syntax redex
|
||||
(syntax-rules () [(_ a) (*redex (scheme a))]))
|
||||
|
||||
|
||||
@(define hole (make-element #f (list "[]")))
|
||||
@(define (*sub c e) (make-element #f (list c "[" e "]")))
|
||||
@(define langle (make-element 'tt (list "<")))
|
||||
@(define rangle (make-element 'tt (list ">")))
|
||||
@(define comma (make-element 'tt (list ", ")))
|
||||
@(define-syntax sub
|
||||
(syntax-rules () [(_ a b) (*sub (scheme a) (scheme b))]))
|
||||
@(define (*state c e) (make-element #f (list langle c comma e rangle)))
|
||||
@(define-syntax state
|
||||
(syntax-rules () [(_ a b) (*state (scheme a) (scheme b))]))
|
||||
@(define (frame n)
|
||||
(make-element "schemevariable"
|
||||
(list "C" (make-element 'subscript (list (format "~a" n))))))
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@title[#:tag "eval-model"]{Evaluation Model}
|
||||
|
||||
Scheme evaluation can be viewed as the simplification of expressions
|
||||
to obtain values. For example, just as an elementary-school student
|
||||
simplifies
|
||||
|
||||
@verbatim{ 1 + 1 = 2}
|
||||
|
||||
Scheme evaluation simplifies
|
||||
|
||||
@schemeblock[
|
||||
(+ 1 1) #, @reduces 2
|
||||
]
|
||||
|
||||
The arrow @reduces above replaces the more traditional @tt{=} to
|
||||
emphasize that evaluation proceeds in a particular direction towards
|
||||
simpler expressions. In particular, a @deftech{value} is an
|
||||
expression that evaluation simplifies no further, such as the number
|
||||
@scheme[2].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "cont-model"]{Sub-expression Evaluation and Continuations}
|
||||
|
||||
Some simplifications require more than one step. For example:
|
||||
|
||||
@schemeblock[
|
||||
(- 4 #,(redex (+ 1 1))) #,reduces #,(redex (- 4 2)) #,reduces 2
|
||||
]
|
||||
|
||||
An expression that is not a @tech{value} can always be partitioned
|
||||
into two parts: a @deftech{redex}, which is the part that changed in a
|
||||
single-step simplification (show in blue), and the
|
||||
@deftech{continuation}, which is the surrounding expression
|
||||
context. In @scheme[(- 4 (+ 1 1))], the redex is @scheme[(+ 1 1)], and
|
||||
the continuation is @scheme[(- 4 #, @hole)], where @hole takes the
|
||||
place of the redex. That is, the continuation says how to ``continue''
|
||||
after the @tech{redex} is reduced to a @tech{value}.
|
||||
|
||||
Before some things can be evaluated, some sub-expressions must be
|
||||
evaluated; for example, in the application @scheme[(- 4 (+ 1 1))], the
|
||||
application of @scheme[-] cannot be reduced until the sub-expression
|
||||
@scheme[(+ 1 1)] is reduced.
|
||||
|
||||
Thus, the specification of each syntactic form specifies how (some of)
|
||||
its sub-expressions are evaluated, and then how the results are
|
||||
combined to reduce the form away.
|
||||
|
||||
The @deftech{dynamic extent} of an expression is the sequence of
|
||||
evaluation steps during which an expression contains the @tech{redex}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Tail Position}
|
||||
|
||||
An expression @scheme[_expr1] is in @deftech{tail position} with
|
||||
respect to an enclosing expression @scheme[_expr2] if, whenever
|
||||
@scheme[_expr1] becomes a redex, its @tech{continuation} is the same
|
||||
as was the enclosing @scheme[_expr2]'s @tech{continuation}.
|
||||
|
||||
For example, the @scheme[(+ 1 1)] expression is @italic{not} in @tech{tail
|
||||
position} with respect to @scheme[(- 4 (+ 1 1))]. To illustrate, we use
|
||||
the notation @sub[_C _expr] to mean the expression that is produced by
|
||||
substituting @scheme[_expr] in place of @hole in the @tech{continuation}
|
||||
@scheme[_C]:
|
||||
|
||||
@schemeblock[
|
||||
#, @sub[_C (- 4 (+ 1 1))] #, @reduces #, @sub[_C (- 4 2)]
|
||||
]
|
||||
|
||||
In this case, the @tech{continuation} for reducing @scheme[(+ 1 1)] is
|
||||
@sub[_C (+ 4 #, @hole)], not just @scheme[_C].
|
||||
|
||||
In contrast, @scheme[(+ 1 1)] is in @tech{tail position} with respect
|
||||
to @scheme[(if (zero? 0) (+ 1 1) 3)], because, for any continuation
|
||||
@scheme[_C],
|
||||
|
||||
@schemeblock[
|
||||
#, @sub[_C (if (zero? 0) (+ 1 1) 3)] #, @reduces #, @sub[_C (if #t (+ 1 1) 3)] #, @reduces #, @sub[_C (+ 1 1)]
|
||||
]
|
||||
|
||||
The steps in this reduction sequence are driven by the definition of
|
||||
@scheme[if], and they do not depend on the @tech{continuation}
|
||||
@scheme[_C]. The ``then'' branch of an @scheme[if] form is always in
|
||||
@tech{tail position} with respect to the @scheme[if] form. Due to a
|
||||
similar reduction rule for @scheme[if] and @scheme[#f], the ``else''
|
||||
branch of an @scheme[if] form is also in @tech{tail position}.
|
||||
|
||||
@tech{Tail-position} specifications provide a guarantee about the
|
||||
asymptotic space consumption of a computation. In general, the
|
||||
specification of @tech{tail positions} goes with each syntactic form,
|
||||
like @scheme[if].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "values-model"]{Multiple Return Values}
|
||||
|
||||
A Scheme expression can evaluate to @deftech{multiple values}, in the
|
||||
same way that a procedure can accept multiple arguments.
|
||||
|
||||
Most @tech{continuations} expect a particular number of result
|
||||
@tech{values}. Indeed, most @tech{continuations}, such as @scheme[(+
|
||||
#, @hole 1)] expect a single @tech{value}. The @tech{continuation}
|
||||
@scheme[(let-values ([(x y) #, @hole]) _expr)] expects two result
|
||||
@tech{values}; the first result replaces @scheme[x] in the body
|
||||
@scheme[_expr], and the second replaces @scheme[y] in
|
||||
@scheme[_expr]. The @tech{continuation} @scheme[(begin #, @hole (+ 1
|
||||
2))] accepts any number of result @tech{values}, because it ignores
|
||||
the result(s).
|
||||
|
||||
In general, the specification of a syntactic form inidicates the
|
||||
number of @tech{values} that it produces and the number that it
|
||||
expects from each of its sub-expression. In addtion, some procedures
|
||||
(notably @scheme[values]) produce multiple @tech{values}, and some
|
||||
procedures (notably @scheme[call-with-values]) create continuations
|
||||
internally that accept a certain number of @tech{values}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Top-Level Variables}
|
||||
|
||||
Given
|
||||
|
||||
@verbatim{ x = 10}
|
||||
|
||||
then an algebra student simplifies @tt{x + 1} as follows:
|
||||
|
||||
@verbatim{ x + 1 = 10 + 1 = 11}
|
||||
|
||||
Scheme works much the same way, in that a set of @tech{top-level
|
||||
variables} are available for substitutions on demand during
|
||||
evaluation. For example, given
|
||||
|
||||
@schemeblock[
|
||||
(define x 10)
|
||||
]
|
||||
|
||||
then
|
||||
|
||||
@schemeblock[
|
||||
#,(redex (+ x 1)) #,reduces #,(redex (+ 10 1)) #,reduces 11
|
||||
]
|
||||
|
||||
In Scheme, the way definitions appear is just as important as the way
|
||||
that they are used. Scheme evaluation thus keeps track of both
|
||||
definitions and the current expression, and it extends the set of
|
||||
definitions in response to evaluating forms such as @scheme[define].
|
||||
|
||||
Each evaluation step, then, takes the current set of definitions and
|
||||
program to a new set of definitions and program. Before a
|
||||
@scheme[define] can be moved into the set of definitions, its
|
||||
right-hand expression must be reduced to a @tech{value}.
|
||||
|
||||
@prog-steps/no-obj[
|
||||
[{}
|
||||
(begin (define x (code:hilite (+ 9 1))) (+ x 1))]
|
||||
[{}
|
||||
(begin (code:hilite (define x 10)) (+ x 1))]
|
||||
[{(define x 10)}
|
||||
(code:hilite (begin (void) (+ x 1)))]
|
||||
[{(define x 10)}
|
||||
(+ (code:hilite x) 1)]
|
||||
[{(define x 10)}
|
||||
(code:hilite (+ 10 1))]
|
||||
[{(define x 10)}
|
||||
11]
|
||||
]
|
||||
|
||||
Using @scheme[set!], a program can change the value associated with an
|
||||
existing @tech{top-level variable}:
|
||||
|
||||
@prog-steps/no-obj[
|
||||
[{(define x 10)}
|
||||
(begin (code:hilite (set! x 8)) x)]
|
||||
[{(define x 8)}
|
||||
(code:hilite (begin (void) x))]
|
||||
[{(define x 8)}
|
||||
(code:hilite x)]
|
||||
[{(define x 8)}
|
||||
8]
|
||||
]
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Objects and Imperative Update}
|
||||
|
||||
In addition to @scheme[set!] for imperative update of @tech{top-level
|
||||
variables}, various procedures enable the modification of elements
|
||||
within a compound data structure. For example, @scheme[vector-set!]
|
||||
modifies the content of a vector.
|
||||
|
||||
To allow such modifications to data, we must distingiush between
|
||||
@tech{values}, which are the results of expressions, and
|
||||
@deftech{objects}, which hold the data referenced by a value.
|
||||
|
||||
A few kinds of @tech{objects} can serve directly as values, including
|
||||
booleans, @scheme[(void)], and small exact integers. More generally,
|
||||
however, a @tech{value} is a reference to an @tech{object}. For
|
||||
example, a @tech{value} can be a reference to a particular vector that
|
||||
currently holds the value @scheme[10] in its first slot. If an
|
||||
@tech{object} is modified, then the modification is visible through
|
||||
all copies of the @tech{value} that reference the same @tech{object}.
|
||||
|
||||
In the evaluation model, a set of @tech{objects} must be carried along
|
||||
with each step in evaluation, just like the definition set. Operations
|
||||
that create @tech{objects}, such as @scheme[vector], add to the set of
|
||||
@tech{objects}:
|
||||
|
||||
@prog-steps[
|
||||
[{}
|
||||
{}
|
||||
(begin (define x (code:hilite (vector 10 20)))
|
||||
(define y x)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{}
|
||||
(begin (code:hilite (define x <o1>))
|
||||
(define y x)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)}
|
||||
(code:hilite (begin (void)
|
||||
(define y x)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0)))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)}
|
||||
(begin (define y (code:hilite x))
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)}
|
||||
(begin (code:hilite (define y <o1>))
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(code:hilite (begin (void)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0)))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(begin (vector-set! (code:hilite x) 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(begin (code:hilite (vector-set! <o1> 0 11))
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(code:hilite (begin (void)
|
||||
(vector-ref y 0)))]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(vector-ref (code:hilite y) 0)]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(code:hilite (vector-ref <o1> 0))]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
11]
|
||||
]
|
||||
|
||||
The distinction between a @tech{top-level variable} and an object
|
||||
reference is crucial. A @tech{top-level variable} is not a
|
||||
@tech{value}; each time a @tech{variable} expression is evaluated, the
|
||||
value is extracted from the current set of definitions. An object
|
||||
reference, in contrast is a value, and therefore needs no further
|
||||
evaluation. The model evaluation steps above use angle-bracketed
|
||||
@scheme[<o1>] for an object reference to distinguish it from a
|
||||
@tech{variable} name.
|
||||
|
||||
A direct object reference can never appear in a text-based source
|
||||
program. A program representation created with
|
||||
@scheme[datum->syntax-object], however, can embed direct references to
|
||||
existing @tech{objects}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "model-eq"]{Object Identity and Comparisons}
|
||||
|
||||
The @scheme[eq?] operator compares two @tech{values}, returning
|
||||
@scheme[#t] when the values refer to the same @tech{object}. This form
|
||||
of equality is suitable for comparing objects that support imperative
|
||||
update (e.g., to determine that the effect of modifying an object
|
||||
through one reference is visible through another reference). Also, an
|
||||
@scheme[eq?] test evaluates quickly, and @scheme[eq?]-based hashing
|
||||
is more lightweight than @scheme[equal?]-based hashing in hash tables.
|
||||
|
||||
In some cases, however, @scheme[eq?] is unsuitable as a comparison
|
||||
operator, because the generation of @tech{objects} is not clearly
|
||||
defined. In particular, two applications of @scheme[+] to the same two
|
||||
exact integers may or may not produce results that are @scheme[eq?],
|
||||
although the results are always @scheme[equal?]. Similarly, evaluation
|
||||
of a @scheme[lambda] form typically generates a new procedure
|
||||
@tech{object}, but it may re-use a procedure @tech{object} previously
|
||||
generated by the same source @scheme[lambda] form.
|
||||
|
||||
The behavior of a datatype with respect to @scheme[eq?] is generally
|
||||
specified with the datatype and its associated procedures.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "gc-model"]{Garbage Collection}
|
||||
|
||||
In the program state
|
||||
|
||||
@prog-steps[
|
||||
[{(define <o1> (vector 10 20))
|
||||
(define <o2> (vector 0))}
|
||||
{(define x <o1>)}
|
||||
(+ 1 x)]
|
||||
]
|
||||
|
||||
evaluation cannot depend on @scheme[<o2>], because it is not part of
|
||||
the program to evaluate, and it is not referenced by any definition
|
||||
that is accessible in the program. The @tech{object} @scheme[<o2>] may
|
||||
therefore be removed from the evaluation by @deftech{garbage
|
||||
collection}.
|
||||
|
||||
A few special compound datatypes hold @deftech{weak references} to
|
||||
objects. Such weak references are treated specially by the garbage
|
||||
collector in determining which @tech{objects} are reachable for the
|
||||
remainder of the computation. If an @tech{object} is reachable only
|
||||
via a @tech{weak reference}, then the object can be reclaimed, and the
|
||||
@tech{weak reference} is replaced by a different @tech{value}
|
||||
(typically @scheme[#f]).
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Procedure Applications and Local Variables}
|
||||
|
||||
Given
|
||||
|
||||
@verbatim{ f(x) = x + 10}
|
||||
|
||||
then an algebra student simplifies @tt{f(1)} as follows:
|
||||
|
||||
@verbatim{ f(7) = 7 + 10 = 17}
|
||||
|
||||
The key step in this simplification is take the body of the defined
|
||||
function @tt{f}, and then replace each @tt{x} with the actual
|
||||
@tech{value} @tt{1}.
|
||||
|
||||
Scheme procedure application works much the same way. A procedure is
|
||||
an @tech{object}, so evaluating @scheme[(f 7)] starts with a
|
||||
@tech{variable} lookup:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)}
|
||||
((code:hilite f) 7)]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)}
|
||||
(code:hilite (<p1> 7))]
|
||||
]
|
||||
|
||||
Unlike in algebra, however, the @tech{value} associated with an
|
||||
argument can be changed in the body of a procedure by using
|
||||
@scheme[set!], as in the example @scheme[(lambda (x) (begin (set! x 3)
|
||||
x))]. Since the @tech{value} associated with @scheme[x] can be
|
||||
changed, an actual value for cannot be substituted for @scheme[x] when
|
||||
the procedure is applied.
|
||||
|
||||
Instead, a new @deftech{location} is created for each @tech{variable}
|
||||
on each application. The argument @tech{value} is placed in the
|
||||
@tech{location}, and each instance of the @tech{variable} in the
|
||||
procedure body is replaced with the new @tech{location}:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)}
|
||||
(code:hilite (<p1> 7))]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
(+ (code:hilite xloc) 10)]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
(code:hilite (+ 7 10))]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
17]
|
||||
]
|
||||
|
||||
A @tech{location} is the same as a @tech{top-level variable}, but when
|
||||
a @tech{location} is generated, it (conceptually) uses a name that has
|
||||
not been used before and that cannot not be generated again or
|
||||
accessed directly.
|
||||
|
||||
Generating a @tech{location} in this way means that @scheme[set!]
|
||||
evaluates for @tech{local variables} in the same way as for
|
||||
@tech{top-level variables}, because the @tech{local variable} is
|
||||
always replaced with a @tech{location} by the time the @scheme[set!]
|
||||
form is evaluated:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)}
|
||||
((code:hilite f) 7)]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)}
|
||||
(code:hilite (<p1> 7))]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
(begin (code:hilite (set! xloc 3)) xloc)]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 3)}
|
||||
(code:hilite (begin (void) xloc))]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 3)}
|
||||
(code:hilite xloc)]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 3)}
|
||||
3]
|
||||
]
|
||||
|
||||
The substition and @tech{location}-generation step of procedure
|
||||
application requires that the argument is a @tech{value}. Therefore,
|
||||
in @scheme[((lambda (x) (+ x 10)) (+ 1 2))], the @scheme[(+ 1 2)]
|
||||
sub-expression must be simplified to the @tech{value} @scheme[3], and
|
||||
then @scheme[3] can be placed into a @tech{location} for
|
||||
@scheme[x]. In other words, Scheme is a @deftech{call-by-value}
|
||||
language.
|
||||
|
||||
Evaluation of a local-variable form, such as @scheme[(let ([x (+ 1
|
||||
2)]) _expr)], is the same as for a procedure call. After @scheme[(+ 1
|
||||
2)] produces a @tech{value}, it is stored in a fresh @tech{location}
|
||||
that replaces every instance of @scheme[x] in @scheme[_expr].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Variables and Locations}
|
||||
|
||||
A @deftech{variable} is a placeholder for a @tech{value}, and an
|
||||
expressions in an initial program refer to @tech{variables}. A
|
||||
@deftech{top-level variable} is both a @tech{variable} and a
|
||||
@tech{location}. Any other @tech{variable} is always replaced by a
|
||||
@tech{location} at run-time, so that evaluation of expressions
|
||||
involves only @tech{locations}. A single @deftech{local variable}
|
||||
(i.e., a non-top-level, non-module-level @tech{variable}), such as a
|
||||
procedure argument, can correspond to different @tech{locations}
|
||||
through different instantiations.
|
||||
|
||||
For example, in the program
|
||||
|
||||
@schemeblock[(define y (+ (let ([x 5]) x) 6))]
|
||||
|
||||
both @scheme[y] and @scheme[x] are @tech{variables}. The @scheme[y]
|
||||
@tech{variable} is a @tech{top-level variable}, and the @scheme[x] is
|
||||
a @tech{local variable}. When this code is evaluated, a
|
||||
@tech{location} is created for @scheme[x] to hold the value
|
||||
@scheme[5], and a @tech{location} is also created for @scheme[y] to
|
||||
hold the value @scheme[6].
|
||||
|
||||
The replacement of a @tech{variable} with a @tech{location} during
|
||||
evaluation implements Scheme's @deftech{lexical scoping}. For example,
|
||||
when a procedure-argument @tech{variable} @scheme[x] is replaced by
|
||||
the @tech{location} @scheme[xloc], then it is replaced throughout the
|
||||
body of the procedure, including with any nested @scheme[lambda]
|
||||
forms. As a result, future references of the @tech{variable} always
|
||||
access the same @tech{location}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "module-eval-model"]{Modules and Module-Level Variables}
|
||||
|
||||
Most definitions in PLT Scheme are in modules. In terms of evaluation,
|
||||
a module is essentially a prefix on a defined name, so that different
|
||||
modules can define the name. That is, a @deftech{module-level
|
||||
variable} is like a @tech{top-level variable} from the perspective of
|
||||
evaluation.
|
||||
|
||||
One difference between a module an a top-level definition is that a
|
||||
module can be declared without instantiating its module-level
|
||||
definitions. Evaluation of a @scheme[require] @deftech{instantiates}
|
||||
(i.e., triggers the @deftech{instantiation} of) a declared module,
|
||||
which creates variables that correspond to its module-level
|
||||
definitions.
|
||||
|
||||
For example, given the module declaration
|
||||
|
||||
@schemeblock[
|
||||
(module m mzscheme
|
||||
(define x 10))
|
||||
]
|
||||
|
||||
the evaluation of @scheme[(require m)] creates the variable @scheme[x]
|
||||
and installs @scheme[10] as its value. This @scheme[x] is unrelated to
|
||||
any top-level definition of @scheme[x].
|
||||
|
||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@subsection[#:tag "module-phase"]{Module Phases}
|
||||
|
||||
A module can be @tech{instantiate}d in multiple @deftech{phases}. A
|
||||
phase is an integer that, again, is effectively a prefix on the names
|
||||
of module-level definitions. A top-level @scheme[require]
|
||||
@tech{instantiates} a module at @tech{phase} 0, if the module is not
|
||||
already @tech{instantiate}d at phase 0. A top-level
|
||||
@scheme[require-for-syntax] @tech{instantiates} a module at
|
||||
@tech{phase} 1 (if it is not already @tech{instantiate}d at that
|
||||
level); a @scheme[require-for-syntax] also has a different binding
|
||||
effect on further program parsing, as described in
|
||||
@secref["intro-binding"].
|
||||
|
||||
Within a module, some definitions are shifted by a phase already; the
|
||||
@scheme[define-for-syntax] form is like @scheme[define], but it
|
||||
defines a variable at relative @tech{phase} 1, instead of relative
|
||||
@tech{phase} 0. Thus, if the module is @tech{instantiate}d at phase 1,
|
||||
the variables for @scheme[define-for-syntax] are created at phase 2,
|
||||
and so on. Moreover, this relative phase acts as another layer of
|
||||
prefixing, so that a @scheme[define] of @scheme[x] and a
|
||||
@scheme[define-for-syntax] of @scheme[x] can co-exist in a module
|
||||
without colliding. Again, the higher phases are mainly related to
|
||||
program parsing, instead of normal evaluation.
|
||||
|
||||
If a module @tech{instantiate}d at @tech{phase} @math{n}
|
||||
@scheme[require]s another module, then the @scheme[require]d module is
|
||||
first @tech{instantiate}d at phase @math{n}, and so on
|
||||
transitively. (Module @scheme[require]s cannot form cycles.) If a
|
||||
module @tech{instantiate}d at phase @math{n}
|
||||
@scheme[require-for-syntax]es another module, the other module is
|
||||
first @tech{instantiate}d at @tech{phase} @math{n+1}, and so on. If a
|
||||
module @tech{instantiate}d at phase @math{n} for non-zero @math{n}
|
||||
@scheme[require-for-template]s another module, the other module is
|
||||
first @tech{instantiate}d at @tech{phase} @math{n-1}, and so on.
|
||||
|
||||
A final distinction among module @tech{instantiations} is that
|
||||
multiple @tech{instantiations} may exist at phase 1 and higher. These
|
||||
@tech{instantiations} are created by the parsing of module forms (see
|
||||
@secref["mod-parse"]), and are, again, conceptually distinguished
|
||||
by prefixes.
|
||||
|
||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@subsection[#:tag "module-redeclare"]{Module Re-declarations}
|
||||
|
||||
@section-index["modules" "re-define"]
|
||||
|
||||
When a module is declared using a name for which a module is already
|
||||
declared, the new declaration's definitions replace and extend the old
|
||||
declarations. If a variable in the old declaration has no counterpart
|
||||
in the new declaration, the old variable continues to exist, but its
|
||||
binding is not included in the @tech{lexical information} for the
|
||||
module body. If a new variable definition has a counterpart in the old
|
||||
declaration, it effectively assigns to the old variable.
|
||||
|
||||
If a module is @tech{instantiate}d in any @tech{phase}s before it is
|
||||
re-declared, each re-declaration of the module is immediately
|
||||
@tech{instantiate}d in the same @tech{phase}s.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "mark-model"]{Continuation Frames and Marks}
|
||||
|
||||
Every continuation @scheme[_C] can be partitioned into
|
||||
@deftech{continuation frames} @frame[1], @frame[2], ..., @frame["n"]
|
||||
such that @scheme[_C] = @*sub[@frame[1] @*sub[@frame[2] @*sub["..."
|
||||
@frame["n"]]]], and no frame @frame["i"] can be itself partitioned
|
||||
into smaller continuations. Evaluation steps add and remove frames to
|
||||
the current continuation, typically one at a time.
|
||||
|
||||
Each frame is conceptually annotated with a set of
|
||||
@deftech{continuation marks}. A mark consists of a key and its value;
|
||||
the key is an arbitrary value, and each frame includes at most one
|
||||
mark for any key. Various operations set and extract marks from
|
||||
continuations, so that marks can be used to attach information to a
|
||||
dynamic extent. For example, marks can be used to record information
|
||||
for a ``stack trace'' to be used when an exception is raised, or
|
||||
to implement dynamic scope.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "prompt-model"]{Prompts, Delimited Continuations, and Barriers}
|
||||
|
||||
A @deftech{prompt} is a special kind of continuation frame that is
|
||||
annotated with a specific @deftech{prompt-tag} (essentially a
|
||||
continuation mark). Various operations allow the capture of frames in
|
||||
the continuation from the redex position out to the nearest enclosing
|
||||
prompt with a particular prompt tag; such a continuation is sometimes
|
||||
called a @deftech{delimited continuation}. Other operations allow the
|
||||
current continuation to be extended with a captured continuation
|
||||
(specifically, a @deftech{composable continuation}). Yet other
|
||||
operations abort the computation to the nearest enclosing prompt with
|
||||
a particular tag, or replace the continuation to the nearest enclosing
|
||||
prompt with another one. When a delimited continuation is captured,
|
||||
the marks associated with the relevant frames are also captured.
|
||||
|
||||
A @deftech{continuation barrier} is another kind of continuation frame
|
||||
that prohibits certain replacements of the current continuation with
|
||||
another. Specifically, while an abort is allowed to remove a portion
|
||||
of the continuation containing a prompt, the continuation can be
|
||||
replaced by another only when the replacement also includes the
|
||||
continuation barrier. Certain operations install barriers
|
||||
automatically; in particular, when an exception handler is called, a
|
||||
continuation barrier prohibits the continuation of the handler from
|
||||
capturing the continuation past the exception point.
|
||||
|
||||
A @deftech{escape continuation} is essentially a derived concept. It
|
||||
combines a prompt for escape purposes with a continuation for
|
||||
mark-gathering purposes. as the name implies, escape continuations are
|
||||
used only to abort to the point of capture, which means that
|
||||
escape-continuation aborts can cross continuation barriers.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "thread-model"]{Threads}
|
||||
|
||||
Scheme supports multiple, pre-emptive @deftech{threads} of
|
||||
evaluation. In terms of the evaluation model, this means that each
|
||||
step in evaluation actually consists of multiple concurrent
|
||||
expressions, rather than a single expression. The expressions all
|
||||
share the same objects and top-level variables, so that they can
|
||||
communicate through shared state. Most evaluation steps involve a
|
||||
single step in a single expression, but certain synchronization
|
||||
primitives require multiple threads to progress together in one step.
|
||||
|
||||
In addition to the state that is shared among all threads, each thread
|
||||
has its own private state that is accessed through @deftech{thread
|
||||
cells}. A thread cell is similar to a normal mutable object, but a
|
||||
change to the value inside a thread cell is seen only when extracting
|
||||
a value from the cell from the same thread. A thread cell can be
|
||||
@deftech{preserved}; when a new thread is created, the creating
|
||||
thread's value for a preserved thread cell serves as the initial value
|
||||
for the cell in the created thread. For a non-preserved thread cell, a
|
||||
new thread sees the same initial value (specified when the thread cell
|
||||
is created) as all other threads.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "parameter-model"]{Parameters}
|
||||
|
||||
@deftech{Parameters} are essentially a derived concept in Scheme; they
|
||||
are defined in terms of continuation marks and thread cells. However,
|
||||
parameters are also built in, in the sense that some primitive
|
||||
procedures consult parameter values. For example, the default output
|
||||
stream for primitive output operations is determined by a parameter.
|
||||
|
||||
A parameter is a setting that is both thread-specific and
|
||||
continuation-specific. In the empty continuation, each parameter
|
||||
corresponds to a preserved thread cell; a corresponding
|
||||
@deftech{parameter procedure} accesses and sets the thread cell's
|
||||
value for the current thread.
|
||||
|
||||
In a non-empty continuation, a parameter's value is determined through
|
||||
a @deftech{parameterization} that is associated with the nearest
|
||||
enclosing continuation frame though a continuation mark (whose key is
|
||||
not directly accessible). A parameterization maps each parameter to a
|
||||
preserved thread cell, and the combination of thread cell and current
|
||||
thread yields the parameter's value. A parameter procedure sets or
|
||||
accesses the relevant thread cell for its parameter.
|
||||
|
||||
Various operations, such as @scheme[parameterize] or
|
||||
@scheme[with-parameterization], install a parameterization into the
|
||||
current continuation's frame.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "exn-model"]{Exceptions}
|
||||
|
||||
@deftech{Exceptions} are essentially a derived concept in Scheme; they
|
||||
are defined in terms of continuations, prompts, and continuation
|
||||
marks. However, exceptions are also built in, in the sense that
|
||||
primitive forms and procedures may raise exceptions.
|
||||
|
||||
A handler for uncaught exceptions is designated through a built-in
|
||||
parameter. A handler to catch exceptions can be associated with a
|
||||
continuation frame though a continuation mark (whose key is not
|
||||
directly accessible). When an exception is raised, the current
|
||||
continuation's marks determine a chain of handler procedures that are
|
||||
consulted to handle the exception.
|
||||
|
||||
One potential action of an exception handler is to abort the current
|
||||
continuation up to an enclosing prompt with a particular tag. The
|
||||
default handler for uncaught exceptions, in particular, aborts to a
|
||||
particular tag for which a prompt is always present, because the
|
||||
prompt is installed in the outermost frame of the continuation for any
|
||||
new thread.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "custodian-model"]{Custodians}
|
||||
|
||||
A @deftech{custodian} manages a collection of threads, file-stream
|
||||
ports, TCP ports, TCP listeners, UDP sockets, and byte converters.
|
||||
Whenever a thread, file-stream port, TCP port, TCP listener, or UDP
|
||||
socket is created, it is placed under the management of the
|
||||
@deftech{current custodian} as determined by the
|
||||
@scheme[current-custodian] @tech{parameter}.
|
||||
|
||||
@margin-note{In MrEd, custodians also manage eventspaces.}
|
||||
|
||||
Except for the root custodian, every @tech{custodian} itself it
|
||||
managed by a @tech{custodian}, so that custodians form a hierarchy.
|
||||
Every object managed by a subordinate custodian is also managed by the
|
||||
custodian's owner.
|
||||
|
||||
When a @tech{custodian} is shut down via
|
||||
@scheme[custodian-shutdown-all], it forcibly and immediately closes
|
||||
the ports, TCP connections, etc. that it manages, as well as
|
||||
terminating (or suspending) its threads. A custodian that has been
|
||||
shut down cannot manage new objects. If the current custodian is shut
|
||||
down before a procedure is called to create a managed resource (e.g.,
|
||||
@scheme[open-input-port], @scheme[thread]), the
|
||||
@exnraise[exn:fail:contract].
|
||||
|
||||
A thread can have multiple managing custodians, and a suspended thread
|
||||
created with @scheme[thread/suspend-to-kill] can have zero
|
||||
custodians. Extra custodians become associated with a thread through
|
||||
@scheme[thread-resume] (see @secref["threadkill"]). When a thread
|
||||
has multiple custodians, it is not necessarily killed by a
|
||||
@scheme[custodian-shutdown-all], but shut-down custodians are removed
|
||||
from the thread's managing set, and the thread is killed when its
|
||||
managing set becomes empty.
|
||||
|
||||
The values managed by a custodian are only weakly held by the
|
||||
custodian. As a result, a @tech{will} can be executed for a value that
|
||||
is managed by a custodian. In addition, a custodian only weakly
|
||||
references its subordinate custodians; if a subordinate custodian is
|
||||
unreferenced but has its own subordinates, then the custodian may be
|
||||
collected, at which point its subordinates become immediately
|
||||
subordinate to the collected custodian's superordinate custodian.
|
||||
|
||||
In addition to the other entities managed by a custodian, a
|
||||
@defterm{custodian box} created with @scheme[make-custodian-box]
|
||||
strongly holds onto a value placed in the box until the box's
|
||||
custodian is shut down. The custodian only weakly retains the box
|
||||
itself, however (so the box and its content can be collected if there
|
||||
are no other references to them).
|
||||
|
||||
When MzScheme is compiled with support for per-custodian memory
|
||||
accounting (see @scheme[custodian-memory-accounting-available?]), the
|
||||
@scheme[current-memory-use] procedure can report a custodian-specific
|
||||
result. This result determines how much memory is occupied by objects
|
||||
that are reachable from the custodian's managed values, especially its
|
||||
threads, and including its sub-custodians' managed values. If an
|
||||
object is reachable from two custodians where neither is an ancestor
|
||||
of the other, an object is arbitrarily charged to one of the other,
|
||||
and the choice can change after each collection; objects reachable
|
||||
from both a custodian and its descendant, however, are reliably
|
||||
charged to the descendant. Reachability for per-custodian accounting
|
||||
does not include weak references, references to threads managed by
|
||||
non-descendant custodians, references to non-descendant custodians, or
|
||||
references to custodian boxes for non-descendant custodians.
|
|
@ -251,11 +251,12 @@ case, the procedure result of @scheme[clause-transform-expr] is called
|
|||
to transform the clause.
|
||||
|
||||
When @scheme[id] is used in any other expression position, the result
|
||||
of @scheme[expr-transform-expr] is used; if it is an identifier
|
||||
@scheme[_other-id], then any use of @scheme[id] is converted to a use
|
||||
of @scheme[_other-id]; otherwise,@scheme[expr-transform-expr] must
|
||||
produce a procedure that is used as a macro transformer.
|
||||
|
||||
of @scheme[expr-transform-expr] is used. If it is a procedure of zero
|
||||
arguments, then the result must be an identifier @scheme[_other-id],
|
||||
and any use of @scheme[id] is converted to a use of
|
||||
@scheme[_other-id]. Otherwise,@scheme[expr-transform-expr] must
|
||||
produce a procedure (of one argument) that is used as a macro
|
||||
transformer.
|
||||
|
||||
When the @scheme[clause-transform-expr] transformer is used, it is
|
||||
given a @scheme[_clause] as an argument, where the clause's form is
|
||||
|
@ -298,10 +299,14 @@ spliced into the iteration essentially as follows:
|
|||
]
|
||||
|
||||
where @scheme[_body-bindings] and @scheme[_done-expr] are from the
|
||||
context of the @scheme[:do-in] use. The actual @scheme[loop] binding
|
||||
and call has additional loop arguments to support iterations in
|
||||
parallel with the @scheme[:do-in] form, and the other pieces are
|
||||
similarly accompanied by pieces form parallel iterations.}
|
||||
context of the @scheme[:do-in] use. The identifiers bound by the
|
||||
@scheme[for] clause are typically part of the @scheme[([(inner-id ...)
|
||||
inner-expr] ...)] section.
|
||||
|
||||
The actual @scheme[loop] binding and call has additional loop
|
||||
arguments to support iterations in parallel with the @scheme[:do-in]
|
||||
form, and the other pieces are similarly accompanied by pieces form
|
||||
parallel iterations.}
|
||||
|
||||
|
||||
@section{Do Loops}
|
||||
|
|
273
collects/scribblings/reference/hashes.scrbl
Normal file
273
collects/scribblings/reference/hashes.scrbl
Normal file
|
@ -0,0 +1,273 @@
|
|||
#lang scribble/doc
|
||||
@(require "mz.ss")
|
||||
|
||||
@title[#:tag "hashtables"]{Hash Tables}
|
||||
|
||||
A @deftech{hash table} (or simply @deftech{hash}) maps each of its
|
||||
keys to a single value. For a given hash table, keys are equivalent
|
||||
via @scheme[equal?] or @scheme[eq?], and keys are retained either
|
||||
strongly or weakly (see @secref["weakbox"]). A hash table is also
|
||||
either mutable or immutable; immutable tables support constant-time
|
||||
functional update.
|
||||
|
||||
A hash table can be used as a two-valued @tech{sequence} (see
|
||||
@secref["sequences"]). The keys and values of the hash table serve as
|
||||
elements of the sequence (i.e., each element is a key and its
|
||||
associated value). If a mapping is added to or removed from the hash
|
||||
table during iteration, then an iteration step may fail with
|
||||
@scheme[exn:fail:contract], or the iteration may skip or duplicate
|
||||
keys and values. See also @scheme[in-hash], @scheme[in-hash-keys],
|
||||
@scheme[in-hash-values], and @scheme[in-hash-pairs].
|
||||
|
||||
Two hash tables are @scheme[equal?] if they use the same
|
||||
key-comparison procedure (@scheme[equal?] or @scheme[eq?]), if they
|
||||
both hold keys strongly or weakly, and if they have the same
|
||||
mutability.
|
||||
|
||||
@bold{Caveats concerning concurrent modification:} A mutable hash
|
||||
table can be manipulated with @scheme[hash-ref], @scheme[hash-set!],
|
||||
and @scheme[hash-remove!] concurrently by multiple threads, and the
|
||||
operations are protected by a table-specific semaphore as needed. Two
|
||||
caveats apply, however:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{If a thread is terminated while applying @scheme[hash-ref],
|
||||
@scheme[hash-set!], or @scheme[hash-remove!] to a hash table that
|
||||
uses @scheme[equal?] key comparisons, all current and future
|
||||
operations on the hash table block indefinitely.}
|
||||
|
||||
@item{The @scheme[hash-map] and @scheme[hash-for-each] procedures do
|
||||
not use the table's semaphore. Consequently, if a hash table is
|
||||
extended with new keys by another thread while a map or for-each
|
||||
traversal is in process, arbitrary key--value pairs can be dropped
|
||||
or duplicated in the traversal. Similarly, if a map or for-each
|
||||
procedure itself extends the table, arbitrary key--value pairs can
|
||||
be dropped or duplicated. However, key mappings can be deleted or
|
||||
remapped by any thread with no adverse affects (i.e., the change
|
||||
does not affect a traversal if the key has been seen already,
|
||||
otherwise the traversal skips a deleted key or uses the remapped
|
||||
key's new value).}
|
||||
|
||||
}
|
||||
|
||||
@bold{Caveat concerning mutable keys:} If a key into an
|
||||
@scheme[equal?]-based hash table is mutated (e.g., a key string is
|
||||
modified with @scheme[string-set!]), then the hash table's behavior
|
||||
for insertion and lookup operations becomes unpredictable.
|
||||
|
||||
|
||||
@defproc[(hash? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is a @tech{hash table}, @scheme[#f]
|
||||
otherwise.}
|
||||
|
||||
@defproc[(hash-eq? [hash hash?]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[hash] compares keys with @scheme[eq?],
|
||||
@scheme[#f] if it compares with @scheme[equal?].}
|
||||
|
||||
|
||||
@defproc[(hash-weak? [hash hash?]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[hash] retains its keys weakly,
|
||||
@scheme[#f] if it retains keys strongly.}
|
||||
|
||||
|
||||
@defproc[(make-hash) hash?]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys strongly and that
|
||||
uses @scheme[equal?] to compare keys. See also
|
||||
@scheme[make-custom-hash].}
|
||||
|
||||
|
||||
@defproc[(make-hasheq) (and/c hash? hash-eq?)]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys strongly and that
|
||||
uses @scheme[eq?] to compare keys.}
|
||||
|
||||
|
||||
@defproc[(make-weak-hash) (and/c hash? hash-weak?)]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys weakly and that
|
||||
uses @scheme[equal?] to compare keys. See also
|
||||
@scheme[make-weak-custom-hash].}
|
||||
|
||||
|
||||
@defproc[(make-weak-hasheq) (and/c hash? hash-eq? hash-weak?)]{
|
||||
|
||||
Creates an empty mutable hash table that holds keys weakly and that
|
||||
uses @scheme[eq?] to compare keys.}
|
||||
|
||||
|
||||
@defproc[(make-immutable-hash [assocs (listof pair?)])
|
||||
(and/c hash? immutable?)]{
|
||||
|
||||
Creates an immutable hash table that compares keys with
|
||||
@scheme[equal?]. In each element of @scheme[assocs], the @scheme[car]
|
||||
of each pair is a key, and the @scheme[cdr] is the corresponding
|
||||
value. The mappings are added to the table in the order that they
|
||||
appear in @scheme[assocs], so later mappings can hide earlier
|
||||
mappings.}
|
||||
|
||||
@defproc[(make-immutable-hasheq [assocs (listof pair?)])
|
||||
(and/c hash? hash-eq? immutable?)]{
|
||||
|
||||
Like @scheme[make-immutable-hash], but the resulting hash table
|
||||
compares keys with @scheme[eq?].}
|
||||
|
||||
|
||||
@defproc[(hash-set! [hash (and/c hash? (not/c immutable?))]
|
||||
[key any/c]
|
||||
[v any/c]) void?]{
|
||||
|
||||
Maps @scheme[key] to @scheme[v] in @scheme[hash], overwriting
|
||||
any existing mapping for @scheme[key].}
|
||||
|
||||
|
||||
@defproc[(hash-set [hash (and/c hash? immutable?)]
|
||||
[key any/c]
|
||||
[v any/c])
|
||||
(and/c hash? immutable?)]{
|
||||
|
||||
Functionally extends @scheme[hash] by mapping @scheme[key] to
|
||||
@scheme[v], overwriting any existing mapping for @scheme[key], and
|
||||
returning an extended hash table.}
|
||||
|
||||
|
||||
@defproc[(hash-ref [hash hash?]
|
||||
[key any/c]
|
||||
[failure-result any/c (lambda () (raise (make-exn:fail ....)))])
|
||||
any]{
|
||||
|
||||
Returns the value for @scheme[key] in @scheme[hash]. If no value
|
||||
is found for @scheme[key], then @scheme[failure-result] determines the
|
||||
result:
|
||||
|
||||
@itemize{
|
||||
|
||||
@item{If @scheme[failure-result] is a procedure, it is called
|
||||
(through a tail call) with no arguments to produce the result.}
|
||||
|
||||
@item{Otherwise, @scheme[failure-result] is returned as the result.}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
@defproc[(hash-remove! [hash (and/c hash? (not/c immutable?))]
|
||||
[key any/c])
|
||||
void?]{
|
||||
|
||||
Removes any existing mapping for @scheme[key] in @scheme[hash].}
|
||||
|
||||
|
||||
@defproc[(hash-remove [hash (and/c hash? immutable?)]
|
||||
[key any/c])
|
||||
(and/c hash? immutable?)]{
|
||||
|
||||
Functionally removes any existing mapping for @scheme[key] in
|
||||
@scheme[hash], returning the updated hash table.}
|
||||
|
||||
|
||||
@defproc[(hash-map [hash hash?]
|
||||
[proc (any/c any/c . -> . any/c)])
|
||||
(listof any/c)]{
|
||||
|
||||
Applies the procedure @scheme[proc] to each element in
|
||||
@scheme[hash] in an unspecified order, accumulating the results
|
||||
into a list. The procedure @scheme[proc] is called each time with a
|
||||
key and its value. See the caveat above about concurrent
|
||||
modification.}
|
||||
|
||||
|
||||
@defproc[(hash-for-each [hash hash?]
|
||||
[proc (any/c any/c . -> . any)])
|
||||
void?]{
|
||||
|
||||
Applies @scheme[proc] to each element in @scheme[hash] (for the
|
||||
side-effects of @scheme[proc]) in an unspecified order. The procedure
|
||||
@scheme[proc] is called each time with a key and its value. See the
|
||||
caveat above about concurrent modification.}
|
||||
|
||||
|
||||
@defproc[(hash-count [hash hash?])
|
||||
nonnegative-exact-integer?]{
|
||||
|
||||
Returns the number of keys mapped by @scheme[hash]. If
|
||||
@scheme[hash] is not created with @scheme['weak], then the
|
||||
result is computed in constant time and atomically. If
|
||||
@scheme[hash] is created with @scheme['weak], see the caveat
|
||||
above about concurrent modification.}
|
||||
|
||||
|
||||
@defproc[(hash-iterate-first [hash hash?])
|
||||
(or/c false/c nonnegative-exact-integer?)]{
|
||||
|
||||
Returns @scheme[#f] if @scheme[hash] contains no elements, otherwise
|
||||
it returns an integer that is a index to the first element in the hash
|
||||
table; ``first'' refers to an unspecified ordering of the table
|
||||
elements, and the index values are not necessarily consecutive
|
||||
integers. For a mutable @scheme[hash], this index is guaranteed to
|
||||
refer to the first item only as long as no items are added to or
|
||||
removed from @scheme[hash].}
|
||||
|
||||
@defproc[(hash-iterate-next [hash hash?]
|
||||
[pos nonnegative-exact-integer?])
|
||||
(or/c false/c nonnegative-exact-integer?)]{
|
||||
|
||||
Returns either an integer that is an index to the element in
|
||||
@scheme[hash] after the element indexed by @scheme[pos] (which is not
|
||||
necessarily one more than @scheme[pos]) or @scheme[#f] if @scheme[pos]
|
||||
refers to the last element in @scheme[hash]. If @scheme[pos] is not a
|
||||
valid index, then the @exnraise[exn:fail:contract]. For a mutable
|
||||
@scheme[hash], the result index is guaranteed to refer to its item
|
||||
only as long as no items are added to or removed from @scheme[hash].}
|
||||
|
||||
|
||||
@defproc[(hash-iterate-key [hash hash?]
|
||||
[pos nonnegative-exact-integer?])
|
||||
any]{
|
||||
|
||||
Returns the key for the element in @scheme[hash] at index
|
||||
@scheme[pos]. If @scheme[pos] is not a valid index for
|
||||
@scheme[hash], the @exnraise[exn:fail:contract].}
|
||||
|
||||
|
||||
@defproc[(hash-iterate-value [hash hash?]
|
||||
[pos nonnegative-exact-integer?])
|
||||
any]{
|
||||
|
||||
Returns the value for the element in @scheme[hash] at index
|
||||
@scheme[pos]. If @scheme[pos] is not a valid index for
|
||||
@scheme[hash], the @exnraise[exn:fail:contract].}
|
||||
|
||||
|
||||
@defproc[(hash-copy [hash hash?])
|
||||
(and/c hash? (not/c immutable?))]{
|
||||
|
||||
Returns a mutable hash table with the same mappings, same
|
||||
key-comparison mode, and same key-holding strength as @scheme[hash].}
|
||||
|
||||
|
||||
@defproc[(eq-hash-code [v any/c]) exact-integer?]{
|
||||
|
||||
Returns an exact integer; for any two @scheme[eq?] values, the
|
||||
returned integer is the same. Furthermore, for the result integer
|
||||
@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)]
|
||||
implies @scheme[(eq? k j)].}
|
||||
|
||||
|
||||
@defproc[(equal-hash-code [v any/c]) exact-integer?]{
|
||||
|
||||
Returns an exact integer; for any two @scheme[equal?] values, the
|
||||
returned integer is the same. Furthermore, for the result integer
|
||||
@scheme[k] and any other exact integer @scheme[j], @scheme[(= k j)]
|
||||
implies @scheme[(eq? k j)]. A has code is computed even when
|
||||
@scheme[v] contains a cycle through pairs, vectors, boxes, and/or
|
||||
inspectable structure fields. See also @scheme[prop:equal+hash].}
|
||||
|
||||
@defproc[(equal-secondary-hash-code [v any/c]) exact-integer?]{
|
||||
|
||||
Like @scheme[equal-hash-code], but computes a secondary value suitable
|
||||
for use in double hashing.}
|
||||
|
|
@ -1,772 +1,9 @@
|
|||
#lang scribble/doc
|
||||
@(require scribble/struct
|
||||
(for-syntax scheme/base)
|
||||
"mz.ss"
|
||||
"prog-steps.ss")
|
||||
@(require "mz.ss")
|
||||
|
||||
@(define reduces (make-element #f (list 'rarr)))
|
||||
@(define rspace (make-element "ghost" (list 'rarr)))
|
||||
@title[#:tag "model" #:style 'toc]{Language Model}
|
||||
|
||||
@(define *redex (lambda (c)
|
||||
(make-element "highlighted" (list c))))
|
||||
@(define-syntax redex
|
||||
(syntax-rules () [(_ a) (*redex (scheme a))]))
|
||||
@local-table-of-contents[]
|
||||
|
||||
|
||||
@(define hole (make-element #f (list "[]")))
|
||||
@(define (*sub c e) (make-element #f (list c "[" e "]")))
|
||||
@(define langle (make-element 'tt (list "<")))
|
||||
@(define rangle (make-element 'tt (list ">")))
|
||||
@(define comma (make-element 'tt (list ", ")))
|
||||
@(define-syntax sub
|
||||
(syntax-rules () [(_ a b) (*sub (scheme a) (scheme b))]))
|
||||
@(define (*state c e) (make-element #f (list langle c comma e rangle)))
|
||||
@(define-syntax state
|
||||
(syntax-rules () [(_ a b) (*state (scheme a) (scheme b))]))
|
||||
@(define (frame n)
|
||||
(make-element "schemevariable"
|
||||
(list "C" (make-element 'subscript (list (format "~a" n))))))
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@title{Evaluation Model}
|
||||
|
||||
Scheme evaluation can be viewed as the simplification of expressions
|
||||
to obtain values. For example, just as an elementary-school student
|
||||
simplifies
|
||||
|
||||
@verbatim{ 1 + 1 = 2}
|
||||
|
||||
Scheme evaluation simplifies
|
||||
|
||||
@schemeblock[
|
||||
(+ 1 1) #, @reduces 2
|
||||
]
|
||||
|
||||
The arrow @reduces above replaces the more traditional @tt{=} to
|
||||
emphasize that evaluation proceeds in a particular direction towards
|
||||
simpler expressions. In particular, a @deftech{value} is an
|
||||
expression that evaluation simplifies no further, such as the number
|
||||
@scheme[2].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "cont-model"]{Sub-expression Evaluation and Continuations}
|
||||
|
||||
Some simplifications require more than one step. For example:
|
||||
|
||||
@schemeblock[
|
||||
(- 4 #,(redex (+ 1 1))) #,reduces #,(redex (- 4 2)) #,reduces 2
|
||||
]
|
||||
|
||||
An expression that is not a @tech{value} can always be partitioned
|
||||
into two parts: a @deftech{redex}, which is the part that changed in a
|
||||
single-step simplification (show in blue), and the
|
||||
@deftech{continuation}, which is the surrounding expression
|
||||
context. In @scheme[(- 4 (+ 1 1))], the redex is @scheme[(+ 1 1)], and
|
||||
the continuation is @scheme[(- 4 #, @hole)], where @hole takes the
|
||||
place of the redex. That is, the continuation says how to ``continue''
|
||||
after the @tech{redex} is reduced to a @tech{value}.
|
||||
|
||||
Before some things can be evaluated, some sub-expressions must be
|
||||
evaluated; for example, in the application @scheme[(- 4 (+ 1 1))], the
|
||||
application of @scheme[-] cannot be reduced until the sub-expression
|
||||
@scheme[(+ 1 1)] is reduced.
|
||||
|
||||
Thus, the specification of each syntactic form specifies how (some of)
|
||||
its sub-expressions are evaluated, and then how the results are
|
||||
combined to reduce the form away.
|
||||
|
||||
The @deftech{dynamic extent} of an expression is the sequence of
|
||||
evaluation steps during which an expression contains the @tech{redex}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Tail Position}
|
||||
|
||||
An expression @scheme[_expr1] is in @deftech{tail position} with
|
||||
respect to an enclosing expression @scheme[_expr2] if, whenever
|
||||
@scheme[_expr1] becomes a redex, its @tech{continuation} is the same
|
||||
as was the enclosing @scheme[_expr2]'s @tech{continuation}.
|
||||
|
||||
For example, the @scheme[(+ 1 1)] expression is @italic{not} in @tech{tail
|
||||
position} with respect to @scheme[(- 4 (+ 1 1))]. To illustrate, we use
|
||||
the notation @sub[_C _expr] to mean the expression that is produced by
|
||||
substituting @scheme[_expr] in place of @hole in the @tech{continuation}
|
||||
@scheme[_C]:
|
||||
|
||||
@schemeblock[
|
||||
#, @sub[_C (- 4 (+ 1 1))] #, @reduces #, @sub[_C (- 4 2)]
|
||||
]
|
||||
|
||||
In this case, the @tech{continuation} for reducing @scheme[(+ 1 1)] is
|
||||
@sub[_C (+ 4 #, @hole)], not just @scheme[_C].
|
||||
|
||||
In contrast, @scheme[(+ 1 1)] is in @tech{tail position} with respect
|
||||
to @scheme[(if (zero? 0) (+ 1 1) 3)], because, for any continuation
|
||||
@scheme[_C],
|
||||
|
||||
@schemeblock[
|
||||
#, @sub[_C (if (zero? 0) (+ 1 1) 3)] #, @reduces #, @sub[_C (if #t (+ 1 1) 3)] #, @reduces #, @sub[_C (+ 1 1)]
|
||||
]
|
||||
|
||||
The steps in this reduction sequence are driven by the definition of
|
||||
@scheme[if], and they do not depend on the @tech{continuation}
|
||||
@scheme[_C]. The ``then'' branch of an @scheme[if] form is always in
|
||||
@tech{tail position} with respect to the @scheme[if] form. Due to a
|
||||
similar reduction rule for @scheme[if] and @scheme[#f], the ``else''
|
||||
branch of an @scheme[if] form is also in @tech{tail position}.
|
||||
|
||||
@tech{Tail-position} specifications provide a guarantee about the
|
||||
asymptotic space consumption of a computation. In general, the
|
||||
specification of @tech{tail positions} goes with each syntactic form,
|
||||
like @scheme[if].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "values-model"]{Multiple Return Values}
|
||||
|
||||
A Scheme expression can evaluate to @deftech{multiple values}, in the
|
||||
same way that a procedure can accept multiple arguments.
|
||||
|
||||
Most @tech{continuations} expect a particular number of result
|
||||
@tech{values}. Indeed, most @tech{continuations}, such as @scheme[(+
|
||||
#, @hole 1)] expect a single @tech{value}. The @tech{continuation}
|
||||
@scheme[(let-values ([(x y) #, @hole]) _expr)] expects two result
|
||||
@tech{values}; the first result replaces @scheme[x] in the body
|
||||
@scheme[_expr], and the second replaces @scheme[y] in
|
||||
@scheme[_expr]. The @tech{continuation} @scheme[(begin #, @hole (+ 1
|
||||
2))] accepts any number of result @tech{values}, because it ignores
|
||||
the result(s).
|
||||
|
||||
In general, the specification of a syntactic form inidicates the
|
||||
number of @tech{values} that it produces and the number that it
|
||||
expects from each of its sub-expression. In addtion, some procedures
|
||||
(notably @scheme[values]) produce multiple @tech{values}, and some
|
||||
procedures (notably @scheme[call-with-values]) create continuations
|
||||
internally that accept a certain number of @tech{values}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Top-Level Variables}
|
||||
|
||||
Given
|
||||
|
||||
@verbatim{ x = 10}
|
||||
|
||||
then an algebra student simplifies @tt{x + 1} as follows:
|
||||
|
||||
@verbatim{ x + 1 = 10 + 1 = 11}
|
||||
|
||||
Scheme works much the same way, in that a set of @tech{top-level
|
||||
variables} are available for substitutions on demand during
|
||||
evaluation. For example, given
|
||||
|
||||
@schemeblock[
|
||||
(define x 10)
|
||||
]
|
||||
|
||||
then
|
||||
|
||||
@schemeblock[
|
||||
#,(redex (+ x 1)) #,reduces #,(redex (+ 10 1)) #,reduces 11
|
||||
]
|
||||
|
||||
In Scheme, the way definitions appear is just as important as the way
|
||||
that they are used. Scheme evaluation thus keeps track of both
|
||||
definitions and the current expression, and it extends the set of
|
||||
definitions in response to evaluating forms such as @scheme[define].
|
||||
|
||||
Each evaluation step, then, takes the current set of definitions and
|
||||
program to a new set of definitions and program. Before a
|
||||
@scheme[define] can be moved into the set of definitions, its
|
||||
right-hand expression must be reduced to a @tech{value}.
|
||||
|
||||
@prog-steps/no-obj[
|
||||
[{}
|
||||
(begin (define x (code:hilite (+ 9 1))) (+ x 1))]
|
||||
[{}
|
||||
(begin (code:hilite (define x 10)) (+ x 1))]
|
||||
[{(define x 10)}
|
||||
(code:hilite (begin (void) (+ x 1)))]
|
||||
[{(define x 10)}
|
||||
(+ (code:hilite x) 1)]
|
||||
[{(define x 10)}
|
||||
(code:hilite (+ 10 1))]
|
||||
[{(define x 10)}
|
||||
11]
|
||||
]
|
||||
|
||||
Using @scheme[set!], a program can change the value associated with an
|
||||
existing @tech{top-level variable}:
|
||||
|
||||
@prog-steps/no-obj[
|
||||
[{(define x 10)}
|
||||
(begin (code:hilite (set! x 8)) x)]
|
||||
[{(define x 8)}
|
||||
(code:hilite (begin (void) x))]
|
||||
[{(define x 8)}
|
||||
(code:hilite x)]
|
||||
[{(define x 8)}
|
||||
8]
|
||||
]
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Objects and Imperative Update}
|
||||
|
||||
In addition to @scheme[set!] for imperative update of @tech{top-level
|
||||
variables}, various procedures enable the modification of elements
|
||||
within a compound data structure. For example, @scheme[vector-set!]
|
||||
modifies the content of a vector.
|
||||
|
||||
To allow such modifications to data, we must distingiush between
|
||||
@tech{values}, which are the results of expressions, and
|
||||
@deftech{objects}, which hold the data referenced by a value.
|
||||
|
||||
A few kinds of @tech{objects} can serve directly as values, including
|
||||
booleans, @scheme[(void)], and small exact integers. More generally,
|
||||
however, a @tech{value} is a reference to an @tech{object}. For
|
||||
example, a @tech{value} can be a reference to a particular vector that
|
||||
currently holds the value @scheme[10] in its first slot. If an
|
||||
@tech{object} is modified, then the modification is visible through
|
||||
all copies of the @tech{value} that reference the same @tech{object}.
|
||||
|
||||
In the evaluation model, a set of @tech{objects} must be carried along
|
||||
with each step in evaluation, just like the definition set. Operations
|
||||
that create @tech{objects}, such as @scheme[vector], add to the set of
|
||||
@tech{objects}:
|
||||
|
||||
@prog-steps[
|
||||
[{}
|
||||
{}
|
||||
(begin (define x (code:hilite (vector 10 20)))
|
||||
(define y x)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{}
|
||||
(begin (code:hilite (define x <o1>))
|
||||
(define y x)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)}
|
||||
(code:hilite (begin (void)
|
||||
(define y x)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0)))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)}
|
||||
(begin (define y (code:hilite x))
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)}
|
||||
(begin (code:hilite (define y <o1>))
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(code:hilite (begin (void)
|
||||
(vector-set! x 0 11)
|
||||
(vector-ref y 0)))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(begin (vector-set! (code:hilite x) 0 11)
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 10 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(begin (code:hilite (vector-set! <o1> 0 11))
|
||||
(vector-ref y 0))]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(code:hilite (begin (void)
|
||||
(vector-ref y 0)))]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(vector-ref (code:hilite y) 0)]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
(code:hilite (vector-ref <o1> 0))]
|
||||
[{(define <o1> (vector 11 20))}
|
||||
{(define x <o1>)
|
||||
(define y <o1>)}
|
||||
11]
|
||||
]
|
||||
|
||||
The distinction between a @tech{top-level variable} and an object
|
||||
reference is crucial. A @tech{top-level variable} is not a
|
||||
@tech{value}; each time a @tech{variable} expression is evaluated, the
|
||||
value is extracted from the current set of definitions. An object
|
||||
reference, in contrast is a value, and therefore needs no further
|
||||
evaluation. The model evaluation steps above use angle-bracketed
|
||||
@scheme[<o1>] for an object reference to distinguish it from a
|
||||
@tech{variable} name.
|
||||
|
||||
A direct object reference can never appear in a text-based source
|
||||
program. A program representation created with
|
||||
@scheme[datum->syntax-object], however, can embed direct references to
|
||||
existing @tech{objects}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "model-eq"]{Object Identity and Comparisons}
|
||||
|
||||
The @scheme[eq?] operator compares two @tech{values}, returning
|
||||
@scheme[#t] when the values refer to the same @tech{object}. This form
|
||||
of equality is suitable for comparing objects that support imperative
|
||||
update (e.g., to determine that the effect of modifying an object
|
||||
through one reference is visible through another reference). Also, an
|
||||
@scheme[eq?] test evaluates quickly, and @scheme[eq?]-based hashing
|
||||
is more lightweight than @scheme[equal?]-based hashing in hash tables.
|
||||
|
||||
In some cases, however, @scheme[eq?] is unsuitable as a comparison
|
||||
operator, because the generation of @tech{objects} is not clearly
|
||||
defined. In particular, two applications of @scheme[+] to the same two
|
||||
exact integers may or may not produce results that are @scheme[eq?],
|
||||
although the results are always @scheme[equal?]. Similarly, evaluation
|
||||
of a @scheme[lambda] form typically generates a new procedure
|
||||
@tech{object}, but it may re-use a procedure @tech{object} previously
|
||||
generated by the same source @scheme[lambda] form.
|
||||
|
||||
The behavior of a datatype with respect to @scheme[eq?] is generally
|
||||
specified with the datatype and its associated procedures.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "gc-model"]{Garbage Collection}
|
||||
|
||||
In the program state
|
||||
|
||||
@prog-steps[
|
||||
[{(define <o1> (vector 10 20))
|
||||
(define <o2> (vector 0))}
|
||||
{(define x <o1>)}
|
||||
(+ 1 x)]
|
||||
]
|
||||
|
||||
evaluation cannot depend on @scheme[<o2>], because it is not part of
|
||||
the program to evaluate, and it is not referenced by any definition
|
||||
that is accessible in the program. The @tech{object} @scheme[<o2>] may
|
||||
therefore be removed from the evaluation by @deftech{garbage
|
||||
collection}.
|
||||
|
||||
A few special compound datatypes hold @deftech{weak references} to
|
||||
objects. Such weak references are treated specially by the garbage
|
||||
collector in determining which @tech{objects} are reachable for the
|
||||
remainder of the computation. If an @tech{object} is reachable only
|
||||
via a @tech{weak reference}, then the object can be reclaimed, and the
|
||||
@tech{weak reference} is replaced by a different @tech{value}
|
||||
(typically @scheme[#f]).
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Procedure Applications and Local Variables}
|
||||
|
||||
Given
|
||||
|
||||
@verbatim{ f(x) = x + 10}
|
||||
|
||||
then an algebra student simplifies @tt{f(1)} as follows:
|
||||
|
||||
@verbatim{ f(7) = 7 + 10 = 17}
|
||||
|
||||
The key step in this simplification is take the body of the defined
|
||||
function @tt{f}, and then replace each @tt{x} with the actual
|
||||
@tech{value} @tt{1}.
|
||||
|
||||
Scheme procedure application works much the same way. A procedure is
|
||||
an @tech{object}, so evaluating @scheme[(f 7)] starts with a
|
||||
@tech{variable} lookup:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)}
|
||||
((code:hilite f) 7)]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)}
|
||||
(code:hilite (<p1> 7))]
|
||||
]
|
||||
|
||||
Unlike in algebra, however, the @tech{value} associated with an
|
||||
argument can be changed in the body of a procedure by using
|
||||
@scheme[set!], as in the example @scheme[(lambda (x) (begin (set! x 3)
|
||||
x))]. Since the @tech{value} associated with @scheme[x] can be
|
||||
changed, an actual value for cannot be substituted for @scheme[x] when
|
||||
the procedure is applied.
|
||||
|
||||
Instead, a new @deftech{location} is created for each @tech{variable}
|
||||
on each application. The argument @tech{value} is placed in the
|
||||
@tech{location}, and each instance of the @tech{variable} in the
|
||||
procedure body is replaced with the new @tech{location}:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)}
|
||||
(code:hilite (<p1> 7))]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
(+ (code:hilite xloc) 10)]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
(code:hilite (+ 7 10))]
|
||||
[{(define <p1> (lambda (x) (+ x 10)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
17]
|
||||
]
|
||||
|
||||
A @tech{location} is the same as a @tech{top-level variable}, but when
|
||||
a @tech{location} is generated, it (conceptually) uses a name that has
|
||||
not been used before and that cannot not be generated again or
|
||||
accessed directly.
|
||||
|
||||
Generating a @tech{location} in this way means that @scheme[set!]
|
||||
evaluates for @tech{local variables} in the same way as for
|
||||
@tech{top-level variables}, because the @tech{local variable} is
|
||||
always replaced with a @tech{location} by the time the @scheme[set!]
|
||||
form is evaluated:
|
||||
|
||||
@prog-steps[
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)}
|
||||
((code:hilite f) 7)]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)}
|
||||
(code:hilite (<p1> 7))]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 7)}
|
||||
(begin (code:hilite (set! xloc 3)) xloc)]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 3)}
|
||||
(code:hilite (begin (void) xloc))]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 3)}
|
||||
(code:hilite xloc)]
|
||||
[{(define <p1> (lambda (x) (begin (set! x 3) x)))}
|
||||
{(define f <p1>)
|
||||
(define xloc 3)}
|
||||
3]
|
||||
]
|
||||
|
||||
The substition and @tech{location}-generation step of procedure
|
||||
application requires that the argument is a @tech{value}. Therefore,
|
||||
in @scheme[((lambda (x) (+ x 10)) (+ 1 2))], the @scheme[(+ 1 2)]
|
||||
sub-expression must be simplified to the @tech{value} @scheme[3], and
|
||||
then @scheme[3] can be placed into a @tech{location} for
|
||||
@scheme[x]. In other words, Scheme is a @deftech{call-by-value}
|
||||
language.
|
||||
|
||||
Evaluation of a local-variable form, such as @scheme[(let ([x (+ 1
|
||||
2)]) _expr)], is the same as for a procedure call. After @scheme[(+ 1
|
||||
2)] produces a @tech{value}, it is stored in a fresh @tech{location}
|
||||
that replaces every instance of @scheme[x] in @scheme[_expr].
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section{Variables and Locations}
|
||||
|
||||
A @deftech{variable} is a placeholder for a @tech{value}, and an
|
||||
expressions in an initial program refer to @tech{variables}. A
|
||||
@deftech{top-level variable} is both a @tech{variable} and a
|
||||
@tech{location}. Any other @tech{variable} is always replaced by a
|
||||
@tech{location} at run-time, so that evaluation of expressions
|
||||
involves only @tech{locations}. A single @deftech{local variable}
|
||||
(i.e., a non-top-level, non-module-level @tech{variable}), such as a
|
||||
procedure argument, can correspond to different @tech{locations}
|
||||
through different instantiations.
|
||||
|
||||
For example, in the program
|
||||
|
||||
@schemeblock[(define y (+ (let ([x 5]) x) 6))]
|
||||
|
||||
both @scheme[y] and @scheme[x] are @tech{variables}. The @scheme[y]
|
||||
@tech{variable} is a @tech{top-level variable}, and the @scheme[x] is
|
||||
a @tech{local variable}. When this code is evaluated, a
|
||||
@tech{location} is created for @scheme[x] to hold the value
|
||||
@scheme[5], and a @tech{location} is also created for @scheme[y] to
|
||||
hold the value @scheme[6].
|
||||
|
||||
The replacement of a @tech{variable} with a @tech{location} during
|
||||
evaluation implements Scheme's @deftech{lexical scoping}. For example,
|
||||
when a procedure-argument @tech{variable} @scheme[x] is replaced by
|
||||
the @tech{location} @scheme[xloc], then it is replaced throughout the
|
||||
body of the procedure, including with any nested @scheme[lambda]
|
||||
forms. As a result, future references of the @tech{variable} always
|
||||
access the same @tech{location}.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "module-eval-model"]{Modules and Module-Level Variables}
|
||||
|
||||
Most definitions in PLT Scheme are in modules. In terms of evaluation,
|
||||
a module is essentially a prefix on a defined name, so that different
|
||||
modules can define the name. That is, a @deftech{module-level
|
||||
variable} is like a @tech{top-level variable} from the perspective of
|
||||
evaluation.
|
||||
|
||||
One difference between a module an a top-level definition is that a
|
||||
module can be declared without instantiating its module-level
|
||||
definitions. Evaluation of a @scheme[require] @deftech{instantiates}
|
||||
(i.e., triggers the @deftech{instantiation} of) a declared module,
|
||||
which creates variables that correspond to its module-level
|
||||
definitions.
|
||||
|
||||
For example, given the module declaration
|
||||
|
||||
@schemeblock[
|
||||
(module m mzscheme
|
||||
(define x 10))
|
||||
]
|
||||
|
||||
the evaluation of @scheme[(require m)] creates the variable @scheme[x]
|
||||
and installs @scheme[10] as its value. This @scheme[x] is unrelated to
|
||||
any top-level definition of @scheme[x].
|
||||
|
||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@subsection[#:tag "module-phase"]{Module Phases}
|
||||
|
||||
A module can be @tech{instantiate}d in multiple @deftech{phases}. A
|
||||
phase is an integer that, again, is effectively a prefix on the names
|
||||
of module-level definitions. A top-level @scheme[require]
|
||||
@tech{instantiates} a module at @tech{phase} 0, if the module is not
|
||||
already @tech{instantiate}d at phase 0. A top-level
|
||||
@scheme[require-for-syntax] @tech{instantiates} a module at
|
||||
@tech{phase} 1 (if it is not already @tech{instantiate}d at that
|
||||
level); a @scheme[require-for-syntax] also has a different binding
|
||||
effect on further program parsing, as described in
|
||||
@secref["intro-binding"].
|
||||
|
||||
Within a module, some definitions are shifted by a phase already; the
|
||||
@scheme[define-for-syntax] form is like @scheme[define], but it
|
||||
defines a variable at relative @tech{phase} 1, instead of relative
|
||||
@tech{phase} 0. Thus, if the module is @tech{instantiate}d at phase 1,
|
||||
the variables for @scheme[define-for-syntax] are created at phase 2,
|
||||
and so on. Moreover, this relative phase acts as another layer of
|
||||
prefixing, so that a @scheme[define] of @scheme[x] and a
|
||||
@scheme[define-for-syntax] of @scheme[x] can co-exist in a module
|
||||
without colliding. Again, the higher phases are mainly related to
|
||||
program parsing, instead of normal evaluation.
|
||||
|
||||
If a module @tech{instantiate}d at @tech{phase} @math{n}
|
||||
@scheme[require]s another module, then the @scheme[require]d module is
|
||||
first @tech{instantiate}d at phase @math{n}, and so on
|
||||
transitively. (Module @scheme[require]s cannot form cycles.) If a
|
||||
module @tech{instantiate}d at phase @math{n}
|
||||
@scheme[require-for-syntax]es another module, the other module is
|
||||
first @tech{instantiate}d at @tech{phase} @math{n+1}, and so on. If a
|
||||
module @tech{instantiate}d at phase @math{n} for non-zero @math{n}
|
||||
@scheme[require-for-template]s another module, the other module is
|
||||
first @tech{instantiate}d at @tech{phase} @math{n-1}, and so on.
|
||||
|
||||
A final distinction among module @tech{instantiations} is that
|
||||
multiple @tech{instantiations} may exist at phase 1 and higher. These
|
||||
@tech{instantiations} are created by the parsing of module forms (see
|
||||
@secref["mod-parse"]), and are, again, conceptually distinguished
|
||||
by prefixes.
|
||||
|
||||
@;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
@subsection[#:tag "module-redeclare"]{Module Re-declarations}
|
||||
|
||||
@section-index["modules" "re-define"]
|
||||
|
||||
When a module is declared using a name for which a module is already
|
||||
declared, the new declaration's definitions replace and extend the old
|
||||
declarations. If a variable in the old declaration has no counterpart
|
||||
in the new declaration, the old variable continues to exist, but its
|
||||
binding is not included in the @tech{lexical information} for the
|
||||
module body. If a new variable definition has a counterpart in the old
|
||||
declaration, it effectively assigns to the old variable.
|
||||
|
||||
If a module is @tech{instantiate}d in any @tech{phase}s before it is
|
||||
re-declared, each re-declaration of the module is immediately
|
||||
@tech{instantiate}d in the same @tech{phase}s.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "mark-model"]{Continuation Frames and Marks}
|
||||
|
||||
Every continuation @scheme[_C] can be partitioned into
|
||||
@deftech{continuation frames} @frame[1], @frame[2], ..., @frame["n"]
|
||||
such that @scheme[_C] = @*sub[@frame[1] @*sub[@frame[2] @*sub["..."
|
||||
@frame["n"]]]], and no frame @frame["i"] can be itself partitioned
|
||||
into smaller continuations. Evaluation steps add and remove frames to
|
||||
the current continuation, typically one at a time.
|
||||
|
||||
Each frame is conceptually annotated with a set of
|
||||
@deftech{continuation marks}. A mark consists of a key and its value;
|
||||
the key is an arbitrary value, and each frame includes at most one
|
||||
mark for any key. Various operations set and extract marks from
|
||||
continuations, so that marks can be used to attach information to a
|
||||
dynamic extent. For example, marks can be used to record information
|
||||
for a ``stack trace'' to be used when an exception is raised, or
|
||||
to implement dynamic scope.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "prompt-model"]{Prompts, Delimited Continuations, and Barriers}
|
||||
|
||||
A @deftech{prompt} is a special kind of continuation frame that is
|
||||
annotated with a specific @deftech{prompt-tag} (essentially a
|
||||
continuation mark). Various operations allow the capture of frames in
|
||||
the continuation from the redex position out to the nearest enclosing
|
||||
prompt with a particular prompt tag; such a continuation is sometimes
|
||||
called a @deftech{delimited continuation}. Other operations allow the
|
||||
current continuation to be extended with a captured continuation
|
||||
(specifically, a @deftech{composable continuation}). Yet other
|
||||
operations abort the computation to the nearest enclosing prompt with
|
||||
a particular tag, or replace the continuation to the nearest enclosing
|
||||
prompt with another one. When a delimited continuation is captured,
|
||||
the marks associated with the relevant frames are also captured.
|
||||
|
||||
A @deftech{continuation barrier} is another kind of continuation frame
|
||||
that prohibits certain replacements of the current continuation with
|
||||
another. Specifically, while an abort is allowed to remove a portion
|
||||
of the continuation containing a prompt, the continuation can be
|
||||
replaced by another only when the replacement also includes the
|
||||
continuation barrier. Certain operations install barriers
|
||||
automatically; in particular, when an exception handler is called, a
|
||||
continuation barrier prohibits the continuation of the handler from
|
||||
capturing the continuation past the exception point.
|
||||
|
||||
A @deftech{escape continuation} is essentially a derived concept. It
|
||||
combines a prompt for escape purposes with a continuation for
|
||||
mark-gathering purposes. as the name implies, escape continuations are
|
||||
used only to abort to the point of capture, which means that
|
||||
escape-continuation aborts can cross continuation barriers.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "thread-model"]{Threads}
|
||||
|
||||
Scheme supports multiple, pre-emptive @deftech{threads} of
|
||||
evaluation. In terms of the evaluation model, this means that each
|
||||
step in evaluation actually consists of multiple concurrent
|
||||
expressions, rather than a single expression. The expressions all
|
||||
share the same objects and top-level variables, so that they can
|
||||
communicate through shared state. Most evaluation steps involve a
|
||||
single step in a single expression, but certain synchronization
|
||||
primitives require multiple threads to progress together in one step.
|
||||
|
||||
In addition to the state that is shared among all threads, each thread
|
||||
has its own private state that is accessed through @deftech{thread
|
||||
cells}. A thread cell is similar to a normal mutable object, but a
|
||||
change to the value inside a thread cell is seen only when extracting
|
||||
a value from the cell from the same thread. A thread cell can be
|
||||
@deftech{preserved}; when a new thread is created, the creating
|
||||
thread's value for a preserved thread cell serves as the initial value
|
||||
for the cell in the created thread. For a non-preserved thread cell, a
|
||||
new thread sees the same initial value (specified when the thread cell
|
||||
is created) as all other threads.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "parameter-model"]{Parameters}
|
||||
|
||||
@deftech{Parameters} are essentially a derived concept in Scheme; they
|
||||
are defined in terms of continuation marks and thread cells. However,
|
||||
parameters are also built in, in the sense that some primitive
|
||||
procedures consult parameter values. For example, the default output
|
||||
stream for primitive output operations is determined by a parameter.
|
||||
|
||||
A parameter is a setting that is both thread-specific and
|
||||
continuation-specific. In the empty continuation, each parameter
|
||||
corresponds to a preserved thread cell; a corresponding
|
||||
@deftech{parameter procedure} accesses and sets the thread cell's
|
||||
value for the current thread.
|
||||
|
||||
In a non-empty continuation, a parameter's value is determined through
|
||||
a @deftech{parameterization} that is associated with the nearest
|
||||
enclosing continuation frame though a continuation mark (whose key is
|
||||
not directly accessible). A parameterization maps each parameter to a
|
||||
preserved thread cell, and the combination of thread cell and current
|
||||
thread yields the parameter's value. A parameter procedure sets or
|
||||
accesses the relevant thread cell for its parameter.
|
||||
|
||||
Various operations, such as @scheme[parameterize] or
|
||||
@scheme[with-parameterization], install a parameterization into the
|
||||
current continuation's frame.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "exn-model"]{Exceptions}
|
||||
|
||||
@deftech{Exceptions} are essentially a derived concept in Scheme; they
|
||||
are defined in terms of continuations, prompts, and continuation
|
||||
marks. However, exceptions are also built in, in the sense that
|
||||
primitive forms and procedures may raise exceptions.
|
||||
|
||||
A handler for uncaught exceptions is designated through a built-in
|
||||
parameter. A handler to catch exceptions can be associated with a
|
||||
continuation frame though a continuation mark (whose key is not
|
||||
directly accessible). When an exception is raised, the current
|
||||
continuation's marks determine a chain of handler procedures that are
|
||||
consulted to handle the exception.
|
||||
|
||||
One potential action of an exception handler is to abort the current
|
||||
continuation up to an enclosing prompt with a particular tag. The
|
||||
default handler for uncaught exceptions, in particular, aborts to a
|
||||
particular tag for which a prompt is always present, because the
|
||||
prompt is installed in the outermost frame of the continuation for any
|
||||
new thread.
|
||||
|
||||
@;------------------------------------------------------------------------
|
||||
@section[#:tag "custodian-model"]{Custodians}
|
||||
|
||||
A @deftech{custodian} manages a collection of threads, file-stream
|
||||
ports, TCP ports, TCP listeners, UDP sockets, and byte converters.
|
||||
Whenever a thread, file-stream port, TCP port, TCP listener, or UDP
|
||||
socket is created, it is placed under the management of the
|
||||
@deftech{current custodian} as determined by the
|
||||
@scheme[current-custodian] @tech{parameter}.
|
||||
|
||||
@margin-note{In MrEd, custodians also manage eventspaces.}
|
||||
|
||||
Except for the root custodian, every @tech{custodian} itself it
|
||||
managed by a @tech{custodian}, so that custodians form a hierarchy.
|
||||
Every object managed by a subordinate custodian is also managed by the
|
||||
custodian's owner.
|
||||
|
||||
When a @tech{custodian} is shut down via
|
||||
@scheme[custodian-shutdown-all], it forcibly and immediately closes
|
||||
the ports, TCP connections, etc. that it manages, as well as
|
||||
terminating (or suspending) its threads. A custodian that has been
|
||||
shut down cannot manage new objects. If the current custodian is shut
|
||||
down before a procedure is called to create a managed resource (e.g.,
|
||||
@scheme[open-input-port], @scheme[thread]), the
|
||||
@exnraise[exn:fail:contract].
|
||||
|
||||
A thread can have multiple managing custodians, and a suspended thread
|
||||
created with @scheme[thread/suspend-to-kill] can have zero
|
||||
custodians. Extra custodians become associated with a thread through
|
||||
@scheme[thread-resume] (see @secref["threadkill"]). When a thread
|
||||
has multiple custodians, it is not necessarily killed by a
|
||||
@scheme[custodian-shutdown-all], but shut-down custodians are removed
|
||||
from the thread's managing set, and the thread is killed when its
|
||||
managing set becomes empty.
|
||||
|
||||
The values managed by a custodian are only weakly held by the
|
||||
custodian. As a result, a @tech{will} can be executed for a value that
|
||||
is managed by a custodian. In addition, a custodian only weakly
|
||||
references its subordinate custodians; if a subordinate custodian is
|
||||
unreferenced but has its own subordinates, then the custodian may be
|
||||
collected, at which point its subordinates become immediately
|
||||
subordinate to the collected custodian's superordinate custodian.
|
||||
|
||||
In addition to the other entities managed by a custodian, a
|
||||
@defterm{custodian box} created with @scheme[make-custodian-box]
|
||||
strongly holds onto a value placed in the box until the box's
|
||||
custodian is shut down. The custodian only weakly retains the box
|
||||
itself, however (so the box and its content can be collected if there
|
||||
are no other references to them).
|
||||
|
||||
When MzScheme is compiled with support for per-custodian memory
|
||||
accounting (see @scheme[custodian-memory-accounting-available?]), the
|
||||
@scheme[current-memory-use] procedure can report a custodian-specific
|
||||
result. This result determines how much memory is occupied by objects
|
||||
that are reachable from the custodian's managed values, especially its
|
||||
threads, and including its sub-custodians' managed values. If an
|
||||
object is reachable from two custodians where neither is an ancestor
|
||||
of the other, an object is arbitrarily charged to one of the other,
|
||||
and the choice can change after each collection; objects reachable
|
||||
from both a custodian and its descendant, however, are reliably
|
||||
charged to the descendant. Reachability for per-custodian accounting
|
||||
does not include weak references, references to threads managed by
|
||||
non-descendant custodians, references to non-descendant custodians, or
|
||||
references to custodian boxes for non-descendant custodians.
|
||||
@include-section["eval-model.scrbl"]
|
||||
@include-section["syntax-model.scrbl"]
|
||||
|
|
|
@ -27,7 +27,6 @@ languages, where @schememodname[scheme] includes all of
|
|||
@table-of-contents[]
|
||||
|
||||
@include-section["model.scrbl"]
|
||||
@include-section["syntax-model.scrbl"]
|
||||
@include-section["syntax.scrbl"]
|
||||
@include-section["data.scrbl"]
|
||||
@include-section["struct.scrbl"]
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
@local-table-of-contents[]
|
||||
|
||||
A @pidefterm{string} is a fixed-length array of
|
||||
A @deftech{string} is a fixed-length array of
|
||||
@seclink["characters"]{characters}.
|
||||
|
||||
@index['("strings" "immutable")]{A} string can be @defterm{mutable} or
|
||||
|
|
|
@ -8,10 +8,10 @@
|
|||
|
||||
@guideintro["define-struct"]{structure types via @scheme[define-struct]}
|
||||
|
||||
A @deftech{structure type} is a record datatype composing a number
|
||||
of @idefterm{fields}. A @pidefterm{structure}, an instance of a
|
||||
structure type, is a first-class value that contains a value for each
|
||||
field of the structure type. A structure instance is created with a
|
||||
A @deftech{structure type} is a record datatype composing a number of
|
||||
@idefterm{fields}. A @deftech{structure}, an instance of a structure
|
||||
type, is a first-class value that contains a value for each field of
|
||||
the structure type. A structure instance is created with a
|
||||
type-specific @tech{constructor} procedure, and its field values are
|
||||
accessed and changed with type-specific @tech{accessor} and
|
||||
@tech{mutator} procedures. In addition, each structure type has a
|
||||
|
|
145
collects/scribblings/reference/vectors.scrbl
Normal file
145
collects/scribblings/reference/vectors.scrbl
Normal file
|
@ -0,0 +1,145 @@
|
|||
#lang scribble/doc
|
||||
@(require "mz.ss")
|
||||
|
||||
@title[#:tag "vectors"]{Vectors}
|
||||
|
||||
A @deftech{vector} is a fixed-length array with constant-time access
|
||||
and update of the vector slots, which are numbered from @scheme[0] to
|
||||
one less than the number of slots in the vector.
|
||||
|
||||
Two vectors are @scheme[equal?] if they have the same length, and if
|
||||
the values in corresponding slots of the the vectors are
|
||||
@scheme[equal?].
|
||||
|
||||
A vector can be @defterm{mutable} or @defterm{immutable}. When an
|
||||
immutable vector is provided to a procedure like @scheme[vector-set!],
|
||||
the @exnraise[exn:fail:contract]. Vectors generated by the default
|
||||
reader (see @secref["parse-string"]) are immutable.
|
||||
|
||||
A vector can be used as a single-valued sequence (see
|
||||
@secref["sequences"]). The elements of the vector serve as elements
|
||||
of the sequence. See also @scheme[in-vector].
|
||||
|
||||
@defproc[(vector? [v any/c]) boolean?]{
|
||||
|
||||
Returns @scheme[#t] if @scheme[v] is a vector, @scheme[#f] otherwise.}
|
||||
|
||||
|
||||
@defproc[(make-vector [size nonnegative-exact-integer?]
|
||||
[v any/c 0]) vector?]{
|
||||
|
||||
Returns a mutable vector with @scheme[size] slots, where all slots are
|
||||
initialized to contain @scheme[v].}
|
||||
|
||||
|
||||
@defproc[(vector [v any/c] ...) vector?]{
|
||||
|
||||
Returns a mutable vector with as many slots as provided @scheme[v]s,
|
||||
where the slots are initialized to contain the given @scheme[v]s in
|
||||
order.}
|
||||
|
||||
|
||||
@defproc[(vector-immutable [v any/c] ...) (and/c vector?
|
||||
immutable?)]{
|
||||
|
||||
Returns an immutable vector with as many slots as provided
|
||||
@scheme[v]s, where the slots are contain the given @scheme[v]s in
|
||||
order.}
|
||||
|
||||
|
||||
|
||||
@defproc[(vector-length [vec vector?]) nonnegative-exact-integer?]{
|
||||
|
||||
Returns the length of @scheme[vec] (i.e., the number of slots in the
|
||||
vector).}
|
||||
|
||||
@defproc[(vector-ref [vec vector?][pos nonnegative-exact-integer?]) any/c]{
|
||||
|
||||
Returns the element in slot @scheme[pos] of @scheme[vec]. The first
|
||||
slot is position @scheme[0], and the last slot is one less than
|
||||
@scheme[(vector-length vec)].}
|
||||
|
||||
@defproc[(vector-set! [vec (and/c vector? (not/c immutable?))]
|
||||
[pos nonnegative-exact-integer?]
|
||||
[v any/c])
|
||||
void?]{
|
||||
|
||||
Updates the slot @scheme[pos] of @scheme[vec] to contain @scheme[v].}
|
||||
|
||||
|
||||
@defproc[(vector->list [vec vector?])
|
||||
list?]{
|
||||
|
||||
Returns a list with the same length and elements as @scheme[vec].}
|
||||
|
||||
|
||||
@defproc[(list->vector [lst list?])
|
||||
vector?]{
|
||||
|
||||
Returns a mutable vector with the same length and elements as
|
||||
@scheme[lst].}
|
||||
|
||||
|
||||
@defproc[(vector->immutable-vector [vec vector?])
|
||||
(and/c vector? immutable?)]{
|
||||
|
||||
Returns an immutable vector with the same length and elements as @scheme[vec].
|
||||
If @scheme[vec] is itself immutable, then it is returned as the result.}
|
||||
|
||||
|
||||
@defproc[(vector-fill! [vec (and/c vector? (not/c immutable?))]
|
||||
[v any/c])
|
||||
void?]{
|
||||
|
||||
Changes all slots of @scheme[vec] to contain @scheme[v].}
|
||||
|
||||
|
||||
@defproc[(vector-copy! [dest (and/c vector? (not/c immutable?))]
|
||||
[dest-start exact-nonnegative-integer?]
|
||||
[src vector?]
|
||||
[src-start exact-nonnegative-integer? 0]
|
||||
[src-end exact-nonnegative-integer? (vector-length src)])
|
||||
void?]{
|
||||
|
||||
Changes the elements of @scheme[dest] starting at position
|
||||
@scheme[dest-start] to match the elements in @scheme[src] from
|
||||
@scheme[src-start] (inclusive) to @scheme[src-end] (exclusive). The
|
||||
vectors @scheme[dest] and @scheme[src] can be the same vector, and in
|
||||
that case the destination region can overlap with the source region;
|
||||
the destination elements after the copy match the source elements
|
||||
from before the copy. If any of @scheme[dest-start],
|
||||
@scheme[src-start], or @scheme[src-end] are out of range (taking into
|
||||
account the sizes of the vectors and the source and destination
|
||||
regions), the @exnraise[exn:fail:contract].
|
||||
|
||||
@examples[(define v (vector 'A 'p 'p 'l 'e))
|
||||
(vector-copy! v 4 #(y))
|
||||
(vector-copy! v 0 v 3 4)
|
||||
v]}
|
||||
|
||||
|
||||
@defproc[(vector->values [vec vector?]
|
||||
[start-pos nonnegative-exact-integer? 0]
|
||||
[end-pos nonnegative-exact-integer? (vector-length vec)])
|
||||
any]{
|
||||
|
||||
Returns @math{@scheme[end-pos] - @scheme[start-pos]} values, which are
|
||||
the elements of @scheme[vec] from @scheme[start-pos] (inclusive) to
|
||||
@scheme[end-pos] (exclusive). If @scheme[start-pos] or
|
||||
@scheme[end-pos] are greater than @scheme[(vector-length vec)], or if
|
||||
@scheme[end-pos] is less than @scheme[start-pos], the
|
||||
@exnraise[exn:fail:contract].}
|
||||
|
||||
@defproc[(build-vector [n exact-nonnegative-integer?]
|
||||
[proc (exact-nonnegative-integer? . -> . any.c)])
|
||||
vector?]{
|
||||
|
||||
Creates a vector of @scheme[n] elements by applying @scheme[proc] to
|
||||
the integers from @scheme[0] to @scheme[(sub1 n)] in order. If
|
||||
@scheme[_vec] is the resulting vector, then @scheme[(vector-ref _vec
|
||||
_i)] is the value produced by @scheme[(proc _i)].
|
||||
|
||||
@examples[
|
||||
(build-vector 5 add1)
|
||||
]}
|
||||
|
109
collects/tests/mzscheme/dict.ss
Normal file
109
collects/tests/mzscheme/dict.ss
Normal file
|
@ -0,0 +1,109 @@
|
|||
|
||||
(load-relative "loadtest.ss")
|
||||
|
||||
(Section 'dict)
|
||||
|
||||
(require scheme/dict)
|
||||
|
||||
;; Currently relying on the `map' an `for-each' to test `dict-iterate-...',
|
||||
;; and custom hashes to test `prop:dict' use.
|
||||
|
||||
(define (try-simple d mutable? can-remove? can-update? [orig-one 1])
|
||||
(test #t dict? d)
|
||||
|
||||
(test 'one dict-ref d 1)
|
||||
(test 'nope dict-ref d 100 'nope)
|
||||
(test 'nope dict-ref d 100 (lambda () 'nope))
|
||||
|
||||
(test #t ormap values (dict-map d (lambda (k v) (equal? k orig-one))))
|
||||
(test #t ormap values (dict-map d (lambda (k v) (equal? v 'one))))
|
||||
(test #f ormap values (dict-map d (lambda (k v) (equal? k 100))))
|
||||
|
||||
(test mutable? dict-mutable? d)
|
||||
(test can-remove? dict-can-remove-keys? d)
|
||||
(test can-update? dict-can-functional-set? d)
|
||||
|
||||
(test (dict-map d cons) 'in-dict
|
||||
(for/list ([(k v) (in-dict d)])
|
||||
(cons k v)))
|
||||
(test (dict-map d cons) 'in-dict/keys/vals
|
||||
(for/list ([k (in-dict-keys d)]
|
||||
[v (in-dict-values d)])
|
||||
(cons k v)))
|
||||
(test (dict-map d cons) 'in-dict-pairs
|
||||
(for/list ([p (in-dict-pairs d)])
|
||||
p))
|
||||
|
||||
(let ([l null])
|
||||
(dict-for-each d (lambda (k v) (set! l (cons (cons k v) l))))
|
||||
(test (reverse l) dict-map d cons)
|
||||
(test (length l) dict-count d))
|
||||
|
||||
(if (not can-remove?)
|
||||
(begin
|
||||
(err/rt-test (dict-remove! d 1))
|
||||
(err/rt-test (dict-remove d 1))
|
||||
(err/rt-test (dict-set d 1 "ONE"))
|
||||
(test (void) dict-set! d 1 "ONE")
|
||||
(test "ONE" dict-ref d 1))
|
||||
(let ([cnt (dict-count d)]
|
||||
[smaller (if mutable?
|
||||
(begin
|
||||
(err/rt-test (dict-remove d 1))
|
||||
(dict-remove! d 1)
|
||||
d)
|
||||
(begin
|
||||
(err/rt-test (dict-remove! d 1))
|
||||
(dict-remove d 1)))])
|
||||
(test 'nope dict-ref smaller 1 'nope)
|
||||
(test (sub1 cnt) dict-count smaller)
|
||||
(let ([try-add
|
||||
(lambda (d val)
|
||||
(let ([bigger (if mutable?
|
||||
(begin
|
||||
(err/rt-test (dict-set smaller 1 val))
|
||||
(dict-set! smaller 1 val)
|
||||
d)
|
||||
(begin
|
||||
(err/rt-test (dict-set! smaller 1 val))
|
||||
(dict-set smaller 1 val)))])
|
||||
(test cnt dict-count bigger)
|
||||
(when (eq? val 'one)
|
||||
(unless (pair? d)
|
||||
(test #t equal? d bigger)))))])
|
||||
(try-add smaller "ONE")
|
||||
(try-add d "ONE")
|
||||
(try-add d 'one)))))
|
||||
|
||||
(try-simple (vector 'zero 'one 'two) #t #f #f)
|
||||
(try-simple #hash((1 . one) (#f . 7)) #f #t #t)
|
||||
(try-simple #hasheq((1 . one) (#f . 7)) #f #t #t)
|
||||
(try-simple (hash-copy #hash((1 . one) (#f . 7))) #t #t #f)
|
||||
(try-simple (hash-copy #hasheq((1 . one) (#f . 7))) #t #t #f)
|
||||
(try-simple '((0 . zero) (1 . one)) #f #t #t)
|
||||
(try-simple '((1 . one) (0 . zero)) #f #t #t)
|
||||
(try-simple (let ([h (make-custom-hash (lambda (a b)
|
||||
(string=? (format "~a" a)
|
||||
(format "~a" b)))
|
||||
(lambda (a)
|
||||
(equal-hash-code (format "~a" a))))])
|
||||
(dict-set! h "1" 'one)
|
||||
(dict-set! h "2" 'two)
|
||||
h)
|
||||
#t #t #f
|
||||
"1")
|
||||
(try-simple (let* ([h (make-immutable-custom-hash
|
||||
(lambda (a b)
|
||||
(string=? (format "~a" a)
|
||||
(format "~a" b)))
|
||||
(lambda (a)
|
||||
(equal-hash-code (format "~a" a))))]
|
||||
[h (dict-set h "1" 'one)]
|
||||
[h (dict-set h "2" 'two)])
|
||||
h)
|
||||
#f #t #t
|
||||
"1")
|
||||
|
||||
;; ----------------------------------------
|
||||
|
||||
(report-errs)
|
|
@ -1674,7 +1674,7 @@ static Scheme_Object *hash_table_put(int argc, Scheme_Object *argv[])
|
|||
Scheme_Object *v = argv[0];
|
||||
|
||||
if (!SCHEME_HASHTRP(v)) {
|
||||
scheme_wrong_type("hash-set", "immutable table", 0, argc, argv);
|
||||
scheme_wrong_type("hash-set", "immutable hash", 0, argc, argv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1750,7 +1750,7 @@ static Scheme_Object *hash_table_remove_bang(int argc, Scheme_Object *argv[])
|
|||
static Scheme_Object *hash_table_remove(int argc, Scheme_Object *argv[])
|
||||
{
|
||||
if (!SCHEME_HASHTRP(argv[0]))
|
||||
scheme_wrong_type("hash-remove", "immutable table", 0, argc, argv);
|
||||
scheme_wrong_type("hash-remove", "immutable hash", 0, argc, argv);
|
||||
|
||||
return (Scheme_Object *)scheme_hash_tree_set((Scheme_Hash_Tree *)argv[0], argv[1], NULL);
|
||||
}
|
||||
|
@ -2173,7 +2173,7 @@ static Scheme_Object *do_make_hash_placeholder(const char *who, int eq, int argc
|
|||
|
||||
ph = scheme_alloc_object();
|
||||
ph->type = scheme_table_placeholder_type;
|
||||
SCHEME_IPTR_VAL(ph) = l;
|
||||
SCHEME_IPTR_VAL(ph) = argv[0];
|
||||
SCHEME_PINT_VAL(ph) = eq;
|
||||
|
||||
return ph;
|
||||
|
|
Loading…
Reference in New Issue
Block a user