メタプログラミングRuby的CLOS (1) — #:g1

Posted 2010-12-10 10:43:00 GMT

巷では、メタプログラミングRubyの評判が高い様子ですが、Common LispもCLOSという動的なオブジェクト指向システムを持っているぞ、ということでこの本で取り上げられている手法にちまちま対抗意識を燃やしてチャレンジしてみることにしました。
といっても全部やるのも大変なので目についたところを適当に再現して遊んでみます。
まず、RubyとCLOSで違うのは、CLOSはクラスの中でメソッドが定義されているわけではない、というところ。
この辺りのギャップが結構あります。
それはさておき、適当に真似てみます。
下準備

(defclass my-class () ())

;; make-instanceが長いので、newという別名を付けてみる (setf (fdefinition 'new) (symbol-function 'make-instance))

(defvar *c* (new 'my-class))

メソッドを動的に呼ぶ

とりあえず普通の書き方

(defmethod my-method ((self my-class) arg)
  (* 2 arg))

(my-method *c* 8) ;=> 16

動的と思われる書き方

(let ((meth #'my-method))
  (funcall meth
           *c*
           8))
;=> 16
メソッドがファーストクラスなので変数に入れて呼べます。

メソッド名を文字列等から作成

;; MY-METHODという名前のシンボルを文字列から作成して呼び出し
(funcall (intern (concatenate 'string "MY" "-" "METHOD"))
         *c*
         8)
;=> 16

メソッドを動的に作る

作る方ですが、お手軽に行くならEVAL、他にはメソッドオブジェクトを色々直接操作という感じになると思います

定義する式を作成してEVAL

(let ((name (intern (concatenate 'string "MY" "-" "METHOD"))))
  (eval
   `(defmethod ,name ((self my-class) arg)
      (* 3 arg))))

(my-method *c* 3) ;=> 9

まるごと無名でも行けるぜ、ヒャッハーみたいな

(let* ((gf (new 'standard-generic-function))
       (meth (new 'standard-method
                  :function (lambda (args ignore)
                              (destructuring-bind (self arg)
                                                  args
                                (* arg 3)))
                  :lambda-list '(self arg)
                  :specializers (list (find-class 'my-class)
                                      (find-class t)))))
  (add-method gf meth)
  (funcall gf *c* 3))
;=> 9
まあ、長いですけども…

次回につづく

comments powered by Disqus