Python では、次のようにします。
class foo:
def __init__(self):
self.x = self
それ以外の場合、オブジェクトはそれ自体のパラメーターになります。コモンリスプでどうやってそれを行うことができますか?
(defclass mn ()
((pai :accessor mn-pai
:initarg :pai
:initform self)))
Python では、次のようにします。
class foo:
def __init__(self):
self.x = self
それ以外の場合、オブジェクトはそれ自体のパラメーターになります。コモンリスプでどうやってそれを行うことができますか?
(defclass mn ()
((pai :accessor mn-pai
:initarg :pai
:initform self)))
DEFCLASS
スロットの説明では、オブジェクト自体を参照することはできません。しかし、インスタンスの初期化のためのメソッドを書くことができます。これは Python の例に似ています。
私たちのクラス:
? (defclass foo ()
((bar :accessor foo-bar :initarg :foo)))
#<STANDARD-CLASS FOO>
:after
のメソッドを作成しますinitialize-instance
。この汎用関数は CLOS によって提供され、その目的は新しいインスタンスを初期化することです。最初の引数は、初期化するインスタンスです。このメソッドは、クラスのインスタンスを作成するときに Lisp システムによって呼び出されますfoo
。
アクセサーの使用foo-bar
:
? (defmethod initialize-instance :after ((object foo) &key)
(setf (foo-bar object) object))
#<STANDARD-METHOD INITIALIZE-INSTANCE :AFTER (FOO)>
または でスロットを設定し(setf slot-value)
ます。
? (defmethod initialize-instance :after ((object foo) &key)
(setf (slot-value object 'bar) object))
#<STANDARD-METHOD INITIALIZE-INSTANCE :AFTER (FOO)>
インスタンス パラメータには任意の名前を付けることができることに注意してobject
くださいself
。しかし、名前にはセマンティクスがありません。CLOS ではマルチディスパッチがあるため (ディスパッチは複数の引数に対して機能し、デフォルトのディスパッチされた引数はありません)、self
セマンティクスはありません。
次に、 class のインスタンスを作成して記述しますfoo
。
? (describe (make-instance 'foo))
#<FOO #x302000D20C0D>
Class: #<STANDARD-CLASS FOO>
Wrapper: #<CCL::CLASS-WRAPPER FOO #x302000D2B43D>
Instance slots
BAR: #<FOO #x302000D20C0D>
ご覧のとおりbar
、そのインスタンスのスロットはインスタンス自体に設定されています。
CLOS には "this" や "self" の概念がありません。これは、ジェネリック関数を使用することで、処理対象のインスタンスが引数として渡されるためです。
したがって、アクセサーを使用した例を考えるとmn-pai
:
(setf instance (make-instance 'mn))
(mn-pai instance 1)
ここでinstance
は、アクセサーに引数として渡されます。
メソッドを作成した場合:
(defmethod inc-pai (an-mn amount)
(incf (mn-pai an-mn) amount))
ここでも、インスタンスが最初の引数として渡されていることがわかります。したがって、使用する明示的な引数が常に存在します。
今考えてみましょう:
(defmethod inc-both (an-mn another-mn amount)
(incf (mn-pai an-mn) amount)
(incf (mn-pai another-mn) amount))
では、通常のクラス ベースのシステムでは、このメソッドをどこに配置しますか? ユーティリティクラスで?これは「mn」クラスのメソッドですか?それは、既成の分類に逆らいます。
今考えてみましょう:
(defclass mn2 ()
((pai :accessor mn2-pai)))
これを行う場合:
(setf an-mn (make-instance 'mn))
(setf an-mn2 (make-instance 'mn2))
(inc-both an-mn an-mn2)
mn2 にはアクセサーがないため、2 行目は失敗しmn-pai
ます。
ただし、これは機能します。
(defmethod inc-both2 (an-mn another-mn amount)
(incf (slot-value 'pai an-mn) amount)
(incf (slot-value 'pai another-mn) amount))
slot-value
は CLOS のプリミティブ アクセサーであり、両方のクラスに という名前のスロットがあるためですpai
。ただし、アクセサ関数を呼び出すことはできません。むしろ、スロットを直接設定しています。おそらくあなたが望むものではありません。そしてもちろん、名前は偶然です。類似した名前と共有スロット名を保存するクラス間に関係はありません。
しかし、これを行うことができます:
(defmethod inc-both ((mn an-mn) (mn2 another-mn) amount)
(incf (mn-pai an-mn) amount)
(incf (mn-pai2 another-mn) amount))
これは、ランタイムがパラメーターの型に基づいてディスパッチするため機能します。「知っている」は、引数を修飾したときにそうでなければならないことをシステムに伝えたためanother-mn
、のインスタンスです。mn2
しかし、繰り返しますが、クラスベースのシステムでは、この種のメソッドの「場所」がないことがわかります。通常、ある種の Utility クラスを作成してそこに貼り付けるか、グローバル名前空間に通常の関数を配置します。
CLOS にはクラスがありますが、実際にはクラス ベースのシステムではありません。
これは、(CLOS がサポートする) 多重継承のシナリオでも発生します。では「自分」とは誰のことでしょうか。