当前位置: 动力学知识库 > 问答 > 编程问答 >

common lisp - Unexpected modification of literal source of uncompiled lambda functions in CLISP?

问题描述:

I wrote the following function

(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))))

If I run (test 'a), the result is (on CLISP) #<FUNCTION :LAMBDA NIL (LET ((SLOT A)) NIL)>; if I run (test 'b) the result is #<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>.

And, until here, nothing strange. But when I run (fdefinition 'a) I get #<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>; and if I run (fdefinition 'b) I get #<FUNCTION :LAMBDA NIL (LET ((SLOT B)) NIL)>. Isn't it strange? Shouldn't it be #<FUNCTION :LAMBDA NIL (LET ((SLOT A)) NIL)> the answer to (fdefinition 'a)?

网友答案:

You're modifying literal data (and then using that data as body of the body of a lambda function), and that has undefined consequences. See Unexpected persistence of data for more about that. It is a bit surprising that this is what's happening, but it appears that CLISP is saving the actual source (the value of lst) in the lambda function (rather than compiling it, and not copying it) so when you modify the code block, you see the results change in every lambda function that references the same code block. There's only one instance of the list (let ((slot name)) nil), and it is being shared by all the different lambda functions you're creating. When you modify that single list, all of the lambda functions that use it see the change.

The simplest thing (i.e., the smallest change to your code, but not necessarily the best solution) that you could to do get the results that you want would be to use copy-tree to make a fresh code block:

  (let ((lst (copy-tree (list 'lambda '()
                              '(let ((slot name))
                                nil)))))
    ; ...

However, I think that the best way to handle this would just be to make use of Common Lisp's lexical closures and to avoid the mess with eval at all:

(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)>

While the results look the same, the functions are different because they capture different lexical environments, and so they do what they are supposed to. E.g., look at what happens if you have the body return the value of the variable:

(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)
B
分享给朋友:
您可能感兴趣的文章:
随机阅读: