在 Common Lisp 中,编写定义 class 的宏的最佳方法是什么?
In Common Lisp, what's the best way to write a macro that defines a class?
我正在尝试用 Common Lisp 编写一个宏,它定义了一个 class 和我指定的变量槽。到目前为止,一切正常(clisp 给我留下了深刻的印象!)为此:
(defmacro notifier (class slot)
"Defines a setf method in (class) for (slot) which calls the object's changed method."
`(defmethod (setf ,slot) (val (item ,class))
(setf (slot-value item ',slot) val)
(changed item ',slot)))
(defmacro notifiers (class slots)
"Defines setf methods in (class) for all of (slots) which call the object's changed method."
`(progn
,@(loop for s in slots collecting `(notifier ,class ,s))))
(defmacro defclass-notifier-slots (class nslots slots)
"Defines a class with (nslots) giving a list of slots created with notifiers, and (slots) giving a list of slots created with regular accessors."
`(progn
(defclass ,class ()
( ,@(loop for s in nslots collecting `(,s :reader ,s))
,@(loop for s in slots collecting `(,s :accessor ,s))))
(notifiers ,class ,nslots)))
问题是,现在我想创建的不只是 只是 我在调用宏时指定的插槽,而是其他一些具有变体名称的插槽。为了做到这一点,我必须使用一个丑陋的 "symbol-name, alter string, intern" 序列来生成变体名称作为插槽名称,而且我已经看到关于 SO 的答案说你应该避免这样做。那么有没有更好的方法呢?
在宏或其辅助函数之一中构造新符号没有错。
每当您多次需要某个东西或需要以某种方式记录它时,请编写一个函数。
由于我们需要使用可能是新的符号,因此确保符号位于正确的包中是有意义的。这里我们假设prefix符号的包就是正确的包。
(defun make-suffix-symbol (prefix suffix)
(check-type prefix symbol)
(check-type suffix (or string symbol))
(when (symbolp suffix)
(setf suffix (symbol-name suffix)))
(intern (concatenate 'string
(symbol-name prefix)
suffix)
(symbol-package prefix)))
CL-USER 12 > (make-suffix-symbol 'http-user::foo "BAR")
HTTP-USER::FOOBAR
或使用FORMAT
:
(defun make-suffix-symbol (prefix suffix &optional (format-string "~A~A"))
(check-type prefix symbol)
(check-type suffix (or string symbol))
(when (symbolp suffix)
(setf suffix (symbol-name suffix)))
(intern (format nil
format-string
(symbol-name prefix)
suffix)
(symbol-package prefix)))
CL-USER 14 > (make-suffix-symbol 'http-user::foo "BAR" "~a-~a")
HTTP-USER::FOO-BAR
我正在尝试用 Common Lisp 编写一个宏,它定义了一个 class 和我指定的变量槽。到目前为止,一切正常(clisp 给我留下了深刻的印象!)为此:
(defmacro notifier (class slot)
"Defines a setf method in (class) for (slot) which calls the object's changed method."
`(defmethod (setf ,slot) (val (item ,class))
(setf (slot-value item ',slot) val)
(changed item ',slot)))
(defmacro notifiers (class slots)
"Defines setf methods in (class) for all of (slots) which call the object's changed method."
`(progn
,@(loop for s in slots collecting `(notifier ,class ,s))))
(defmacro defclass-notifier-slots (class nslots slots)
"Defines a class with (nslots) giving a list of slots created with notifiers, and (slots) giving a list of slots created with regular accessors."
`(progn
(defclass ,class ()
( ,@(loop for s in nslots collecting `(,s :reader ,s))
,@(loop for s in slots collecting `(,s :accessor ,s))))
(notifiers ,class ,nslots)))
问题是,现在我想创建的不只是 只是 我在调用宏时指定的插槽,而是其他一些具有变体名称的插槽。为了做到这一点,我必须使用一个丑陋的 "symbol-name, alter string, intern" 序列来生成变体名称作为插槽名称,而且我已经看到关于 SO 的答案说你应该避免这样做。那么有没有更好的方法呢?
在宏或其辅助函数之一中构造新符号没有错。
每当您多次需要某个东西或需要以某种方式记录它时,请编写一个函数。
由于我们需要使用可能是新的符号,因此确保符号位于正确的包中是有意义的。这里我们假设prefix符号的包就是正确的包。
(defun make-suffix-symbol (prefix suffix)
(check-type prefix symbol)
(check-type suffix (or string symbol))
(when (symbolp suffix)
(setf suffix (symbol-name suffix)))
(intern (concatenate 'string
(symbol-name prefix)
suffix)
(symbol-package prefix)))
CL-USER 12 > (make-suffix-symbol 'http-user::foo "BAR")
HTTP-USER::FOOBAR
或使用FORMAT
:
(defun make-suffix-symbol (prefix suffix &optional (format-string "~A~A"))
(check-type prefix symbol)
(check-type suffix (or string symbol))
(when (symbolp suffix)
(setf suffix (symbol-name suffix)))
(intern (format nil
format-string
(symbol-name prefix)
suffix)
(symbol-package prefix)))
CL-USER 14 > (make-suffix-symbol 'http-user::foo "BAR" "~a-~a")
HTTP-USER::FOO-BAR