#:g1: 定番メソッドコンビネーション紹介: progn

Posted 2018-12-06 17:46:30 GMT

Lisp メソッドコンビネーション Advent Calendar 2018 7日目 》

今回はCommon Lispの組み込みメソッドコンビネーションのprognを紹介していきます。

prognメソッドコンビネーション

prognは、メソッドの集合をprognで囲んだように実行するものです。

メソッドコンビネーションアドベントカレンダー 5日目で、フィルターのパターンをメソッドコンビネーションですっきり書けないか考えていましたが、アイテムの一時置き場を用意して更新していくことが可能であれば、prognで書けそうだと思ったので試してみました。

メニューにはアイテム置き場とフィルター適用済アイテムを持たせて、初期化とソートを:aroundで、他各種フィルターの適用は、prognメソッドコンビネーションで実行してみています。

(defclass menu ()
  ((items :initform '() :initarg :items :accessor items)
   (filtered-items :initform '() :initarg :items :accessor filtered-items)))

(defclass fizz-filtered-menu (menu) ())

(defclass buzz-filtered-menu (menu) ())

(defclass *-filtered-menu (fizz-filtered-menu buzz-filtered-menu) ())

(defgeneric filter (menu) (:method-combination progn))

(defmethod filter :around ((menu *-filtered-menu)) (setf (filtered-items menu) ;filtered-items初期化 (items menu)) (call-next-method) ;フィルタリング (setf (filtered-items menu) ;昇順にソート (sort (filtered-items menu) #'<)) menu)

(defmethod filter progn ((menu fizz-filtered-menu)) (setf (filtered-items menu) (remove-if-not (lambda (x) (zerop (mod x 3))) (filtered-items menu))) menu)

(defmethod filter progn ((menu buzz-filtered-menu)) (setf (filtered-items menu) (remove-if-not (lambda (x) (zerop (mod x 5))) (filtered-items menu))) menu)

フィルターを掛けるためだけに、mixin的なクラスを定義するのが、微妙な気がしますが、まあ良しとしましょう。
実行してみましょう。

(let ((menu (make-instance '*-filtered-menu 
                           :items (loop :repeat 100 :collect (random 100)))))
  (filtered-items (filter menu)))(0 15 30 45 45 60 60 60 75) 

先日定義してみたmc-expandで展開形を確認してみると、:aroundで囲まれたprognということが判ります。

(mc-expand #'filter 'progn nil (make-instance '*-filtered-menu))(call-method 
 #<standard-method filter (:around) (*-filtered-menu) 41C008E0CB>
 ((make-method
   (progn
     (call-method #<standard-method filter (progn) (fizz-filtered-menu) 402003E7FB> nil)
     (call-method #<standard-method filter (progn) (buzz-filtered-menu) 4020052C8B> nil))))) 

:around修飾子の謎 その二

:aroundは、メソッド単体を囲んで実行するというよりは、コンビネーション全体を取り囲うものなのでは? Flavorsのdefwrapperのような使い方をするのではないか? と昨日書きましたが、改めてSonya Keene氏 のObject-Oriented Programming in Common Lisp(邦訳:Common Lispオブジェクト指向)を確認したところ、「5. Controlling the Generic Dispatch」の章でちゃんとそんな風に説明してました。
前に読んでた筈でしたが、すっかり忘れていたようです……。

Object-Oriented Programming in Common Lisp(邦訳:Common Lispオブジェクト指向)は、Common Lispにオブジェクトシステムを導入するのにあたって貢献した人達(主にSymbolics)が執筆に協力している本なので、割合に説明が細かいです。

また、多くの書籍では、Common Lispと他の言語のオブジェクトシステムとの対比でオブジェクトシステムに変な機能があるとか無いとかで説明されることも多いですが、この本は、Common Lispネイティブな視点で書かれている所もお勧めです(ANSI CL成立前の仕様だったりするのが若干残念)

次回も、標準組み込みのメソッドコンビネーションを紹介したりしようかなと思います。


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus