6

所以,我已经阅读
了 lisp 中的 setq 和 defvar
http://www.cs.ucf.edu/courses/cop4020/spr2006/plsetup.html
在 Lisp 中,我该如何修复“警告:假定特殊?”
在其他地方关于 setf 和 defvar 之间的区别。所以我决定尝试一下这个想法:

CL-USER> (defun foo ()
       (setf x 10)
       (print x))

; in: DEFUN FOO
;     (SETF X 10)
; ==>
;   (SETQ X 10)
; 
; caught WARNING:
;   undefined variable: X
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
FOO
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {10040F1543}>.
CL-USER> (foo)

10 
10
CL-USER> x
10

好的,我知道应该使用 setf 来更改现有变量的值,但是未定义变量警告似乎在 SBCL 中处理得很好(尽管我已经读过不同的 CL 实现可能会以不同的方式处理这个问题,因此它不是最好的办法)。

进入第二个测试:

CL-USER> (defun bar ()
       (defvar y 15)
       (print y))

; in: DEFUN BAR
;     (PRINT Y)
; 
; caught WARNING:
;   undefined variable: Y
; 
; compilation unit finished
;   Undefined variable:
;     Y
;   caught 1 WARNING condition
BAR
CL-USER> y
; Evaluation aborted on #<UNBOUND-VARIABLE Y {10045033D3}>.
CL-USER> (bar)

15 
15
CL-USER> y
15

根据链接,我将 setf 更改为 defvar 我认为应该一次创建和绑定变量。现在我的未定义变量警告被推送到 (print y) 行......这里发生了什么?

作为第二个问题,我希望函数内分配的任何变量的值在函数之外都无法访问,就像 Python 中的情况一样:

>>> def foo():
...     x = 10
...     print x
... 
>>> foo()
10
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined

我猜这与 common lisp 处理范围的方式有关,即 defvar 创建了一个“全局特殊变量”......所以我最后一次尝试了 (let ...)

CL-USER> (defun baz () (let ((z 10)) (print z)) (incf z 10) (print z))

; in: DEFUN BAZ
;     (INCF Z 10)
; --> LET* 
; ==>
;   (SETQ Z #:NEW0)
; 
; caught WARNING:
;   undefined variable: Z
; 
; compilation unit finished
;   Undefined variable:
;     Z
;   caught 1 WARNING condition

在阅读了 defvar、defparameter、setf 和 setq 之间的区别之后,这个似乎工作正常:

CL-USER> (defun apple ()
       (defparameter x 10)
       (print 10))

APPLE
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {1004436993}>.
CL-USER> (apple)

10 
10
CL-USER> x
10

重申我的问题:1) setf、defvar 和 let 到底发生了什么?
2) 有没有办法让 common lisp 像 python 示例中那样在函数内限定变量?

4

2 回答 2

6

回答2) DEFVAR定义一个变量。但它没有被执行。所以编译器不知道表单中的变量print- 在编译DEFUN表单时.. 它也在DEFUN. 因此它不在顶层。作为顶级形式,编译器会识别DEFVAR并注意到它y是一个全局特殊变量。

重申我的问题:1) setf、defvar 和 let 到底发生了什么?2) 有没有办法让 common lisp 像 python 示例中那样在函数内限定变量?

1)SETF设置一个变量值,但没有定义它。如果该变量未定义,那么 Common Lisp 标准并没有真正说明会发生什么。大多数 Common Lisp 实现都会做一些有用的事情。通常它会像变量被声明为特殊一样被执行(因此您也会收到警告)。

DEFVAR用作定义全局特殊变量的顶级形式(通常不在函数内部)。由于DEFVAR声明变量名是特殊的,因此编写一个带有星号的变量是一个非常有用的约定:*y*而不是仅仅y.

LET定义局部变量的范围。

2) Common Lisp 函数有参数列表来引入变量。除此之外,它们没有定义变量范围。如果要在函数中引入局部变量,请使用LET.

>>> def foo():
...     x = 10
...     print x

(defun foo ()
  (let ((x 10))
    (print x)))

同样:函数不提供变量的范围,因此在函数内分配变量将自动将其定义为函数局部。改为使用LET

另请注意,这LET语法糖,主要是:(let ((a 1) (b 2)) (+ a b))基本上与((lambda (a b) (+ a b)) 1 2). 它只是一个简单的函数应用程序,以不同的方式编写,以改进它以供人类读者使用。

Common Lisp 中还支持旧语法:

(defun foo (&aux (x 10))
  (print x))

上面定义了一个局部变量X,就像 aLET会做的那样。

于 2013-01-14T09:38:44.800 回答
4

我会保持简短:

1) setf、defvar 和 let 到底发生了什么?

  • defvardefparameter用于声明和设置全局特殊变量。它们仅在名称已绑定时有所不同。

  • setf用于赋值(给特殊变量、词法变量和其他——可能是自定义的——可设置的地方。)

  • let(and let*) 创建在其主体内可见的新变量绑定。它可以根据(全局或局部)声明创建词法或特殊绑定。如果没有特殊声明,将创建词法绑定。

2) 有没有办法让 common lisp 像 python 示例中那样在函数内限定变量?

将代码放在let's body 中,绑定可见:

CL-USER> (defun baz ()
           (let ((z 10))
             (print z)
             (incf z 10) ; i.e. (setf z (+ z 10))
             (print z)))
BAZ
CL-USER> (baz)

10 
20 
20
CL-USER> z
; Evaluation aborted on #<UNBOUND-VARIABLE #x186C611E>.
于 2013-01-14T09:43:05.380 回答