setq和defvar在lisp
我看到Practical Common Lisp使用(defvar * db * nil)来设置全局variables。 使用setq出于同样的目的不行吗?
使用defvar vs setq有什么优点/缺点?
有几种方法可以引入variables。
DEFVAR和DEFPARAMETER引入全局dynamicvariables。 DEFVAR
可选地将其设置为某个值,除非它已经被定义。 DEFPARAMETER
将其始终设置为提供的值。 SETQ不会引入variables。
(defparameter *number-of-processes* 10) (defvar *world* (make-world)) ; the world is made only once.
请注意,你可能永远不想DEFVAR
variables与像x
, y
, stream
, limit
,…为什么? 因为这些variables会被宣布为特殊的,而且难以撤销。 特殊声明是全局的,variables的所有进一步使用都将使用dynamic绑定。
坏:
(defvar x 10) ; global special variable X, naming convention violated (defvar y 20) ; global special variable Y, naming convention violated (defun foo () (+ xy)) ; refers to special variables X and y (defun bar (xy) ; OOPS!! X and Y are special variables ; even though they are parameters of a function! (+ (foo) xy)) (bar 5 7) ; -> 24
更好:总是用特殊variables名称*
标记!
(defvar *x* 10) ; global special variable *X* (defvar *y* 20) ; global special variable *Y* (defun foo () (+ *x* *y*)) ; refers to special variables X and y (defun bar (xy) ; Yep! X and Y are lexical variables (+ (foo) xy)) (bar 5 7) ; -> 42
局部variables引入了DEFUN , LAMBDA , LET , MULTIPLE-VALUE-BIND等等。
(defun foo (i-am-a-local-variable) (print i-am-a-local-variable)) (let ((i-am-also-a-local-variable 'hehe)) (print i-am-also-a-local-variable))
现在,默认情况下,以上两种forms的局部variables都是词法的,除非声明为SPECIAL 。 那么他们将是dynamicvariables。
接下来,还有几种forms将variables设置为新的值。 SET , SETQ , SETF等等。 SETQ
和SETF
可以设置词法和特殊(dynamic)variables。
可移植代码需要设置已经声明的variables。 设置未声明的variables的确切效果是由标准未定义的。
所以,如果你知道你的Common Lisp实现,你可以使用
(setq world (make-new-world))
在高级的Read-Eval-Print-Loop中。 但是不要在你的代码中使用它,因为这个效果是不可移植的。 通常SETQ
将设置variables。 但是一些实现可能会在不知道的时候声明variablesSPECIAL (CMU Common Lisp默认是这样做的)。 这几乎总是不是人们想要的。 如果您知道自己做了什么,可将其用于临时使用,但不能用于代码。
同样在这里:
(defun make-shiny-new-world () (setq world (make-world 'shiny)))
首先,这样的variables应该写成*world*
(与周围的*
字符),以明确它是一个全局特殊variables。 其次,应该先用DEFVAR
或DEFPARAMETER
声明。
一个典型的Lisp编译器会抱怨上面的variables是未声明的。 由于Common Lisp中不存在全局词汇variables,因此编译器必须生成用于dynamic查找的代码。 一些编译器然后说,好吧,我们假设这是一个dynamic的查找,让我们声明它是特殊的 – 因为这是我们所假设的。
defvar
引入了一个dynamicvariables,而setq
被用来赋值给dynamic或词法variables。 在调用函数的环境中查找dynamicvariables的值,而在定义函数的环境中查找词法variables的值。 下面的例子将会使不同之处变得明显:
;; dynamic variable sample > (defvar *x* 100) *X* > (defun fx () *x*) FX > (fx) 100 > (let ((*x* 500)) (fx)) ;; gets the value of *x* from the dynamic scope. 500 > (fx) ;; *x* now refers to the global binding. 100 ;; example of using a lexical variable > (let ((y 200)) (let ((fy (lambda () (format t "~a~%" y)))) (funcall fy) ;; => 200 (let ((y 500)) (funcall fy) ;; => 200, the value of lexically bound y (setq y 500) ;; => y in the current environment is modified (funcall fy)) ;; => 200, the value of lexically bound y, which was ;; unaffected by setq (setq y 500) => ;; value of the original y is modified. (funcall fy))) ;; => 500, the new value of y in fy's defining environment.
dynamicvariables对于传递一个默认值很有用。 例如,我们可以将dynamicvariables*out*
绑定到标准输出,以便它成为所有io函数的默认输出。 要覆盖这个行为,我们只需要引入一个本地绑定:
> (defun my-print (s) (format *out* "~a~%" s)) MY-PRINT > (my-print "hello") hello > (let ((*out* some-stream)) (my-print " cruel ")) ;; goes to some-stream > (my-print " world.") world
词法variables的一个常见用法是定义闭包,用状态模拟对象。 在第一个例子中, fy
绑定环境中的variablesy
有效地成为该函数的私有状态。
只有当defvar
没有被赋值时, defvar
才会赋值给variables。 所以下面的*x*
重定义不会改变原来的绑定:
> (defvar *x* 400) *X* > *x* 100
我们可以使用setq
为*x*
指定一个新的值:
> (setq *x* 400) 400 > *x* 400 > (fx) 400 > (let ((*x* 500)) (fx)) ;; setq changed the binding of *x*, but ;; its dynamic property still remains. 500 > (fx) 400
DEFVARbuild立一个新的variables。 SETQ分配给一个variables。
我用过的大多数Lisp实现都会在SETQvariables不存在的情况下发出警告。
defvar
和defparameter
都引入了全局variables。 正如肯注意到的, setq
分配给一个variables。
另外, defvar
不会破坏以前被defvar
东西。 Seibel在书中(第6章)稍后说:“实际上,即使你改变了使用variables的源代码,你也应该使用DEFVAR来定义包含你想要保留的数据的variables。
http://www.gigamonkeys.com/book/variables.html
例如,如果在简单数据库章节中有数据库的全局*db*
(defvar *db* nil)
…然后你开始在REPL上玩它 – 添加,删除东西等等 – 但是然后你改变了包含defvar格式的源文件,重新加载该文件不会消除*db*
和所有的改变你可能已经做出了…我相信setq
会和defparameter
。 更有经验的Lisper请纠正我,如果我错了。