(defclass document ()
((title :initarg :title :reader doc-title)
(lang :initarg :lang :reader doc-lang)
(author :initarg :author :reader doc-author)
(thema :initarg :thema :reader doc-thema)
(keyword :initarg :keyword :reader doc-keyword :initform nil)
(pub-year :initarg :pub-year :reader doc-pub-year :initform 1997)
)
)
(defmethod display ((doc document))
(format t "{~A by ~A#~A#~A#~S}"
(and (slot-boundp doc 'title) (doc-title doc))
(and (slot-boundp doc 'author) (doc-author doc))
(and (slot-boundp doc 'thema) (doc-thema doc))
(and (slot-boundp doc 'lang) (doc-lang doc))
(and (slot-boundp doc 'keyword) (doc-keyword doc))
(and (slot-boundp doc 'pub-year)(doc-pub-year doc))
)
)
このように定義された document を、thema, author, lang の順に分類するための
定義は次のようになる。
(defclassifier simple-classifier1 (thema () (lambda (x) (doc-thema x)) (lambda (x) (slot-exists-boundp x 'thema))) (author (thema) (lambda (x) (slot-value x 'author)) (lambda (x) (slot-exists-boundp x 'author) )) (lang (author) (lambda (x) (doc-lang x) ) (lambda (x) (slot-exists-boundp x 'lang))) )
simple-classifier1 というのは、classifierの名前であり、make-classifierの パタメタとして指定する。
その後に続くものは、分類の各単位の定義となる。 その中の最初のもの
(category () (lambda (x) (doc-thema x)) (lambda (x) (slot-exists-boundp x 'thema)))について説明しよう。 このひとまとまりのものを category と呼ぶ。
themaは、このcategoryの名前である。docのslotのthemaに着目しているので、 このような名前を付けた。
次の()は、このcategoryの親のcategoryの名前である。ここでは、親はないので
()になっている。
classifierには、すべてのcategoryのrootとなるcategoryがただひとつ存在する。
それは t category と呼ばれ、親のない categoryは、t categoryの直接の子供だと
解釈される。
ユーザーが t categoryを定義することはできない。
次の関数は、後で説明するので、ひとつ飛ばして、最後の関数
(lambda (x) (slot-exists-boundp x 'thema))
を説明しよう。
これは、この category に属するobjectが満たさなくてはならない条件(constraint)
を示している。
1引数の関数であり、その引数には object が渡され、値としてtか nilを返すことが必要である。(nil以外はすべてtとみなすのだけど)
ここでは、themaというslotが存在し、値がbindされていることが条件になっている
さて、ひとつ飛ばした関数 (lambda (x) (doc-thema x))の説明をするためには、
representationとpartitionについて説明しなくてはならない。
categoryのconstraintを満たすobjectは、この category に属すると考えられるが、
それらのobjectは、さらに partition という同値類に分類される。
その同値類のひとつひとつにつけられた名前がrepresentationである。
上の本のthemaの例であれば、「小説」であるとか「コンピュータ科学」であるとか
いったthemaの個々の値を、そのrepresentationとすることができ、それに属する
objectの集合がpartitionとなる。
注意すべき点は、partitionは、ひとつのcategoryに対して複数存在し、 個々のpartitionが、categoryの定義に従って、子供のpartitionを持つ ということである。
例えば、authorが「大村」と「田中」と二人いた場合、「小説」の下にもこれらの partitionがつきうるのと同時に、「コンピュータサイエンス」の下にも つくかもしれない。
さて、関数の説明に戻ると、(lambda (x) (doc-thema x))は、representationを 定義する関数であると同時に、同じrepresentationを持つ集合としてそのpartitionも決める。
一引数の関数であり、その引数にはobjectをとり、値としてatomや文字列を 返す関数であることが必要である。(たぶんどんなobjectでもいいはず)
t categoryのpartitionは唯一つ存在し、それはt representationまたは t partitionと呼ばれる。これは、すべてのpartitionのrootになる。
(setf bug1 (make-classifier 'simple-classifier1))また、次のようなdocumentのinstanceがあるとしよう。
(setf doc (list
(make-instance 'document
:title "reference manual"
:lang 'japan
:author "omura"
:thema "Computer Science"
:keyword '("Object Oriented" "Lisp")
)
(make-instance 'document
:title "user's manual"
:lang 'japan
:author "omura"
:thema "mathematics"
:keyword '("Object Oriented" "Lisp")
)
(make-instance 'document
:title "Quick Start"
:lang 'japan
:author "tanaka"
:thema "Novel"
:keyword '("Object Oriented" "Lisp")
)))
このとき、つぎのようにして、bug1にobjectを登録する。
(put-object* bug1 doc)登録された様子を見るためのutility showを使うと、次のようにみえる
(show bug1 'display)
[T]/
[Novel]/
[tanaka]/
[JAPAN]/
{Quick Start by tanaka#Novel#JAPAN#("Object Oriented" "Lisp")}
[mathematics]/
[omura]/
[JAPAN]/
{user's manual by omura#mathematics#JAPAN#("Object Oriented" "Lisp")}
[Computer Science]/
[omura]/
[JAPAN]/
{reference manual by omura#Computer Science#JAPAN#("Object Oriented" "Lisp")}
NIL
[]で囲まれた名前が partition であり、{}で囲まれた部分は、method display によって
表示されたdocumentの情報である。