Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2007-11-19

CLOSチュートリアル (1)

| 15:37 | CLOSチュートリアル (1) - わだばLisperになる を含むブックマーク はてなブックマーク - CLOSチュートリアル (1) - わだばLisperになる

自分は、多少Lispでのリスト操作はできるようになった気はしますが、CLOSとなると全く訳が分かりません。

そもそもオブジェクト指向言語自体まともに触ったことが無いし、全く未知の領域で、Lispとオブジェクト指向がどう融合するのか見当もつきません。

しかし、CLOSは折角Common Lispに標準で付いてくるし、ちょっとは分かるようになれたら良いなと思っていた折、Common Lisp クックブックを訳されているid:cl-internさんのCommon Lisp クックブックで、CLOSの記事(

no title)の翻訳の開始を目にし、おおこれは素晴らしいと思っていたところ、丁度、練習問題が付いてくるようなので、良い機会なので練習問題に挑戦してみることにしました。

ということで早速挑戦。

CLOSチュートリアル 3.2. defclass マクロの練習問題より

  1. with-slots を使って set-point-values を書き直しなさい。
;; 解答
(defun set-point-values (point x-pos y-pos z-pos)
  (with-slots (x y z) point
    (setf x x-pos
	  y y-pos
	  z z-pos)))

with-slotsを使うと名前の衝突を起してしまうので、x-pos等として衝突を回避してみましたが、他に回避方法はあるのでしょうか。

with-slotsはマクロなので、マクロを展開して眺めてみても回避はできないっぽいんですが…。

  1. symbol-macrolet を使って with-slots を実装しなさい。symbol-macrolet の最初の引数は、次のペア (変数名 スロット名) のリストになります。
;; 解答
(defmacro my-with-slots ((&rest vars) instance &body body)
  (let ((inst (gensym "INST-")))
    `(let ((,inst ,instance))
       (declare (ignorable ,inst))
       (symbol-macrolet ,(mapcar (lambda (v) 
				   `(,v (slot-value ,inst ',v)))
				 vars)
	 ,@body))))

sbclのmacroexpandの結果から類推してマクロ作成。ちょっとズルかもしれない。

(my-with-slots (x y z) bar
  (list x y z))

(LET ((#:INST-2958 BAR))
  (DECLARE (IGNORABLE #:INST-2958))
  (SYMBOL-MACROLET ((X (SLOT-VALUE #:INST-2958 'X))
                    (Y (SLOT-VALUE #:INST-2958 'Y))
                    (Z (SLOT-VALUE #:INST-2958 'Z)))
    (LIST X Y Z)))

のように展開されます。

  1. defclass に defstruct の機能を追加したマクロ defclass-plus を実装しなさい。新しいクラスを定義したときに、そのクラスのコンストラクタ関数、述語関数、アクセサ関数、コピー関数も自動的に定義するようにします。退屈かもしれませんが、納得できるまでやってみてください。
;; 解答

;; 本体
(defmacro defclass-plus (name direct-super-classes direct-slots &rest options)
  `(prog1
     (defclass ,name ,direct-super-classes 
        ,(mapcar (lambda (s) `(,s :initarg ,(intern (string s) :keyword))) 
	         direct-slots)
          ,@options)
     (constructer-maker ,name)
     (pred-maker ,name)
     (accessor-maker ,name ,@direct-slots)
     (copier-maker ,name ,@direct-slots)
     (setter-maker ,name ,@direct-slots)))

;; 関数の名前を付けるための補助関数
(defun symbol-name-conc (&rest names)
  (values
   (intern 
    (string-upcase 
     (apply #'concatenate 'string (mapcar #'string names))))))

;; コンストラクタ関数 make-~を作るマクロ
(defmacro constructer-maker (name)
  `(defun ,(symbol-name-conc "MAKE-" name) (&rest initargs)
     (apply #'make-instance ',name initargs)))

;; 述語関数 ~-pを作るマクロ
(defmacro pred-maker (name)
  `(defun ,(symbol-name-conc name "-P") (object)
     (eq ',name (type-of object))))

;; アクセサ関数を作るマクロ
(defmacro accessor-maker (name &rest slots)
  `(progn
     ,@(mapcar (lambda (s) 
		 `(defun ,(symbol-name-conc name "-" s) (object)
		    (slot-value object ',s)))
	       slots)))

;; コピー関数を作るマクロ
(defmacro copier-maker (name &rest slots)
  (let ((new (gensym)))
    `(defun ,(symbol-name-conc "COPY-" name) (object)
       (setf ,new (make-instance ',name))
       (mapc (lambda (s) (setf (slot-value ,new s) (slot-value object s)))
	     ',slots)
       ,new)))

;; (setf (accessor-x object) 30)のようなことができるようにするマクロ
(defmacro setter-maker (name &rest slots)
  `(progn
     ,@(mapcar (lambda (s) 
		 (let ((name (symbol-name-conc name "-" s)))
		   `(define-setf-expander ,name (obj)
		    (let ((tem (gensym)))
		      (values nil nil `(,tem)
			      `(setf (slot-value ,obj ',',s) ,tem)
			      `(,',name ,obj))))))
	       slots)))

defstructの説明がこの前の章にあって、その機能の説明を全部盛り込むと結構複雑になりました。

(make-foo :x 40)のように初期値を取るようにするには、defclassで、:initargを使う必要があるようなので、まだチュートリアルに説明は出てきませんが、:initargを使ってみました。

色々使ったことが無い関数やマクロを触ることになったのでかなり試行錯誤という感じです。

また、謎なところでは、マクロの中で、:xや、:yを作る方法が分からず、(intern (string s) :keyword)とかしています。

安直に、`(:,keyword)のような感じで展開できると思っていたんですが、できなかったためですが、こういう時、普通はどうするんでしょう。