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)))と同意。
・・・しばらく後。
ここまでのまとめ
何に感動したのか、書いておこう。
今回作ったオブジェクト指向ライクな感じの複素数システムでは、オブジェクトがメソッドを知っている訳では無く、環境がメソッドを知ってる。環境がどうやってメソッドを知るかと言うと、引数リストからメソッドを検索する。驚きだ。
つまり、Rubyで例えるなら、
5.times {|i| puts i}
- 5は数値で引数は手続きである事がわかる。
- 環境から(数値 手続き)を引数に持つメソッド一覧を検索。
- メソッド一覧からtimesを探し、timesの手続きを取得。
- (apply #
'(5 # ))で適用する。
引数リストからメソッドを検索するというアイディアに感動した。これは凄い。
(たぶんRubyではIntegerから検索してるので、こんな処理はしてない)