Разная инициализация, Common Lisp - PullRequest
0 голосов
/ 26 апреля 2019

Могу ли я имитировать различные конструкторы в CL?

Для уточнения, скажем, в C ++, я могу создавать разные конструкторы для одного и того же класса в зависимости от того, какие аргументы передаются.

Могу ли я сделать это с CLOS? Возможно, у вас разные initialize-instance s аргументы клавиш или что-то в этом роде?

Ответы [ 2 ]

1 голос
/ 26 апреля 2019

Один из подходов к этому - использовать вторичный метод инициализации:

(defclass myclass ()
  ((s1 :initarg :s1 :accessor s1)))

(defgeneric initialize-myclass (dispatch class &key))

(defmethod initialize-instance :after ((c myclass) &rest args &key (dispatch 'normal)
                                       &allow-other-keys)
  (apply #'initialize-myclass dispatch c args))

(defmethod initialize-myclass ((dispatch (eql 'normal)) (class myclass) &key))

(defmethod initialize-myclass ((dispatch (eql 'special)) (class myclass)
                               &key x &allow-other-keys)
  (print x))

Теперь вы можете сказать

(make-instance 'myclass :dispatch 'special ...)

Например. Обратите внимание, что это не обязательно хороший способ сделать это, но он работает, и я использовал это. Заметьте также, что у меня может быть неправильное значение по умолчанию для аргумента-ключевого слова: я никогда не помню, где вам нужно сказать &allow-other-keys, а где нет, и где правильное место, чтобы сказать, что это.

Основная проблема здесь в том, что мы хотим, чтобы отправлялась дополнительная вещь: initialize-instance может отправлять данные по классу определяемого объекта, но это all , по которому оно может отправляться. В частности, он не может отправить один из аргументов ключевого слова, потому что вы не можете сделать это в CLOS. Но мы можем взять один из его ключевых аргументов (dispatch здесь) и «благословить» его в качестве позиционного аргумента для универсальной функции вторичной инициализации, которая затем может отправить этот аргумент.

0 голосов
/ 26 апреля 2019

Ну, initialize-instance обычно определяется как метод :after для запуска какой-либо пост-обработки после инициализации экземпляра через make-instance. Что вы могли бы сделать, это использовать параметрический полиморфизм (диспетчеризация по параметрам) и иметь различные методы для инициализации вашего экземпляра на основе предоставленных параметров. Рассмотрим следующий пример:

CL-USER> (defclass my-class ()
       ((a :initarg :a
           :accessor my-class-a)))
#<STANDARD-CLASS COMMON-LISP-USER::MY-CLASS>
CL-USER> (defmethod make-my-class ((a number))
       (make-instance 'my-class :a (format nil "Look ma, a number ~a" a)))
#<STANDARD-METHOD COMMON-LISP-USER::MAKE-MY-CLASS (NUMBER) {1016445273}>
CL-USER> (defmethod make-my-class ((a string))
       (make-instance 'my-class :a (format nil "Look ma, a string ~a" a)))
#<STANDARD-METHOD COMMON-LISP-USER::MAKE-MY-CLASS (STRING) {10166065C3}>
CL-USER> (make-my-class 10)
#<MY-CLASS {1016690E33}>
CL-USER> (my-class-a *)
"Look ma, a number 10"
CL-USER> (make-my-class "foo")
#<MY-CLASS {1016694CD3}>
CL-USER> (my-class-a *)
"Look ma, a string foo"

Как видите, метод make-my-class отправляет аргумент и соответственно инициализирует my-class.

...