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 中检查)。但请考虑以下文体更正:

  1. 对于全局变量,请在名称周围使用耳罩*,因此应该是*myvar*。全局变量是特殊的(有一种方法可以使它们正常,但不一定是个好主意)。特殊变量具有 dynamic 作用域,与普通变量的 lexical 作用域相反。
  2. 变量必须用defvardefparameter声明。您可以为此使用 setq 但编译器会抱怨该变量未定义。此外,setq 变量不会是特殊的。
  3. 为了使变量 *myvar* 在函数体内显得特殊,它要么需要在函数定义之前声明(使用 defvardefparameter),要么需要在函数体中用 (declare (special *myvar*)) 声明特殊,然后用 defvardefparameter.
  4. 声明

这里是声明和各自输出的可能组合的代码:

;; 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) 具有完全相同的效果,您应该使用它。