Macros 动态定义setf扩展器

Macros 动态定义setf扩展器,macros,common-lisp,symbols,dynamically-generated,setf,Macros,Common Lisp,Symbols,Dynamically Generated,Setf,我试图定义一个宏,它将接受结构的名称、键和结构中哈希表的名称,并定义函数来访问和修改哈希表中键下的值 (defmacro make-hash-accessor (struct-name key hash) (let ((key-accessor (gensym)) (hash-accessor (gensym))) `(let ((,key-accessor (accessor-name ,struct-name ,key)) (,hash-

我试图定义一个宏,它将接受结构的名称、键和结构中哈希表的名称,并定义函数来访问和修改哈希表中键下的值

(defmacro make-hash-accessor (struct-name key hash)
  (let ((key-accessor  (gensym))
        (hash-accessor (gensym)))
    `(let ((,key-accessor  (accessor-name ,struct-name ,key))
           (,hash-accessor (accessor-name ,struct-name ,hash)))
       (setf (fdefinition ,key-accessor) ; reads
             (lambda (instance)
               (gethash ',key
                (funcall ,hash-accessor instance))))
       (setf (fdefinition '(setf ,key-accessor)) ; modifies
             (lambda (instance to-value)
               (setf (gethash ',key
                      (funcall ,hash-accessor instance))
                 to-value))))))

;; Returns the symbol that would be the name of an accessor for a struct's slot
(defmacro accessor-name (struct-name slot)
  `(intern
    (concatenate 'string (symbol-name ',struct-name) "-" (symbol-name ',slot))))
为了测试这一点,我有:

(defstruct tester
  (hash (make-hash-table)))

(defvar too (make-tester))
(setf (gethash 'x (tester-hash too)) 3)
当我跑的时候

(make-hash-accessor tester x hash)
然后

它应该返回
3t
,但是

(setf (tester-x too) 5)
给出了错误:

The function (COMMON-LISP:SETF COMMON-LISP-USER::TESTER-X) is undefined.
   [Condition of type UNDEFINED-FUNCTION]
(宏扩展-1'(使哈希访问器测试仪x哈希))
扩展为

(LET ((#:G690 (ACCESSOR-NAME TESTER X)) (#:G691 (ACCESSOR-NAME TESTER HASH)))
  (SETF (FDEFINITION #:G690)
        (LAMBDA (INSTANCE) (GETHASH 'X (FUNCALL #:G691 INSTANCE))))
  (SETF (FDEFINITION '(SETF #:G690))
        (LAMBDA (INSTANCE TO-VALUE)
          (SETF (GETHASH 'X (FUNCALL #:G691 INSTANCE)) TO-VALUE))))
T
我用的是SBCL。我做错了什么?

您应该尽可能使用。 具体来说,这里不是针对
访问者名称,而是针对您的访问者:

(定义散列访问器(结构名称键散列)
(flet)(混凝土符号(s1和s2)
(内部(连接“字符串(符号名称s1)”-“(符号名称s2()())))
(let((散列键(concat符号结构名称键))
(获取哈希(concat符号结构名称哈希)))
`(项目
(defun,散列键(实例)
(gethash',key(,get hash实例)))
(defun(setf,散列键)(到值实例)
(setf(gethash',key(,get hash instance))到value)
“,哈希键)))
(defstruct测试仪
(哈希(生成哈希表)))
(也是defvar(制造测试仪))
(setf(gethash'x(测试仪哈希也是))3)
也
==>#S(TESTER:HASH#S(HASH-TABLE:testfasthash-EQL(X.3)))
(定义哈希访问器测试仪x哈希)
==>测试仪-x
(测试仪-x也适用)
==> 7; T
(setf(测试仪-x也)5)
也
==>#S(测试者:HASH#S(HASH-TABLE:testfasthash-EQL(X.5)))
请注意,我对宏使用了一个更为传统的名称:因为它定义了accessorts,所以通常将其命名为
define-…
(参见)。
make-…
通常用于返回对象的函数(参见)

另见 记住,无论是在缩进还是在命名变量、函数和宏时,样式都很重要。

您应该尽可能使用。 具体来说,这里不是针对
访问者名称,而是针对您的访问者:

(定义散列访问器(结构名称键散列)
(flet)(混凝土符号(s1和s2)
(内部(连接“字符串(符号名称s1)”-“(符号名称s2()())))
(let((散列键(concat符号结构名称键))
(获取哈希(concat符号结构名称哈希)))
`(项目
(defun,散列键(实例)
(gethash',key(,get hash实例)))
(defun(setf,散列键)(到值实例)
(setf(gethash',key(,get hash instance))到value)
“,哈希键)))
(defstruct测试仪
(哈希(生成哈希表)))
(也是defvar(制造测试仪))
(setf(gethash'x(测试仪哈希也是))3)
也
==>#S(TESTER:HASH#S(HASH-TABLE:testfasthash-EQL(X.3)))
(定义哈希访问器测试仪x哈希)
==>测试仪-x
(测试仪-x也适用)
==> 7; T
(setf(测试仪-x也)5)
也
==>#S(测试者:HASH#S(HASH-TABLE:testfasthash-EQL(X.5)))
请注意,我对宏使用了一个更为传统的名称:因为它定义了accessorts,所以通常将其命名为
define-…
(参见)。
make-…
通常用于返回对象的函数(参见)

另见
请记住,无论是在缩进中还是在命名变量、函数和宏时,样式都很重要。

命名宏可能更清晰,因为它是定义函数,而不是返回函数。也可以将
ACCESSOR-NAME
移动到本地函数,这样就不必担心它在编译时可用了。@jkiiski:没错,我尝试使用OP的符号,但我会编辑。命名宏
DEFINE-HASH-ACCESSOR
可能更清晰,因为它是定义函数,而不是返回它们。也可以将
ACCESSOR-NAME
移动到本地函数,这样就不必担心它在编译时可用了。@jkiiski:你说得对,我试图使用OP的符号,但我会编辑它。
(LET ((#:G690 (ACCESSOR-NAME TESTER X)) (#:G691 (ACCESSOR-NAME TESTER HASH)))
  (SETF (FDEFINITION #:G690)
        (LAMBDA (INSTANCE) (GETHASH 'X (FUNCALL #:G691 INSTANCE))))
  (SETF (FDEFINITION '(SETF #:G690))
        (LAMBDA (INSTANCE TO-VALUE)
          (SETF (GETHASH 'X (FUNCALL #:G691 INSTANCE)) TO-VALUE))))
T