以编程方式生成符号宏
Programmatically generating symbol macros
我有一个由两部分组成的数据结构:
- 哈希table 将符号映射到索引
- 包含数据的向量的向量
例如:
(defparameter *h* (make-hash-table))
(setf (gethash 'a *h*) 0)
(setf (gethash 'b *h*) 1)
(setf (gethash 'c *h*) 2)
(defparameter *v-of-v* #(#(1 2 3 4) ;vector a
#(5 6 7 8) ;vector b
#(9 10 11 12))) ;vector c
我想定义一个符号宏来获取向量 a
而无需通过哈希图。在 REPL:
(define-symbol-macro a (aref *v-of-v* 0))
工作正常:
* a
#(1 2 3 4)
但是可能有很多命名向量,我不知道映射是什么,所以我需要自动化这个过程:
(defun do-all-names ()
(maphash #'(lambda (key index)
(define-symbol-macro key (aref *v-of-v* index)))
*h*))
但这没有任何作用。我尝试过的任何组合都没有将 do-all-names 设为宏、back-quote/comma 模板等。我开始怀疑这是否与 define-symbol-macro
无关本身。这似乎是一个很少使用的功能,On Lisp 只提到了两次。这里和其他地方都没有太多提及。在这种情况下,我使用的是 SBCL 2.1
有人有什么想法吗?
我不太确定你所说的“我不知道提前映射是什么”是什么意思。
你可以这样做:
(macrolet ((define-accessors ()
`(progn
,@(loop for key being the hash-keys of *h*
collect
`(define-symbol-macro ,key (aref *v-of-v* ,(gethash key *h*)))))))
(define-accessors))
如果您知道您不需要全局访问权限,那么,您可以这样做:
(defmacro with-named-vector-accessors (&body body) ; is that the name you want?
`(symbol-macrolet (,@(loop for key being the hash-keys of *h*
collect `(,key (aref *v-of-v* ,(gethash key *h*)))))
,@body))
;;; Example Usage:
(with-named-vector-accessors
(list a b c)) ;=> (#(1 2 3 4) #(5 6 7 8) #(9 10 11 12))
此外,
如果您知道 *h*
并且每个符号在宏扩展时映射到的索引,则上述方法有效。
如果你在宏展开时知道 *h*
但每个符号映射到的索引在宏展开后会发生变化,你会想要收集 (,key (aref *v-of-v* (gethash ,key *h*)))
.
PS:如果您发现 loop
不适合哈希表,您可以使用 iterate
库的语法:
(iter (for (key value) in-hashtable *h*)
(collect `(,key (aref *v-of-v* ,value))))
你需要类似上面的东西在运行时做:
(defun do-all-names ()
(maphash #'(lambda (key index)
(eval `(define-symbol-macro ,key (aref *v-of-v* ,index)))
*h*))
DEFINE-SYMBOL-MACRO
是一个宏,不会评估其所有参数。所以你需要为每个参数对生成一个新的宏形式并对其进行评估。
另一种方法,通常在编译时,是编写一个在顶层生成这些表格的宏:
(progn
(define-symbol-macro a (aref *v-of-v* 0))
(define-symbol-macro b (aref *v-of-v* 1))
; ....
)
我有一个由两部分组成的数据结构:
- 哈希table 将符号映射到索引
- 包含数据的向量的向量
例如:
(defparameter *h* (make-hash-table))
(setf (gethash 'a *h*) 0)
(setf (gethash 'b *h*) 1)
(setf (gethash 'c *h*) 2)
(defparameter *v-of-v* #(#(1 2 3 4) ;vector a
#(5 6 7 8) ;vector b
#(9 10 11 12))) ;vector c
我想定义一个符号宏来获取向量 a
而无需通过哈希图。在 REPL:
(define-symbol-macro a (aref *v-of-v* 0))
工作正常:
* a
#(1 2 3 4)
但是可能有很多命名向量,我不知道映射是什么,所以我需要自动化这个过程:
(defun do-all-names ()
(maphash #'(lambda (key index)
(define-symbol-macro key (aref *v-of-v* index)))
*h*))
但这没有任何作用。我尝试过的任何组合都没有将 do-all-names 设为宏、back-quote/comma 模板等。我开始怀疑这是否与 define-symbol-macro
无关本身。这似乎是一个很少使用的功能,On Lisp 只提到了两次。这里和其他地方都没有太多提及。在这种情况下,我使用的是 SBCL 2.1
有人有什么想法吗?
我不太确定你所说的“我不知道提前映射是什么”是什么意思。
你可以这样做:
(macrolet ((define-accessors ()
`(progn
,@(loop for key being the hash-keys of *h*
collect
`(define-symbol-macro ,key (aref *v-of-v* ,(gethash key *h*)))))))
(define-accessors))
如果您知道您不需要全局访问权限,那么,您可以这样做:
(defmacro with-named-vector-accessors (&body body) ; is that the name you want?
`(symbol-macrolet (,@(loop for key being the hash-keys of *h*
collect `(,key (aref *v-of-v* ,(gethash key *h*)))))
,@body))
;;; Example Usage:
(with-named-vector-accessors
(list a b c)) ;=> (#(1 2 3 4) #(5 6 7 8) #(9 10 11 12))
此外,
如果您知道
*h*
并且每个符号在宏扩展时映射到的索引,则上述方法有效。如果你在宏展开时知道
*h*
但每个符号映射到的索引在宏展开后会发生变化,你会想要收集(,key (aref *v-of-v* (gethash ,key *h*)))
.
PS:如果您发现 loop
不适合哈希表,您可以使用 iterate
库的语法:
(iter (for (key value) in-hashtable *h*)
(collect `(,key (aref *v-of-v* ,value))))
你需要类似上面的东西在运行时做:
(defun do-all-names ()
(maphash #'(lambda (key index)
(eval `(define-symbol-macro ,key (aref *v-of-v* ,index)))
*h*))
DEFINE-SYMBOL-MACRO
是一个宏,不会评估其所有参数。所以你需要为每个参数对生成一个新的宏形式并对其进行评估。
另一种方法,通常在编译时,是编写一个在顶层生成这些表格的宏:
(progn
(define-symbol-macro a (aref *v-of-v* 0))
(define-symbol-macro b (aref *v-of-v* 1))
; ....
)