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-12-05

CLOSチュートリアル (4)

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

Common Lisp クックブックさんのところのCLOSのチュートリアルに新しい練習問題(CLOSチュートリアル - 4.2. defmethod マクロ)が出たので挑戦!。

問題.1

aardvark と antelope の表示を、デフォルトのものよりもわかりやすくしなさい。すべてのCLOSオブジェクトは print-object メソッドによって表示されます。このメソッドの引数は (オブジェクト ストリーム) です。また、デフォルトのメソッド(standard-object を表示する)がどう定義されているのか考えなさい。

解答.1

ちょっと飾りをつけるということで、aardvarkは、継承関係の一覧を追加で表示し、antelopeはスロットの一覧を追加で表示させることにしてみました。スロットの一覧取得には、Meta!の、moptilitiesパッケージを使ってみています。とりあえず、どっちも総称関数にしてクラスを特定できない場合は、print-objectに処理を丸投げしてます。

(require :moptilities)

(defmethod print-aardvark-object ((obj aardvark) stream)
  (print-unreadable-object (obj stream :type 'T :identity 'T)
    (format stream "(~{~A~^ -> ~})" (mapcar #'class-name 
					  (#+sbcl sb-mop:class-precedence-list
					   #+openmcl ccl:class-precedence-list
					   (class-of obj))))))

(defmethod print-aardvark-object (obj stream)
  (print-object obj stream))

;; 動作
;;==> #<AARDVARK (AARDVARK -> MAMMAL -> ANIMAL -> STANDARD-OBJECT -> T) #x8B7F466>

(defmethod print-antelope-object ((obj antelope) stream)
  (print-unreadable-object (obj stream :type 'T :identity 'T)
    (format stream "[SLOTS:~{~A~^ ~}]" (moptilities:slot-names obj))))

(defmethod print-antelope-object (obj stream)
  (print-object obj stream))

;; 動作
;; ==> #<ANTELOPE [slots:LEGS COMES-FROM DIET] #x8BFF466>

また、print-objectは、総称関数で色々混み入った定義のようですが、簡単に定義するなら

(defmethod my-print-object (obj stream)
  (print-unreadable-object (obj stream :type 'T :identity 'T)))

のように定義できると思います。

問題.2

次のメソッド定義を見て、コンパイラが不要な処理を無視できる可能性についてできるだけ詳しくまとめなさい。(この問題を作るのに Steve Haflich の助けを借りました。)

解答.2

これはちょっと難しくて良く分からないけれど、とりあえず、自分が考えられる範囲で。

1. このメソッドでは、引数bazはfrobクラスと特定されていることになっているため、(mangle)がbaz値のクラスを変更するようなことをしないことを証明でき、かつ、barの結果が、frobクラスに特定されることが証明できれば、etypecaseの判定は無くすことができると思う。

(defmethod foo ((baz frob))
  (loop :initially (mangle)
        :while baz do (setf baz (bar baz))))
さらに、その場合bazがnilになることはない(frobクラスがnilになることはない)ので、
(defmethod foo ((baz frob))
  (loop :initially (mangle)
        :do (setf baz (bar baz))))
にできるんじゃないだろうか。

2. (mangle)が、bazをnilにしてしまう場合。

(defmethod foo ((baz frob))
  (mangle)
  nil)

にできると思う。

…しかし全然この解答には自信がないです。