Я думаю, что, возможно, неправильно понял первоначальный замысел. Сначала я подумал, что вы спрашиваете, как сгенерировать имена средств доступа для определения класса, к которому относится третья часть ответа. После прочтения во второй раз на самом деле звучит так, будто вы хотите сгенерировать новый символ и вызвать его с некоторым аргументом. Это тоже достаточно просто, и дается во второй части этого ответа. И вторая, и третья части зависят от возможности создать символ с именем, составленным из имен других символов, и с этого мы и начнем.
«Объединение» символов
У каждого символа есть имя (строка), которое можно получить с помощью symbol-name< /а>. Вы можете использовать конкатенацию, чтобы создать новую строку из некоторых старых строк, а затем используйте intern, чтобы получить символ с новым именем.
(intern (concatenate 'string
(symbol-name 'person)
"-"
(symbol-name 'name)))
;=> PERSON-NAME
Реконструкция имени аксессора
(defmacro gets (class-name slot-name object)
(let ((accessor-name
(intern (concatenate 'string
(symbol-name class-name)
"-"
(symbol-name slot-name))
(symbol-package class-name))))
`(,accessor-name ,object)))
(macroexpand-1 '(gets person name some-person))
;=> (PERSON-NAME SOME-PERSON)
Однако по ряду причин это не очень надежно. (i) Вы не знаете, есть ли у слота метод доступа вида <class-name>-<slot-name>
. (ii) Даже если у слота есть аксессор вида <class-name>-<slot-name>
, вы не знаете, в каком пакете он находится. В коде выше я сделал разумное предположение, что это то же самое, что и пакет имени класса, но это не так. совсем не требуется. Вы могли бы, например:
(defclass a:person ()
((b:name :accessor c:person-name)))
и тогда этот подход не будет работать вообще. (iii) Это не очень хорошо работает с наследованием. Если вы создадите подкласс person
, скажем, с north-american-person
, то вы все равно сможете вызвать person-name
с помощью north-american-person
, но вы не сможете вызвать north-american-person-name
ни с чем. (iv) Это похоже на новое изобретение slot-value. Вы уже можете получить доступ к значению слота, используя только имя слота с помощью (slot-value object slot-name)
, и я не вижу причин, по которым ваш макрос gets
не должен просто расширяться до этого. Там вам не придется беспокоиться о конкретном имени средства доступа (если оно вообще есть) или о пакете имени класса, а только о фактическом имени слота.
Генерация имен доступа
Вам просто нужно извлечь имена символов и сгенерировать новый символ с нужным именем.
Если вы хотите автоматически генерировать методы доступа с именами в стиле defstruct, вы можете сделать это следующим образом:
(defmacro define-class (name direct-superclasses slots &rest options)
(flet ((%slot (slot)
(destructuring-bind (slot-name &rest options)
(if (listp slot) slot (list slot))
`(,slot-name ,@options :accessor ,(intern (concatenate 'string
(symbol-name name)
"-"
(symbol-name slot-name)))))))
`(defclass ,name ,direct-superclasses
,(mapcar #'%slot slots)
,@options)))
Вы можете убедиться в том, что это производит именно тот код, который вы ожидаете, посмотрев на макрорасширение:
(pprint (macroexpand-1 '(define-class person ()
((name :type string :initarg :name)
(age :type integer :initarg :age)
home))))
(DEFCLASS PERSON NIL
((NAME :TYPE STRING :INITARG :NAME :ACCESSOR PERSON-NAME)
(AGE :TYPE INTEGER :INITARG :AGE :ACCESSOR PERSON-AGE)
(HOME :ACCESSOR PERSON-HOME)))
И мы видим, что он работает так, как ожидалось:
(define-class person ()
((name :type string :initarg :name)
(age :type integer :initarg :age)
home))
(person-name (make-instance 'person :name "John"))
;=> "John"
Другие комментарии к вашему коду
(defmacro object (class &rest args)
`(make-instance ',class ,@args))
Как указал Райнер, это не очень полезный. В большинстве случаев это то же самое, что и
(defun object (class &rest args)
(apply 'make-instance class args))
за исключением того, что вы можете (funcall #'object …)
и (apply #'object …)
с функцией, но не можете с макросом.
Ваш макрос gets не более полезен, чем slot-value , который принимает объект и имя слота. Для него не требуется имя класса, и он будет работать, даже если у класса нет модуля чтения или доступа.
Не создавайте (наивно) имена символов с format
Я создавал имена символов с конкатенацией и именем символа. Иногда вы увидите, что люди используют формат для создания имен, например, (format nil "~A-~A" 'person 'name)
, но это может привести к проблемам с настройками заглавных букв, которые можно изменить. Например, ниже мы определяем функцию foo-bar и отмечаем, что подход, основанный на формате, не работает, но подход, основанный на конкатенации, работает.
CL-USER> (defun foo-bar ()
(print 'hello))
FOO-BAR
CL-USER> (foo-bar)
HELLO
HELLO
CL-USER> (setf *print-case* :capitalize)
:Capitalize
CL-USER> (funcall (intern (concatenate 'string (symbol-name 'foo) "-" (symbol-name 'bar))))
Hello
Hello
CL-USER> (format nil "~a-~a" 'foo 'bar)
"Foo-Bar"
CL-USER> (intern (format nil "~a-~a" 'foo 'bar))
|Foo-Bar|
Nil
CL-USER> (funcall (intern (format nil "~a-~a" 'foo 'bar)))
; Evaluation aborted on #<Undefined-Function Foo-Bar {1002BF8AF1}>.
Проблема здесь в том, что мы не сохраняем регистр имен символов аргументов. Чтобы сохранить случай, нам нужно явно извлечь имена символов, а не позволять функциям печати сопоставлять имя символа с какой-либо другой строкой. Чтобы проиллюстрировать проблему, рассмотрим:
CL-USER> (setf (readtable-case *readtable*) :preserve)
PRESERVE
;; The symbol-names of foo and bar are "foo" and "bar", but
;; you're upcasing them, so you end up with the name "FOO-BAR".
CL-USER> (FORMAT NIL "~{~A~^-~}" (MAPCAR 'STRING-UPCASE '(foo bar)))
"FOO-BAR"
;; If you just concatenate their symbol-names, though, you
;; end up with "foo-bar".
CL-USER> (CONCATENATE 'STRING (SYMBOL-NAME 'foo) "-" (SYMBOL-NAME 'bar))
"foo-bar"
;; You can map symbol-name instead of string-upcase, though, and
;; then you'll get the desired result, "foo-bar"
CL-USER> (FORMAT NIL "~{~A~^-~}" (MAPCAR 'SYMBOL-NAME '(foo bar)))
"foo-bar"
person
Joshua Taylor
schedule
26.06.2014
object
? Для меня это не имеет никакого смысла. Макросgets
тоже не имеет никакого смысла, ИМХО. - person Rainer Joswig   schedule 26.06.2014