Объект как параметр самого себя в lisp - PullRequest
0 голосов
/ 13 июня 2018

В Python я бы сделал так:

class foo:
    def __init__(self):
        self.x = self

В противном случае теперь объект является параметром самого себя.Как я могу сделать это в общих чертах?

(defclass mn ()
  ((pai   :accessor mn-pai
          :initarg :pai
          :initform self)))

Ответы [ 3 ]

0 голосов
/ 13 июня 2018

В описании слота 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 нет.

Теперь мы создаем и описываем экземпляр классаfoo:

? (describe (make-instance 'foo))
#<FOO #x302000D20C0D>
Class: #<STANDARD-CLASS FOO>
Wrapper: #<CCL::CLASS-WRAPPER FOO #x302000D2B43D>
Instance slots
BAR: #<FOO #x302000D20C0D>

Как видите, в слоте bar этого экземпляра установлен сам экземпляр.

0 голосов
/ 13 июня 2018

Обратите внимание, что initform оценивается в лексическом контексте defclass, но в динамическом контексте make-instance.Это позволяет вам определить специальную переменную с именем *this* (вы можете использовать this, но это может сбить с толку) и использовать ее при инициализации объектов.

(defvar *this*)

Определить миксин для классов, которые могутссылка *this*:

(defclass knows-this () ())

(defmethod shared-initialize :around ((object knows-this) slot-names &rest args)
  (declare (ignore args))
  (let ((*this* object))
    (call-next-method)))

Например:

(defclass foo (knows-this)
  ((myself :initform *this*)))

(describe (make-instance 'foo))

#<FOO {100AC6EF13}>
  [standard-object]

Slots with :INSTANCE allocation:
  MYSELF                         = #<FOO {100AC6EF13}>
0 голосов
/ 13 июня 2018

CLOS не имеет понятия "это" или "я", потому что с помощью универсальных функций любой экземпляр, с которым выполняется действие, передается в качестве аргумента.

Итак, учитывая ваш пример сметод доступа 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не имеет 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).Кто тогда "я"?

...