こんにちは。

今回は第14章〜第17章まで読みました。全体的に読み物っぽく、実際に動かしてみる、というのは多くありませんでした。

特にオブジェクト指向再入門ではずらずらと説明が書かれていることが多く、あまり頭に入ってきませんでした。

ダメダメです。





第14章 ファイルとファイルI/O

データを書き出す関数はいくつかある WRITE-CHAR、WRITE-LINE、WRITE-STRING、TERPRI、FRESH-LINEなど いくつかの関数はデータS式として書き出す。PRINTやPRIN1やPPRINTなどだ。

メソッドってクラス内の定義されてるんじゃないんですね。勘違いしてました。 このページ読んだら総称関数のイメージがわきました。

メソッドの特徴は、同じ名前のメソッドをいくつも定義することができ、引数のデータ型によって、その中から実際に呼び出すメソッドを自動的に選択することです。該当するメソッドが見つからない場合はエラーとなります。CLOS では同一名のメソッドの集まりを「総称関数 (generic function) 」と呼びます。この総称関数がＣ++や Java などの オブジェクト指向 とはちょっと違う CLOS の特徴です。

総称関数の複数の必須パラメータを特定化したメソッドのことを多重メソッドという。

これまで見てきたのは基本メソッドだが、他に補助メソッドと呼ばれる:before、:after、:aroundがある。文章が多くて何もわからない。

このページになんの話をしているのか全くわからない。ちんぷんかんぷん。

実効メソッドは3つのステップに沿って作られる。 まず、総称関数が実際に渡された引数に基づいて適用可能なメソッドのリストを作る。 次に、そのパラメータの特定子に応じて適用可能メソッドのリストが並び替えられる。 最後に、並び替えられたリストの順番にメソッドが取られ、実効メソッドを作るためにコードが結合される。

CALL-NEXT-METHOD関数は適応可能なメソッドを結合するのに使われる総称関数の機構

次にwithdrawメソッドを定義する。 メソッドは総称関数と同じ数の必須パラメータおよびオプショナルパラメータが必要。

現在の残高がamountより少なかったらエラーを通知する"

"amountで指定された額を口座から引き落とす

第17章 オブジェクト指向再入門：クラス

総称関数がオブジェクト指向の動詞なら、クラスは名詞だ。

＊DEFCLASS ユーザ定義クラス（これはCommon Lispの言語仕様にある言葉ではないが、ここではSTANDARD-OBJECTのサブクラスをそう呼ぶことにする)はDEFCLASSマクロを使って作ることができる。クラスにはデータ型として3つの側面がある。名前と、他のクラスとの関係と、クラスのインスタンスが持つスロット（フィールド、メンバ）の名前。 ( defclass name ( direct-superclass-name* ) ( slot-specifier* ))

＊スロット指定子 DEFCLASSフォームの大部分を占めるのはスロット指定子のリストだ。各スロットはインスタンスにおいて値を保持できる場所である。SLOT-VALUEでアクセスできる。

次の場合は２つのスロットが定義される。 ( defclass bank-account () ( customer-name balance )) ( make-instance ' bank-account ) -> #<BANK-ACCOUNT { 1005717253 } > ( defparameter *account* ( make-instance ' bank-account )) -> *ACCOUNT* ( setf ( slot-value *account* ' customer-name ) "John Doe" ) -> "John Doe" ( setf ( slot-value *account* ' balance ) 1000 ) -> 1000 ( slot-value *account* ' balance ) -> 1000

＊オブジェクトの初期化 スロットの値を初期化するのには3種類の方法がある。

DEFCLASSフォームのスロット指定子にオプション:initargか:initformをつける。

あるいはMAKE-INSTANCEによって呼ばれる総称関数INITIALIZE-INSTANCEに対するメソッドを定義する。 ( defclass bank-account () (( customer-name :initarg :customer-name ) ( balance :initarg :balance :initform 0 ))) ( defparameter *account* ( make-instance ' bank-account :customer-name "John Doe" :balance 1000 )) ( slot-value *account* ' customer-name ) -> "John Doe" ( slot-value *account* ' balance ) -> 1000 このとき、:customer-nameに値を渡さないと、slot-valueしたときにエラーになる。

それを防ぐためには:initformでエラーを通知するようにすればいい。 ( defvar *account-numbers* 0 ) ( defclass bank-account () (( customer-name initarg :customer-name initform ( error "Must supply a customer name." )) ( balance :initarg :balance :initform 0 ) ( account-number :initform ( incf *account-numbers* )))) 基本的にはinitargとinitformを組み合わせればいいが、initformのS式には他のスロットの値が使えない。

そこで、INITIALZE-INSTANCEを使う。

例えば、上のクラスにaccount-typeスロットを加え、預金残高に応じて初期値を:gold、:silver、:bronzeにしたい。

その場合には次のようになる。 ( defclass bank-account () (( customer-name initarg :customer-name initform ( error "Must supply a customer name." )) ( balance :initarg :balance :initform 0 ) ( account-number :initform ( incf *account-numbers* ) account-type ))) ( defmethod initialize-instane :after (( account bank-account ) &key ) ( let (( balance ( slot-value account ' balance ))) ( setf ( slot-value account ' account-type ) ( cond (( >= balance 100000 ) :gold ) (( >= balance 50000 ) :silver ) ( t :bronze )))))

＊アクセス関数 オブジェクトのスロットに直接アクセスするのは脆弱なコードになってしまう上に変更も大変なので、アクセサ関数を作る。

その時に、bank-accountのサブクラスを定義することがわかっているなら、総称関数として定義すればいい ( defgeneric balance ( account )) ( defmethod balance (( account bank-account )) ( slot-value account ' balance )) 更にSETFマクロを拡張してSETF関数を作ることで便利になる。 ( defun ( setf customer-name ) ( name account ) ( setf ( slot-value account ' customer-name ) name )) ( defgeneric ( setf customer-name ) value account ) ( defmethod ( setf customer-name ) ( value ( account bank-account )) ( setf ( slot-value account ' customer-name ) value )) ( defgeneric customer-name ( account )) ( defmethod customer-name (( account bank-account )) ( slot-value account ' customer-name )) せっかくここまで書いたのに、DEFCLASSには3つのスロットオプションがある（先に言ってほしい）。:readerと:writerと:accessorだ。:accessorは前の２つを両方とも生成するもの。加えて説明する:documentationオプションがある。