0
我試圖定義一個宏,該結構將在結構中使用結構的名稱,鍵和哈希表的名稱,並定義函數以訪問和修改鍵下的值哈希值。動態定義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-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)
然後
(tester-x too)
返回3 T
,因爲它應該,但
(setf (tester-x too) 5)
給出了錯誤:
The function (COMMON-LISP:SETF COMMON-LISP-USER::TESTER-X) is undefined.
[Condition of type UNDEFINED-FUNCTION]
(macroexpand-1 '(make-hash-accessor tester x hash))
擴展到
(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。我究竟做錯了什麼?
命名宏DEFINE-HASH-ACCESSOR可能會更清晰,因爲它定義了函數,而不是返回它們。也許還可以將'ACCESSOR-NAME'移到本地函數,所以不需要擔心它在編譯時可用。 – jkiiski
@jkiiski:你說得對,我試圖保留OP的標記,但我會編輯。 – sds