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 |

2008-12-12

ContextLとデザインパターン - Bridge

| 17:16 | ContextLとデザインパターン - Bridge - わだばLisperになる を含むブックマーク はてなブックマーク - ContextLとデザインパターン - Bridge - わだばLisperになる

昨日書いた私が考えるBridgeパターンの例では、機能追加側は一つの定義で良いものの追加された機能のクラスをいちいち継承してやる必要があり、いまいち釈然としなかったのですが、こういう場合は、ContextLが使えるんじゃないかということで、ContextLを使って書いてみました。

  1. 機能の追加は、クラスの継承関係で
  2. 実装の違いはレイヤの違い(継承関係)で

という風にしてみました。もちろん、レイヤとクラスを逆にしても構いません。

これなら、実装と機能追加の双方は独立に追加して行けます。

なんだか、AOPとかリフレクションみたいになってしまいましたが、委譲が良く分かっていない人が書くとBridgeパターンはこうなるのかもしれません。

眺めてみて思うのですが、MOP(ContextL)を使わないということになると、やはりマルチメソッドを使うことになるのかなと思います。

(require :contextl)

(defpackage :design-patterns
  (:use :cl :contextl))

(in-package :design-patterns)

;; time付き用のクラス
(defclass with-time () ())

;; ベース(空実装)
(define-layered-function fib (class n))

;; time付き(ベースを呼んで味付け)
(define-layered-method fib ((class with-time) n)
  (time (call-next-method)))

;; 末尾再帰レイヤ
(deflayer tail)

(define-layered-method fib :in tail (class n)
  (labels ((*fib (n a1 a2)
             (if (< n 2)
                 a1
                 (*fib (1- n) 
                       (+ a1 a2)
                       a1))))
    (*fib n 1 0)))

;; 普通の再帰レイヤ
(deflayer recur)

(define-layered-method fib :in recur (class n)
  (labels ((*fib (n)
             (if (< n 2)
                 n
                 (+ (*fib (1- n))
                    (*fib (- n 2))))))
    (*fib n)))

;;; 動作

;; 普通の再帰
(with-active-layers (recur)
  (let ((n 30))
    (format T "普通:~A~%" (fib 'T n))
    (format T "time付き:~A~%" (fib (make-instance 'with-time) n))))

;;>>>
;; 普通:832040
;; Evaluation took:
;;   0.074 seconds of real time
;;   0.072004 seconds of total run time (0.072004 user, 0.000000 system)
;;   97.30% CPU
;;   178,337,061 processor cycles
;;   114,608 bytes consed
;;  
;; time付き:832040

;; 末尾再帰
(with-active-layers (tail)
  (let ((n 30))
    (format T "普通:~A~%" (fib 'T n))
    (format T "time付き:~A~%" (fib (make-instance 'with-time) n))))
;;>>>
;; 普通:832040
;; Evaluation took:
;;   0.000 seconds of real time
;;   0.000000 seconds of total run time (0.000000 user, 0.000000 system)
;;   100.00% CPU
;;   3,447 processor cycles
;;   0 bytes consed
;;  
;; time付き:832040

2008-11-04

ContextLとデザインパターン - Template Method

| 01:19 | ContextLとデザインパターン - Template Method - わだばLisperになる を含むブックマーク はてなブックマーク - ContextLとデザインパターン - Template Method - わだばLisperになる

ContextLの概要の説明を読んで、最初にぱっと思いついたのは、デザインパターンでのTemplate Method的な動きがContextLを使えば簡単に記述できるのではないかということでした。

デザインパターンでは、クラスを利用して色々な状況の切り換えパターンが紹介されています。

ContextLでは、クラスの代りにレイヤ機能によって色々と切り換えることができるのですが、その名前の通りコンテクストを切り換えるようなパターンには応用できそうだと思いました。

ということで記念カキコ。

(defpackage :design-patterns 
  (:use :cl))

(in-package :design-patterns)

(use-package :contextl)

;; テンプレート
(define-layered-function template (string)
  (:documentation "テンプレ"))
(define-layered-method template ((string string))
  (op2 (op1 string)))

(define-layered-function op1 (string)
  (:documentation "処理その1"))
(define-layered-method op1 ((string string))
  string)

(define-layered-function op2 (string)
  (:documentation "処理その2"))
(define-layered-method op2 ((string string))
  string)

;; テンプレに従った詳細な実装 その1
(deflayer concrete)

(define-layered-method op1 :in concrete ((string string))
  (string-upcase string))

(define-layered-method op2 :in concrete ((string string))
  (format t "~A~%" string)))

;; テンプレに従った詳細な実装 その2
(deflayer concrete2)

(define-layered-method op1 :in concrete2 ((string string))
  (string-capitalize string))

(define-layered-method op2 :in concrete2 ((string string))
  (princ string)
  (terpri)))

;; テンプレに従った詳細な実装 その2を継承したその3
(deflayer concrete3 (concrete2))

(define-layered-method op2 :in concrete3 ((string string))
  (princ "**")
  (princ string)
  (princ "**")                 
  (terpri))


;;; 動作

;; Tは一番下のルートレイヤ
(with-active-layers (t)
  (template "foo bar baz"))
;=> "foo bar baz"

(with-active-layers (concrete)
  (template "foo bar baz"))
;>>> FOO BAR BAZ
(with-active-layers (concrete2)
  (template "foo bar baz"))
;>>> Foo Bar Baz

(with-active-layers (concrete3)
  (template "foo bar baz"))
;>>> **Foo Bar Baz**

2008-10-29

ContextLを使ってみよう

| 12:08 | ContextLを使ってみよう - わだばLisperになる を含むブックマーク はてなブックマーク - ContextLを使ってみよう - わだばLisperになる

先日開催されたLisp50で作者のPascal Costanza氏によってデモされたこともあって、ちょっと話題になり気味のContextL。

以前から存在は知っていたのですが、コンテクスト指向とか、アスペクト指向というのがさっぱり理解できなかったので放置していました。

しかし、自分は、最近MOPづいているので、これは試してみた方が良いだろうということでとりあえず触ってだけはみることにしました。

インストールは、(asdf-install:install :contextl)のみでOK。

test/demo3.lispになんとなくの使い方と、本家サイトに論文があるので、それを眺めれば筋の良い人は理解できるんだと思います。

どうやらコンテクストという名のレイヤで階層を分けて、特定のレイヤで呼び出すとそのコンテクストに応じたメソッドが呼び出されるという仕組みのようです。

自分は筋が悪いので、良く分からないながらも適当に思い付いた利用法を書き散らかしてみることにしました。

とりあえず、クラスは定義しないで、メソッドのみを実行して遊んでみています。

(require :contextl)

(in-package :contextl-user)

;; 総称関数(defgenericみたいなもの)
(define-layered-function fib (number))

;; 基本
(define-layered-method fib ((n integer))
  (if (< n 2)
      n
      (+ (fib (1- n))
         (fib (- n 2)))))

;; 型宣言してみる/optimize-speedレイヤ
(deflayer optimize-speed)

(define-layered-method fib
  :in optimize-speed ((n integer))
  (declare (fixnum n))
  (if (< n 2)
      n
      (+ (fib (1- n))
         (fib (- n 2)))))

;; 末尾再帰で実装したもの/tail-recurレイヤ
(deflayer tail-recur)

(define-layered-method fib
  :in tail-recur ((n integer))
  (labels ((*fib (n a1 a2)
             (declare (fixnum n a1 a2))
             (if (< n 2)
                 a1
                 (*fib (1- n) (+ a1 a2) a1))))
    (*fib n 1 0)))

;; 無理矢理まぜてみたもの/mixedレイヤ
(deflayer mixed)

(define-layered-method fib
  :in mixed ((n integer))
  (declare (fixnum n))
  (if (< n 2)
      n
      (+ (with-active-layers (tail-recur)
           (fib (1- n)))
         (with-active-layers (optimize-speed)
           (fib (- n 2))))))

;; 試してみる
(progn
  (time (fib 30))

  (time 
   (with-active-layers (optimize-speed)
     (fib 30)))
  
  (time 
   (with-active-layers (tail-recur)
     (fib 30)))
  
  (time 
   (with-active-layers (mixed)
     (fib 30))))

;; >>>
Evaluation took:
  0.231 seconds of real time
  0.228015 seconds of total run time (0.228015 user, 0.000000 system)
  98.70% CPU
  554,444,019 processor cycles
  365,728 bytes consed
  
Evaluation took: optimize-speed
  0.179 seconds of real time
  0.180011 seconds of total run time (0.180011 user, 0.000000 system)
  100.56% CPU
  430,328,007 processor cycles
  219,808 bytes consed
  
Evaluation took: tail-recur
  0.000 seconds of real time
  0.000000 seconds of total run time (0.000000 user, 0.000000 system)
  100.00% CPU
  8,469 processor cycles
  3,008 bytes consed
  
Evaluation took: mixed
  0.039 seconds of real time
  0.040002 seconds of total run time (0.040002 user, 0.000000 system)
  102.56% CPU
  93,135,816 processor cycles
  0 bytes consed

まだ、レイヤが入れ子になった場合にどのレイヤが有効になるのか等、詳しく分っていませんが、なかなか面白そうです。

とか書いてみたんですが、さらに眺めてみると、fibonacci-test.lispというもっと高度なことをやってるファイルがありました…。