(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の情報である。