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. 

请注意,你可能永远不想DEFVARvariables与像xystreamlimit ,…为什么? 因为这些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等等。 SETQSETF可以设置词法和特殊(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。 其次,应该先用DEFVARDEFPARAMETER声明。

一个典型的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不存在的情况下发出警告。

defvardefparameter都引入了全局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请纠正我,如果我错了。