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-08

CLOSチュートリアル (5)

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

Common Lisp クックブックさんのところのCLOSのチュートリアルに新しい練習問題(CLOSチュートリアル - 4.3. 総称関数と次メソッド)が出たので挑戦!。

問題.1

あなたが使用しているLispの処理系の、class-precedence-list の総称関数を調べなさい。

解答.1

OpenMCL 1.1の場合で確認したところ、class-precedence-listは、総称関数として定義されているものの、cclパッケージの%inited-class-cplを呼び出している定義が一つ。

その%inited-class-cplは、%class-cplが一覧をみつけられたならそれを返し、見付からない場合、ccl:*cpl-classes*一覧に問い合わせのクラスが存在する場合、ccl:compute-cplでリストを計算、存在しない場合、動的にアップデートをかけてその後再度計算。

…という感じですが、こんな答じゃ駄目だろうなと。各処理系のclass-precedence-listの簡単なバージョンを実装してみせるのが一番良い解答なのかなという気がしないでもないのですが、実力的に無理なので、できるようになったら再挑戦したいところ。

問題.2

(comes-from Eric) を評価したとき、スロットのリーダ comes-from はどのクラスから継承されているでしょうか?そのメソッドをオーバーライドし、 antelope が常にアフリカから来ることにしなさい(現実的ではありませんが)。

解答.2

とりあえず、comes-fromはどこからかというのは、

(generic-function-methods #'comes-from)
;=>(#<STANDARD-READER-METHOD COMES-FROM (ANIMAL)>) 

で、animalから来てると思います。

それで、オーバーライドですが、簡単に、

(defmethod comes-from ((antelope antelope))
  "Africa")

という定義で

(comes-from Eric)
;=> Brittany

;; オーバーライドする
(defmethod comes-from ((antelope antelope))
  "Africa")

(comes-from (make-instance 'animal :comes-from "foo"))
;=>foo
(comes-from (make-instance 'mammal :comes-from "foo"))
;=>foo
(comes-from (make-instance 'antelope :comes-from "foo"))
;=> Africa
(comes-from Eric)
;=> Africa

というようにantelopeを継承するものは、"Africa"を返すようになります。

問題.3

call-next-method の無限エクステントの効果を試してみなさい。

解答.3

これはなかなかどの辺が、無限エクステントなのか理解できていないのですが、

(defclass one () ())
(defclass two (one) ())
(defclass three (two) ())

というクラスを定義して

(defmethod baz (obj)
  (print "end"))

(defmethod baz ((one one))
  (print "1"))

(defmethod baz ((two two))
  (print "2"))

(defmethod baz ((three three))
  (print "3")
  #'call-next-method)

というメソッドを定義します。

それで、threeのインスタンスを作成するんですが、threeは、返り値として自身のcall-next-methodを返すので、それを変数に保存します。

(setq baz (baz (make-instance 'three)))
;-> 3

それで、保存した変数にfuncallを適用すると

(funcall baz)
;-> 2

となり、上位メソッドが呼ばれていることが確認できます。

それで、ここにきてthreeクラスの内容を変更して、oneが直接のスーパークラスということに変更します。

(defclass three (one) ())

実行結果を確認してみます。

(funcall (baz (make-instance 'three)))
;-> 3
;-> 1

threeの次にoneのメソッドが呼ばれています。

ここで、さっき保存したものを再度呼び出してみます。

(funcall baz)
;-> 2

という風にもとのままです。

…うーん、これで確かめたことになるんでしょうか。

call-next-method自体が、次のメソッドが何であるかを静的に保存しているってのは確認できてる気はするんですが…。

ちなみに、メソッド自体を消しても

(fmakunbound 'baz)

(baz 'two)
;-> error

(funcall baz)
;->2

呼びだせます。

ゲスト



トラックバック - http://cadr.g.hatena.ne.jp/g000001/20071208