使用setf,defvar,let和scope分配变量

时间:2013-01-14 07:50:57

标签: variables scope common-lisp

所以,我从中读过 setq and defvar in lisp
http://www.cs.ucf.edu/courses/cop4020/spr2006/plsetup.html
In Lisp, how do I fix "Warning: Assumed Special?"
关于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,我认为应该创建并一次绑定变量。现在我的未定义变量警告被推入(打印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

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

CL-使用者&gt; (defun baz()            (让((z 10))(打印z))            (incf z 10)            (打印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

阅读What's difference between defvar, defparameter, setf and 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)有没有办法让常见的lisp来扩展函数内的变量,就像在python示例中一样?

2 个答案:

答案 0 :(得分:5)

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

  

重申我的问题:1)setf,defvar和let真正发生了什么?   2)有没有办法让常见的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,就像LET一样。

答案 1 :(得分:4)

我会简短地说:

  

1)setf,defvar和let真正发生了什么?

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

  • setf用于赋值(特殊变量,词汇变量和其他 - 可能是自定义 - 可设置的位置。)

  • let(和let*)创建在其正文中可见的新变量绑定。它可以创建词法或特殊绑定,具体取决于(全局或本地)声明。如果没有特殊声明,则会创建词法绑定。

  

2)有没有办法让常见的lisp来扩展a中的变量   函数如python示例?

将代码放在let的正文中,可以看到绑定:

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