Как выполнить функцию на заданном месте
У меня есть список, который содержит некоторые символы и значения. Цель состоит в том, чтобы установить слот класса с помощью средства доступа, символ которого представлен в списке:
(defclass my-class ()
((attr :accessor attr)))
(let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(setf `(,(car to-call) obj) (cadr to-call)))
Я пробовал через макрос:
(defmacro call-accessor (to-call)
`(setf (,(car to-call) obj) "some-value"))
(let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(call-accessor to-call))
Что тоже не получается, так как to-call
это символ, а не список.
eval
не работает сto-call
является лексической переменной;- Это не возможно сделать
let
над макросом, чтобы дать ему список; - Я пробовал с
with-slots
а такжеwith-accessors
но проблема остается той же, потому что они тоже макросы. - Я рассмотрел макросы, которые объявляют другой макрос, и symbol-macrolet тоже.
Как я могу установить слот через аксессор, соответствующий символу в моем списке?
Спасибо.
3 ответа
Вызов функции доступа
Цель состоит в том, чтобы установить слот класса с помощью аксессора
Аксессор - это пара функций. Вы можете получить часть, которая устанавливает значение через FDEFINITION
, Название функции представляет собой список (SETF accessor-name )
, Это необычно: Common Lisp имеет в этом случае имена функций, которые не являются символами, а списками.
CL-USER 14 > (let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(funcall (fdefinition `(setf ,(first to-call)))
(second to-call)
obj)
(describe obj))
#<MY-CLASS 40200614FB> is a MY-CLASS
ATTR "some-value"
Используя функцию call-accessor
:
CL-USER 25 > (let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(flet ((call-accessor (obj to-call)
(funcall (fdefinition `(setf ,(first to-call)))
(second to-call)
obj)))
(call-accessor obj to-call)
(describe obj)))
#<MY-CLASS 402000220B> is a MY-CLASS
ATTR "some-value"
используя SETF с APPLY, чтобы скрыть вызов FDEFINITION
Использовать setf
с вычисленным средством доступа, возможно, потребуется использовать apply
Форма и пользовательская функция.
Что-то вроде call-accessor
естественно, будет функцией, потому что она выполняет поиск во время выполнения и принимает значения. Попытка использовать макрос будет более полезной, если метод доступа будет известен во время компиляции.
CL-USER 23 > (let ((to-call '(attr "some-value"))
(obj (make-instance 'my-class)))
(flet (((setf my-setter) (new-value object accessor)
(funcall (fdefinition `(setf ,accessor))
new-value
obj)))
(flet ((call-accessor (obj to-call)
(setf (apply #'my-setter obj (list (first to-call)))
(second to-call))))
(call-accessor obj to-call)
(describe obj))))
#<MY-CLASS 40200009AB> is a MY-CLASS
ATTR "some-value"
Выбор структуры данных
Я думаю, что все в порядке, чтобы вычислить функции доступа и тому подобное. Там могут быть случаи использования для этого. CLOS был разработан, чтобы быть динамичным и отражающим, чтобы позволить эти вещи.
Ты можешь использовать setf
из slot-value
, используя символ имени слота в вашей паре данных:
(let ((to-call '(attr "some-value")))
(setf (slot-value obj (first to-call)) (second to-call)))
Однако, используя slot-value
напрямую обычно имеет смысл только тогда, когда вы спорите с внутренними объектами (такими как методы / комбинации инициализирующего экземпляра; или, возможно, вы работаете над каким-то механизмом сериализации).
Если это не так, вы используете объект просто как ассоциативную структуру данных. Я бы предложил вместо этого использовать alist, plist или hash-map.
Я согласен, что альтернативная структура, такая как hash-table
вероятно, следует использовать. Но чтобы расширить ответ Сванте относительно slot-value
, рабочий код просто:
(setf (slot-value obj 'attr) "some-value")