pro*_*eek 47 lisp variables common-lisp variable-declaration assign
我看到实用的Common Lisp使用(defvar *db* nil)
设立一个全局变量.是否可以setq
用于同一目的?
使用defvar
vs. 的优点/缺点是setq
什么?
Rai*_*wig 45
有几种方法可以引入变量.
DEFVAR和DEFPARAMETER引入了全局动态变量.DEFVAR
除非已经定义,否则可以选择将其设置为某个值.DEFPARAMETER
将其始终设置为提供的值.
SETQ不引入变量.
(defparameter *number-of-processes* 10)
(defvar *world* (make-world)) ; the world is made only once.
Run Code Online (Sandbox Code Playgroud)
请注意,你可能永远都不想DEFVAR
变量有相似的名字x
,y
,stream
,limit
,...为什么?因为这些变量会被声明为特殊的,并且难以撤消.特殊声明是全局的,变量的所有进一步使用都将使用动态绑定.
坏:
(defvar x 10) ; global special variable X, naming convention violated
(defvar y 20) ; global special variable Y, naming convention violated
(defun foo ()
(+ x y)) ; refers to special variables X and y
(defun bar (x y) ; OOPS!! X and Y are special variables
; even though they are parameters of a function!
(+ (foo) x y))
(bar 5 7) ; -> 24
Run Code Online (Sandbox Code Playgroud)
更好:始终*
在他们的名字中标记特殊变量!
(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 (x y) ; Yep! X and Y are lexical variables
(+ (foo) x y))
(bar 5 7) ; -> 42
Run Code Online (Sandbox Code Playgroud)
使用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))
Run Code Online (Sandbox Code Playgroud)
现在,默认情况下,上述两种形式的局部变量都是词法,除非它们被声明为SPECIAL.然后他们将是动态变量.
接下来,还有几种形式可以将变量设置为新值. SET,SETQ,SETF等.SETQ
并且SETF
可以设置词法和特殊(动态)变量.
对于已设置已声明的变量的可移植代码,它是必需的.设置未声明的变量的确切效果是标准未定义的.
因此,如果您知道Common Lisp实现的功能,您可以使用
(setq world (make-new-world))
Run Code Online (Sandbox Code Playgroud)
在顶层的Read-Eval-Print-Loop中.但是不要在代码中使用它,因为效果不可移植.通常SETQ
会设置变量.但是一些实现也可能在它不知道时声明变量SPECIAL(默认情况下CMU Common Lisp会这样做).这几乎总是不是人们想要的.如果您知道自己在做什么,请将其用于临时用途,但不能用于代码.
同样在这里:
(defun make-shiny-new-world ()
(setq world (make-world 'shiny)))
Run Code Online (Sandbox Code Playgroud)
首先,这些变量应该写成*world*
(带有周围的*
字符),以表明它是一个全局特殊变量.其次,它应该在之前DEFVAR
或DEFPARAMETER
之前声明.
一个典型的Lisp编译器会抱怨上面的变量是未声明的.由于Common Lisp中不存在全局词法变量,因此编译器必须为动态查找生成代码.然后一些编译器说,好吧,我们假设这是一个动态查找,让我们声明它是特殊的 - 因为这是我们所假设的.
Vij*_*hew 18
defvar
引入动态变量,同时setq
用于为动态或词法变量赋值.在调用函数的环境中查找动态变量的值,而在定义函数的环境中查找词法变量的值.以下示例将明确区分:
;; 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.
Run Code Online (Sandbox Code Playgroud)
动态变量对于传递默认值很有用.例如,我们可以将动态变量绑定*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
Run Code Online (Sandbox Code Playgroud)
词法变量的常见用法是定义闭包,模拟具有状态的对象.在第一个例子中,y
绑定环境中的变量fy
实际上成为该函数的私有状态.
defvar
只有在尚未分配变量的情况下才会为变量赋值.所以下面的重新定义*x*
不会改变原来的绑定:
> (defvar *x* 400)
*X*
> *x*
100
Run Code Online (Sandbox Code Playgroud)
我们可以在新的值赋给*x*
使用setq
:
> (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
Run Code Online (Sandbox Code Playgroud)
defvar
并defparameter
引入全局变量.正如Ken所说,setq
分配给一个变量.
此外,defvar
不会破坏先前的东西defvar
.Seibel在本书后面(第6章)中说:"实际上,你应该使用DEFVAR来定义包含你想要保留的数据的变量,即使你对使用变量的源代码进行了更改."
http://www.gigamonkeys.com/book/variables.html
例如,如果您*db*
在Simple Database章节中拥有数据库的全局:
(defvar *db* nil)
Run Code Online (Sandbox Code Playgroud)
...然后你开始在REPL上玩它 - 添加,删除东西等 - 然后你对包含该defvar表单的源文件进行了更改,重新加载该文件不会消失*db*
以及你可能拥有的所有更改制作......我相信setq
会那样defparameter
.如果我错了,请更有经验的Lisper请纠正我.