setq 的 Lisp 作用域问题?
Lisp scope issue with setq?
我是 lisp 菜鸟,才用了两周左右...
我有一些用setq声明的全局变量:
(setq myvar '(WHATEVER))
和一个函数,它应该修改我告诉它的任何变量:
(defun MYFUN (varname)
(setq varname '(POOP))
)
但是当我打电话时:(MYFUN 'myvar)
并检查 myvar
的值,现在它仍然是 (WHATEVER)
如何使在 MYFUN
中所做的更改保持不变?
您正在设置局部变量 varname
的值,而不是其名称包含的全局变量。要执行您想要的操作,您需要使用 symbol-value
访问器间接访问它以获取全局值的变量。
(defun myfun (varname)
(setf (symbol-value varname) '(poop)))
编辑:第一个没注意到这个:
您在函数体的范围内有局部 varname
和全局 varname
。本地名称隐藏全局名称。因此,如果您将本地名称更改为 var
,它应该按照您编写的方式工作(已在 SBCL 1.2.13 中检查)。但请考虑以下文体更正:
- 对于全局变量,请在名称周围使用耳罩
*
,因此应该是*myvar*
。全局变量是特殊的(有一种方法可以使它们正常,但不一定是个好主意)。特殊变量具有 dynamic 作用域,与普通变量的 lexical 作用域相反。
- 变量必须用
defvar
或defparameter
声明。您可以为此使用 setq
但编译器会抱怨该变量未定义。此外,setq
变量不会是特殊的。
- 为了使变量
*myvar*
在函数体内显得特殊,它要么需要在函数定义之前声明(使用 defvar
或 defparameter
),要么需要在函数体中用 (declare (special *myvar*))
声明特殊,然后用 defvar
或 defparameter
. 声明
这里是声明和各自输出的可能组合的代码:
;; This is a model solution:
(defvar *myvar* 'a)
*MYVAR*
(defun foo (var)
(setq *myvar* var))
(foo 'b)
*myvar*
B
;; Not using DEFVAR or DEFPARAMETER
(setq myvar 'a)
A
(defun bar (var)
(setq myvar var))
;; The value of the global MYVAR is still changed
(bar 'b)
myvar
B
(defun show-myvar ()
myvar)
;; But MYVAR is not special
(let ((myvar 'z))
(show-myvar))
B
;; Also can assign value to undeclared variable
(defun bar2 (var)
(setq myvar-1 var))
BAR2
(setq myvar-1 'a)
A
;; And it works
(bar2 'b)
myvar-1
B
;; Finally: show special undeclared (yet) variable
(defun show-special ()
(declare (special *special-var*))
*special-var*)
(defvar *special-var* 'a)
*SPECIAL-VAR*
(let ((*special-var* 'z))
(show-special))
Z
;; The same but with SETQ: variable is still not special
(defun show-special-setq ()
(declare (special *special-var-setq*))
*special-var-setq*)
(setq *special-var-setq* 'a)
A
(let ((*special-var-setq* 'z))
(show-special-setq))
A
没有 "declaring a global variable with setq" 这样的东西,只有 "set the value of a variable with setq",如果你在顶级词法环境中这样做,结果会很有趣。
如果您查看变量 varname
包含的内容,它很可能是列表 (poop)
.
另外,setq
末尾的"q"实际上表示"quoted"(也就是说,setq
特殊形式不会计算第一个(和第三个,和第五个...) 参数,但会为第二个(第四个和第六个...)这样做。
从历史上看,它被用作一种便利,其中 (set (quote var) value)
不如 (setq var value)
方便。但是,(set var value)
与 (setf (symbol-value var) value)
具有完全相同的效果,您应该使用它。
我是 lisp 菜鸟,才用了两周左右...
我有一些用setq声明的全局变量:
(setq myvar '(WHATEVER))
和一个函数,它应该修改我告诉它的任何变量:
(defun MYFUN (varname)
(setq varname '(POOP))
)
但是当我打电话时:(MYFUN 'myvar)
并检查 myvar
的值,现在它仍然是 (WHATEVER)
如何使在 MYFUN
中所做的更改保持不变?
您正在设置局部变量 varname
的值,而不是其名称包含的全局变量。要执行您想要的操作,您需要使用 symbol-value
访问器间接访问它以获取全局值的变量。
(defun myfun (varname)
(setf (symbol-value varname) '(poop)))
编辑:第一个没注意到这个:
您在函数体的范围内有局部 varname
和全局 varname
。本地名称隐藏全局名称。因此,如果您将本地名称更改为 var
,它应该按照您编写的方式工作(已在 SBCL 1.2.13 中检查)。但请考虑以下文体更正:
- 对于全局变量,请在名称周围使用耳罩
*
,因此应该是*myvar*
。全局变量是特殊的(有一种方法可以使它们正常,但不一定是个好主意)。特殊变量具有 dynamic 作用域,与普通变量的 lexical 作用域相反。 - 变量必须用
defvar
或defparameter
声明。您可以为此使用setq
但编译器会抱怨该变量未定义。此外,setq
变量不会是特殊的。 - 为了使变量
*myvar*
在函数体内显得特殊,它要么需要在函数定义之前声明(使用defvar
或defparameter
),要么需要在函数体中用(declare (special *myvar*))
声明特殊,然后用defvar
或defparameter
. 声明
这里是声明和各自输出的可能组合的代码:
;; This is a model solution:
(defvar *myvar* 'a)
*MYVAR*
(defun foo (var)
(setq *myvar* var))
(foo 'b)
*myvar*
B
;; Not using DEFVAR or DEFPARAMETER
(setq myvar 'a)
A
(defun bar (var)
(setq myvar var))
;; The value of the global MYVAR is still changed
(bar 'b)
myvar
B
(defun show-myvar ()
myvar)
;; But MYVAR is not special
(let ((myvar 'z))
(show-myvar))
B
;; Also can assign value to undeclared variable
(defun bar2 (var)
(setq myvar-1 var))
BAR2
(setq myvar-1 'a)
A
;; And it works
(bar2 'b)
myvar-1
B
;; Finally: show special undeclared (yet) variable
(defun show-special ()
(declare (special *special-var*))
*special-var*)
(defvar *special-var* 'a)
*SPECIAL-VAR*
(let ((*special-var* 'z))
(show-special))
Z
;; The same but with SETQ: variable is still not special
(defun show-special-setq ()
(declare (special *special-var-setq*))
*special-var-setq*)
(setq *special-var-setq* 'a)
A
(let ((*special-var-setq* 'z))
(show-special-setq))
A
没有 "declaring a global variable with setq" 这样的东西,只有 "set the value of a variable with setq",如果你在顶级词法环境中这样做,结果会很有趣。
如果您查看变量 varname
包含的内容,它很可能是列表 (poop)
.
另外,setq
末尾的"q"实际上表示"quoted"(也就是说,setq
特殊形式不会计算第一个(和第三个,和第五个...) 参数,但会为第二个(第四个和第六个...)这样做。
从历史上看,它被用作一种便利,其中 (set (quote var) value)
不如 (setq var value)
方便。但是,(set var value)
与 (setf (symbol-value var) value)
具有完全相同的效果,您应该使用它。