summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Arvydas Silanskas 2020-10-18 01:36:50 +0300
committerGravatar Arvydas Silanskas 2020-10-18 01:36:50 +0300
commitdf2693f79ac55b6700930353226a96e46f39af51 (patch)
tree0569a377d0f552493dc4227a23ec23f36db37838
parentinternals impl; tests (diff)
tests against externals; registration; alist and plist implementations:
-rw-r--r--alist-impl.scm77
-rw-r--r--dictionaries-impl.scm5
-rw-r--r--dictionaries-test.scm335
-rw-r--r--dictionaries.scm48
-rw-r--r--externals.scm128
-rw-r--r--internals.scm24
-rw-r--r--plist-impl.scm105
7 files changed, 669 insertions, 53 deletions
diff --git a/alist-impl.scm b/alist-impl.scm
new file mode 100644
index 0000000..a77d74f
--- /dev/null
+++ b/alist-impl.scm
@@ -0,0 +1,77 @@
+(define (make-alist-impl)
+
+ (define (alist? vec l)
+ (and (list? l) (every pair? l)))
+
+ (define (alist-map! vec proc alist)
+ (map
+ (lambda (e)
+ (define key (car e))
+ (define value (cdr e))
+ (cons key (proc key value)))
+ alist))
+
+ (define (alist-filter! vec pred alist)
+ (filter
+ (lambda (e)
+ (pred (car e) (cdr e)))
+ alist))
+
+ (define (alist-search! vec alist key failure success)
+ (define (handle-success pair)
+ (define old-key (car pair))
+ (define old-value (cdr pair))
+ (define (update new-key new-value obj)
+ (cond
+ ((and (eq? old-key
+ new-key)
+ (eq? old-value
+ new-value))
+ (values alist obj))
+ (else
+ (let ((new-list
+ (alist-cons
+ new-key new-value
+ (alist-delete old-key alist))))
+ (values new-list obj)))))
+ (define (remove obj)
+ (values (alist-delete old-key alist) obj))
+ (success old-key old-value update remove))
+
+ (define (handle-failure)
+ (define (insert value obj)
+ (values (alist-cons key value alist)
+ obj))
+ (define (ignore obj)
+ (values alist obj))
+ (failure insert ignore))
+ (cond
+ ((assoc key alist) => handle-success)
+ (else (handle-failure))))
+
+ (define (alist-size vec alist)
+ (define keys (map car alist))
+ (define (fold-proc el set)
+ (lset-adjoin equal? set el))
+ (define key-set (fold fold-proc '() keys))
+ (length key-set))
+
+ (define (alist-foreach vec proc alist)
+ (define (proc* e)
+ (proc (car e) (cdr e)))
+ (for-each proc* alist))
+
+ (define (alist->alist vec alist)
+ alist)
+
+
+ (define vec (vector-copy model-vec))
+ (vector-set! vec d? alist?)
+ (vector-set! vec dmap! alist-map!)
+ (vector-set! vec dfilter! alist-filter!)
+ (vector-set! vec dsearch! alist-search!)
+ (vector-set! vec dsize alist-size)
+ (vector-set! vec dfor-each alist-foreach)
+ (vector-set! vec d->alist alist->alist)
+
+ vec)
diff --git a/dictionaries-impl.scm b/dictionaries-impl.scm
new file mode 100644
index 0000000..a4ecc5c
--- /dev/null
+++ b/dictionaries-impl.scm
@@ -0,0 +1,5 @@
+(include "indexes.scm")
+(include "internals.scm")
+(include "alist-impl.scm")
+(include "plist-impl.scm")
+(include "externals.scm")
diff --git a/dictionaries-test.scm b/dictionaries-test.scm
new file mode 100644
index 0000000..0736261
--- /dev/null
+++ b/dictionaries-test.scm
@@ -0,0 +1,335 @@
+(import (scheme base)
+ (srfi 1)
+ (srfi 64)
+ (dictionaries))
+
+(define (do-test alist->dict)
+
+ (test-group
+ "dictionary?"
+ (test-assert (dictionary? (alist->dict '())))
+ (test-assert (dictionary? (alist->dict '((a . b))))))
+
+ (test-group
+ "dict-empty?"
+ (test-assert (dict-empty? (alist->dict '())))
+ (test-assert (not (dict-empty? (alist->dict '((a . b)))))))
+
+ (test-group
+ "dict-contains?"
+ (test-assert (not (dict-contains? (alist->dict '()) 'a)))
+ (test-assert (not (dict-contains? (alist->dict '((b . c))) 'a)))
+ (test-assert (dict-contains? (alist->dict '((a . b))) 'a)))
+
+ (test-group
+ "dict-ref"
+ (test-assert (dict-ref (alist->dict '((a . b))) 'a (lambda () #f) (lambda (x) #t)))
+ (test-assert (dict-ref (alist->dict '((a . b))) 'b (lambda () #t) (lambda (x) #f))))
+
+ (test-group
+ "dict-ref/default"
+ (test-equal (dict-ref/default (alist->dict '((a . b))) 'a 'c) 'b)
+ (test-equal (dict-ref/default (alist->dict '((a* . b))) 'a 'c) 'c))
+
+ (test-group
+ "dict-set!"
+ (define d (dict-set! (alist->dict '((a . b))) 'a 'c 'a2 'b2))
+ (test-equal 'c (dict-ref d 'a ))
+ (test-equal 'b2 (dict-ref d 'a2)))
+
+ (test-group
+ "dict-adjoin!"
+ (define d (dict-adjoin! (alist->dict '((a . b))) 'a 'c 'a2 'b2))
+ (test-equal 'b (dict-ref d 'a))
+ (test-equal 'b2 (dict-ref d 'a2)))
+
+ (test-group
+ "dict-delete!"
+ (define d (dict-delete! (alist->dict '((a . b) (c . d))) 'a 'b))
+ (test-equal (dict->alist d) '((c . d))))
+
+ (test-group
+ "dict-delete-all!"
+ (define d (dict-delete-all! (alist->dict '((a . b) (c . d))) '(a b)))
+ (test-equal (dict->alist d) '((c . d))))
+
+ (test-group
+ "dict-replace!"
+ (define d (dict-replace! '((a . b) (c . d)) 'a 'b2))
+ (test-equal 'b2 (dict-ref d 'a))
+ (test-equal 'd (dict-ref d 'c)))
+
+ (test-group
+ "dict-intern!"
+
+ ;; intern existing
+ (let ()
+ (define-values
+ (d value)
+ (dict-intern! (alist->dict '((a . b))) 'a (lambda () 'd)))
+ (test-equal 'b (dict-ref d 'a))
+ (test-equal 'b value))
+
+ ;; intern missing
+ (let ()
+ (define-values
+ (d value)
+ (dict-intern! (alist->dict '((a . b))) 'c (lambda () 'd)))
+ (test-equal 'b (dict-ref d 'a))
+ (test-equal 'd (dict-ref d 'c))
+ (test-equal 'd value)))
+
+ (test-group
+ "dict-update!"
+
+ ;; update existing
+ (let ()
+ (define d (dict-update! (alist->dict '((a . "b"))) 'a
+ (lambda (value)
+ (string-append value "2"))
+ error
+ (lambda (x) (string-append x "1"))))
+ (test-equal "b12" (dict-ref d 'a)))
+
+ ;; update missing
+ (let ()
+ (define d (dict-update! (alist->dict '((a . "b"))) 'c
+ (lambda (value)
+ (string-append value "2"))
+ (lambda () "d1")
+ (lambda (x) (string-append x "1"))))
+ (test-equal "d12" (dict-ref d 'c))))
+
+ (test-group
+ "dict-update/default!"
+ ;; update existing
+ (let ()
+ (define d (dict-update/default! (alist->dict '((a . "b"))) 'a
+ (lambda (value)
+ (string-append value "2"))
+ "d1"))
+ (test-equal "b2" (dict-ref d 'a)))
+
+ ;; update missing
+ (let ()
+ (define d (dict-update/default! (alist->dict '((a . "b"))) 'c
+ (lambda (value)
+ (string-append value "2"))
+ "d1"))
+ (test-equal "d12" (dict-ref d 'c))))
+
+ (test-group
+ "dict-pop!"
+ (define-values
+ (new-dict key value)
+ (dict-pop! (alist->dict '((a . b) (c . d))) error))
+ (test-equal (dict->alist new-dict) '((c . d)))
+ (test-equal key 'a)
+ (test-equal value 'b))
+
+ (test-group
+ "dict-map!"
+ (define d (dict-map!
+ (lambda (key value)
+ (string-append value "2"))
+ (alist->dict '((a . "a") (b . "b")))))
+ (test-equal "a2" (dict-ref d 'a))
+ (test-equal "b2" (dict-ref d 'b)))
+
+ (test-group
+ "dict-filter!"
+ (define d (dict-filter!
+ (lambda (key value)
+ (equal? value 'b))
+ (alist->dict '((a . b) (c . d)))))
+ (test-equal '((a . b)) (dict->alist d)))
+
+ (test-group
+ "dict-remove!"
+ (define d (dict-remove!
+ (lambda (key value)
+ (equal? value 'b))
+ (alist->dict '((a . b) (c . d)))))
+ (test-equal '((c . d)) (dict->alist d)))
+
+ (test-group
+ "dict-search!"
+
+ ;; ignore
+ (let ()
+ (define-values
+ (dict value)
+ (dict-search! '((a . b)) 'c
+ (lambda (insert ignore)
+ (ignore 'foo))
+ (lambda args
+ (error))))
+ (test-equal '((a . b)) (dict->alist dict))
+ (test-equal value 'foo))
+
+ ;; insert
+ (let ()
+ (define-values
+ (dict value)
+ (dict-search! (alist->dict '((a . b))) 'c
+ (lambda (insert ignore)
+ (insert 'd 'foo))
+ (lambda args
+ (error))))
+ (test-equal 'b (dict-ref dict 'a))
+ (test-equal 'd (dict-ref dict 'c))
+ (test-equal value 'foo))
+
+ ;; update
+ (let ()
+ (define-values
+ (dict value)
+ (dict-search! (alist->dict '((a . b))) 'a
+ (lambda args
+ (error))
+ (lambda (key value update delete)
+ (update 'a2 'b2 'foo))))
+ (test-equal '((a2 . b2)) (dict->alist dict))
+ (test-equal value 'foo))
+
+ ;; delete
+ (let ()
+ (define-values
+ (dict value)
+ (dict-search! (alist->dict '((a . b) (c . d))) 'a
+ (lambda args
+ (error))
+ (lambda (key value update delete)
+ (delete 'foo))))
+ (test-equal '((c . d)) (dict->alist dict))
+ (test-equal value 'foo)))
+
+ (test-group
+ "dict-size"
+ (test-equal 2 (dict-size (alist->dict '((a . b) (c . d)))))
+ (test-equal 0 (dict-size (alist->dict '()))))
+
+ (test-group
+ "dict-for-each"
+ (define lst '())
+ (dict-for-each
+ (lambda (key value)
+ (set! lst (append lst (list key value))))
+ (alist->dict '((a . b) (c . d))))
+ (test-equal '(a b c d) lst))
+
+ (test-group
+ "dict-count"
+ (define count (dict-count
+ (lambda (key value)
+ (equal? value 'b))
+ (alist->dict '((a . b) (c . d)))))
+ (test-equal count 1))
+
+ (test-group
+ "dict-any"
+
+ (let ()
+ (define value
+ (dict-any
+ (lambda (key value)
+ (if (equal? 'b value) 'foo #f))
+ (alist->dict '((a . b) (c . d)))))
+ (test-equal value 'foo))
+
+ (let ()
+ (define value
+ (dict-any
+ (lambda (key value)
+ (if (equal? 'e value) 'foo #f))
+ (alist->dict '((a . b) (c . d)))))
+ (test-equal value #f)))
+
+ (test-group
+ "dict-every"
+ (let ()
+ (define value
+ (dict-every
+ (lambda (key value)
+ (if (equal? 'b value) 'foo #f))
+ (alist->dict '((a . b) (c . b)))))
+ (test-equal value 'foo))
+
+ (let ()
+ (define value
+ (dict-every
+ (lambda (key value)
+ (if (equal? 'b value) 'foo #f))
+ (alist->dict '())))
+ (test-equal value #t))
+
+ (let ()
+ (define value
+ (dict-every
+ (lambda (key value)
+ (if (equal? 'b value) 'foo #f))
+ (alist->dict '((a . b) (c . d)))))
+ (test-equal value #f)))
+
+ (test-group
+ "dict-keys"
+ (define keys
+ (dict-keys (alist->dict '((a . b) (c . d)))))
+ (test-equal '(a c) keys))
+
+ (test-group
+ "dict-values"
+ (define vals
+ (dict-values (alist->dict '((a . b) (c . d)))))
+ (test-equal '(b d) vals))
+
+ (test-group
+ "dict-entries"
+ (define-values
+ (keys vals)
+ (dict-entries (alist->dict '((a . b) (c . d)))))
+ (test-equal '(a c) keys)
+ (test-equal '(b d) vals))
+
+ (test-group
+ "dict-fold"
+ (define value
+ (dict-fold
+ (lambda (key value acc)
+ (append acc (list key value)))
+ '()
+ (alist->dict '((a . b) (c . d)))))
+ (test-equal value '(a b c d)))
+
+ (test-group
+ "dict-map->list"
+ (define lst
+ (dict-map->list
+ (lambda (key value)
+ (string-append (symbol->string key)
+ value))
+ (alist->dict '((a . "b") (c . "d")))))
+ (test-equal '("ab" "cd") lst))
+
+ (test-group
+ "dict->alist"
+ (define alist
+ (dict->alist (alist->dict '((a . b) (c . d)))))
+ (test-equal alist '((a . b) (c . d)))))
+
+(test-begin "Dictionaries")
+
+(test-group
+ "alist"
+ (do-test (lambda (alist) alist)))
+
+(test-group
+ "plist"
+ (do-test
+ (lambda (alist)
+ (apply append
+ (map (lambda (pair)
+ (list (car pair) (cdr pair)))
+ alist)))))
+
+(test-end)
diff --git a/dictionaries.scm b/dictionaries.scm
new file mode 100644
index 0000000..b16cd01
--- /dev/null
+++ b/dictionaries.scm
@@ -0,0 +1,48 @@
+(define-library
+ (dictionaries)
+ (import (scheme base)
+ (scheme case-lambda)
+ (srfi 1))
+ (export
+
+ ;; predicates
+ dictionary?
+ dict-empty?
+ dict-contains?
+
+ ;; lookup
+ dict-ref
+ dict-ref/default
+
+ ;; mutation
+ dict-set!
+ dict-adjoin!
+ dict-delete!
+ dict-delete-all!
+ dict-replace!
+ dict-intern!
+ dict-update!
+ dict-update/default!
+ dict-pop!
+ dict-map!
+ dict-filter!
+ dict-remove!
+ dict-search!
+
+ ;; whole dictionary
+ dict-size
+ dict-for-each
+ dict-count
+ dict-any
+ dict-every
+ dict-keys
+ dict-values
+ dict-entries
+ dict-fold
+ dict-map->list
+ dict->alist
+
+ ;; registering dictionary types
+ register-dictionary!)
+
+ (include "dictionaries-impl.scm"))
diff --git a/externals.scm b/externals.scm
index 1a7f0e3..8d8cf2e 100644
--- a/externals.scm
+++ b/externals.scm
@@ -1,38 +1,84 @@
-;;; External (exported) procedure definitions
+(define registry
+ (list
+ (make-alist-impl)
+ (make-plist-impl)))
+
+(define (lookup dictionary fail-on-notfound?)
+ (let loop ((r registry))
+ (cond
+ ((null? r) (if fail-on-notfound?
+ (error "Not a recognized dictionary" dictionary)
+ #f))
+ ((dcall d? (car r) dictionary) (car r))
+ (else (loop (cdr r))))))
+
+(define (make-internal-wrapper name proc)
+ (cond
+ ((or (equal? name 'dict-set!)
+ (equal? name 'dict-adjoin!)
+ (equal? name 'dict-delete!))
+ (lambda (vec dict objs)
+ (apply proc (cons dict objs))))
+ (else
+ (lambda (vec . args)
+ (apply proc args)))))
+
+(define (register-dictionary! . lst)
+ (define vec (vector-copy model-vec))
+ (do ((lst lst (cddr lst)))
+ ((null? lst))
+ (when (null? (cdr lst))
+ (error "Uneven amount of arguments"))
+ (let ((proc-name (car lst))
+ (proc (cadr lst)))
+ (define index
+ (cond
+ ((assoc proc-name dname-map) => cdr)
+ (else (error "Unrecognized procedure name" proc-name))))
+ (unless (procedure? proc)
+ (error "Not a procedure" proc))
+ (vector-set! vec index (make-internal-wrapper proc-name proc))))
+ (let loop ((reg registry))
+ (define new-reg (reverse (cons vec (reverse reg))))
+ (if (eq? reg registry)
+ (set! registry new-reg)
+ (loop registry))))
+
+;;; External (exported) procedure definitions
(define-syntax dispatch
(syntax-rules ()
- (dispatch index dictionary args ...)
- (let ((vec (lookup dictionary #t)) ; error if not found
- ((vector-ref vec index) vec dictionary args ...))))))
+ ((dispatch index dictionary args ...)
+ (let ((vec (lookup dictionary #t))) ; error if not found
+ ((vector-ref vec index) vec dictionary args ...)))))
(define-syntax proc-dispatch
(syntax-rules ()
- (dispatch index dictionary args ...)
- (let ((vec (lookup dictionary #t)) ; error if not found
- ((vector-ref vec index) vec args ...))))))
+ ((dispatch index dictionary args ...)
+ (let ((vec (lookup dictionary #t))) ; error if not found
+ ((vector-ref vec index) vec args ...)))))
(define (dictionary? obj)
(if (lookup obj #f) #t #f)) ; #f if not found
(define (dict-empty? dictionary)
- (dispatch dempty dictionary))
+ (dispatch dempty? dictionary))
(define (dict-contains? dictionary key)
(dispatch dcontains? dictionary key))
(define dict-ref
(case-lambda
- ((vec dictionary key)
- (dict-ref vec dictionary key error values))
- ((vec dictionary key failure)
- (dict-ref vec dictionary key failure values))
- ((vec dictionary key failure success))
- (dict-ref* vec dictionary key failure success)))))
-
+ ((dictionary key)
+ (dict-ref dictionary key error values))
+ ((dictionary key failure)
+ (dict-ref dictionary key failure values))
+ ((dictionary key failure success)
+ (dict-ref* dictionary key failure success))))
(define (dict-ref* dictionary key failure success)
(dispatch dref dictionary key failure success))
+
(define (dict-ref/default dictionary key default)
(dispatch dref/default dictionary key default))
@@ -46,7 +92,7 @@
(dispatch ddelete! dictionary keys))
(define (dict-delete-all! dictionary keylist)
- (dispatch ddelete-all dictionary keylist))
+ (dispatch ddelete-all! dictionary keylist))
(define (dict-replace! dictionary key value)
(dispatch dreplace! dictionary key value))
@@ -55,25 +101,25 @@
(dispatch dintern! dictionary key failure))
(define dict-update!
- (case-lambda)
- ((vec dictionary key updater))
- (dict-update! vec dictionary key updater error values)))
- ((vec dictionary key updater failure))
- (dict-update! vec dictionary key updater failure values)))
- ((vec dictionary key updater failure success))
- (dispatch dupdate! dictionary key updater failure success))
+ (case-lambda
+ ((dictionary key updater)
+ (dict-update! dictionary key updater error values))
+ ((dictionary key updater failure)
+ (dict-update! dictionary key updater failure values))
+ ((dictionary key updater failure success)
+ (dispatch dupdate! dictionary key updater failure success))))
(define (dict-update/default! dictionary key updater default)
- (dispatch dupdate/default dictionary key updater default))
+ (dispatch dupdate/default! dictionary key updater default))
(define dict-pop!
- (case-lambda)
- ((vec dictionary))
- (dict-pop!* vec dictionary error)))
- ((vec dictionary failure))
- (dict-pop!* vec dictionary failure)))))
+ (case-lambda
+ ((dictionary)
+ (dict-pop!* dictionary error))
+ ((dictionary failure)
+ (dict-pop!* dictionary failure))))
-(define (dict-pop!* vec dictionary failure)
+(define (dict-pop!* dictionary failure)
(dispatch dpop! dictionary failure))
(define (dict-map! proc dictionary)
@@ -83,34 +129,34 @@
(proc-dispatch dfilter! dictionary pred dictionary))
(define (dict-remove! pred dictionary)
- (dispatch dremove! dictionary yyy))
+ (proc-dispatch dremove! dictionary pred dictionary))
(define (dict-search! dictionary key failure success)
- (dispatch dsearch! dictionary yyy)
+ (dispatch dsearch! dictionary key failure success))
(define (dict-size dictionary)
- (dispatch dsize dictionary yyy)
+ (dispatch dsize dictionary))
(define (dict-for-each proc dictionary)
- (proc-dispatch dfor-each dictionary proc dictionary)
+ (proc-dispatch dfor-each dictionary proc dictionary))
(define (dict-count pred dictionary)
- (dispatch dcount dictionary yyy))
+ (proc-dispatch dcount dictionary pred dictionary))
(define (dict-any pred dictionary)
- (dispatch dany dictionary yyy))
+ (proc-dispatch dany dictionary pred dictionary))
(define (dict-every pred dictionary)
- (dispatch devery dictionary yyy))
+ (proc-dispatch devery dictionary pred dictionary))
(define (dict-keys dictionary)
- (dispatch dkeys dictionary yyy))
+ (dispatch dkeys dictionary))
(define (dict-values dictionary)
- (dispatch dvalues dictionary yyy))
+ (dispatch dvalues dictionary))
(define (dict-entries dictionary)
- (dispatch dentries dictionary yyy))
+ (dispatch dentries dictionary))
(define (dict-fold proc knil dictionary)
(proc-dispatch dfold dictionary proc knil dictionary))
@@ -119,4 +165,4 @@
(proc-dispatch dmap->list dictionary proc dictionary))
(define (dict->alist dictionary)
- (dispatch d->alist dictionary yyy))
+ (dispatch d->alist dictionary))
diff --git a/internals.scm b/internals.scm
index c732684..fe8263e 100644
--- a/internals.scm
+++ b/internals.scm
@@ -46,7 +46,7 @@
((null? objs)
dictionary)
((null? (cdr objs))
- (error "mismatch of key / values argument list"))
+ (error "mismatch of key / values argument list" objs))
(else (let*-values
(((key) (car objs))
((value) (cadr objs))
@@ -58,13 +58,13 @@
(loop (cddr objs)
new-d))))))
-(define (idict-set! vec dictionary . objs)
+(define (idict-set! vec dictionary objs)
(idict-set!* vec dictionary #f objs))
-(define (idict-adjoin! vec dictionary . objs)
+(define (idict-adjoin! vec dictionary objs)
(idict-set!* vec dictionary #t objs))
-(define (idict-delete! vec dictionary . keys)
+(define (idict-delete! vec dictionary keys)
(dcall ddelete-all! vec dictionary keys))
(define (idict-delete-all! vec dictionary keylist)
@@ -122,7 +122,7 @@
(dcall dfor-each vec
(lambda (key value)
(define new-dict
- (dcall ddelete! vec dictionary key))
+ (dcall ddelete! vec dictionary (list key)))
(cont new-dict key value))
dictionary))))
(define empty? (dcall dempty? vec dictionary))
@@ -210,13 +210,13 @@
acc)
(define (idict-map->list vec proc dictionary)
- (call-with-values
- (lambda ()
- (dcall dentries vec dictionary))
- (lambda (keys vals)
- (map proc
- keys
- vals))))
+ (define reverse-lst
+ (dcall dfold vec
+ (lambda (key value lst)
+ (cons (proc key value) lst))
+ '()
+ dictionary))
+ (reverse reverse-lst))
(define (idict->alist vec dictionary)
(dcall dmap->list vec
diff --git a/plist-impl.scm b/plist-impl.scm
new file mode 100644
index 0000000..8fa41a1
--- /dev/null
+++ b/plist-impl.scm
@@ -0,0 +1,105 @@
+(define (make-plist-impl)
+
+ (define (plist? vec l)
+ (and (list? l)
+ (not (null? l))
+ (symbol? (car l))))
+
+ (define (plist-map! vec proc plist)
+ (let loop ((pl plist))
+ (cond
+ ((null? pl) plist)
+ ((null? (cdr pl)) (error "Malformed plist" plist))
+ (else
+ (let ((key (car pl))
+ (value (cadr pl))
+ (rest (cddr pl)))
+ (set-car! (cdr pl)
+ (proc key value))
+ (loop rest))))))
+
+ (define (plist-filter! vec pred plist)
+ (define head (cons #f plist))
+ (let loop ((pl plist)
+ (parent-cell head))
+ (cond
+ ((null? pl) (cdr head))
+ ((null? (cdr pl)) (error "Malformed plist" plist))
+ (else
+ (let ((key (car pl))
+ (value (cadr pl))
+ (rest (cddr pl)))
+ (if (pred key value)
+ (loop rest
+ (cdr pl))
+ (loop (begin
+ (set-cdr! parent-cell rest)
+ rest)
+ parent-cell)))))))
+
+ ;; head is a pair, whose cdr is the plist
+ ;; if found, returns a pair, whose cdr is rest of plist, and cadr is key that was searched for
+ ;; if not found, returns #f
+ ;;
+ ;; the pair indirection is used so that calling set-cdr! on the result allows the plist to be mutated
+ (define (find-plist-entry key head)
+ (define plist (cdr head))
+ (cond
+ ((null? plist) #f)
+ ((equal? key (car plist)) head)
+ (else (find-plist-entry key (cdr plist)))))
+
+ (define (plist-search! vec plist key failure success)
+ (define plist-head (cons #t plist))
+ (define (handle-success head)
+ (define key-cell (cdr head))
+ (define val-cell (cddr head))
+ (define (update new-key new-value obj)
+ (set-car! key-cell new-key)
+ (set-car! val-cell new-value)
+ (values plist obj))
+ (define (remove obj)
+ (set-cdr! head (cddr (cdr head)))
+ (values (cdr plist-head) obj))
+ (success (car key-cell) (car val-cell) update remove))
+
+ (define (handle-failure)
+ (define (insert value obj)
+ (values (cons key (cons value plist))
+ obj))
+ (define (ignore obj)
+ (values plist obj))
+ (failure insert ignore))
+ (cond
+ ((find-plist-entry key plist-head) => handle-success)
+ (else (handle-failure))))
+
+ (define (plist-size vec plist)
+ (define keys
+ (let loop ((pl plist)
+ (keys '()))
+ (if (null? pl)
+ keys
+ (loop (cddr pl)
+ (cons (car pl) keys)))))
+ (define (fold-proc el set)
+ (lset-adjoin equal? set el))
+ (define key-set (fold fold-proc '() keys))
+ (length key-set))
+
+ (define (plist-foreach vec proc plist)
+ (let loop ((pl plist))
+ (if (null? pl) #t
+ (begin
+ (proc (car pl) (cadr pl))
+ (loop (cddr pl))))))
+
+ (define vec (vector-copy model-vec))
+ (vector-set! vec d? plist?)
+ (vector-set! vec dmap! plist-map!)
+ (vector-set! vec dfilter! plist-filter!)
+ (vector-set! vec dsearch! plist-search!)
+ (vector-set! vec dsize plist-size)
+ (vector-set! vec dfor-each plist-foreach)
+
+ vec)