代码之家  ›  专栏  ›  技术社区  ›  prosseek

Lisp中的setq和defvar

  •  45
  • prosseek  · 技术社区  · 14 年前

    Practical Common Lisp 使用 (defvar *db* nil) 全局变量 . 用这个不行吗 setq 为了同样的目的?

    defvar 与。 setq公司

    4 回复  |  直到 7 年前
        1
  •  52
  •   Rainer Joswig Michael Fox    8 年前

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

    DEFVAR DEFPARAMETER 介绍 全球的 DEFVAR 可以选择将其设置为某个值,除非已经定义了该值。 DEFPARAMETER 将其始终设置为提供的值。 SETQ 不引入变量。

    (defparameter *number-of-processes* 10)
    
    (defvar *world* (make-world))     ; the world is made only once.
    

    注意,你可能永远都不想 名称如下的变量 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
    

    更好:总是用 *

    (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
    

    局部变量用 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))
    

    现在,在默认情况下,上述两种形式的局部变量是词法的,除非它们是声明的 . 那么它们就是动态变量。

    接下来,还有几个表单可以将变量设置为新值。 SET , SETQ公司 , SETF 和其他人。 SETQ SETF 可以设置词法和特殊(动态)变量。

    可移植代码需要设置已经声明的变量。设置一个未声明的变量的确切效果没有被标准定义。

    因此,如果您知道常见的Lisp实现的功能,那么可以使用

    (setq world (make-new-world))
    

    在顶层。但是不要在代码中使用它,因为它的效果是不可移植的。典型的 将设置变量。但是一些实现也可能声明变量 特别 当它不知道的时候(CMU Common Lisp默认会这样做)。这几乎总是不是人们想要的。如果你知道你在做什么的话,就把它作为一种临时的用途,而不是代码。

    彼此彼此:

    (defun make-shiny-new-world ()
      (setq world (make-world 'shiny)))
    

    首先,这些变量应该写成 *world* (与周围 字符),以明确它是一个全局特殊变量。第二,它本应与 以前。

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

        2
  •  22
  •   Vijay Mathew Chor-ming Lung    14 年前

    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.
    

    动态变量对于传递默认值很有用。例如,我们可以绑定动态变量 *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
    

    词法变量的一个常见用法是定义闭包,用状态模拟对象。在第一个示例中,变量 y fy

    除霜 *x* 不会更改原始绑定:

    > (defvar *x* 400)
    *X*
    > *x*
    100
    

    我们可以给 *十* 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
    
        3
  •  9
  •   Ken    14 年前

    DEFVAR 建立新变量。 SETQ

    我使用过的大多数Lisp实现都会在设置一个不存在的变量时发出警告。

        4
  •  8
  •   okonomichiyaki    14 年前

    defvar defparameter 两者都引入了全局变量。正如肯所说, setq

    此外, 不会击打以前的东西 除霜 -塞贝尔在书的后面(第6章)说:“实际上,你应该使用DEFVAR来定义变量,这些变量将包含你想要保留的数据,即使你对使用该变量的源代码做了更改。”

    http://www.gigamonkeys.com/book/variables.html

    例如,如果您有一个全局 *db* 对于“简单数据库”一章中的数据库:

    (defvar *db* nil)
    

    *分贝* def参数