Lisp中的setq和defvar

pro*_*eek 47 lisp variables common-lisp variable-declaration assign

我看到实用的Common Lisp使用(defvar *db* nil)设立一个全局变量.是否可以setq用于同一目的?

使用defvarvs. 的优点/缺点是setq什么?

Rai*_*wig 45

有几种方法可以引入变量.

DEFVARDEFPARAMETER引入了全局动态变量.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* (带有周围的*字符),以表明它是一个全局特殊变量.其次,它应该在之前DEFVARDEFPARAMETER之前声明.

一个典型的Lisp编译器会抱怨上面的变量是未声明的.由于Common Lisp中不存在全局词法变量,因此编译器必须为动态查找生成代码.然后一些编译器说,好吧,我们假设这是一个动态查找,让我们声明它是特殊的 - 因为这是我们所假设的.

  • @ian这就是为什么许多人使用"耳套"(也就是说,他们从不使用`(defvar x foo)`,他们使用`(defvar*x*foo)`的原因之一,这样你就不太可能一个失误. (3认同)
  • 我很惊讶,一旦你用defvar声明变量是全局的,你就无法使用相同的名称创建一个局部变量,所以(let((x 1))x)如果x是x,则可能产生意外的结果由defvar宣布. (2认同)

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)

  • 不幸的是这是错的.a(setq y 200)对未声明/定义的变量的确切影响是未定义的.Common Lisp也没有全局词汇变量.SETQ设置变量.而已.动态变量或词法变量,取决于提供的变量.LET绑定.SETQ集. (5认同)

Ken*_*Ken 8

DEFVAR建立了一个新变量. SETQ分配给变量.

如果您将SETQ设置为尚不存在的变量,我使用的大多数Lisp实现都会发出警告.


mic*_*kig 8

defvardefparameter引入全局变量.正如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请纠正我.