Lisp 中的 setq 和 defvar

发布于 2024-09-26 03:59:50 字数 245 浏览 1 评论 0原文

我看到 Practical Common Lisp 使用 (defvar *db* nil)用于设置全局变量。使用 setq 来达到同样的目的不是可以吗?

使用 defvarsetq 相比有何优点/缺点?

I see that the Practical Common Lisp uses (defvar *db* nil) for setting up a global variable. Isn't it OK to use setq for the same purpose?

What are the advantages/disadvantages of using defvar vs. setq?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧伤慢歌 2024-10-03 03:59:50

引入变量的方法有多种。

DEFVAR< /a> 和 DEFPARAMETER 引入全局 动态变量。 DEFVAR 可以选择将其设置为某个值,除非它已经定义。 DEFPARAMETER 将其始终设置为提供的值。
SETQ 不引入变量。

(defparameter *number-of-processes* 10)

(defvar *world* (make-world))     ; the world is made only once.

请注意,您可能永远不想将 DEFVAR 变量命名为 xystreamlimit,...为什么?因为这些变量将被声明为特殊的并且很难撤消。特殊声明是全局的,并且该变量的所有进一步使用都将使用动态绑定。

坏:

(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, 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))

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

接下来,还有几种形式可以将变量设置为新值。 SETSETQSETF 等。 SETQSETF 可以设置词法变量和特殊(动态)变量。

可移植代码要求设置已声明的变量。标准未定义设置未声明变量的确切效果。

因此,如果您知道 Common Lisp 实现的作用,则可以

(setq world (make-new-world))

在顶层的 Read-Eval-Print-Loop 中使用。但不要在代码中使用它,因为效果不可移植。通常 SETQ 将设置变量。但某些实现也可能在不知道的情况下声明变量SPECIAL(CMU Common Lisp 默认情况下会这样做)。这几乎总是不是人们想要的。如果您知道自己在做什么,则可以将其用于休闲用途,但不能用于代码。

同样:

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

首先,此类变量应写为 *world* (带有周围的 * 字符),以明确它是全局特殊变量。其次,它之前应该使用 DEFVARDEFPARAMETER 进行声明。

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

There are several ways to introduce variables.

DEFVAR and DEFPARAMETER introduce global dynamic variables. DEFVAR optionally sets it to some value, unless it is already defined. DEFPARAMETER sets it always to the provided value.
SETQ does not introduce a variable.

(defparameter *number-of-processes* 10)

(defvar *world* (make-world))     ; the world is made only once.

Notice that you likely never want to DEFVAR variables with names like x, y, stream, limit, ... Why? Because these variables then would be declared special and its difficult to undo that. The special declaration is global and all further uses of the variable would use dynamic binding.

BAD:

(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

BETTER: Always mark special variables with * in their names!

(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

Local variables are introduced with DEFUN, LAMBDA, LET, MULTIPLE-VALUE-BIND and many others.

(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))

Now, by default the local variables in above two forms are lexical, unless they are declared SPECIAL. Then they would be dynamic variables.

Next, there are also several forms to set a variable to new values. SET, SETQ, SETF and others. SETQ and SETF can set both lexical and special (dynamic) variables.

It is required for portable code that one sets variables that are already declared. The exact effect of setting a not declared variable is undefined by the standard.

So, if you know what your Common Lisp implementation does, you can use

(setq world (make-new-world))

in the Read-Eval-Print-Loop at the toplevel. But don't use it in your code, since the effect is not portable. Typically SETQ will set the variable. But some implementation might also declare the variable SPECIAL when it doesn't know it (CMU Common Lisp does that by default). That's almost always not what one would want. Use it for casual use if you know what you do, but not for code.

Same here:

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

First, such variables should be written as *world* (with the surrounding * characters), to make clear that it is a global special variable. Second, it should have been declared with DEFVAR or DEFPARAMETER before.

A typical Lisp compiler will complain that above variable is undeclared. Since global lexical variables don't exist in Common Lisp, the compiler has to generate code for a dynamic lookup. Some compiler then say, okay, we assume that this is a dynamic lookup, let's declare it to be special - since that is what we assume anyway.

谈情不如逗狗 2024-10-03 03:59:50

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

词法变量的常见用途是定义闭包,以模拟具有状态的对象。在第一个示例中,fy 绑定环境中的变量y 实际上成为该函数的私有状态。

仅当变量尚未赋值时,defvar 才会为变量赋值。因此,以下重新定义 *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

defvar introduces a dynamic variable while setq is used to assign a value to a dynamic or lexical variable. The value of a dynamic variable is looked up in the environment that calls the function, while the value of a lexical variable is looked up in the environment where the function was defined. The following example will make the difference clear:

;; 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.

Dynamic variables are useful for passing around a default value. For instance, we can bind the dynamic variable *out* to the standard output, so that it becomes the default output of all io functions. To override this behavior, we just introduce a local binding:

> (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

A common use of lexical variables is in defining closures, to emulate objects with state. In the first example, the variable y in the binding environment of fy effectively became the private state of that function.

defvar will assign a value to a variable only if it is not already assigned. So the following re-definition of *x* will not change the original binding:

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

We can assign a new value to *x* by using 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
雨巷深深 2024-10-03 03:59:50

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

如果您 SETQ 到一个尚不存在的变量,我使用过的大多数 Lisp 实现都会发出警告。

DEFVAR establishes a new variable. SETQ assigns to a variable.

Most Lisp implementations I've used will issue a warning if you SETQ to a variable that doesn't yet exist.

昵称有卵用 2024-10-03 03:59:50

defvardefparameter 都引入了全局变量。正如 Ken 所说,setq 分配给一个变量。

此外,defvar 不会破坏之前 defvar 编辑的内容。 Seibel 在本书后面(第 6 章)中说道:“实际上,您应该使用 DEFVAR 来定义变量,这些变量将包含您想要保留的数据,即使您对使用该变量的源代码进行了更改。”

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

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

(defvar *db* nil)

...您开始在 REPL 中使用它 - 添加、删除内容等 - 但随后您进行更改包含 defvar 表单的源文件,重新加载该文件不会清除 *db* 以及您可能所做的所有更改...我相信 setq 会, defparameter 也是如此。如果我错了,请更有经验的 Lisper 纠正我。

defvar and defparameter both introduce global variables. As Ken notes, setq assigns to a variable.

In addition, defvar will not clobber something previously defvar-ed. Seibel says later in the book (Chapter 6): "Practically speaking, you should use DEFVAR to define variables that will contain data you'd want to keep even if you made a change to the source code that uses the variable."

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

For instance, if you have a global *db* for the database in the Simple Database chapter:

(defvar *db* nil)

...and you start playing with it at the REPL - adding, deleting things, etc - but then you make a change to the source file which contains that defvar form, reloading that file will not wipe out *db* and all the changes you might have made... I believe that setq will, as will defparameter. A more experienced Lisper please correct me if I'm wrong though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文