Я думаю, что это не совсем просто. Вы могли бы сделать что-то вроде этого, которое работает только для чтения (я использовал поддельную структуру toy
, поэтому мой код работает, что приведено здесь):
(defstruct toy
(first nil)
(second nil))
(defun foo (a-toy)
(symbol-macrolet ((x (or (toy-first a-toy) (toy-second a-toy))))
...))
Но теперь (setf x ...)
ужасно незаконно. Вы можете обойти это, как только вы определились с тем, что (setf x ...)
должно делать , определив некоторые локальные функции. Здесь я решил, что он должен установить не-1009 * слот, поскольку это имеет смысл для меня.
(defun bar (a-toy)
(flet ((toy-slot (the-toy)
(or (toy-first the-toy) (toy-second the-toy)))
((setf toy-slot) (new the-toy)
(if (toy-first the-toy)
(setf (toy-first the-toy) new)
(setf (toy-second the-toy) new))))
(symbol-macrolet ((x (toy-slot a-toy)))
(setf x 2)
a-toy)))
И теперь вы можете обернуть все это в один макрос:
(defmacro binding-toy-slot ((x toy) &body forms)
(let ((tsn (make-symbol "TOY-SLOT")))
`(flet ((,tsn (the-toy)
(or (toy-first the-toy) (toy-second the-toy)))
((setf ,tsn) (new the-toy)
(if (toy-first the-toy)
(setf (toy-first the-toy) new)
(setf (toy-second the-toy) new))))
(symbol-macrolet ((,x (,tsn ,toy)))
,@forms))))
(defun bar (a-toy)
(binding-toy-slot (x a-toy)
(setf x 3)
a-toy))
Очевидно, вы можете обобщить binding-toy-slot
, поэтому он, например, берет список имен доступа к слотам или что-то в этом роде.
Возможно, есть и лучшие способы сделать это, о которых я не думал: могут быть хитрые трюки с расширениями setf
, которые позволят вам сделать это без маленьких вспомогательных функций. Вы также можете иметь вспомогательные функции global , которые передают объект и список методов доступа, которые могут попробовать сделать код немного меньше (хотя вы, вероятно, можете добиться такого же небольшого кода в любой серьезной реализации, объявив встроенные помощники). что должно привести к их полной компиляции).
Альтернативный и, возможно, лучший подход заключается в определении протокола, которого вы хотите достичь, с помощью универсальных функций. Это означает, что вещи определены глобально, и это связано с ответом Каз, но не совсем так.
Итак, скажем, у меня есть некоторый класс (это может быть структура, но если сделать ее полноценной standard-class
, то у нас будут свободные слоты, что приятно):
(defclass toy ()
((first :initarg :first)
(second :initarg :second)))
Теперь вы можете определить обобщенные функции с именами, такими как appropriate-slot-value
& (setf appropriate-slot-value)
, или вы можете определить GF, который возвращает name соответствующего слота, например:
(define-condition no-appropriate-slot (unbound-slot)
;; this is not the right place in the condition heirarchy probably
()
(:report "no appropriate slot was bound"))
(defgeneric appropriate-slot-name (object &key for)
(:method :around (object &key (for ':read))
(call-next-method object :for for)))
(defmethod appropriate-slot-name ((object toy) &key for)
(let ((found (find-if (lambda (slot)
(slot-boundp object slot))
'(first second))))
(ecase for
((:read)
(unless found
(error 'no-appropriate-slot :name '(first second) :instance object))
found)
((:write)
(or found 'first)))))
И теперь пара функций доступа может быть простой функцией, которая будет работать для любого класса, где есть метод для appropriate-slot-name
:
(defun appropriate-slot-value (object)
(slot-value object (appropriate-slot-name object :for ':read)))
(defun (setf appropriate-slot-value) (new object)
;; set the bound slot, or the first slot
(setf (slot-value object (appropriate-slot-name object :for ':write)) new))
Наконец, теперь у нас могут быть функции, которые просто используют symbol-macrolet
очевидным образом:
(defun foo (something)
(symbol-macrolet ((s (appropriate-slot-value something)))
... s ... (setf s ...) ...))
Итак, это другой подход.