Object Classifier/Object Browser入門

written by omura.ki.rim.to.jp

目次

  1. Object Classifierとは
  2. 簡単な例
  3. やや複雑な例
  4. Object Browserを使う
  5. classifier定義の構文と意味

Object Classifierとは

LispやSmalltalkあるいは、動的にobjectを扱うようなアプリケーション において、その実行空間に存在するobjectを、ブラウズする機能は必ず必要になる。

たとえば、LISPやSmalltalkのinspectorは、個々のobjectをブラウズするツールとして不可欠なものだ。
また、WindowsのExploreも、ファイルやディレクトリをオブジェクトと考えたときの ひとつのブラウザの形態を示していると考えられる。

このような、Object Browserを考えるための基礎となるメカニズムとして、Object Classifierを考案した。

Object Classifierは、一群のobjectを、ユーザが定義した分類条件に応じて階層化 してみせる装置である。

たとえば、本のobjectがあったとき、まず本のテーマで分類し、その中を著者で分類し、 最後に、書かれた言語で分類する、などといった階層構造を指定すると、それにしたがって本のobjectを 分類してくれるものである。

Object Browserは、Classiifierによって分類されたオブジェクトを表示したり操作したり するためのユーザインターフェイスにすぎない。

プロトタイプについて

今回、Object BrowserとObject Classifierのプロトタイプを作成した。
言語はLispで行ったが、objectに対するreflectionの機能があれば、どのような 言語でも実装可能である。
例えば、JavaBeansを扱うような環境では、有効に使えるのではないだろうか。

簡単な例

たとえば、上にあげた本のobjectを分類することを考えてみよう。
(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)
   )
  )
これはLISPのオブジェクト指向言語CLOSによる表現であり、 クラス document を定義している。
documentには、title, lang, author, thema, keyword, pub-yearという slot(instance variable)がある。
:initargの後ろの名前が、初期値を設定するときのキーワードを示し、 :readerの後ろの名前が、その値に対する read-only のアクセスmethod名を 示している

documentのインスタンスを表示するためのmethod displayを次のように定義 する。
詳細は、略。

(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
    nil
   (thema (t)
         (lambda (obj part) (doc-thema obj))
         (lambda (obj part) (slot-exists-boundp obj 'thema)))
   (author (thema)
         (lambda (obj part) (slot-value obj 'author))
         (lambda (obj part) (slot-exists-boundp obj 'author) ))
   (lang (author)
         (lambda (obj part) (doc-lang obj) )
         (lambda (obj part) (slot-exists-boundp obj 'lang)))
   )

定義の説明

簡単に説明しよう。
まず、defclassifierによって、classifierと呼ばれる、分類方法を定義したものが 作成される。

classifierはclassとして実装されていて、そのインスタンスは、objectを分類する 入れ物として働く。
同じclassifierの定義に基づいて、中に入れるobjectの異なる、いろいろな入れ物を 作ることができる

この例では、simple-classifier1 という名前のclassifierを定義している。

名前の次の nil は、今は無視しよう。
その後に続くものが、実際の分類の定義となる。 最初の

   (thema ()
         (lambda (obj part) (doc-thema obj))
         (lambda (obj part) (slot-exists-boundp obj 'thema)))
について説明しよう。

このひとまとまりのものを category と呼ぶ。
objectは、このcategoryの定義にしたがって分類される。

themaは、このcategoryの名前である。docのslotのthemaに着目しているので、 このような名前を付けた。

次の(t)は、このcategoryの親のcategoryの名前を指定する。ここでは、t という カテゴリーを親として指定している。
classifierには、すべてのcategoryのrootとなるcategoryがただひとつ存在する。 それは t category と呼ばれる。
ユーザーが t categoryを定義することはできない。

次の関数は、後で説明するので、ひとつ飛ばして、最後の関数

(lambda (obj part) (slot-exists-boundp obj 'thema)
を説明しよう。
これは、この category に属するobjectが満たさなくてはならない条件(constraint) を示している。

constraintは2引数の関数であり、その第一引数には分類の対象となる object が渡される。 第二引数については、より高度な機能に関するところで説明しよう。 この関数は、値としてtかnilを返すことが必要である。(nil以外はすべてtとみなす)

ここでは、themaというslotが存在し、値がbindされていることが条件になっている

さて、ひとつ飛ばした関数 (lambda (obj part) (doc-thema obj))の説明をするためには、 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をとり、値として任意のオブジェクト 返す関数であることが必要である。

t categoryのpartitionは唯一つ存在し、それはt representationまたは t partitionと呼ばれる。これは、すべてのpartitionのrootになる。

実行例

次のような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")
                           )))
このとき、ObjectBrowserを使うと、docをsimple-classifier1で分類した様子が、 次のようにみえる。

leafに表示されている

#<Document ...>は、オブジェクトを示している。
このオブジェクトの表現は、ObjectBrowserに、表示用のmethodを指示することによって 変更することができる。

やや複雑な例

ファイルをブラウズする場合のclassifierを次のように定義すると 下図のように表示できる。

ここでは、defclassifierの第二引数を用いている。
第二引数で指定した変数(この例ではlevel)は、classiferの各partitionに対応 づけられ、その状態を保持する。
ここでは、ディレクトリ階層の何レベル目かを示す数値を持つようにした。


(defun max-level (part)
   (if part
      (loop for parent in (parent* part) maximize (level parent))
      0
      )
   )

(defun my-dir-name (obj part)
   (setf (level part) (1+ (max-level part)))
   (nth (level part)(pathname-directory obj))
   )

(defun dir-p (obj part)
   (nth (1+ (max-level part)) (pathname-directory obj))
   )

(defclassifier
  filelist2
  '((level :initform 0 :accessor level))
  (directory (t directory)
    my-dir-name
    dir-p
    )
  )

(filebrowser 'filelist2  "K:\\ALLEGRO\\lispwork\\testdir\\")

オブジェクトの表現が、生のデータになっているので、次のように表示形式を かえることもできる。



(filebrowser 'filelist2 "K:\\ALLEGRO\\lispwork\\testdir\\" 'file-namestring)

classifier定義の構文と意味

仕様はここ

注意事項

  1. categoryの定義で、親が複数あるとき、親の中の二つのcategoryに、親子関係があってはならない。 つまり、aがbの祖先であることをa>bと書くとして、category c の親として(a b)が指定されている場合、 acategoryの定義がcyclicな場合の意味が、まだ完全には分かっていない。複雑な表現をすると、 どうなるのか分からない。