在CLISP中意外地修改了未编译的lambda函数的文字源? [英] Unexpected modification of literal source of uncompiled lambda functions in CLISP?

查看:77
本文介绍了在CLISP中意外地修改了未编译的lambda函数的文字源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了以下函数

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

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

推荐答案

您正在修改文字数据(然后将该数据用作lambda函数的主体),并且具有不确定的结果。有关更多信息,请参见意外的数据持久性 令人惊讶的是,这是正在发生的事情,但是CLISP似乎在lambda函数中保存了实际的源( lst 的值)(而不是对其进行编译) ,而不是复制它),因此,当您修改代码块时,您会看到在引用每个相同代码块的每个 lambda函数中结果发生了变化。 (让((插槽名称))为nil)列表只有一个实例,该实例将由您创建的所有不同的lambda函数共享。修改单个列表时,使用它的所有lambda函数的 all 都会看到更改。

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)))))
    ; ...

但是,我认为最好处理此问题的方法只是利用Common Lisp的词法闭包,并完全避免与 eval 混淆:

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

虽然结果 look 相同,但功能不同,因为它们捕获了不同的词汇环境,因此它们可以执行应有的功能。例如,看看如果您的主体返回变量的值会发生什么:

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

这篇关于在CLISP中意外地修改了未编译的lambda函数的文字源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆