SICPを読む(86) 問題 2.74 データベース統合を急げ

楽しげな問題なので、ちょっと改題。

問題 2.74

ボクノス有限責任会社(Boxnos Enterprises, Inc)は世界に類を見ない弱小企業である。社長tanakaは危機感を感じていた。このままでは、OSなんかいつまで経っても作れないぞ。

「これはいかん」

そこで、社長tanakaは考えた。世界有数のハッカーを集めたSICP有限責任会社(SICP Enterprises, Inc)を買収して、OS製作を加速させるのだ〜。

大金を払って買収したのはいいが重要な問題が発生した。社員データベースの構造が異なっていて統合を計る必要に迫られた。金が無いので、データベースの再構築なんて事は出来ない。事業所ごとに異なったデータ構造を保ちながら、データベースの統合を急げ。

という問題です。データは以下のとおり。

(define boxnos-db
  '((Tanaka (0  Kashiwa))
    (Hoge   (20 Matudo ))
    (Moge   (23 Kitasenju))))

(define sicp-db
    '((Wada    Tokyo          2200)
      (Sassman Masasachusetts 2400)))

データベースは、名前のキーを持っていますが、住所、給与のデータ部が全く異なります。しかも給与は月収と年収。困っちゃうな・・・。


借金だらけなので、社長tanakaがデータベースの統合を進めていきます。トホホ。

a

本部のために、従業員ファイルから指定した従業員レコードを返すget-recodeを実装します。

(define (install-boxnos-package)
  ;; private
  (define db boxnos-db)

  ;; public
  (define (get-record name)
    (cons 'boxnos
          (assq name db)))

  (put 'get-record 'boxnos get-record))

(install-boxnos-package)

(define (install-sicp-package)
  ;; private
  (define db sicp-db)

  ;; public
  (define (get-record name)
    (cons 'sicp
          (assq name db)))

  (put 'get-record 'sicp get-record))

(install-sicp-package)

(define (get-record file name)
  ((get 'get-record file) name))

(get-record 'boxnos 'Tanaka) ; (boxnos Tanaka (0 Kashiwa))
(get-record 'sicp 'Sassman)  ; (sicp Sassman Masasachusetts 2400)

社長tanaka失敗です。

get-recordで取得したのはデータベースから取得しただけの生データ。タグだけ付けただけで、整形されてません。全然ダメです。

しかし、資金が底を付いてます!!

修正はせずそのまま突き進みます。泥沼の様相です。

b

本部の為に、従業員レコードから給与の情報を返すget-salaryを実装していきます。挽回せねば・・・。

(define (install-boxnos-package)
  ;; private
  (define db boxnos-db)

  ;; accsesser
  (define name car)
  (define address cadadr)
  (define (salary r)  (* (caadr r) 12))

  ;; public
  (define (get-record name)
    (cons 'boxnos
          (assq name db)))

  (put 'get-name    'boxnos name)
  (put 'get-address 'boxnos address)
  (put 'get-salary  'boxnos salary)
  (put 'get-record  'boxnos get-record))

(install-boxnos-package)

(define (install-sicp-package)
  ;; private
  (define db sicp-db)

  ;; accsesser
  (define name car)
  (define address cadr)
  (define salary caddr)

  ;; public
  (define (get-record name)
    (cons 'sicp
          (assq name db)))

  (put 'get-name    'sicp name)
  (put 'get-address 'sicp address)
  (put 'get-salary  'sicp salary)
  (put 'get-record  'sicp get-record))

(install-sicp-package)

;; global
(define (get-package record)
  (car record))
(define (get-data record)
  (cdr record))

(define (get-record file name)
  ((get 'get-record file) name))

(define (get-info info record)
  ((get info (get-package record)) (get-data record)))
(define (get-name record) (get-info 'get-name record))
(define (get-address record) (get-info 'get-address record))
(define (get-salary record) (get-info 'get-salary record))

;; test
(define tanaka (get-record 'boxnos 'Tanaka)) ; (boxnos Tanaka (0 Kashiwa))
(define sassman (get-record 'sicp 'Sassman)) ; (sicp Sassman Masasachusetts 2400)

(get-name tanaka)    ; Tanaka
(get-address tanaka) ; Kashiwa
(get-salary tanaka)  ; 0

(get-name sassman)    ; Sassman
(get-address sassman) ; Masasachusetts
(get-salary sassman)  ; 2400

タグを付けたのが役立ちました。データ構造はそのままでも、同じ手続きでデータを取得出来ました。一安心です。

c

全ての従業員ファイルリストから従業員レコードを取得します。

(define (find-employee-record name lis)
  (if (null? lis)
      (error "undefind name" name)
      (let ((record (get-record (car lis) name)))
        (if (cdr record)
            record
            (find-employee-record name (cdr lis))))))

(find-employee-record 'Wada '(boxnos sicp)) ; (sicp Wada Tokyo 2200)

共通アクセサを使えばデータを取得出来るので生データでもいいんです!!

急いで作ったので、get-recordの時、エラー処理を忘れてました。えへへ。

見付からないと、(sicp . #f)などという変わったデータを返すので、それにあわせて修正をかけてます。従業員検索するときは、find-employee-recordを使った方が安心です。


予算が無い中で一気に仕上げてみました。データベースが違っても同じ手続きでデータを取得することが出来るようになりました。データベースの再構築は予算が出来てから行いたいと思います。

d

もしも、ボクノス有限責任会社が順風満帆に成長し、新しい会社を買収したとき、新しい従業員情報を中央システムに組み込むには、今のデータベースと同じように構築すれば良い。現在のシステムに手を加える必要は無い。


「データベース統合を急げ」ミッション完了〜。楽しかった。