137 lines
5.0 KiB
Scheme
137 lines
5.0 KiB
Scheme
; enf.scm -- pure Scheme enfilade reference
|
|||
|
|
(define (make-enfilade) (vector 'enfilade '() '()))
|
||
|
|
(define (enfilade-children e) (vector-ref e 1))
|
||
|
|
(define (enfilade-values e) (vector-ref e 2))
|
||
|
|
(define (set-enfilade-children! e x) (vector-set! e 1 x))
|
||
|
|
(define (set-enfilade-values! e x) (vector-set! e 2 x))
|
||
|
|
|
||
|
|
(define (assoc-number k xs)
|
||
|
|
(cond ((null? xs) #f)
|
||
|
|
((= k (caar xs)) (car xs))
|
||
|
|
(else (assoc-number k (cdr xs)))))
|
||
|
|
|
||
|
|
(define (alist-put xs k v)
|
||
|
|
(let ((p (assoc-number k xs)))
|
||
|
|
(if p (begin (set-cdr! p v) xs) (cons (cons k v) xs))))
|
||
|
|
|
||
|
|
(define (alist-remove xs k)
|
||
|
|
(cond ((null? xs) '())
|
||
|
|
((= k (caar xs)) (cdr xs))
|
||
|
|
(else (cons (car xs) (alist-remove (cdr xs) k)))))
|
||
|
|
|
||
|
|
(define (insert-number n xs)
|
||
|
|
(cond ((null? xs) (list n))
|
||
|
|
((<= n (car xs)) (cons n xs))
|
||
|
|
(else (cons (car xs) (insert-number n (cdr xs))))))
|
||
|
|
|
||
|
|
(define (sort-numbers xs)
|
||
|
|
(if (null? xs) '()
|
||
|
|
(insert-number (car xs) (sort-numbers (cdr xs)))))
|
||
|
|
|
||
|
|
(define (unique-numbers xs)
|
||
|
|
(cond ((null? xs) '())
|
||
|
|
((and (pair? (cdr xs)) (= (car xs) (cadr xs)))
|
||
|
|
(unique-numbers (cdr xs)))
|
||
|
|
(else (cons (car xs) (unique-numbers (cdr xs))))))
|
||
|
|
|
||
|
|
(define (tumbler-parse s)
|
||
|
|
(if (= (string-length s) 0) '()
|
||
|
|
(let loop ((i 0) (start 0) (out '()))
|
||
|
|
(if (= i (string-length s))
|
||
|
|
(reverse (cons (string->number (substring s start i)) out))
|
||
|
|
(if (char=? (string-ref s i) #\.)
|
||
|
|
(loop (+ i 1) (+ i 1)
|
||
|
|
(cons (string->number (substring s start i)) out))
|
||
|
|
(loop (+ i 1) start out))))))
|
||
|
|
|
||
|
|
(define (tumbler->list t)
|
||
|
|
(cond ((string? t) (tumbler-parse t))
|
||
|
|
((list? t) t)
|
||
|
|
(else (error 'wrong-type-arg "tumbler must be a string or list"))))
|
||
|
|
|
||
|
|
(define (tumbler-compare a b)
|
||
|
|
(let loop ((a (tumbler->list a)) (b (tumbler->list b)))
|
||
|
|
(cond ((and (null? a) (null? b)) 0)
|
||
|
|
((null? a) -1)
|
||
|
|
((null? b) 1)
|
||
|
|
((< (car a) (car b)) -1)
|
||
|
|
((> (car a) (car b)) 1)
|
||
|
|
(else (loop (cdr a) (cdr b))))))
|
||
|
|
|
||
|
|
(define (enfilade-get e key)
|
||
|
|
(let ((v (assoc-number key (enfilade-values e)))
|
||
|
|
(c (assoc-number key (enfilade-children e))))
|
||
|
|
(append (if v (list (cdr v)) '())
|
||
|
|
(if c (enfilade-get-range (cdr c)) '()))))
|
||
|
|
|
||
|
|
(define (enfilade-get-by-tumbler e t)
|
||
|
|
(let loop ((node e) (parts (tumbler->list t)))
|
||
|
|
(cond ((null? parts) (enfilade-get-range node))
|
||
|
|
((null? (cdr parts)) (enfilade-get node (car parts)))
|
||
|
|
(else
|
||
|
|
(let ((c (assoc-number (car parts) (enfilade-children node))))
|
||
|
|
(if c (loop (cdr c) (cdr parts)) '()))))))
|
||
|
|
|
||
|
|
(define (enfilade-put-data e t datum)
|
||
|
|
(let ((parts (tumbler->list t)))
|
||
|
|
(if (null? parts) (error 'out-of-range "empty tumbler"))
|
||
|
|
(let loop ((node e) (parts parts))
|
||
|
|
(if (null? (cdr parts))
|
||
|
|
(set-enfilade-values!
|
||
|
|
node (alist-put (enfilade-values node) (car parts) datum))
|
||
|
|
(let* ((k (car parts))
|
||
|
|
(p (assoc-number k (enfilade-children node)))
|
||
|
|
(child (if p (cdr p) (make-enfilade))))
|
||
|
|
(if (not p)
|
||
|
|
(set-enfilade-children!
|
||
|
|
node (alist-put (enfilade-children node) k child)))
|
||
|
|
(loop child (cdr parts)))))
|
||
|
|
datum))
|
||
|
|
|
||
|
|
(define (enfilade-empty? e)
|
||
|
|
(and (null? (enfilade-children e)) (null? (enfilade-values e))))
|
||
|
|
|
||
|
|
(define (enfilade-remove e t)
|
||
|
|
(let ((parts (tumbler->list t)))
|
||
|
|
(if (null? parts) #f
|
||
|
|
(let remove-at ((node e) (parts parts))
|
||
|
|
(let ((k (car parts)))
|
||
|
|
(if (null? (cdr parts))
|
||
|
|
(if (assoc-number k (enfilade-values node))
|
||
|
|
(begin
|
||
|
|
(set-enfilade-values!
|
||
|
|
node (alist-remove (enfilade-values node) k))
|
||
|
|
#t)
|
||
|
|
#f)
|
||
|
|
(let ((p (assoc-number k (enfilade-children node))))
|
||
|
|
(if (not p) #f
|
||
|
|
(let* ((child (cdr p))
|
||
|
|
(removed (remove-at child (cdr parts))))
|
||
|
|
(if (and removed (enfilade-empty? child))
|
||
|
|
(set-enfilade-children!
|
||
|
|
node (alist-remove (enfilade-children node) k)))
|
||
|
|
removed)))))))))
|
||
|
|
|
||
|
|
(define (enfilade-keys e)
|
||
|
|
(unique-numbers
|
||
|
|
(sort-numbers
|
||
|
|
(append (map car (enfilade-values e))
|
||
|
|
(map car (enfilade-children e))))))
|
||
|
|
|
||
|
|
(define (enfilade-get-range e . bounds)
|
||
|
|
(let ((start (if (null? bounds) #f (car bounds)))
|
||
|
|
(end (if (or (null? bounds) (null? (cdr bounds))) #f (cadr bounds))))
|
||
|
|
(let loop ((ks (enfilade-keys e)) (out '()))
|
||
|
|
(if (null? ks) out
|
||
|
|
(let ((k (car ks)))
|
||
|
|
(loop (cdr ks)
|
||
|
|
(if (and (or (not start) (>= k start))
|
||
|
|
(or (not end) (<= k end)))
|
||
|
|
(append out (enfilade-get e k))
|
||
|
|
out)))))))
|
||
|
|
|
||
|
|
; Example:
|
||
|
|
; (define e (make-enfilade))
|
||
|
|
; (enfilade-put-data e "1.2.3" 42)
|
||
|
|
; (enfilade-put-data e "1.2.4" 43)
|
||
|
|
; (enfilade-get-by-tumbler e "1.2") ; => (42 43)
|