next up previous
Next: 11.3 メッセージ送信プログラミング Up: 11 データ指向型プログラミング Previous: 11.1 構造体でデータを表現する方法

11.2 クロージャによるオブジェクトと手続きの一体化

今度は,構造体ではなく,クロージャでデータを表現する例を示す.

(defun new-account (name &optional (balance 0.00)
                    (interest-rate .06))
  #'(lambda (message)
      (case message
        (withdraw #'(lambda (amt)
                      (if (<= amt balance)
                          (decf balance amt)
                          'insufficient-funds)))
        (deposit  #'(lambda (amt) (incf balance amt)))
        (balance  #'(lambda () balance))
        (name     #'(lambda () name))
        (interest #'(lambda ()
                      (incf balance
                       (* interest-rate balance)))))))
このような,新しい口座を開く手続きnew-accountを定義する. ここでは,アカウントが関数クロージャになっている. そして,その関数クロージャは,メッセージを引数と してそのメッセージに応じた処理を行う場合分けを行う 手続きとなっている. 使い方は,

> (setq acc (new-account "abc"))
#<Interpreted Closure ...>

> (funcall acc 'name)
#<Interpreted Closure ...>
> (apply (funcall acc 'name) nil)
"abc"

> (funcall acc 'balance)
#<Interpreted Closure ...>
> (apply (funcall acc 'balance) nil)
0.0

> (apply (funcall acc 'deposit) '(10))
10.0
> (apply (funcall acc 'balance) nil)
10.0
という具合に,accにnew-accountで作られるクロージャを代入する. このクロージャには,balanceとinterest-rateが閉じ込められている. このクロージャの中の関数がmessageを受け取って, そのmessage内容によってcaseで分けた手続きを返す. その返された手続きにmessageに応じた引数をapplyすると その手続きが実行されて,目的のものを得ることができる. 上の例で,10だけdepositした後,balanceを見るとbalanceが増えていること がわかる.クロージャの中にbalanceが保存されているからである.

generated through LaTeX2HTML. M.Inaba 平成18年5月7日