Методы получения и установки классов в Common Lisp

oop lisp common-lisp setter clos

294 просмотра

2 ответа

У меня часто есть класс, который составлен из списка другого класса. Например, у меня будет класс списка векторов, состоящий из векторов. Чтобы избежать написания длинных операторов, я пишу метод для доступа к встроенному классу. Однако этот метод действует только как получатель; Я не могу использовать его для установки значения слота. Есть ли способ использовать метод для установки значения слота класса?

Ниже приведен минимальный пример:

(defclass vector ()
  ((name :accessor vector-name
         :initarg :name)))

(defclass vector-list ()
  ((vectors :accessor vector-list-vectors
            :initarg :vectors)))

(defun make-vector-list ()
  (make-instance 'vector-list
    :vectors (list
               (make-instance 'vector :name 'v1)
               (make-instance 'vector :name 'v2))))

(defmethod access-vector-name ((vt vector-list) vector-idx)
  (vector-name (nth vector-idx (vector-list-vectors vt))))


;; returns V1
(print (access-vector-name (make-vector-list) 0))

;; Now, trying to set the same slot returns an error
;; How can I set the slot?
(setf (access-vector-name (make-vector-list) 0) 'new); --> error
Автор: audrow Источник Размещён: 08.11.2019 10:56

Ответы (2)


6 плюса

Решение

Проще всего было бы написать:

(setf (aref (access-vector-name ...) index) value)`

Но если вы не хотите раскрывать тот факт, что у вас есть массивы / векторы, вы можете определить собственный расширитель setf.

Во-первых, определите только access-vector-nameкак :readerв вашем классе. Затем:

(defun (setf access-vector-name) (newval obj index)
  (setf (aref (access-vector-name obj) index) newval))

Если целью является скрыть основную реализацию, возможно access-vector-name, это плохое имя.

Автор: coredump Размещён: 20.08.2016 06:00

4 плюса

Вам просто нужно определить метод установки для этого. Однако ваш код не является законным в его нынешнем виде: VECTORэто определенный символ в CLпакете (и на самом деле он называет и функцию, и тип), поэтому определение вызываемого класса VECTORужасно недопустимо (и приличная реализация будет препятствовать этому). Вот версия вашего кода с базовым классом, переименованным в VEC, и с методом установки.

(defclass vec ()
  ;; Don't call it VECTOR since it's a function in CL
  ((name :accessor vec-name
         :initarg :name)))

(defclass vec-list ()
  ((vecs :accessor vec-list-vecs
         :initarg :vecs)))

(defun make-vec-list ()
  (make-instance 'vec-list
    :vecs (list
           (make-instance 'vec :name 'v1)
           (make-instance 'vec :name 'v2))))

(defmethod access-vec-name ((vt vec-list) vec-idx)
  (vec-name (nth vec-idx (vec-list-vecs vt))))

(defmethod (setf access-vec-name) (new (vt vec-list) vec-idx)
  (setf (vec-name (nth vec-idx (vec-list-vecs vt))) new))

В CLOS нет предопределенного макроса для определения методов доступа, подобных этому, вне определений классов: я не уверен, почему, но, возможно, потому, что случаи, когда это действительно «чистый» метод доступа, подобный этому, относительно редки.

Автор: tfb Размещён: 20.08.2016 08:58
Вопросы из категории :
32x32