SICPを読む(84) 2.4.3 データ主導プログラミングと加法性

まだまだ解説が続きます。演習問題はまだかぁ〜

データ主導ってことは、現在の主流。オブジェクト指向ライクな感じに仕立てあげていくみたい。

put,get

解説読もうと思ったら、

put,getは言語に組み込まれていると仮定しよう。

って、組み込まれてないよ・・・。

どうやら、グローバルで環境を設定しておくみたい。連想リストを使って表現してみます。

(define env '())

(define (put op type item)
  (let ((type-env (assq type env)))
    (cond ((not type-env)
           (set! type-env (list type))))         ; init type
    (set! env (cons (cons type                   ; push type
                          (cons (cons op item)   ; push op + item
                                (cdr type-env))) ; push old type-env
                    env))))                      ; push old env

(define (get op type)
  (let ((type-env (assq type env)))
    (if (pair? type-env)
        (let ((op-env (assq op (cdr type-env))))
          (if (pair? op-env)
              (cdr op-env)
              (error "undefind operand" op)))
        (error "undefind type" type))))


完成。envは2重の連想リストになってます。

どんどんputすると、こんな感じになります。

((type1 (op1 . item) (op2 . item) ...)
 (type2 (op1 . item) ... )
 ...

Scheme自作が役立ちました。


typeがクラス、opがメソッドみたいな感じかな。(put 'real-part 'rectangular realpart)と使うようにしたので、もしかしたら修正を加えるかも。次に進もう。

ガーン

やっぱり、assqではちょっと足りないみたいなので、修正。

(define env '())

(define (lookup x lis)
  (find (lambda (y)
          (equal? (car y) x))
        lis))

(define (put op type item)
  (let ((type-env (lookup type env)))
    (cond ((not type-env)
           (set! type-env (list type))))         ; init type
    (set! env (cons (cons type                   ; push type
                          (cons (cons op item)   ; push op + item
                                (cdr type-env))) ; push old type-env
                    env))))                      ; push old env

(define (get op type)
  (let ((type-env (lookup type env)))
    (if (pair? type-env)
        (let ((op-env (assq op (cdr type-env))))
          (if (pair? op-env)
              (cdr op-env)
              (error "undefind operand" op)))
        (error "undefind type" type))))


(put '+ '(int int) +)
(put '+ '(double int) +)

(get '+ '(int int))    ; #<primitive:+>
(get '+ '(double int)) ; #<primitive:+>

lookupに変更。引数の型リストを入れるらしい。


あぁ、C++みたいな事ができるのか・・・って・・・スゲー面白そうだ!!スゲー楽しみだ!!


SICP読んでヨカッタヨ(涙

もうちょい

どうやら、lookupの代わりに、assocというのが使えるみたい。もうちょい修正。

;; env
(define *env* '())

(define (push-env! new)
  (set! *env* (cons new *env*)))

(define (put op type item)
  (let ((type-env (assoc type *env*)))
    (cond ((not type-env)
           (set! type-env (list type))))
    (push-env! (cons* type
                      (cons op item)
                      (cdr type-env)))))

(define (get op type)
  (define (lookup f x error-message lis)
    (let ((l (f x lis)))
      (if (pair? l)
          (cdr l)
          (error error-message x))))
  (lookup assq op "undefind operand"
          (lookup assoc type "undefind type" *env*)))
  • Schemeグローバル変数使うときは、*hoge*にするのが習慣だった。
  • lookupを違う意味で使ってみた。だいぶすっきり。
  • srfi-1のcons*を使ってみた。スゲーらくちん。
    • (cons* 1 2 3 4)は(cons 1 (cons 2 (cons 3 4)))と同意。


・・・しばらく後。

すげぇよ

こいつはすげぇ。感動だよ。

SICPからクリスマスプレゼントを貰った気分だ。

ここまでのまとめ

何に感動したのか、書いておこう。

今回作ったオブジェクト指向ライクな感じの複素数システムでは、オブジェクトがメソッドを知っている訳では無く、環境がメソッドを知ってる。環境がどうやってメソッドを知るかと言うと、引数リストからメソッドを検索する。驚きだ。

つまり、Rubyで例えるなら、

5.times {|i| puts i}
  • 5は数値で引数は手続きである事がわかる。
  • 環境から(数値 手続き)を引数に持つメソッド一覧を検索。
  • メソッド一覧からtimesを探し、timesの手続きを取得。
  • (apply # '(5 #))で適用する。

引数リストからメソッドを検索するというアイディアに感動した。これは凄い。

(たぶんRubyではIntegerから検索してるので、こんな処理はしてない)