我编写了以下函数
(defun test (name)
(let ((lst (list 'lambda '()
'(let ((slot name))
nil))))
(setf (car (cdr (car (car (cdr (car (cdr (cdr lst)))))))) name)
(let ((anonymous-function (eval lst)))
(setf (fdefinition name) anonymous-function))))如果运行(test 'a),结果是(在CLISP上) #<FUNCTION :LAMBDA NIL (LET ((SLOT A)) NIL)>;如果运行(test 'b),则结果是#<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>。在这里之前没什么奇怪的。但是当我运行(fdefinition 'a)时,我得到了#<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>;如果运行(fdefinition 'b),则得到了#<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>。是不是很奇怪?难道不是#<FUNCTION :LAMBDA NIL (LET ((SLOT A)) NIL)>是(fdefinition 'a)的答案吗?
发布于 2015-01-19 22:07:51
您正在修改文字数据(然后使用该数据作为lambda函数体的主体),这会产生未定义的结果。有关这方面的更多信息,请参见Unexpected persistence of data。这种情况有点令人惊讶,但似乎CLISP正在将实际的源(lst的值)保存在lambda函数中(而不是编译它,而不是复制它),所以当您修改代码块时,您会看到引用相同代码块的每个lambda函数的结果都会发生变化。列表只有一个实例(let((时隙名))),您正在创建的所有不同的lambda函数都共享它。当您修改单个列表时,所有使用它的lambda函数都会看到更改。
最简单的事情(即对代码的最小更改,但不一定是最好的解决方案)是使用copy-tree生成一个新的代码块:
(let ((lst (copy-tree (list 'lambda '()
'(let ((slot name))
nil)))))
; ...但是,我认为处理这一问题的最好方法是使用Common的词汇闭包,并完全避免使用eval:
(defun test (name)
(let ((f (lambda ()
(let ((slot name))
nil))))
(setf (fdefinition name) f)))
CL-USER> (test 'a)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) NIL)>
CL-USER> (test 'b)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) NIL)>虽然结果看起来是一样的,但是函数是不同的,因为它们捕获了不同的词汇环境,所以他们做他们应该做的事情。例如,看看如果主体返回变量的值会发生什么:
(defun test (name)
(let ((f (lambda ()
(let ((slot name))
slot))))
(setf (fdefinition name) f)))
CL-USER> (test 'a)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) SLOT)>
CL-USER> (a)
A
CL-USER> (test 'b)
#<FUNCTION :LAMBDA NIL (LET ((SLOT NAME)) SLOT)>
CL-USER> (b)
Bhttps://stackoverflow.com/questions/28034163
复制相似问题