SICPを読む(107) 問題 3.24,3.25,3.26 - 多次元キー
SICPも大変だ。
問題 3.24,3.25
equal?以外にも対応し、多次元のキーに対応せよ。という問題。
手続きの連鎖にしてみた。
keyとvaluesの位置を変えて引数を可変長にしてみたけど、applyがいっぱい必要になってしまった。ちと失敗。
getとsetのAPIはそのまま使えるようにした。
(define (make-table . search-method) (letrec ((local-table (list '*table*)) (search (if (null? search-method) assoc (car search-method))) (lookup (lambda keys (let ((res (search (car keys) (cdr local-table)))) (if (or (not res) (null? (cdr keys)) (not (procedure? (cdr res)))) res (apply ((cdr res) 'lookup) (cdr keys)))))) (insert! (lambda (value . keys) (let* ((record (lookup (car keys))) (next (not (null? (cdr keys)))) (new-entry! (lambda (e) (set-cdr! local-table (cons (cons (car keys) e) (cdr local-table))))) (next-insert! (lambda (n) (apply (n 'insert!) value (cdr keys))))) (if record (if next (if (procedure? (cdr record)) (next-insert! (cdr record)) ; 下へ (let ((new (make-table search))) ; 値をテーブルに上書き (set-cdr! record new) (next-insert! new))) (set-cdr! record value)) ; 値で上書き (if next (let ((new (make-table search))) ; 新しくテーブルを作って下へ (new-entry! new) (next-insert! new)) (new-entry! value))) ; ふつうに挿入 local-table)))) (lambda (m) (case m ('insert! insert!) ('lookup lookup) (else local-table)))))
テスト。
(define tbl (make-table assq)) (insert! tbl "new entry" 'hoge) ; (*table* (hoge . new entry)) ((tbl 'insert!) "new entry2" 'moge) ; (*table* (moge . new entry2) (hoge . new entry)) ((tbl 'insert!) "over write test" 'hoge) ; (*table* (moge . new entry2) (hoge . over write test)) ((tbl 'lookup) 'hoge) ; (hoge . over write test) ((tbl 'insert!) "table over write test1" 'hoge 'moge) ; (*table* (moge . new entry2) (hoge . #<procedure>)) ((tbl 'insert!) "table over write test2" 'hoge 'moge 'hoge) ; (*table* (moge . new entry2) (hoge . #<procedure>)) ((tbl 'lookup) 'hoge 'moge) ; (moge . #<procedure>) ((tbl 'lookup) 'hoge 'moge 'hoge) ; (hoge . table over write test2) ((tbl 'lookup) 'hoge 'moge 'moge) ; #f (define *operation-table* (make-table)) (define (get key) (let ((res (apply (*operation-table* 'lookup) key))) (and res (cdr res)))) (define (set key value) (apply (*operation-table* 'insert!) (cons value key))) (set '(pakage1 +) +) ; (*table* (pakage1 . #<procedure>)) ((get '(pakage1 +)) 1 2) ; 3 (set '(system out printLine) display) ; (*table* (system . #<procedure>) (pakage1 . #<procedure>)) ((get '(system out printLine)) "Hello, world") ; Hello, world (set '((Int +)) +) ; (*table* ((Int +) . #<primitive:+>) (system . #<procedure>) (pakage1 . #<procedure>)) (set '((String +)) string-append) ; (*table* ((String +) . #<primitive:string-append>) ((Int +) . #<primitive:+>) (system . #<procedure>) (pakage1 . #<procedure>)) ((get '((String +))) "Hello, " "Class.") ; Hello, Class. (get '(hoge)) ; #f
案外大変だった。
あ、型は(Int Int) +だったっけ・・・ま、動くと思うのでいいや。
バグ
上書き判定にprocedure?を使っているので、
(set '(pakage1 +) +) (set '(pakage1 + +) +) ; エラー
ということは出来ないというバグあり。
あと、
(set '() +) ; エラー
keyの引数ゼロの時の処理は書いてない。
問題 3.26
ちと2進木を用意するのが大変そうなので前に作ったハッシュで。
殆どAPIを変えずに移行することが出来た。
殆どAPIが変わってないということは、バグも持ち越してる訳ですが・・・。
(define (make-multi-hash) (letrec ((local-table (make-hash)) (lookup (lambda keys (let ((res (hash-value local-table (car keys)))) (if (or (not res) (null? (cdr keys)) (not (procedure? (cdr res)))) res (apply ((cdr res) 'lookup) (cdr keys)))))) (insert! (lambda (value . keys) (let* ((record (lookup (car keys))) (next (not (null? (cdr keys)))) (new-entry! (lambda (e) (hash-set! local-table (car keys) e))) (next-insert! (lambda (n) (apply (n 'insert!) value (cdr keys))))) (if record (if next (if (procedure? (cdr record)) (next-insert! (cdr record)) ; 下へ (let ((new (make-multi-hash))) ; 値をテーブルに上書き (hash-set! local-table record new) (next-insert! new))) (new-entry! value)) ; 上書き (if next (let ((new (make-multi-hash))) ; 新しくテーブルを作って下へ (new-entry! new) (next-insert! new)) (new-entry! value))))))) (lambda (m) (case m ('insert! insert!) ('lookup lookup) (else local-table)))))
元々上書き出来るのでアレなんですが。
Schemeでも、ちゃんと継承できるようにしないと・・・。