Code Review
Compare your solutions
#|
Упражнение 3.26
При поиске в таблице, как она реализована выше, приходится просматривать список записей. В сущности,
это представление с неупорядоченным списком из раздела 2.3.3. Для больших таблиц может оказаться
эффективнее организовать таблицу иначе. Опишите реализацию таблицы, в которой записи (ключ, значение)
организованы в виде бинарного дерева, в предположении, что ключи можно каким-то образом упорядочить
(например, численно или по алфавиту). (Ср. с упражнением 2.66 из главы 2.)
|#
(#%require rackunit
compatibility/mlist)
(define (order-proc value1 value2)
(cond ((= value1 value2) 0)
((> value1 value2) 1)
(else -1)))
(define (make-table order-proc)
(define (make-entry key value)
(mlist (mlist (mcons key value)) '() '()))
(define (entry-key entry) (mcar (mcar (mcar entry))))
(define (entry-value entry) (mcdr (mcar (mcar entry))))
(define (entry-subtree entry) (mcdr (mcar entry)))
(define (left-branch entry) (mcar (mcdr entry)))
(define (right-branch entry) (mcar (mcdr (mcdr entry))))
(define (set-entry-value! entry value) (set-mcdr! (mcar (mcar entry)) value))
(define (set-entry-subtree! entry subtree) (set-mcdr! (mcar entry) subtree))
(define (set-left-branch! entry value) (set-mcar! (mcdr entry) value))
(define (set-right-branch! entry value) (set-mcar! (mcdr (mcdr entry)) value))
(let ((local-table (make-entry '*table* '())))
(define (assoc key entry)
(define (rec checked-entry parent-entry)
(if (null? checked-entry)
(mcons #f parent-entry)
(let ((checked-entry-key (entry-key checked-entry)))
(let ((order (order-proc key checked-entry-key)))
(cond ((= order 0)
(mcons #t checked-entry))
((= order 1)
(rec (right-branch checked-entry) checked-entry))
(else
(rec (left-branch checked-entry) checked-entry)))))))
(rec entry entry))
(define (lookup keys entry)
(define (rec keys checked-entry)
(let ((key (mcar keys)))
(let ((search-result (assoc key (entry-subtree checked-entry))))
(let ((search-successful? (mcar search-result))
(search-breakpoint (mcdr search-result)))
(cond ((null? (mcdr keys))
(if search-successful?
(entry-value search-breakpoint)
#f))
(else
(if search-successful?
(rec (mcdr keys) search-breakpoint)
#f)))))))
(if (or (null? entry) (null? keys))
#f
(rec keys entry)))
(define (adjust-entry! entry recipient recipient-parent)
(if (null? recipient)
(set-entry-subtree! recipient-parent entry)
(let ((key (entry-key entry))
(recipient-key (entry-key recipient)))
(let ((order (order-proc key recipient-key)))
(cond ((= order 1)
(set-right-branch! recipient entry))
(else
(set-left-branch! recipient entry)))))))
(define (insert! keys value table)
(define (iter! keys entry)
(let ((key (mcar keys)))
(let ((search-result (assoc key (entry-subtree entry))))
(let ((search-successful? (mcar search-result))
(search-breakpoint (mcdr search-result)))
(cond ((null? (mcdr keys))
(if search-successful?
(set-entry-value! search-breakpoint value)
(adjust-entry! (make-entry key value) search-breakpoint entry)))
(else
(if search-successful?
(iter! (mcdr keys) search-breakpoint)
(let ((new-entry (make-entry key '())))
(adjust-entry! new-entry search-breakpoint entry)
(iter! (mcdr keys) new-entry)))))))))
(if (null? keys)
(error "Empty keys -- INSERT!")
(iter! keys table))
'ok)
(define (dispatch m)
(cond ((eq? m 'lookup-proc)
(lambda (keys) (lookup keys local-table)))
((eq? m 'insert-proc!)
(lambda (keys value) (insert! keys value local-table)))
(else (error "Unknown operation -- TABLE" m))))
dispatch))
(define (lookup table keys)
((table 'lookup-proc) keys))
(define (insert! table keys value)
((table 'insert-proc!) keys value))
(define table1 (make-table order-proc))
(check-false (lookup table1 (mlist 1)))
(insert! table1 (mlist 1) 111)
(check-equal? (lookup table1 (mlist 1)) 111)
(insert! table1 (mlist 1 5) 555)
(insert! table1 (mlist 1 3) 333)
(insert! table1 (mlist 1 3 4) 444)
(check-equal? (lookup table1 (mlist 1)) 111)
(check-equal? (lookup table1 (mlist 1 5)) 555)
(check-equal? (lookup table1 (mlist 1 3)) 333)
(check-equal? (lookup table1 (mlist 1 3 4)) 444)
(check-false (lookup table1 (mlist 2)))
(insert! table1 (mlist 1 3) 3333)
(insert! table1 (mlist 1 3 4) 4444)
(check-equal? (lookup table1 (mlist 1 3)) 3333)
(check-equal? (lookup table1 (mlist 1 3 4)) 4444)