如果我创建这样的闭包,
(let ((A (make-array '(10) :initial-element 5)))
(defun h (i)
(aref a i))
(defsetf h (i) (x) `(setf (aref ,a ,i) ,x)))
然后,正如我所料,(h i)
将返回a
的第i个元素:
(h 1) ;; => 5
(h 2) ;; => 5
虽然setf
扩展部分可以正常工作并正确设置a
的第i个元素,但它也会在SBCL中产生警告:
(setf (h 1) 10)
; in: SETF (H 1)
; (SETF (AREF #(5 10 5 5 5 5 5 5 5 5) 1) #:G1124)
; --> LET* MULTIPLE-VALUE-BIND LET FUNCALL SB-C::%FUNCALL
; ==>
; ((SETF AREF) #:NEW0 #(5 10 5 5 5 5 5 5 5 5) 1)
;
; caught WARNING:
; Destructive function (SETF AREF) called on constant data.
; See also:
; The ANSI Standard, Special Operator QUOTE
; The ANSI Standard, Section 3.2.2.3
;
; compilation unit finished
; caught 1 WARNING condition
在GCL中发出错误信号:
>(setf (h 1) 10)
Error:
Fast links are on: do (si::use-fast-links nil) for debugging
Signalled by LAMBDA-CLOSURE.
Condition in LAMBDA-CLOSURE [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on A: Unbound variable:
Broken at LIST. Type :H for Help.
1 Return to top level.
在CLISP和ECL中,该示例工作正常。
我在编写Scheme几年后回到Common Lisp,所以我可能在概念上混合使用这两种语言。我想我已根据规范触发了未定义的行为,但我无法确切地看到我做错了什么。我很感激任何帮助!
答案 0 :(得分:2)
尝试macroexpand
通常很有启发性:
(macroexpand '(setf (h 2) 7))
==>
(LET* ()
(MULTIPLE-VALUE-BIND (#:G655)
7
(SETF (AREF #(5 5 5 5 5 5 5 5 5 5) 2) #:G655)))
正如您所看到的,您的setf
调用会扩展为一个在文字数组上调用setf
的表单,这通常是一个坏主意,事实上,这正是SBCL警告您的约:
Destructive function (SETF AREF) called on constant data.
请注意,尽管警告SBCL(以及CLISP和ECL等其他符合要求的实现)仍会按照您的预期执行操作。
这是因为文字数组由函数h
可访问的局部变量引用。
我建议您改用函数
(let ((A (make-array '(10) :initial-element 5)))
(defun h (i)
(aref a i))
(defun (setf h) (x i)
(setf (aref a i) x)))